Custom Post Type WordPress

WordPress มาพร้อมกับ Post Type พื้นฐาน 2 ประเภทคือ Post (บทความ) และ Page (หน้า) แต่เว็บหลายประเภทต้องการจัดเก็บข้อมูลในรูปแบบที่แตกต่างออกไป เช่น สินค้า, Portfolio, Event, Testimonial หรือ Recipe — นี่คือที่มาของ Custom Post Type

บทความนี้จะอธิบายว่า Custom Post Type คืออะไร ทำไมต้องใช้ และวิธีสร้างทั้งแบบ Plugin ที่ไม่ต้องเขียนโค้ดเลย และแบบโค้ดสำหรับนักพัฒนา

1. Custom Post Type คืออะไร?

Custom Post Type (CPT) คือ "ประเภทเนื้อหา" ที่คุณสร้างขึ้นเองใน WordPress นอกเหนือจาก Post และ Page ที่มีอยู่แล้ว เปรียบเหมือนการเพิ่ม "Drawer" ใหม่ให้ WordPress Admin สำหรับเก็บข้อมูลประเภทเฉพาะ

ตัวอย่าง Custom Post Type ที่พบบ่อย:

2. ความแตกต่างจาก Post และ Page

ประเภทใช้สำหรับมี Category/Tag?เรียงตาม?
Postบทความบล็อก ข่าวมีวันที่
Pageหน้าถาวร เช่น About, Contactไม่มีHierarchy (Parent/Child)
Custom Post Typeเนื้อหาเฉพาะทาง ตั้งค่าเองได้ตั้งค่าได้ตั้งค่าได้

ข้อดีของ Custom Post Type คือสามารถกำหนดได้ว่าจะมี Category, Tag, Featured Image, Custom Fields หรือ Excerpt หรือไม่ ทำให้จัดการข้อมูลได้อย่างเป็นระเบียบมากกว่าการใช้ Post ปกติ

3. สร้างด้วย Plugin — Custom Post Type UI (CPT UI)

CPT UI เป็น Plugin ฟรีที่ใช้งานง่ายที่สุด สร้าง Custom Post Type ได้โดยไม่ต้องเขียนโค้ดแม้แต่บรรทัดเดียว

วิธีติดตั้งและใช้งาน CPT UI

  1. ไปที่ Plugins → Add New ค้นหา "Custom Post Type UI" ติดตั้งและ Activate
  2. ไปที่ CPT UI → Add/Edit Post Types
  3. กรอก Post Type Slug (ชื่อย่อ เช่น portfolio) — ใช้ตัวเล็กไม่มีช่องว่าง
  4. กรอก Plural Label (เช่น "Portfolio") และ Singular Label (เช่น "Portfolio Item")
  5. ในส่วน Supports เลือก: Title, Editor, Thumbnail, Excerpt ตามที่ต้องการ
  6. ใน Has Archive เปิดถ้าต้องการมีหน้า List ทั้งหมด (เช่น /portfolio/)
  7. คลิก Add Post Type — เสร็จแล้ว!

หลังจากนั้นจะเห็นเมนู "Portfolio" ปรากฏใน WordPress Admin Sidebar สามารถเพิ่มรายการได้ทันที

เทคนิค: ใน CPT UI ยังสามารถสร้าง Custom Taxonomy (หมวดหมู่เฉพาะ) ได้ด้วย เช่น สร้าง "ประเภท Portfolio" แยกจาก Category ของ Post ปกติ ไปที่ CPT UI → Add/Edit Taxonomies

4. สร้างด้วยโค้ด register_post_type() ใน functions.php

วิธีนี้เหมาะสำหรับนักพัฒนาที่ต้องการความยืดหยุ่นและไม่อยากพึ่ง Plugin เพิ่มเติม ให้เพิ่มโค้ดนี้ในไฟล์ functions.php ของ Child Theme เท่านั้น

/**
 * สร้าง Custom Post Type: Portfolio
 */
