Code tạo Custom Post Type trong WordPress

Custom Post Type (CPT) là một trong những tính năng mạnh mẽ nhất của WordPress, cho phép bạn tạo ra các loại nội dung tùy chỉnh ngoài Post và Page mặc định.

Thay vì ép buộc mọi nội dung vào định dạng blog truyền thống, CPT giúp bạn tổ chức và quản lý nội dung theo cách phù hợp với dự án cụ thể.

Từ ngày có AI chắc các bài viết về share có lẽ không còn phù hợp vì chỉ cần dán yêu cầu vào AI thì sẽ có code copy paste nhưng bài này là note để dùng nhanh chóng và hy vọng người mới chưa thuần thục AI có thể đọc và thực hành được. Nếu bạn muốn dùng plugin để tạo custom post type thì có thể cài plugin Custom Post Type UI và thao tác trong wp-admin vẫn được nhé.

Tại sao cần sử dụng Custom Post Type?

1. Tổ chức nội dung tốt hơn

  • Phân loại rõ ràng: Mỗi loại nội dung có không gian riêng trong admin
  • Quản lý dễ dàng: Không bị trộn lẫn giữa blog posts và nội dung khác
  • SEO friendly: URL structure rõ ràng và có ý nghĩa

2. Tính linh hoạt cao

  • Custom fields: Thêm metadata phù hợp với từng loại nội dung
  • Taxonomies riêng: Tạo categories và tags chuyên biệt
  • Template tùy chỉnh: Thiết kế giao diện phù hợp

3. Ví dụ thực tế

  • Portfolio: Showcase các dự án đã thực hiện
  • Products: Catalogue sản phẩm cho e-commerce
  • Events: Quản lý sự kiện và lịch trình
  • Team Members: Giới thiệu thành viên đội ngũ
  • Testimonials: Thu thập và hiển thị feedback

Cách tạo Custom Post Type

1. Phương pháp cơ bản với register_post_type()

<?php
function create_portfolio_post_type() {
    $labels = array(
        'name'               => 'Portfolio',
        'singular_name'      => 'Portfolio Item',
        'menu_name'          => 'Portfolio',
        'add_new'            => 'Thêm mới',
        'add_new_item'       => 'Thêm Portfolio mới',
        'edit_item'          => 'Chỉnh sửa Portfolio',
        'new_item'           => 'Portfolio mới',
        'view_item'          => 'Xem Portfolio',
        'search_items'       => 'Tìm kiếm Portfolio',
        'not_found'          => 'Không tìm thấy Portfolio nào',
        'not_found_in_trash' => 'Thùng rác trống'
    );

    $args = array(
        'labels'             => $labels,
        'public'             => true,
        'publicly_queryable' => true,
        'show_ui'            => true,
        'show_in_menu'       => true,
        'show_in_rest'       => true, // Hỗ trợ Gutenberg
        'query_var'          => true,
        'rewrite'            => array('slug' => 'portfolio'),
        'capability_type'    => 'post',
        'has_archive'        => true,
        'hierarchical'       => false,
        'menu_position'      => 20,
        'menu_icon'          => 'dashicons-portfolio',
        'supports'           => array(
            'title',
            'editor',
            'thumbnail',
            'excerpt',
            'custom-fields'
        )
    );

    register_post_type('portfolio', $args);
}
add_action('init', 'create_portfolio_post_type');
?>

2. Thêm Custom Taxonomies

<?php
function create_portfolio_taxonomies() {
    // Portfolio Categories - Hierarchical (như Categories)
    register_taxonomy('portfolio_category', 'portfolio', array(
        'hierarchical' => true,
        'labels' => array(
            'name'              => 'Danh mục Portfolio',
            'singular_name'     => 'Danh mục',
            'search_items'      => 'Tìm danh mục',
            'all_items'         => 'Tất cả danh mục',
            'edit_item'         => 'Chỉnh sửa danh mục',
            'add_new_item'      => 'Thêm danh mục mới'
        ),
        'show_ui'           => true,
        'show_in_rest'      => true,
        'show_admin_column' => true,
        'rewrite'           => array('slug' => 'portfolio-category')
    ));

    // Portfolio Tags - Non-hierarchical (như Tags)
    register_taxonomy('portfolio_tag', 'portfolio', array(
        'hierarchical' => false,
        'labels' => array(
            'name'                       => 'Thẻ Portfolio',
            'singular_name'              => 'Thẻ',
            'search_items'               => 'Tìm thẻ',
            'popular_items'              => 'Thẻ phổ biến',
            'all_items'                  => 'Tất cả thẻ',
            'edit_item'                  => 'Chỉnh sửa thẻ',
            'add_new_item'               => 'Thêm thẻ mới'
        ),
        'show_ui'               => true,
        'show_in_rest'          => true,
        'show_admin_column'     => true,
        'rewrite'               => array('slug' => 'portfolio-tag')
    ));
}
add_action('init', 'create_portfolio_taxonomies');
?>

