Bỏ qua tới nội dung
WordPress· ·10 phút đọc

Đăng ký custom taxonomy WordPress bằng register_taxonomy

Vũ Đức Minh
Đăng ký custom taxonomy WordPress bằng register_taxonomy
Cỡ chữ

Khi một website có nhiều nội dung cùng loại, bạn cần một trục để gom nhóm và lọc chúng. Danh mục bài viết mặc định nhanh chóng trở nên chật chội. Taxonomy tuỳ biến giải quyết đúng việc đó: tạo ra một hệ thống phân loại riêng, tách bạch với category sẵn có, và gắn thẳng vào kiểu nội dung của bạn.

Taxonomy là gì và khác custom post type ra sao

Trong WordPress, taxonomy (hệ phân loại — cách nhóm nội dung) là cơ chế để gom các bản ghi lại với nhau. Bản thân WordPress đã có hai taxonomy mặc định: category (danh mục, phân cấp) và post_tag (thẻ, phẳng).

Cần phân biệt rạch ròi hai khái niệm hay bị nhầm:

  • Custom post type (CPT — kiểu nội dung tuỳ biến) là bản thân nội dung: một sản phẩm, một dự án, một sự kiện. Mỗi bản ghi là một trang riêng.
  • Custom taxonomynhãn để phân loại những nội dung đó: “thương hiệu”, “ngành nghề”, “khu vực”. Mỗi nhãn (gọi là term — thuật ngữ phân loại) gom nhiều bản ghi lại.

Ví dụ dễ hình dung: nếu “Sản phẩm” là một CPT, thì “Thương hiệu” và “Màu sắc” là các taxonomy gắn vào nó. Bạn không tạo một CPT cho từng thương hiệu — bạn tạo các term trong taxonomy “Thương hiệu”.

Phân biệt taxonomy và custom post type trong WordPress
Custom post type là loại nội dung; taxonomy là cách phân nhóm nội dung đó.

Đăng ký taxonomy cơ bản với register_taxonomy

Hàm chuẩn để tạo taxonomy là register_taxonomy(), gọi trong hook init. Theo tài liệu chính thức của WordPress, tuyệt đối không gọi hàm này trước hook init, nếu không taxonomy có thể không đăng ký đúng. Đặt code trong một plugin nhỏ là cách bền nhất, để khi đổi giao diện thì dữ liệu phân loại không biến mất.

function w22_register_thuong_hieu_taxonomy() {
    $labels = array(
        'name'          => _x( 'Thương hiệu', 'taxonomy general name', 'w22' ),
        'singular_name' => _x( 'Thương hiệu', 'taxonomy singular name', 'w22' ),
        'menu_name'     => __( 'Thương hiệu', 'w22' ),
        'all_items'     => __( 'Tất cả thương hiệu', 'w22' ),
        'add_new_item'  => __( 'Thêm thương hiệu', 'w22' ),
    );

    $args = array(
        'labels'            => $labels,
        'hierarchical'      => true,
        'public'            => true,
        'show_admin_column' => true,
        'show_in_rest'      => true,
        'rewrite'           => array( 'slug' => 'thuong-hieu' ),
    );

    register_taxonomy( 'w22_thuong_hieu', array( 'product' ), $args );
}
add_action( 'init', 'w22_register_thuong_hieu_taxonomy' );

Vài điểm quan trọng trong đoạn trên:

  • Tham số đầu là tên taxonomy (tối đa 32 ký tự, chỉ chữ thường, gạch dưới). Nên thêm tiền tố riêng (ở đây là w22_) để tránh đụng tên với plugin khác.
  • Tham số thứ hai là object_type — kiểu nội dung mà taxonomy gắn vào. Truyền mảng nếu muốn gắn nhiều kiểu cùng lúc.
  • show_admin_column => true hiện thêm một cột phân loại ngay trên màn hình danh sách bản ghi, rất tiện khi quản trị.

Hierarchical hay flat — chọn đúng kiểu

Quyết định lớn nhất nằm ở tham số hierarchical. Mặc định của nó là false.

Đặc điểmhierarchical = true (như category)hierarchical = false (như tag)
Quan hệ cha–conCó, term lồng nhiều cấpKhông, danh sách phẳng
Giao diện nhập liệuÔ tick (checkbox)Ô gõ tự do, ngăn cách dấu phẩy
Hợp vớiNgành nghề, khu vực, danh mục sản phẩmTừ khoá, đặc điểm, tag mô tả

Để tạo một taxonomy phẳng kiểu thẻ, chỉ cần đổi đúng một dòng:

$args = array(
    'labels'       => $labels,
    'hierarchical' => false,
    'public'       => true,
    'show_in_rest' => true,
    'rewrite'      => array( 'slug' => 'tinh-nang' ),
);

register_taxonomy( 'w22_tinh_nang', array( 'product' ), $args );

Nguyên tắc thực tế: nếu các nhãn cần lồng vào nhau (Miền Bắc > Hà Nội), chọn phân cấp. Nếu chỉ là một rổ từ khoá ngang hàng, chọn phẳng.

show_in_rest — điều kiện để chạy trong block editor