function asiagb_register_portfolio() {
    $labels = array(
        'name'               => 'Portfolio',
        'singular_name'      => 'Portfolio Item',
        'add_new'            => 'เพิ่มรายการใหม่',
        'add_new_item'       => 'เพิ่ม Portfolio ใหม่',
        'edit_item'          => 'แก้ไข Portfolio',
        'all_items'          => 'Portfolio ทั้งหมด',
        'menu_name'          => 'Portfolio',
    );

    $args = array(
        'labels'             => $labels,
        'public'             => true,
        'publicly_queryable' => true,
        'show_ui'            => true,
        'show_in_menu'       => true,
        'query_var'          => true,
        'rewrite'            => array( 'slug' => 'portfolio' ),
        'capability_type'    => 'post',
        'has_archive'        => true,
        'hierarchical'       => false,
        'menu_position'      => 5,
        'menu_icon'          => 'dashicons-images-alt2',
        'supports'           => array( 'title', 'editor', 'thumbnail', 'excerpt' ),
        'show_in_rest'       => true, // เปิดสำหรับ Gutenberg Block Editor
    );

    register_post_type( 'portfolio', $args );
}
add_action( 'init', 'asiagb_register_portfolio' );

สำคัญ: แก้ไข functions.php ของ Child Theme เท่านั้น ห้ามแก้ Parent Theme เพราะเมื่ออัปเดต Theme ทุกอย่างจะหายไป ถ้ายังไม่มี Child Theme ให้สร้างก่อน

5. เพิ่ม Custom Fields ด้วย ACF (Advanced Custom Fields)

Custom Post Type เก็บได้แค่ Title, Editor Content, และ Featured Image ตามค่าเริ่มต้น ถ้าต้องการ Field พิเศษ เช่น ราคา, URL, วันที่ Event หรือ จำนวน Ingredient ให้ใช้ ACF (Advanced Custom Fields)

วิธีเพิ่ม Custom Fields ด้วย ACF

  1. ติดตั้ง Plugin Advanced Custom Fields (ฟรี) จาก WordPress.org
  2. ไปที่ Custom Fields → Add New
  3. ตั้งชื่อ Field Group เช่น "Portfolio Details"
  4. คลิก Add Field แล้วเลือก Field Type ที่ต้องการ:
    • Text — สำหรับข้อความสั้น เช่น Client Name
    • URL — สำหรับลิงก์ไปดูงานจริง
    • Image — สำหรับรูปเพิ่มเติม
    • Date Picker — สำหรับวันที่โปรเจกต์
    • Select — สำหรับ Category แบบ Dropdown
  5. ใน Location Rules เลือก "Post Type" เท่ากับ "Portfolio"
  6. คลิก Publish — Field จะปรากฏใน Portfolio Editor ทันที

6. แสดงผล Custom Post Type ใน Theme ด้วย WP_Query

เมื่อสร้าง Custom Post Type แล้ว ต้องเขียน Code ดึงข้อมูลมาแสดงใน Template ด้วย WP_Query

<?php
// ดึง Portfolio ทั้งหมด 12 รายการล่าสุด
$portfolio_query = new WP_Query( array(
    'post_type'      => 'portfolio',
    'posts_per_page' => 12,
    'orderby'        => 'date',
    'order'          => 'DESC',
) );

if ( $portfolio_query->have_posts() ) :
    while ( $portfolio_query->have_posts() ) : $portfolio_query->the_post(); ?>

    <article class="portfolio-item">
        <a href="<?php the_permalink(); ?>">
            <?php the_post_thumbnail( 'medium' ); ?>
            <h3><?php the_title(); ?></h3>
        </a>
        <!-- แสดง ACF Field -->
        <p><?php echo get_field( 'client_name' ); ?></p>
    </article>

    <?php endwhile;
    wp_reset_postdata();
else :
    echo '<p>ยังไม่มี Portfolio</p>';
endif; ?>

7. ข้อผิดพลาดที่พบบ่อยเมื่อสร้าง Custom Post Type

นักพัฒนามือใหม่มักพบข้อผิดพลาดเหล่านี้เมื่อเริ่มใช้ Custom Post Type เป็นครั้งแรก การเข้าใจปัญหาล่วงหน้าจะช่วยประหยัดเวลาในการ Debug ได้มาก

1. ลืม Flush Permalinks หลังลงทะเบียน CPT

นี่คือข้อผิดพลาดที่พบบ่อยที่สุด หลังเพิ่มโค้ด register_post_type() ใหม่ต้องไปที่ Settings → Permalinks แล้วกด Save Changes เสมอ มิฉะนั้นหน้า Single และ Archive จะขึ้น 404 ทันที

2. ใช้ชื่อ Post Type Slug ที่ขัดกับ WordPress Core

