WP63
  • Web Development
  • WordPress Development
  • Plugins
  • WP63
  • WordPress Development

กำหนด URL structure ได้ตามใจด้วย AltoRouter

Published July 20, 2020

Updated July 21, 2020

กำหนด URL structure ได้ตามใจด้วย AltoRouter
Share this:
  • Click to share on Facebook (Opens in new window)
  • Click to share on Twitter (Opens in new window)
  • Click to share on Telegram (Opens in new window)
  • Click to share on LINE (Opens in new window)

อธิบายก่อนว่าเมื่อประมาณต้นปีที่ผ่านมา ผมได้ทำเว็บไซต์เว็บหนึ่งที่ภายในเว็บไซต์จะมี whitepaper ให้ดาวน์โหลด ซึ่งก่อนโหลดจะต้องมีการกรอกฟอร์มก่อน แล้วค่อย redirect ไปที่อีก URL หนึ่ง ซึ่งมันก็เป็น permalink เดิมแหละ แค่ต้องมีพารามิเตอร์เพิ่มเข้ามา

ปกติแล้วเราก็ใช้วิธีเพิ่ม query string เข้าไป เช่น URL เดิมเป็นแบบนี้

https://domain.test/whitepaper/paper-name/

เราก็เติม query string เข้าไปเป็นแบบนี้

https://domain.test/whitepaper/paper-name/?download=true

แล้วในเท็มเพลตเราก็เช็คค่าจาก $_GET['download'] เอาตามปกติ แต่ว่าการใส่ query string นั้นมันดูขัดหูขัดตาเหลือเกิน เราเลยจะแก้ไขให้มันเป็น pretty permalink แบบนี้

https://domain.test/whitepaper/paper-name/download/

ซึ่งเราสามารถทำอะไรแบบนี้ได้ด้วยการใช้ router library อย่าง AltoRouter ที่เราเคยพูดถึงไปก่อนหน้านี้

  • การทำ Routing ใน PHP ด้วย AltoRouter

จริงๆ แล้วเวิร์ดเพรสมี Rewrite API ให้ใช้งานด้วยเช่นกัน แต่การใช้งานจะยุ่งยากกว่าเพราะเราต้องเขียน Regular Expression เพิ่มเข้าไปด้วย ดังนั้นการเอา router สักตัวมาเสียบเข้าไปเพื่อจัดการเรื่องนี้แทนนั้นจะสะดวกกว่ามาก

ติดตั้ง AltoRouter

อย่างที่เราเคยกล่าวไว้ในโพสต์ก่อนหน้านี้ว่า AltoRouter นั้นมีหน้าที่เพียงแค่อ่าน route เท่านั้น ไม่ได้มีการประมวลผล logic ใดๆ เพิ่มเติม และจะคืนค่า callback function กลับออกมาให้เราเรียกใช้เอง นั่นทำให้เราสามารถดัดแปลงเอา callback function เหล่านี้ให้คืนค่ากลับออกมาเป็น query vars ที่ต้องการได้

ขั้นแรกให้เราเปิด terminal ไปที่โฟลเดอร์ธีมของเราก่อน แล้วสั่งติดตั้งแพ็คเกจ altorouter/altorouter ด้วย composer

cd wp-content/themes/mytheme
composer require altorouter/altorouter

ถ้าธีมใครที่ไม่ได้ใช้ composer อยู่ก่อนแล้ว ก็ให้ไปเพิ่มคำสั่ง require ไฟล์ autoload.php ลงใน functions.php ด้วย โดยเปิดไฟล์ functions.php ของธีมขึ้นมา แล้ว require autoload ไปที่บรรทัดบนสุด

<?php
use AltoRouter as Router;

require_once 'vendor/autoload.php';

ฟิลเตอร์ request

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

ถ้าเราลองฮุคเข้าไปที่ฟิลเตอร์นี้แล้ว var_dump() ค่าออกมาแสดง ก็จะเจอกับ query vars ของเวิร์ดเพรส

<?php
add_filter( 'request', function( $request ) {
  var_dump( $request );

  return $request;
} );

ซึ่งตัวแปรใน query vars นี้เราสามารถเข้าถึงได้จากที่ไหนก็ได้ผ่านฟังก์ชัน get_query_var( $key ) เพื่อดึงค่านั้นๆ มาใช้งาน

ดังนั้นแนวคิดของเราก็คือใช้ AltoRouter ตรวจสอบ URL ว่าตรงตามที่ต้องการหรือไม่ (เป็น /whitepaper/<post-name>/download/) ถ้าใช่ก็เพิ่มตัวแปรเข้าไปใน query var แล้วไปตรวจสอบเอาในหน้าเท็มเพลต

ตรวจ URL ด้วย AltoRouter

