วิธีเอา Custom Post Type URL Slug ออก

July 6th, 2016

บทความนี้น่าจะมีประโยชน์สำหรับหลายๆ คนที่อยากจะเอา slug จาก Custom Post Type (CPT) ที่เราสร้างออกด้วยเหตุผลทางด้าน SEO พอดีได้มีโอกาสได้ทำตรงส่วนนี้เลยมาเขียนไว้ เผื่อเอาไปใช้กันได้

การเอา slug ออกจาก CPT เป็นการ hack อย่างหนึ่ง ดังนั้นใครที่เอาไปใช้ ขอให้เพิ่มความระมัดระวังด้วยว่าโค้ดนี้จะไปกระทบกับส่วนอื่นหรือเปล่า (แต่เท่าที่ใช้มาก็ไม่กระทบนะ เขียนโค้ดกันไว้) เอาล่ะ มาดูวิธีการทำเลยดีกว่า มีอยู่ 2 ขั้นตอนตามลำดับดังนี้

  1. เอา slug ออกจาก URL
  2. ทำให้ WordPress คิดว่า URL นั้นๆ เป็น CPT

ขั้นตอนที่ 1 เอา slug ออกจาก URL

ก่อนอื่นขอ register ตัว CPT ไว้สักตัวนะครับ ขอใช้ชื่อว่า geekwordpress นะ 😛

register_post_type( 'geekwordpress', array( 'label' => 'Event', 'public' => true ) );

ซึ่งถ้าเราไม่กำหนด rewrite ตอน register ไว้ว่าจะใช้ slug อะไร โดยค่าปกติแล้ว WordPress จะใช้ชื่อ CPT เป็น slug ครับ ในที่นี้คือ geekwordpress นั่นเอง

เราจะใช้ hook ที่มีชื่อว่า post_type_link กันครับ ตรงคำอธิบายเค้าเขียนไว้ว่า

post_type_link is a filter applied to the permalink URL for a post or custom post type prior to printing by the function get_post_permalink.

ตรงความต้องการของเราที่ว่าจะต้องเอา slug ออกจาก URL ก่อนที่ WordPress จะแสดงผล URL ออกไป ทีนี้เราก็มาเขียนฟังก์ชั่นเพื่อเพิ่มเข้าไปใน hook นี้กัน

function remove_geekwordpress_cpt_slug( $permalink, $post ) {
    if ( 'geekwordpress' != $post->post_type || 'publish' != $post->post_status ) {
        return $post_link;
    }
    $post_link = str_replace( '/' . $post->post_type . '/', '/', $post_link );
    return $post_link;
}
add_filter( 'post_type_link', 'remove_geekwordpress_cpt_slug', 10, 2);

โอเค ก่อนอื่นเราต้องเช็คก่อนว่า URL ที่เข้ามาเป็น post type อะไร ถ้าไม่ใช่ geekwordpress ก็ให้ออกจากฟังก์ชั่นไปเลย แต่ถ้าใช่ ก็จะเอา URL หรือ permalink มาเอา slug ออกโดยการแทนที่ด้วย / เสร็จแล้ว ตอนนี้ถ้าเข้าหน้าโพสต์นี้จะเจอหน้า 404 แทน เพราะว่า WordPress นั้นจะมอง URL ที่ไม่มี slug ขั้นระหว่างชื่อไซต์กับชื่อโพสต์เป็นประเภท Posts หรือ Pages เท่านั้น เราจึงต้องมีอีกฟังก์ชั่นเพื่อมาบอก WordPress ว่า URL ที่เราเข้าอยู่นี้เป็น CPT นะ ไปดูขั้นตอนต่อไปกัน

ขั้นตอนที่ 2 ทำให้ WordPress คิดว่า URL นั้นๆ เป็น CPT

ทำอย่างไร? เราจะใช้ hook ที่มีชื่อว่า pre_get_posts ซึ่งเค้าอธิบายไว้ว่า

This hook is called after the query variable object is created, but before the actual query is run.

แปลแบบง่ายๆ ว่า hook นี้โดยเรียกหลังจาก WordPress สร้าง Query แล้ว แต่ยังไม่แสดงผล ฉะนั้นก็หมายความว่าเราสามารถทำอะไรกับโพสต์นั้นๆ ได้ก่อนมันจะแสดงผลนั่นเอง จัดไป