ห้ามตั้งชื่อ Slug เป็น post, page, attachment, revision, nav_menu_item, action, order, theme เพราะซ้ำกับ Reserved Words ของ WordPress ซึ่งจะทำให้ระบบทำงานผิดพลาดโดยไม่มีข้อความแจ้งเตือน

3. ลืมตั้งค่า show_in_rest สำหรับ Gutenberg

หาก CPT ไม่ตอบสนองต่อ Block Editor หรือ REST API ให้ตรวจสอบว่าตั้ง 'show_in_rest' => true แล้วหรือยัง ค่า Default ของ WordPress คือ false ทำให้ Gutenberg ไม่ทำงานกับ CPT นั้น

4. แก้ไขในไฟล์ Parent Theme โดยตรง

การเพิ่มโค้ดลงใน functions.php ของ Parent Theme โดยตรงเป็นความผิดพลาดที่ทำให้โค้ดหายไปทันทีเมื่ออัปเดต Theme ต้องสร้าง Child Theme และเพิ่มโค้ดใน Child Theme เท่านั้น

5. Slug ยาวเกิน 20 ตัวอักษร

WordPress มีข้อจำกัดที่ Post Type Slug ต้องไม่เกิน 20 ตัวอักษร หากยาวกว่านี้จะถูก Truncate อัตโนมัติซึ่งอาจทำให้เกิดพฤติกรรมที่ไม่คาดคิด ใช้ชื่อสั้นๆ ที่สื่อความหมาย เช่น product, portfolio, event

8. เปรียบเทียบ CPT UI กับการเขียนโค้ดเอง

ผู้เริ่มต้นมักสงสัยว่าควรเลือกวิธีไหนระหว่างการใช้ Plugin CPT UI หรือการเขียนโค้ดเอง ทั้งสองวิธีมีจุดเด่นและข้อจำกัดที่แตกต่างกัน การเลือกให้ถูกต้องตั้งแต่ต้นจะช่วยลดการแก้ไขในภายหลัง

เกณฑ์ CPT UI Plugin เขียนโค้ดเอง
ความยากในการตั้งค่า ง่าย ไม่ต้องเขียนโค้ด ต้องรู้ PHP เบื้องต้น
ความยืดหยุ่น ปานกลาง — ตัวเลือกมาตรฐาน สูงสุด — ควบคุมได้ทุก Parameter
การพึ่ง Plugin เพิ่ม ใช่ — CPT หายถ้า Plugin ถูกปิด ไม่ — ทำงานได้ด้วยตัวเอง
เหมาะสำหรับ ผู้ใช้ทั่วไป นักออกแบบ นักพัฒนา โปรเจกต์ระยะยาว

สำหรับโปรเจกต์ที่ต้องการความยั่งยืนและไม่ต้องพึ่งพา Plugin เพิ่มเติม การเขียนโค้ดใน Child Theme เป็นตัวเลือกที่ดีกว่าในระยะยาว แต่หากต้องการความเร็วในการพัฒนาและทีมงานไม่มีความรู้ PHP ลึก CPT UI เป็นตัวเลือกที่เหมาะสม

9. Custom Taxonomy — จัดหมวดหมู่เฉพาะสำหรับ CPT

Custom Taxonomy คือระบบการจัดหมวดหมู่ที่คุณสร้างขึ้นเองสำหรับ Custom Post Type โดยเฉพาะ ต่างจาก Category และ Tag ที่ WordPress สร้างไว้ให้ Taxonomy เฉพาะทางทำให้ข้อมูลมีโครงสร้างที่ชัดเจนกว่า เช่น

ลงทะเบียน Custom Taxonomy ด้วยโค้ด

/**
 * ลงทะเบียน Taxonomy: ประเภท Portfolio
 */
function asiagb_register_portfolio_type() {
    $labels = array(
        'name'          => 'ประเภทงาน',
        'singular_name' => 'ประเภทงาน',
        'all_items'     => 'ประเภทงานทั้งหมด',
        'edit_item'     => 'แก้ไขประเภทงาน',
        'add_new_item'  => 'เพิ่มประเภทงานใหม่',
    );
    register_taxonomy(
        'portfolio_type',        // slug ของ taxonomy
        array( 'portfolio' ),    // ผูกกับ CPT portfolio
        array(
            'hierarchical' => true,  // true = เหมือน Category, false = เหมือน Tag
            'labels'       => $labels,
            'show_ui'      => true,
            'show_in_rest' => true,
            'rewrite'      => array( 'slug' => 'portfolio-type' ),
        )
    );
}
add_action( 'init', 'asiagb_register_portfolio_type' );