เรายังอยู่กันที่ไฟล์ functions.php หลังจากที่เราประกาศ use AltoRouter as Router และ require ไฟล์ autoload.php เข้ามาแล้ว ในฟิลเตอร์ request ให้เรา new Router ขึ้นมา แล้วตรวจสอบแพทเทิร์น URL แบบนี้

<?php
add_filter( 'request', function( $request ) {
  $router = new Router(); 
  
  // กำหนด route ที่ต้องการ
  $router->map( 'GET', '/whitepaper/[:post]/download/?', function( $post ) {
    return [
      'post_type' => 'whitepaper',
      'name' => $post,
      'is_download' => true,
    ];
  });

  // ตรวจสอบว่า URL ปัจจุบันตรงกับแพทเทิร์นใดๆ ที่เราตั้งไว้หรือไม่
  $match = $router->match();

  // ถ้าตรงให้คืนค่า query vars ชุดใหม่จากในฟังก์ชัน
  if ( is_array( $match ) && is_callable( $match['target'] ) ) {
    $request_arguments = call_user_func_array( $match['target'], $match['params'] );

    return $request_arguments;
  }

  // คืนค่า query vars ชุดเดิมในกรณีที่ route ไม่ตรงกับที่กำหนดไว้
  return $request;
} );

จากตัวอย่างนั้นจะเห็นว่าหากแพทเทิร์นของ URL ตรงตามเงื่อนไข เราจะ return ค่า query vars ชุดใหม่ออกมา ซึ่ง query vars ชุดใหม่นี้จะมีคีย์ is_download ติดมาด้วย

อย่างไรก็ตาม ในค่าเริ่มต้นนั้นเวิร์ดเพรสจะยังไม่รู้จัก query var ตัวใหม่ที่เราเพิ่งเพิ่มเข้าไป (คีย์ is_download นั่นแหละ) เราต้องทำให้เวิร์ดเพรสรู้จักมันเสียก่อน ด้วยการฮุคเข้าไปที่ฟิลเตอร์ query_vars แล้วเพิ่มชื่อพารามิเตอร์ของเราลงไป

<?php
add_filter( 'query_vars', function( $vars ) {
  $vars[] = 'is_download';

  return $vars;
} );

จากนั้นในไฟล์เท็มเพลต (ในกรณีนี้จะเป็น single-whitepaper.php) ให้เราตรวจสอบค่า is_download ผ่านฟังก์ชัน get_query_var( 'is_download' ) แล้วแสดงผลออกมาตามที่ต้องการ จะใช้วิธีเปลี่ยนเนื้อหาเอาเฉยๆ หรือแยกออกไปเป็น partial template ก็ตามแต่สะดวก

<?php if ( get_query_var( 'is_download' ) === 'true' ) : ?>
  <h1>Thank you for downloading!</h1>
  <a href="#">Download here</a>
<?php else : ?>
  <?php the_content() ; ?>
<?php endif; ?>

เมื่อเปิดหน้าเว็บด้วย url ปกติ หรือก็คือ /whitepaper/paper-name/ ก็จะขึ้นเป็นหน้าแสดงเนื้อหาปกติ

แต่เมื่อเข้าผ่าน /whitepaper/paper-name/download/ ก็จะขึ้นหน้าให้ดาวน์โหลดแทน

ตัวอย่างโค้ดทั้งหมด

<?php
// functions.php
use AltoRouter as Router;

require_once 'vendor/autoload.php';

add_filter( 'query_vars', function( $vars ) {
  $vars[] = 'is_download';

  return $vars;
} );

add_filter( 'request', function( $request ) {
  $router = new Router(); 

  $router->map( 'GET', '/whitepaper/[:post]/download/?', function( $post ) {
    return [
      'post_type' => 'whitepaper',
      'name' => $post,
      'is_download' => true,
    ];
  });

  // ตรวจสอบว่า URL ปัจจุบันตรงกับแพทเทิร์นใดๆ ที่เราตั้งไว้หรือไม่
  $match = $router->match();

  // ถ้าตรงให้คืนค่า query vars ชุดใหม่จากในฟังก์ชัน
  if ( is_array( $match ) && is_callable( $match['target'] ) ) {
    $request_arguments = call_user_func_array( $match['target'], $match['params'] );

    return $request_arguments;
  }

  // คืนค่า query vars ชุดเดิม
  return $request;
} );
<?php // single-whitepaper.php ?>
<div class="entry-content">
    <?php if ( get_query_var( 'is_download' ) === true ) : ?>
      <div style="text-align: center;">
        <h1>Thank you for downloading!</h1>
        <a href="#">Download here</a>
      </div>
    <?php else : ?>
      <?php the_content() ; ?>
    <?php endif; ?>
</div>

กรณีอื่น: ปรับแต่ง permalink ของ Custom Post Type

