Custom Post Type (CPT) là cơ chế mở rộng cốt lõi của WordPress, cho phép bạn tạo các loại nội dung riêng biệt với cấu trúc và giao diện quản lý độc lập. Thay vì nhồi nhét mọi thứ vào bài viết hoặc trang, CPT giúp tổ chức dữ liệu theo logic nghiệp vụ thực tế.
Bài viết này giải thích CPT WordPress là gì, phân biệt với taxonomy, hướng dẫn tư duy thiết kế cấu trúc dữ liệu, cách tạo custom post type kết hợp ACF, và các lưu ý về permalink, archive, schema cùng SEO trước khi triển khai.
CPT WordPress là gì và tại sao cần dùng
WordPress mặc định có hai loại nội dung chính: Post (bài viết) và Page (trang). Khi website phát triển, việc dùng Post cho mọi thứ tạo ra hỗn loạn: dự án portfolio, đội ngũ nhân sự, địa điểm chi nhánh, tài liệu tải về… đều chung một mớ bài viết không phân biệt.
CPT giải quyết vấn đề này bằng cách tạo loại nội dung riêng biệt với:
- Menu quản lý độc lập trong admin
- Cấu trúc dữ liệu riêng qua custom field
- Template hiển thị chuyên biệt
- URL structure (permalink) riêng biệt
- Query và filter riêng trong database
Các ví dụ CPT phổ biến trong thực tế
| Loại website | CPT phù hợp | Mục đích sử dụng |
|---|---|---|
| Agency creative | Dự án (Projects) | Showcase portfolio với hình ảnh, khách hàng, thời gian thực hiện |
| Công ty dịch vụ | Dịch vụ (Services) | Mô tả chi tiết từng gói dịch vụ, bảng giá, quy trình |
| Doanh nghiệp | Đội ngũ (Team) | Hồ sơ nhân sự, chức vụ, chuyên môn, liên hệ |
| Chuỗi cửa hàng | Địa điểm (Locations) | Thông tin chi nhánh, bản đồ, giờ mở cửa, số điện thoại |
| Giáo dục/Training | Khóa học (Courses) | Nội dung giảng dạy, giảng viên, lịch khai giảng, học phí |
| Bất động sản | Bất động sản (Properties) | Diện tích, giá, vị trí, tiện ích, hình ảnh 360 |
| Thư viện/Tài nguyên | Tài liệu (Documents) | File tải về, phiên bản, ngày cập nhật, phân quyền |
Phân biệt Custom Post Type với Taxonomy
Đây là điểm gây nhầm lẫn phổ biến nhất khi làm việc với CPT WordPress.
Custom Post Type là loại nội dung – nó định nghĩa cái gì được lưu trữ. Mỗi item trong CPT là một bản ghi độc lập có URL riêng, có thể có nội dung dài, hình ảnh đại diện, custom field.
Taxonomy (phân loại) là cách nhóm nội dung – nó định nghĩa cách phân loại các item đó. Taxonomy không có URL chi tiết riêng, chỉ có archive page liệt kê các item thuộc nó.
Ví dụ thực tế với website bất động sản:
- CPT: Bất động sản – mỗi căn nhà là một post riêng với đầy đủ thông tin, có URL
/bat-dong-san/can-ho-sunshine-city/ - Taxonomy: Loại hình (chung cư, nhà phố, biệt thự) – dùng để lọc và nhóm các bất động sản
- Taxonomy: Khu vực (Quận 1, Quận 2, Bình Thạnh) – dùng để phân loại theo địa lý
Khi nào dùng CPT, khi nào dùng Taxonomy?
| Tình huống | Giải pháp | Lý do |
|---|---|---|
| Cần trang chi tiết riêng cho mỗi item | CPT | Taxonomy không có single template |
| Item có nhiều field riêng (giá, diện tích, vị trí) | CPT | Taxonomy chỉ có name, slug, description |
| Cần lưu lịch sử chỉnh sửa, revision | CPT | Taxonomy không có revision |
| Chỉ cần nhóm/lọc nội dung | Taxonomy | Đơn giản, nhẹ, query nhanh |
| Item là giá trị lặp lại, ít thay đổi | Taxonomy | Ví dụ: màu sắc, kích cỡ, tags |
Tư duy thiết kế cấu trúc dữ liệu CPT
Trước khi tạo custom post type, cần trả lời 5 câu hỏi để tránh refactor sau này:
1. Entity chính là gì?
Xác định đối tượng nghiệp vụ cốt lõi. Ví dụ: với website khóa học online, entity chính là Khóa học, không phải Bài giảng hay Giảng viên.
2. Mối quan hệ giữa các entity?
Vẽ sơ đồ quan hệ:
- Một Khóa học có nhiều Bài giảng (1-n)
- Một Giảng viên dạy nhiều Khóa học (1-n)
- Một Học viên đăng ký nhiều Khóa học (n-n)
Quan hệ 1-n thường dùng post parent hoặc custom field reference. Quan hệ n-n cần custom table hoặc plugin như Posts 2 Posts.
3. Cần những field gì?
Liệt kê tất cả thông tin cần lưu, phân loại:
- Core field: title, content, featured image (WordPress có sẵn)
- Custom field: giá, thời lượng, cấp độ, ngày khai giảng (cần ACF)
- Taxonomy: danh mục, tags, địa điểm (dùng để lọc)
4. Cách hiển thị ở frontend?
Xác định:
- Archive page: grid hay list? có filter sidebar không?
- Single template: layout 1 cột hay 2 cột? có related items không?
- Có cần search riêng không?
5. Tích hợp với hệ thống nào?
- Membership: chỉ member xem được nội dung?
- E-commerce: bán khóa học qua WooCommerce?
- Booking: đặt lịch học trực tiếp?
Cách tạo Custom Post Type: Code vs Plugin
Cách 1: Dùng code (functions.php hoặc plugin tùy chỉnh)
Ưu điểm: Kiểm soát hoàn toàn, nhẹ, không phụ thuộc plugin.
function tao_cpt_du_an() {
$labels = array(
'name' => 'Dự án',
'singular_name' => 'Dự án',
'menu_name' => 'Dự án',
'add_new' => 'Thêm dự án mới',
'add_new_item' => 'Thêm dự án mới',
'edit_item' => 'Sửa dự án',
);
$args = array(
'labels' => $labels,
'public' => true,
'has_archive' => true,
'rewrite' => array('slug' => 'du-an'),
'supports' => array('title', 'editor', 'thumbnail', 'excerpt'),
'menu_icon' => 'dashicons-portfolio',
'show_in_rest' => true, // Hỗ trợ Gutenberg
);
register_post_type('du_an', $args);
}
add_action('init', 'tao_cpt_du_an');
Các tham số quan trọng cần lưu ý:
| Tham số | Ý nghĩa | Gợi ý |
|---|---|---|
rewrite['slug'] |
URL prefix | Ngắn gọn, không dấu, không số: du-an thay vì danh-sach-du-an |
has_archive |
Trang danh sách | true nếu cần archive page, false nếu chỉ dùng shortcode |
show_in_rest |
Gutenberg + REST API | Bắt buộc true nếu dùng block editor hoặc headless |
supports |
Các tính năng enabled | Chỉ bật cần thiết: title, thumbnail thường đủ với ACF |
menu_position |
Vị trí menu | 5: sau Posts, 20: sau Pages, 26: sau Comments |
Cách 2: Dùng plugin
CPT UI: Miễn phí, đơn giản, export code được. Phù hợp prototype nhanh.
Pods: Mạnh mẽ hơn, hỗ trợ custom table, relationship phức tạp.
ACF Pro: Từ version 6.0, ACF có Post Type Builder tích hợp, tạo CPT và field trong một giao diện.
Lựa chọn khi nào:
- Website đơn giản, ít CPT: Dùng CPT UI hoặc ACF Pro
- Nhiều CPT phức tạp, cần custom table: Pods hoặc code thuần
- Dự án lớn, team nhiều người: Code thuần, version control bằng Git
Kết hợp Custom Post Type với ACF và Builder
Đây là workflow hiệu đại nhất để xây dựng website WordPress có cấu trúc.
Bước 1: Thiết kế field group với ACF
Ví dụ với CPT Dự án:
| Field | Type | Điều kiện | Ghi chú |
|---|---|---|---|
client |
Text | – | Tên khách hàng |
project_year |
Number | Min 2000, Max 2030 | Năm thực hiện |
project_type |
Select | Options: Website, App, Branding | Loại dự án |
project_url |
URL | – | Link xem live |
gallery |
Gallery | – | Nhiều hình ảnh |
testimonial |
Group | Chứa text, author, role | Đánh giá khách hàng |
Rule location: Post Type = Dự án
Bước 2: Tạo template động với builder
Các builder phổ biến hỗ trợ dynamic content:
| Builder | Cách lấy ACF field | Ưu điểm |
|---|---|---|
| Elementor Pro | Dynamic Tags > ACF Field | Dễ dùng, nhiều widget |
| Bricks | {acf:field_name} | Nhanh, nhẹ, code-friendly |
| Oxygen | Easy Posts + custom PHP | Linh hoạt cao |
| Breakdance | Dynamic Data > ACF | Modern UI, performance tốt |
| Gutenberg + ACF Blocks | get_field() trong block |
Native, không phụ thuộc builder |
Ví dụ với Bricks:
// Trong template single-du_an.php
<h1>Custom Post Type là gì? Cách tổ chức dữ liệu WordPress bằng CPT</h1>
<p>Khách hàng: {acf:client}</p>
<p>Năm: {acf:project_year}</p>
// Conditional visibility
Show if: {acf:project_type} == 'Website'
Bước 3: Query và filter nâng cao
Dùng WP_Query hoặc builder’s query để lọc CPT:
$args = array(
'post_type' => 'du_an',
'posts_per_page' => 6,
'meta_query' => array(
array(
'key' => 'project_type',
'value' => 'Website',
'compare' => '='
),
array(
'key' => 'project_year',
'value' => 2022,
'compare' => '>='
)
),
'tax_query' => array(
array(
'taxonomy' => 'danh_muc_du_an',
'field' => 'slug',
'terms' => 'thuong-mai-dien-tu'
)
)
);
$query = new WP_Query($args);
Lưu ý quan trọng về Permalink, Archive và Schema
Tối ưu Permalink structure
Cấu trúc URL ảnh hưởng trực tiếp SEO và UX:
| Cấu trúc | Ví dụ | Khi nào dùng |
|---|---|---|
| Đơn giản | /du-an/ten-du-an/ |
CPT độc lập, không phân cấp |
| Có taxonomy | /du-an/thuong-mai-dien-tu/ten-du-an/ |
Cần thể hiện phân loại trong URL |
| Theo ngày | /du-an/2024/ten-du-an/ |
Tin tức, sự kiện có tính thời gian |
| Theo location | /chi-nhanh/ha-noi/ten-cua-hang/ |
Chuỗi cửa hàng, địa điểm |
Lưu ý: Thay đổi permalink sau khi có content sẽ gây lỗi 404. Cần redirect 301 hoặc quyết định ngay từ đầu.
Cấu hình Archive page
Mặc định, CPT có archive tại https://domain.com/du-an/. Tùy chỉnh:
// Thay đổi số post trên archive
function du_an_posts_per_page($query) {
if (!is_admin() && $query->is_main_query() && is_post_type_archive('du_an')) {
$query->set('posts_per_page', 12);
$query->set('orderby', 'meta_value_num');
$query->set('meta_key', 'project_year');
$query->set('order', 'DESC');
}
}
add_action('pre_get_posts', 'du_an_posts_per_page');
Tích hợp Schema markup
CPT là cơ hội tốt để thêm Structured Data, giúp Google hiểu rõ nội dung:
| Loại CPT | Schema phù hợp | Plugin hỗ trợ |
|---|---|---|
| Khóa học | Course | Schema Pro, Rank Math |
| Sự kiện | Event | The Events Calendar |
| Địa điểng | LocalBusiness | Local SEO by Rank Math |
| Sản phẩm dịch vụ | Service | Schema Pro |
| Đánh giá | Review | WP Review Pro |
Ví dụ thêm Course schema cho CPT Khóa học:
// Trong single-khoa_hoc.php
$schema = array(
'@context' => 'https://schema.org',
'@type' => 'Course',
'name' => get_the_title(),
'description' => get_the_excerpt(),
'provider' => array(
'@type' => 'Organization',
'name' => get_field('provider_name'),
'sameAs'=> get_field('provider_url')
),
'hasCourseInstance' => array(
'@type' => 'CourseInstance',
'courseMode' => 'online',
'courseWorkload' => 'PT' . get_field('duration') . 'H',
'instructor' => array(
'@type' => 'Person',
'name' => get_field('instructor_name')
)
)
);
echo '<script type="application/ld+json">' . wp_json_encode($schema) . '</script>';
Checklist SEO trước khi triển khai CPT
| Hạng mục | Kiểm tra | Công cụ |
|---|---|---|
| URL structure | Ngắn gọn, mô tả, không thay đổi sau này | Permalink settings |
| Meta template | Title, description động theo field | Rank Math, Yoast SEO |
| XML Sitemap | CPT và taxonomy được include | Rank Math > Sitemap |
| Breadcrumb | Hiển thị đúng cấp trúc: Home > CPT Archive > Taxonomy > Single | Breadcrumb NavXT |
| Internal link | Archive page được link từ menu chính | Manual check |
| Schema | Đúng loại, đầy đủ thuộc tính bắt buộc | Google Rich Results Test |
| Mobile UX | Archive và single đều responsive | Google Mobile-Friendly Test |
| Page speed | Query CPT không chậm | Query Monitor, GTmetrix |
Cấu hình SEO plugin cho CPT
Với Rank Math:
- Titles & Meta > Post Types > [Tên CPT]
- Title template:
%title% | Dự án %project_year% - %sitename% - Description: Lấy từ excerpt hoặc custom field tóm tắt
- Sitemap Settings > Post Types
- Include CPT, set priority 0.8
- Exclude nếu CPT chỉ dùng cho display (không cần index)
- Breadcrumbs
- CPT archive là level 1
- Taxonomy là level 2 (nếu có)
Các lỗi thường gặp khi dùng CPT
| Lỗi | Nguyên nhân | Cách fix |
|---|---|---|
| 404 khi truy cập single CPT | Chưa flush permalink | Settings > Permalinks > Save |
| CPT không hiển thị trong search | 'exclude_from_search' => true |
Đổi thành false |
| Archive 404 | 'has_archive' => false |
Đổi thành true hoặc string |
| ACF field không lưu | Field name trùng với reserved word | Đổi tên field: name → client_name |
| Query chậm | Nhiều meta_query phức tạp | Dùng tax_query thay thế, hoặc custom table |
| Conflict với plugin | CPT slug trùng với plugin khác | Đổi slug: course → khoa_hoc |
Những câu hỏi thường gặp về CPT
CPT có làm chậm website không?
Không đáng kể nếu thiết kế đúng. CPT sử dụng cùng database table (wp_posts) với post thường. Chỉ chậm khi query nhiều meta field không có index hoặc join phức tạp. Giải pháp: dùng taxonomy thay vì meta cho dữ liệu lọc, hoặc custom table với Pods.
Nên dùng CPT hay Page với template riêng?
Dùng CPT khi: số lượng item nhiều, có cấu trúc lặp lại, cần archive, filter, hoặc quản lý bởi người không biết code. Dùng Page template khi: nội dung độc nhất, layout hoàn toàn tùy biến, không cần query hay lọp.
CPT có hỗ trợ đa ngôn ngữ không?
Có, qua plugin như WPML, Polylang, hoặc TranslatePress. Mỗi ngôn ngữ là một post riêng, liên kết với nhau. Lưu ý: permalink của mỗi ngôn ngữ nên có prefix (/en/projects/, /vi/du-an/) hoặc domain riêng.
Có thể chuyển CPT thành post thường không?
Có, dùng plugin Post Type Switcher hoặc SQL query. Tuy nhiên, custom field và taxonomy cần xử lý thủ công. Nên backup trước khi chuyển đổi.
Nên tạo bao nhiêu CPT cho một website?
Không có giới hạn cứng, nhưng thực tế 3-7 CPT là hợp lý. Quá nhiều CPT gây rối admin menu, khó quản lý. Nếu có nhiều entity, cân nhắc gộp hoặc dùng custom taxonomy để phân loại.
Custom Post Type là công cụ mạnh mẽ để WordPress vượt qua giới hạn blog, trở thành hệ thống quản lý nội dung linh hoạt. Thành công với CPT đến từ việc thiết kế cấu trúc dữ liệu rõ ràng ngay từ đầu, kết hợp ACF để mở rộng thông tin, và tối ưu SEO trước khi triển khai. Nếu cần hỗ trợ thiết kế architecture cho dự án WordPress phức tạp, hãy liên hệ để trao đổi chi tiết.