หมายเหตุ: hierarchical => true ทำให้ Taxonomy มีลักษณะแบบ Category (มี Parent/Child ได้) ส่วน false ทำงานแบบ Tag (flat list)

10. ดึงข้อมูล CPT ตาม Custom Taxonomy ด้วย WP_Query

เมื่อมี Custom Taxonomy แล้ว สามารถกรองผลลัพธ์ใน WP_Query ได้ด้วย tax_query ทำให้แสดงเฉพาะ Portfolio ในหมวดที่ต้องการ

<?php
// ดึง Portfolio เฉพาะ "Web Design"
$args = array(
    'post_type'  => 'portfolio',
    'tax_query'  => array(
        array(
            'taxonomy' => 'portfolio_type',
            'field'    => 'slug',
            'terms'    => 'web-design',
        ),
    ),
    'posts_per_page' => 6,
);
$query = new WP_Query( $args );

// Loop แสดงผล
while ( $query->have_posts() ) : $query->the_post();
    echo '<h3>' . get_the_title() . '</h3>';
    echo '<p>' . get_the_excerpt() . '</p>';
endwhile;
wp_reset_postdata();

เทคนิค Advanced: ใช้ get_terms( 'portfolio_type' ) ดึงรายการ Term ทั้งหมดเพื่อสร้าง Filter Menu ให้ผู้ใช้กรองผลลัพธ์แบบ Dynamic ได้ เช่น แท็บ "ทั้งหมด | Web Design | Branding | Photography"

11. Custom Post Type Template Files — การ Integrate กับ Theme

WordPress มีระบบ Template Hierarchy สำหรับ Custom Post Type โดยอัตโนมัติ คุณสามารถสร้างไฟล์ Template เฉพาะให้ CPT แต่ละประเภทได้ใน Theme:

Template File ใช้สำหรับ ตัวอย่าง
single-{post_type}.php หน้าเดี่ยวของ CPT single-portfolio.php
archive-{post_type}.php หน้า Archive (รายการทั้งหมด) archive-portfolio.php
taxonomy-{taxonomy}.php หน้า Taxonomy Term taxonomy-portfolio_type.php
single.php Fallback หากไม่มีไฟล์เฉพาะ ใช้แทนเมื่อไม่มี single-portfolio.php

WordPress จะเลือก Template ที่ specific ที่สุดก่อน เช่น หากมี single-portfolio.php จะใช้ไฟล์นี้ หากไม่มีจะ fallback ไป single.php ตามลำดับ

12. Rewrite Rules — Flush Permalinks

หลังจากสร้าง Custom Post Type ใหม่ ต้อง Flush Permalinks เพื่อให้ URL ทำงานได้ถูกต้อง มิฉะนั้นหน้า Single Post และ Archive จะแสดง 404

วิธี Flush Permalinks:

  1. ไปที่ Settings → Permalinks ใน WordPress Admin
  2. คลิก Save Changes โดยไม่ต้องเปลี่ยนอะไร
  3. WordPress จะสร้าง Rewrite Rules ใหม่อัตโนมัติ

หมายเหตุ: ต้อง Flush Permalinks ทุกครั้งที่เพิ่ม Custom Post Type ใหม่ รวมถึงเมื่อ Activate/Deactivate Plugin CPT UI ด้วย

13. ใช้ Custom Post Type ร่วมกับ Gutenberg

ถ้าต้องการให้ Custom Post Type ใช้งานกับ Gutenberg Block Editor ให้ตรวจสอบว่า ตั้งค่า 'show_in_rest' => true ใน register_post_type() แล้ว

สำหรับ CPT UI ให้ไปที่ CPT UI → Add/Edit Post Types แล้วเปิด Show in REST API เป็น True

เมื่อตั้งค่านี้แล้ว Post Type จะรองรับ Block Editor และ REST API ทำให้ทำงานร่วมกับ Headless WordPress และ Page Builder ที่ทันสมัยได้ด้วย

14. ดึงข้อมูล CPT ผ่าน REST API

เมื่อตั้งค่า 'show_in_rest' => true แล้ว Custom Post Type จะเข้าถึงได้ผ่าน WordPress REST API ทันที ทำให้สามารถพัฒนา Headless WordPress, Mobile App หรือ SPA (Single Page Application) ที่ใช้ข้อมูลจาก WordPress Backend ได้อย่างยืดหยุ่น