Đây là điểm dễ vấp nhất. Nếu thiếu show_in_rest => true, taxonomy sẽ không hiện trong block editor (trình soạn thảo khối — Gutenberg). Người dùng mở bài ra mà không thấy ô chọn thương hiệu đâu cả, dù taxonomy đã đăng ký đúng. Tài liệu WordPress nêu rõ: bật cờ này để taxonomy có mặt trong REST API và sẵn sàng cho block editor.

Đây cũng là một khác biệt so với việc gắn taxonomy vào một custom post type tạo bằng code: ở CPT bạn lo cờ show_in_rest cho cả màn hình soạn thảo, còn ở taxonomy nó quyết định riêng panel phân loại bên thanh bên phải. Cả hai phải cùng bật thì luồng nhập liệu mới mượt.

Gắn taxonomy vào CPT một cách tách rời

Bạn có thể khai báo taxonomies ngay khi đăng ký post type, nhưng cách linh hoạt hơn là gắn riêng bằng register_taxonomy_for_object_type(). Cách này cho phép gắn taxonomy vào một kiểu nội dung do plugin khác tạo ra, mà không phải sửa code của plugin đó:

add_action( 'init', function () {
    register_taxonomy_for_object_type( 'w22_thuong_hieu', 'product' );
}, 20 );

Đặt độ ưu tiên 20 để chắc chắn cả post type lẫn taxonomy đã đăng ký xong trước khi nối chúng lại.

Thêm term meta cho mỗi nhãn phân loại

Term meta (dữ liệu phụ của term — trường thông tin gắn vào từng nhãn) cho phép mỗi term mang thêm dữ liệu riêng: một mã màu, một logo, một đường dẫn. Đăng ký bằng register_term_meta(), cũng trong hook init:

add_action( 'init', function () {
    register_term_meta( 'w22_thuong_hieu', 'w22_website', array(
        'type'              => 'string',
        'single'            => true,
        'show_in_rest'      => true,
        'sanitize_callback' => 'esc_url_raw',
    ) );
} );

Khai báo này cho WordPress biết kiểu dữ liệu, hàm làm sạch (sanitize_callback — lọc giá trị trước khi lưu), và việc có lộ ra REST API hay không. Để người quản trị nhập được giá trị, thêm một ô vào màn hình sửa term qua hook {$taxonomy}_edit_form_fields và lưu lại bằng edited_{$taxonomy}:

add_action( 'w22_thuong_hieu_edit_form_fields', function ( $term ) {
    $value = get_term_meta( $term->term_id, 'w22_website', true );
    ?>
    <tr class="form-field">
        <th><label for="w22_website">Website</label></th>
        <td><input name="w22_website" id="w22_website" type="url"
             value="<?php echo esc_attr( $value ); ?>" /></td>
    </tr>
    <?php
} );

add_action( 'edited_w22_thuong_hieu', function ( $term_id ) {
    if ( isset( $_POST['w22_website'] ) ) {
        update_term_meta( $term_id, 'w22_website',
            esc_url_raw( wp_unslash( $_POST['w22_website'] ) ) );
    }
} );

Trong thực tế tự code plugin web22-core, Web22 thường gắn term meta để mỗi nhãn mang theo logo hoặc đường dẫn, rồi gọi get_term_meta() ra ngoài giao diện. Nhờ show_in_rest => true, giá trị này cũng hiện trong dữ liệu API của term.

Chọn taxonomy hierarchical hay flat khi đăng ký register_taxonomy
Hierarchical hợp danh mục có cấp cha-con; flat hợp thẻ tự do ngang hàng.

Lưu ý sau khi đăng ký

Sau khi thêm taxonomy mới, đường dẫn đẹp (permalink) chưa hoạt động ngay. Vào Cài đặt > Đường dẫn tĩnh và lưu lại một lần để WordPress nạp lại quy tắc rewrite. Nếu trang lưu trữ term báo lỗi 404, gần như chắc chắn là do bước này bị bỏ qua.

Câu hỏi thường gặp

Một taxonomy có gắn được vào nhiều post type không?

Được. Truyền một mảng các post type vào tham số thứ hai của register_taxonomy, hoặc dùng register_taxonomy_for_object_type để gắn thêm sau.

Đổi từ hierarchical sang flat sau này có mất dữ liệu không?

Các term đã tạo vẫn còn, nhưng quan hệ cha–con sẽ không còn ý nghĩa ở chế độ phẳng. Nên chốt kiểu phân loại từ đầu để tránh phải dọn dẹp về sau.

Vì sao taxonomy không hiện trong trình soạn thảo?

Khả năng cao là thiếu show_in_rest => true. Block editor đọc taxonomy qua REST API, nên cờ này là bắt buộc.

Nếu bạn cần một hệ phân loại phức tạp gắn vào dữ liệu riêng và muốn nó bền vững qua các lần đổi giao diện, dịch vụ lập trình WordPress theo yêu cầu của Web22 có thể dựng phần khung dữ liệu này gọn gàng trong một plugin riêng.

Đọc tiếp

Bài viết
cùng chủ đề.

Tất cả bài viết