Giải thích các tham số quan trọng

1. Labels – Nhãn hiển thị

$labels = array(
    'name'               => 'Tên số nhiều',
    'singular_name'      => 'Tên số ít', 
    'menu_name'          => 'Tên trong menu admin',
    'name_admin_bar'     => 'Tên trong admin bar',
    'add_new'            => 'Thêm mới',
    'add_new_item'       => 'Thêm [item] mới',
    'new_item'           => '[Item] mới',
    'edit_item'          => 'Chỉnh sửa [item]',
    'view_item'          => 'Xem [item]',
    'all_items'          => 'Tất cả [items]',
    'search_items'       => 'Tìm kiếm [items]',
    'not_found'          => 'Không tìm thấy [items]'
);

2. Arguments – Cấu hình chính

$args = array(
    'public'             => true,      // Hiển thị public
    'publicly_queryable' => true,      // Có thể query từ frontend
    'show_ui'            => true,      // Hiện UI trong admin
    'show_in_menu'       => true,      // Hiện trong admin menu
    'show_in_rest'       => true,      // Hỗ trợ REST API & Gutenberg
    'query_var'          => true,      // Cho phép query bằng query_var
    'rewrite'            => array('slug' => 'portfolio'), // URL rewrite
    'capability_type'    => 'post',    // Quyền truy cập
    'has_archive'        => true,      // Trang archive
    'hierarchical'       => false,     // Có phân cấp không
    'menu_position'      => 20,        // Vị trí trong menu
    'menu_icon'          => 'dashicons-portfolio' // Icon
);

3. Supports – Tính năng hỗ trợ

'supports' => array(
    'title',           // Tiêu đề
    'editor',          // Nội dung editor
    'thumbnail',       // Featured image
    'excerpt',         // Tóm tắt
    'custom-fields',   // Custom fields
    'revisions',       // Lưu phiên bản
    'page-attributes', // Thứ tự, parent page
    'trackbacks',      // Trackbacks
    'comments'         // Bình luận
)

Query Custom Post Type

1. WP_Query

$portfolio_query = new WP_Query(array(
    'post_type'      =&gt; 'portfolio',
    'posts_per_page' =&gt; 10,
    'meta_query'     =&gt; array(
        array(
            'key'     =&gt; 'featured',
            'value'   =&gt; 'yes',
            'compare' =&gt; '='
        )
    )
));

if ($portfolio_query-&gt;have_posts()) {
    while ($portfolio_query-&gt;have_posts()) {
        $portfolio_query-&gt;the_post();
        // Display portfolio item
        the_title();
        the_content();
    }
    wp_reset_postdata();
}

2. get_posts()

$portfolio_items = get_posts(array(
    'post_type'      => 'portfolio',
    'numberposts'    => 5,
    'meta_key'       => 'project_year',
    'meta_value'     => '2024',
    'orderby'        => 'menu_order',
    'order'          => 'ASC'
));

foreach ($portfolio_items as $item) {
    setup_postdata($item);
    // Display item
}
wp_reset_postdata();

3. Query với Taxonomy

$web_projects = new WP_Query(array(
    'post_type' => 'portfolio',
    'tax_query' => array(
        'relation' => 'AND',
        array(
            'taxonomy' => 'portfolio_category',
            'field'    => 'slug',
            'terms'    => 'web-design'
        ),
        array(
            'taxonomy' => 'portfolio_tag',
            'field'    => 'slug',
            'terms'    => array('responsive', 'modern'),
            'operator' => 'IN'
        )
    )
));

Template Files

1. Archive template: archive-portfolio.php

<?php get_header(); ?>

<div class="portfolio-archive">
    <h1>Portfolio của chúng tôi</h1>
    
    <?php if (have_posts()) : ?>
        <div class="portfolio-grid">
            <?php while (have_posts()) : the_post(); ?>
                <div class="portfolio-item">
                    <?php if (has_post_thumbnail()) : ?>
                        <div class="portfolio-thumbnail">
                            <?php the_post_thumbnail('medium'); ?>
                        </div>
                    <?php endif; ?>
                    
                    <h2><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2>
                    <div class="portfolio-excerpt">
                        <?php the_excerpt(); ?>
                    </div>
                    
                    <div class="portfolio-meta">
                        <?php
                        $categories = get_the_terms(get_the_ID(), 'portfolio_category');
                        if ($categories) {
                            foreach ($categories as $category) {
                                echo '<span class="portfolio-cat">' . $category->name . '</span>';
                            }
                        }
                        ?>
                    </div>
                </div>
            <?php endwhile; ?>
        </div>
        
        <?php the_posts_pagination(); ?>
    <?php endif; ?>