function customize_pre_get_post_request( $query ) {
    global $wpdb;

    if ( ! $query->is_main_query() ) {
        return;
    }

    if ( ! empty( $query->query['name'] ) ) {
        $post_name = $query->get( 'name' );
    }
    else {
        $post_name = $query->get( 'pagename' );
    }
    $post_type = $wpdb->get_var(
        $wpdb->prepare(
            'SELECT post_type FROM ' . $wpdb->posts . ' WHERE post_name = %s LIMIT 1', $post_name
        )
    );

    if ( 'geekwordpress' == $post_type ) {
        $query->set( 'geekwordpress', $post_name );
        $query->set( 'post_type', $post_type );
        $query->is_single = true;
        $query->is_page = false;
    }
}
add_action( 'pre_get_posts', 'customize_pre_get_post_request' );

ในฟังก์ชั่นข้างบนนี้เราจำเป็นต้องรู้ 2 อย่างคือ

  1. ชื่อโพสต์ที่เรากำลังเข้าอยู่
  2. CPT ของโพสต์นั้นๆ

ข้อ 1 เราหาได้โดยการใช้ตัวแปร $query ที่ส่งเข้ามาในฟังก์ชั่น แล้วเก็บใส่ตัวแปร $post_name ตรงจุดนี้ถ้า Permalink settings ของเราเซตไว้เป็นแบบ Post name ชื่อโพสต์จะเก็บอยู่ใน Key ที่ชื่อ name ของ Query แต่ถ้าเป็น Settings แบบอื่น ชื่อโพสต์จะเก็บอยู่ใน key ชื่อ pagename

ข้อ 2 เราจำเป็นใช้ตัวแปร $wpdb เพื่อเอาไว้หาว่าโพสต์ที่กำลังเข้าอยู่เป็น post type อะไร โดยการซัดภาษา SQL เข้าไปตรงๆ เลย (ไม่ต้องห่วงว่าจะไปกระทบกับฐานข้อมูลเรานะ เพราะเราแค่ใช้ SELECT)

ขั้นตอนท้ายสุดนี้จะออกแนว hack เล็กน้อย เราจะเช็คว่า Post type ที่เราได้มาเป็น geekwordpress หรือเปล่า ถ้าไม่ใช่ก็ไม่ต้องทำอะไร แต่ถ้าใช่ก็เราจะเซตชื่อโพสต์เราเข้าไปกับ Key ชื่อ geekwordpress และเซต Post type ของโพสต์ที่เราเข้าอยู่ให้เป็น geekwordpress เนื่องจาก WordPress จำเป็นจะต้องรู้ว่าเรากำลังเข้า Post type อะไรอยู่

และเราก็ต้องบอก WordPress ว่า โพสต์ที่เราดูอยู่เป็นชนิด Post นะ โดยการเซต is_single เป็น true และ is_page เป็น false (จะเป็น true หรือ false ทั้งคู่ไม่ได้)

เสร็จสิ้นกระบวนการ! ลองเอาไปทดสอบกันดูครับ (-/\-)

บทความนี้เขียนขึ้นมาจากโพสต์ที่ไปเจอมา

  1. Remove the slug from your custom post type permalinks

โค้ดสามารถนำไปใช้ได้จริงกับ WordPress เวอร์ชั่น 3.8 และผมคิดว่าไม่น่าจะมีปัญหากับเวอร์ชั่นต่อๆ ไป

หมายเหตุ โค้ดที่ให้ไว้มีปัญหาอย่างหนึ่งคือถ้าเราตั้งชื่อโพสต์ใน post type อื่นๆ เป็นชื่อเดียวกับ post type ที่เราจะเอา slug ออก โค้ดนี้จะสับสนไม่รู้ว่าโพสต์ที่เราจะเข้าไปดูเป็นโพสต์อะไรครับ วิธีแก้ปัญหาเบื้องต้นคือ อย่าตั้งชื่อโพสต์ซ้ำกันนะ แหะๆ ถ้าเจอวิธีแก้ที่ดีกว่านี้จะมาอัพเดทครับ