แม้เวิร์ดเพรสจะมี Custom Post Type มาให้นานแล้ว แต่เรื่องน่ารำคาญของมันคือเรากำหนด permalink structure ให้ Custom Post Type ไม่ได้เลย บางครั้งเราอยากจะทำ permalink แบบมี taxonomy เข้าไปด้วย เช่น /game/shooting/call-of-duty ก็ไม่สามารถทำได้ เพราะมันจะใช้ได้แค่ /game/call-of-duty เท่านั้น

ปัญหานี้แก้ไขได้ด้วย AltoRouter เช่นเดียวกัน

สมมุติว่าเรามี post type ที่ชื่อว่า game และมี taxonomy ที่ชื่อว่า game_type เราสามารถเขียน map route ได้ดังนี้

  $router->map( 'GET', '/game/[:category]/[:post]/?', function( $category, $post ) {
    return [
      'post_type' => 'game',
      'name' => $post,
      'game_type' => $category,
    ];
  });

อ้อ เรา init router ขึ้นมาทีเดียว เราสามารถเอามาแมพได้กับหลาย URL นะ ไม่ต้อง add_filter ใหม่ทุกครั้ง เหมือนกับการเขียน route ในเฟรมเวิร์กอื่นๆ เลย

ข้อควรระวัง: get_permalink()

โดยปกติแล้วเราจะใช้ฟังก์ชัน get_permalink() ในการดึง URL ของโพสต์ต่างๆ ออกมา อย่างไรก็ตามแม้ว่าเราจะใช้ AltoRouter ในการกำหนด router แล้ว เมื่อเราใช้ get_permalink() ในการดึง URL เราก็จะยังได้ URL แบบเดิมของเวิร์ดเพรสเองอยู่ดี เช่นในตัวอย่างข้างบน ฟังก์ชัน get_permalink() จะคืนค่ากลับออกมาเป็น /game/call-of-duty ไม่ใช่ /game/shooting/call-of-duty แต่อย่างใด

สาเหตุมาจากฟิลเตอร์ request นั้นจะทำงานในขั้นตอนการแปลง URL ออกมาเป็น query vars เท่านั้น ไม่ได้มีผลใดๆ กับการสร้าง permalink ของโพสต์ต่างๆ

ถ้าต้องการให้ฟังก์ชัน get_permalink() คืนค่า URL ที่ตรงกับที่เราต้องการออกมาด้วย เราจะต้องฮุคเข้าไปที่ฟิลเตอร์ post_type_link เพื่อแก้ไข ซึ่งเราจะไว้พูดถึงกันในครั้งต่อไป

Share this:
  • Click to share on Facebook (Opens in new window)
  • Click to share on Twitter (Opens in new window)
  • Click to share on Telegram (Opens in new window)
  • Click to share on LINE (Opens in new window)

บทความอื่นๆ ที่น่าสนใจ

Leave a Reply Cancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

© 2021 WP63

  • หน้าแรก
  • ติดต่อเรา
  • นโยบายความเป็นส่วนตัว
  • ข้อตกลงการใช้งาน
  • Web Development
  • WordPress Development
  • Plugins

Like us

Like us

Categories

  • Blog
  • Gutenberg
  • Plugins
  • Shortnotes
  • Snippets
  • Web Development
  • WordPress Development

Popular Posts

  • การเชื่อมต่อฐานข้อมูล MySQL บน PHP
    การเชื่อมต่อฐานข้อมูล MySQL บน PHP
  • ตั้งค่าปลั๊กอิน WebP Express สำหรับใช้ภาพ WebP บนเวิร์ดเพรส
    ตั้งค่าปลั๊กอิน WebP Express สำหรับใช้ภาพ WebP บนเวิร์ดเพรส
  • การใช้ @media print ในการกำหนด CSS สำหรับพิมพ์และ PDF
    การใช้ @media print ในการกำหนด CSS สำหรับพิมพ์และ PDF
  • วิธีเปิดใช้งาน พร้อมย้ายเว็บจากโฮสต์เดิมเข้าสู่ Cloudways
    วิธีเปิดใช้งาน พร้อมย้ายเว็บจากโฮสต์เดิมเข้าสู่ Cloudways
  • การใช้ PSR-4 autoload ใน Composer
    การใช้ PSR-4 autoload ใน Composer
  • การทำ Routing ใน PHP ด้วย AltoRouter
    การทำ Routing ใน PHP ด้วย AltoRouter

Archives

  • January 2021
  • July 2020
  • June 2020
  • May 2020
  • April 2020
  • March 2020
  • February 2020
  • December 2019
  • October 2019
  • September 2019
  • August 2019
  • July 2019
  • June 2019
  • March 2019
  • February 2019
  • December 2018
  • September 2018
  • July 2018
  • June 2018
  • May 2018
  • April 2018
  • March 2018
  • February 2018
  • January 2018