</div>

<?php get_footer(); ?>

2. Single template: single-portfolio.php

<?php get_header(); ?>

<?php while (have_posts()) : the_post(); ?>
    <article class="portfolio-single">
        <header class="portfolio-header">
            <h1><?php the_title(); ?></h1>
            
            <div class="portfolio-meta">
                <?php
                $categories = get_the_terms(get_the_ID(), 'portfolio_category');
                $tags = get_the_terms(get_the_ID(), 'portfolio_tag');
                
                if ($categories) {
                    echo '<div class="categories">';
                    foreach ($categories as $cat) {
                        echo '<a href="' . get_term_link($cat) . '">' . $cat->name . '</a>';
                    }
                    echo '</div>';
                }
                ?>
            </div>
        </header>
        
        <div class="portfolio-content">
            <?php if (has_post_thumbnail()) : ?>
                <div class="portfolio-featured">
                    <?php the_post_thumbnail('large'); ?>
                </div>
            <?php endif; ?>
            
            <div class="portfolio-description">
                <?php the_content(); ?>
            </div>
            
            <?php
            // Custom fields
            $project_url = get_post_meta(get_the_ID(), 'project_url', true);
            $client_name = get_post_meta(get_the_ID(), 'client_name', true);
            $project_year = get_post_meta(get_the_ID(), 'project_year', true);
            
            if ($project_url || $client_name || $project_year) : ?>
                <div class="project-details">
                    <h3>Chi tiết dự án</h3>
                    <?php if ($client_name) : ?>
                        <p><strong>Khách hàng:</strong> <?php echo esc_html($client_name); ?></p>
                    <?php endif; ?>
                    <?php if ($project_year) : ?>
                        <p><strong>Năm thực hiện:</strong> <?php echo esc_html($project_year); ?></p>
                    <?php endif; ?>
                    <?php if ($project_url) : ?>
                        <p><strong>Xem dự án:</strong> <a href="<?php echo esc_url($project_url); ?>" target="_blank">Truy cập website</a></p>
                    <?php endif; ?>
                </div>
            <?php endif; ?>
        </div>
        
        <nav class="portfolio-navigation">
            <?php
            $prev_post = get_previous_post(false, '', 'portfolio_category');
            $next_post = get_next_post(false, '', 'portfolio_category');
            
            if ($prev_post) : ?>
                <a href="<?php echo get_permalink($prev_post->ID); ?>" class="prev-portfolio">
                    ← <?php echo get_the_title($prev_post->ID); ?>
                </a>
            <?php endif; ?>
            
            <?php if ($next_post) : ?>
                <a href="<?php echo get_permalink($next_post->ID); ?>" class="next-portfolio">
                    <?php echo get_the_title($next_post->ID); ?> →
                </a>
            <?php endif; ?>
        </nav>
    </article>
<?php endwhile; ?>

<?php get_footer(); ?>

Tips và Tricks nâng cao

1. Thêm custom columns trong admin

function add_portfolio_admin_columns($columns) {
    $new_columns = array();
    $new_columns['cb'] = $columns['cb'];
    $new_columns['title'] = $columns['title'];
    $new_columns['portfolio_category'] = 'Danh mục';
    $new_columns['featured_image'] = 'Hình ảnh';
    $new_columns['date'] = $columns['date'];
    
    return $new_columns;
}
add_filter('manage_portfolio_posts_columns', 'add_portfolio_admin_columns');

function portfolio_admin_column_content($column, $post_id) {
    switch ($column) {
        case 'portfolio_category':
            $categories = get_the_terms($post_id, 'portfolio_category');
            if ($categories) {
                $cat_names = array();
                foreach ($categories as $cat) {
                    $cat_names[] = $cat->name;
                }
                echo implode(', ', $cat_names);
            }
            break;
            
        case 'featured_image':
            $thumbnail = get_the_post_thumbnail($post_id, array(80, 80));
            echo $thumbnail ? $thumbnail : 'Không có';
            break;
    }
}
add_action('manage_portfolio_posts_custom_column', 'portfolio_admin_column_content', 10, 2);

2. Thêm meta boxes tùy chỉnh