ตัวอย่าง Endpoint ที่ถูกสร้างอัตโนมัติเมื่อตั้ง show_in_rest สำหรับ CPT ชื่อ portfolio:

# ดึง Portfolio ทั้งหมด (JSON)
GET /wp-json/wp/v2/portfolio

# ดึง Portfolio ID เดียว
GET /wp-json/wp/v2/portfolio/123

# กรองตาม taxonomy term
GET /wp-json/wp/v2/portfolio?portfolio_type=web-design

# ดึงพร้อม ACF fields (ต้องติดตั้ง ACF + ตั้ง 'show_in_rest' ใน field group)
GET /wp-json/wp/v2/portfolio?_fields=id,title,acf

หมายเหตุด้านความปลอดภัย: REST API เปิด Public โดย default — ถ้า CPT มีข้อมูลที่ไม่ต้องการให้คนนอกเห็น ให้ตั้ง 'public' => false ร่วมกับ 'show_in_rest' => true เพื่อให้เฉพาะผู้ที่ผ่านการ Authenticate เท่านั้นที่เข้าถึงได้

15. Performance — Cache WP_Query ของ CPT

บนเว็บที่มีผู้เข้าชมสูง Query ที่ดึง Custom Post Type จำนวนมากซ้ำๆ อาจทำให้ Database Load สูง การ Cache ผลลัพธ์ของ WP_Query เป็นแนวทางปฏิบัติที่ดีสำหรับ Custom Post Type ที่ข้อมูลไม่เปลี่ยนบ่อย

วิธี Cache ด้วย Transient API

<?php
// ตรวจ Cache ก่อน
$portfolio_items = get_transient( 'portfolio_all' );

if ( false === $portfolio_items ) {
    $q = new WP_Query( array(
        'post_type'      => 'portfolio',
        'posts_per_page' => 20,
        'no_found_rows'  => true,  // ปิด SQL_CALC_FOUND_ROWS
        'update_post_meta_cache' => true,
    ) );

    $portfolio_items = $q->posts;
    // เก็บ Cache ไว้ 1 ชั่วโมง (3600 วินาที)
    set_transient( 'portfolio_all', $portfolio_items, HOUR_IN_SECONDS );
}

foreach ( $portfolio_items as $post ) {
    setup_postdata( $post );
    the_title( '<h3>', '</h3>' );
}
wp_reset_postdata();

ควรล้าง Transient Cache ทุกครั้งที่มีการเพิ่ม แก้ไข หรือลบ Portfolio ด้วย Action Hook save_post_portfolio เพื่อให้ข้อมูลที่แสดงบนหน้าเว็บอัพเดตทันที

เทคนิค: ใช้ 'no_found_rows' => true ทุกครั้งที่ไม่ต้องการ Pagination เพราะช่วยข้าม SQL_CALC_FOUND_ROWS ซึ่งเป็น Query ที่ช้าที่สุดใน WP_Query

16. ย้าย Custom Post Type ระหว่าง Environment ด้วย WP-CLI

เมื่อต้องย้ายข้อมูล Custom Post Type จาก Development ไป Production หรือทำ Backup เฉพาะส่วน WP-CLI (WordPress Command Line Interface) เป็นเครื่องมือที่มีประสิทธิภาพสูงกว่าการ Export ผ่าน Admin Panel ในหลายกรณี

คำสั่ง WP-CLI การทำงาน
wp post list --post_type=portfolio แสดงรายการ Portfolio ทั้งหมดพร้อม ID
wp export --post_type=portfolio Export เฉพาะ Portfolio เป็นไฟล์ XML
wp import portfolio.xml --authors=create Import ไฟล์ XML ไปยัง WordPress ใหม่
wp post delete $(wp post list --post_type=portfolio --format=ids) ลบ Portfolio ทั้งหมดในครั้งเดียว

WP-CLI ทำงานผ่าน SSH จึงเหมาะสำหรับ Hosting ที่เปิด Shell Access เช่น AsiaGB VPS ซึ่งให้สิทธิ์ Root เต็มรูปแบบ

Hosting WordPress รองรับ Custom Post Type และ ACF

AsiaGB Hosting รองรับ WordPress เต็มรูปแบบ พร้อม PHP หลายเวอร์ชัน, MySQL Database และ DirectAdmin ที่ใช้งานง่าย เริ่มต้น 500 บาท/ปี

ดูแพ็กเกจ Hosting →