function add_portfolio_meta_boxes() {
    add_meta_box(
        'portfolio-details',
        'Chi tiết Portfolio',
        'portfolio_meta_box_callback',
        'portfolio',
        'normal',
        'high'
    );
}
add_action('add_meta_boxes', 'add_portfolio_meta_boxes');

function portfolio_meta_box_callback($post) {
    wp_nonce_field('portfolio_meta_box', 'portfolio_meta_box_nonce');
    
    $client_name = get_post_meta($post->ID, '_client_name', true);
    $project_url = get_post_meta($post->ID, '_project_url', true);
    
    echo '<table class="form-table">';
    echo '<tr><th><label for="client_name">Tên khách hàng</label></th>';
    echo '<td><input type="text" id="client_name" name="client_name" value="' . esc_attr($client_name) . '" /></td></tr>';
    
    echo '<tr><th><label for="project_url">URL dự án</label></th>';
    echo '<td><input type="url" id="project_url" name="project_url" value="' . esc_url($project_url) . '" /></td></tr>';
    echo '</table>';
}

function save_portfolio_meta_box($post_id) {
    if (!isset($_POST['portfolio_meta_box_nonce']) || !wp_verify_nonce($_POST['portfolio_meta_box_nonce'], 'portfolio_meta_box')) {
        return;
    }
    
    if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
        return;
    }
    
    if (!current_user_can('edit_post', $post_id)) {
        return;
    }
    
    if (isset($_POST['client_name'])) {
        update_post_meta($post_id, '_client_name', sanitize_text_field($_POST['client_name']));
    }
    
    if (isset($_POST['project_url'])) {
        update_post_meta($post_id, '_project_url', esc_url_raw($_POST['project_url']));
    }
}
add_action('save_post', 'save_portfolio_meta_box');

3. Shortcode để hiển thị portfolio

function portfolio_shortcode($atts) {
    $atts = shortcode_atts(array(
        'count' => 6,
        'category' => '',
        'orderby' => 'date',
        'order' => 'DESC'
    ), $atts);
    
    $args = array(
        'post_type' => 'portfolio',
        'posts_per_page' => $atts['count'],
        'orderby' => $atts['orderby'],
        'order' => $atts['order']
    );
    
    if (!empty($atts['category'])) {
        $args['tax_query'] = array(
            array(
                'taxonomy' => 'portfolio_category',
                'field' => 'slug',
                'terms' => $atts['category']
            )
        );
    }
    
    $portfolio_query = new WP_Query($args);
    
    ob_start();
    
    if ($portfolio_query->have_posts()) {
        echo '<div class="portfolio-shortcode-grid">';
        while ($portfolio_query->have_posts()) {
            $portfolio_query->the_post();
            echo '<div class="portfolio-item">';
            if (has_post_thumbnail()) {
                echo '<a href="' . get_permalink() . '">' . get_the_post_thumbnail(get_the_ID(), 'medium') . '</a>';
            }
            echo '<h3><a href="' . get_permalink() . '">' . get_the_title() . '</a></h3>';
            echo '<p>' . get_the_excerpt() . '</p>';
            echo '</div>';
        }
        echo '</div>';
        wp_reset_postdata();
    }
    
    return ob_get_clean();
}
add_shortcode('portfolio', 'portfolio_shortcode');

Kết luận

Custom Post Type là công cụ mạnh mẽ để tạo ra các website WordPress chuyên nghiệp và có cấu trúc. Việc hiểu rõ và áp dụng đúng các best practices sẽ giúp bạn tạo ra những tính năng mạnh mẽ và dễ bảo trì.

Những điểm quan trọng cần nhớ:

  • Luôn flush rewrite rules khi activate/deactivate
  • Sử dụng text domain cho tính năng đa ngôn ngữ
  • Tổ chức code theo class để dễ quản lý
  • Tạo template files riêng cho từng post type
  • Sử dụng custom taxonomies để tăng tính linh hoạt
  • Kết hợp với custom fields để mở rộng tính năng

Với hướng dẫn này, bạn có thể tạo ra bất kỳ loại Custom Post Type nào phù hợp với dự án của mình!

5/5 - (8 votes)

Nếu bạn thấy bài viết có ích bạn có thể chia sẻ bài viết này. Yêu cầu thêm bài viết tại đây
Đã copy
vutruso

Vũ Trụ Số chuyên cung cấp hosting cho WordPress, dịch vụ thiết kế website, quản trị website cho doanh nghiệp, các dịch vụ bảo mật website WordPress, tăng tốc website WordPress

Bài viết liên quan