ส่วนที่ 5 – WordPress และการเขียนโปรแกรมเชิงวัตถุ: ตัวอย่าง WordPress – การนำไปใช้: เมนูการดูแลระบบ

เผยแพร่แล้ว: 2022-02-04

ในบทความก่อนหน้าของเราเกี่ยวกับ Object Oriented Programming เราได้พูดถึงการออกแบบที่เราคิดขึ้นมาในที่สุดสำหรับปลั๊กอินเชิงวัตถุของเรา

ตอนนี้ เราจะเข้าสู่ส่วนที่น่าตื่นเต้นที่สุด โดยเราจะเจาะลึกลงไปถึงวิธีที่เราใช้งานมัน!

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

ในตอนท้ายของบทความนี้ หวังว่าคุณจะมีความเข้าใจ OOP ดีขึ้น และตื่นเต้นกับการเขียนปลั๊กอินเชิงวัตถุของคุณเอง!

เริ่มต้น

เราคิดว่าคุณคุ้นเคยกับการพัฒนาปลั๊กอิน WordPress โดยทั่วไป ดังนั้นเราจะเน้นที่แง่มุมเชิงวัตถุของปลั๊กอินของเรา หากคุณเพิ่งเริ่มพัฒนาปลั๊กอินหรือต้องการทบทวน คุณควรเรียนรู้วิธีสร้างปลั๊กอิน WordPress ตัวแรกของคุณก่อน

มาเริ่มกันเลยเหมือนที่เคยทำ โดยการสร้างไฟล์ prsdm-limit-login-attempts.php ใหม่ ภายใต้ไดเร็กทอรีปลั๊กอินของเรา (เช่น /wp-content/plugins/prsdm-limit-login-attempts)

ไฟล์ปลั๊กอินหลักจะรวมส่วนหัวของปลั๊กอินที่คุณคุ้นเคยอยู่แล้ว:

 /** * Plugin Name: PRSDM Limit Login Attempts * Plugin URI: https://pressidium.com * Description: Limit rate of login attempts, including by way of cookies, for each IP. * Author: Pressidium * Author URI: https://pressidium.com * Text Domain: prsdm-limit-login-attempts * License: GPL-2.0+ * Version: 1.0.0 */

และคำสั่ง if แบบง่ายเพื่อป้องกันการเข้าถึงโดยตรง

 if ( ! defined( 'ABSPATH' ) ) { exit; }

นั่นคือทั้งหมดที่เราต้องการในตอนนี้ เราจะกลับมาตรวจสอบไฟล์นี้อีกครั้งในภายหลัง!

การสร้างเมนูการบริหาร

เมื่อคุณกำลังพัฒนาปลั๊กอิน คุณมักจะต้องจัดเตรียมวิธีกำหนดค่าให้กับผู้ใช้ของคุณ นั่นคือที่มาของหน้าการตั้งค่า ในการสร้างหน้าเราจะเพิ่มเมนูการดูแลระบบที่ใช้ WordPress Settings API

ดังนั้น มาเริ่มคิดว่า API เชิงวัตถุ ของเราจะมีลักษณะอย่างไร

ตามหลักการแล้ว เราต้องการยกตัวอย่าง Pressidium_LLA_Settings_Page ของเราและดำเนินการให้เสร็จสิ้น ในการสร้างอินสแตนซ์ของคลาส ต้องใช้คีย์เวิร์ด new

 new Pressidium_LLA_Settings_Page();

ตอนนี้ ลองคิดดูว่าคลาส Pressidium_LLA_Settings_Page ของเราจะหน้าตาเป็นอย่างไร

เราจะเริ่มต้นด้วยการสร้างคลาสใหม่โดยใช้คีย์เวิร์ดของ class :

 class Pressidium_LLA_Settings_Page {}

ชื่อคลาสของเราจะต้องนำหน้าด้วยตัวระบุที่ไม่ซ้ำกัน Pressidium_LLA_ เพื่อป้องกันการชนกันของการตั้งชื่อกับปลั๊กอิน WordPress อื่น ๆ คำนำหน้าป้องกันปลั๊กอินอื่น ๆ จากการเขียนทับและ/หรือเรียกชั้นเรียนของเราโดยไม่ได้ตั้งใจ ตราบใดที่ชื่อคลาสของเราไม่ซ้ำกัน—หรือเราใช้เนมสเปซ— จะไม่มีข้อขัดแย้งใดๆ กับปลั๊กอินอื่นๆ

ตัวสร้าง

ตอนนี้ เราจะขอเข้าสู่ admin_menu และ admin_init เพื่อให้ง่ายขึ้น เราจะเรียก add_action() ในตัวสร้างของเรา (การแจ้งเตือนสปอยเลอร์: เราจะเปลี่ยนแปลงในภายหลัง)

 class Pressidium_LLA_Settings_Page { /** * Settings_Page constructor. */ public function __construct() { add_action( 'admin_menu', array( $this, 'add_page' ) ); add_action( 'admin_init', array( $this, 'register_sections' ) ); } }

คลาสที่มีคอนสตรัคเตอร์ ให้เรียกเมธอดนี้เมื่ออ็อบเจ็กต์ได้รับอินสแตนซ์ ดังนั้น __construct() วิธีการจึงยอดเยี่ยมสำหรับการเริ่มต้นใดๆ ที่เราอาจต้องการดำเนินการ

มาดูการ add_action() ของเราให้ละเอียดยิ่งขึ้น หากคุณเคยพัฒนาปลั๊กอิน WordPress มาก่อน คุณอาจคาดหวังสิ่งนี้:

 add_action( 'admin_menu', 'my_plugin_prefix_add_page' );

แต่เรามี:

 add_action( 'admin_menu', array( $this, 'add_page' ) );

คุณอาจสับสนเกี่ยวกับการใช้อาร์เรย์ที่นี่ เมื่อใดก็ตามที่เราต้องการส่งเมธอดของอ็อบเจ็กต์ที่สร้างอินสแตนซ์เป็น callback/callable เราสามารถใช้อาร์เรย์ที่มีอ็อบเจ็กต์ที่ดัชนี 0 และชื่อเมธอดที่ดัชนี 1

$นี้คืออะไร?

เป็นตัวแปรเทียมที่พร้อมใช้งานเมื่อมีการเรียกเมธอดจากภายในบริบทของอ็อบเจ็กต์ $this คือค่าของวัตถุที่เรียก ในกรณีนี้ $this คือตัวอย่างของ Pressidium_LLA_Settings_Page

นอกจากนี้ “ฟังก์ชัน” ทั้งหมดของเรายังเป็นเมธอด รวมอยู่ในคลาส ดังนั้นจึงไม่จำเป็นต้องเติมชื่อเมธอดของเรานำหน้า

เนมสเปซ

เนมสเปซใน PHP ช่วยให้เราสามารถจัดกลุ่มคลาสที่เกี่ยวข้อง อินเทอร์เฟซ ฟังก์ชัน ฯลฯ ป้องกันการชนกันของการตั้งชื่อระหว่างโค้ดของเรา และ PHP ภายในหรือคลาส/ฟังก์ชันของบุคคลที่สาม

มาใช้กันเลยดีกว่า ดังนั้นเราจึงไม่ต้องนำหน้าชั้นเรียนของเราไปข้างหน้า

เราจะประกาศเนมสเปซโดยใช้คีย์เวิร์ด namespace

 namespace Pressidium;

เนมสเปซสามารถกำหนดได้ด้วยระดับย่อย

 namespace Pressidium\Limit_Login_Attempts;

เนื่องจากเรากำลังสร้างหน้าการตั้งค่า เราจะประกาศเนมสเปซย่อย "หน้า" เพื่อจัดกลุ่มสิ่งที่เกี่ยวข้องกับหน้าการดูแลระบบเข้าด้วยกัน

 namespace Pressidium\Limit_Login_Attempts\Pages;

ในที่สุด เราก็สามารถกำจัดคำนำหน้า Pressidium_LLA_ !

 namespace Pressidium\Limit_Login_Attempts\Pages; class Settings_Page { // ...

ปลั๊กอิน WordPress อื่นที่มีคลาส Settings_Page ไม่ใช่ปัญหาอีกต่อไป เนื่องจากคลาสและคลาสของเราจะไม่อยู่ในเนมสเปซเดียวกัน

เมื่อสร้างอินสแตนซ์ Settings_Page ของเราในเนมสเปซเดียวกัน เราสามารถละเว้นได้:

 namespace Pressidium\Limit_Login_Attempts\Pages; $settings_page = new Settings_Page();

เมื่อสร้างอินสแตนซ์ Settings_Page ของเรานอกเนมสเปซ เราต้องระบุดังนี้:

 namespace Another\Namespace; $settings_page = new \Pressidium\Limit_Login_Attempts\Pages\Settings_Page();

อีกทางหนึ่ง เราสามารถนำเข้าคลาสของเราด้วยตัวดำเนินการ use :

 use Pressidium\Limit_Login_Attempts\Pages\Settings_Page; $settings_page = new Settings_Page();

การเพิ่ม Hook Callbacks

ตอนนี้ เรามาประกาศ add_page() และ register_sections() เหล่านี้กัน

 class Settings_Page { /** * Settings_Page constructor. */ public function __construct() { add_action( 'admin_menu', array( $this, 'add_page' ) ); add_action( 'admin_init', array( $this, 'register_sections' ) ); } /** * Add this page as a top-level menu page. */ public function add_page() { // TODO: Implement this method. } /** * Register sections. */ public function register_sections() { // TODO: Implement this method. } }

วิธีการ add_page() ของเราจะเรียกใช้ฟังก์ชัน add_menu_page() ของ WordPress

 public function add_page() { add_menu_page( __( 'Limit Login Attempts Settings', 'prsdm-limit-login-attempts' ), __( 'Limit Login Attempts', 'prsdm-limit-login-attempts' ), 'manage_options', 'prsdm_limit_login_attempts_settings', array( $this, 'render' ), 'dashicons-shield-alt', null ); }

ดูเหมือนจะเป็นวิธีที่ซับซ้อนในการพัฒนาปลั๊กอิน WordPress เป็นการเรียกฟังก์ชันของ WordPress ด้วยขั้นตอนเพิ่มเติม

นั่นไม่ใช่ "นำมาใช้ใหม่" ได้อย่างแน่นอน เรายังคงต้องเขียนโค้ดพิเศษทั้งหมดนี้สำหรับทุกเมนู/หน้าการดูแลระบบที่เราต้องการเพิ่ม

การปรับโครงสร้างใหม่

ไปข้างหน้าและจัดองค์ประกอบโค้ดของเราใหม่เล็กน้อยเพื่อใช้ประโยชน์จากการเขียนโปรแกรมเชิงวัตถุและทำให้โค้ดของเรา นำกลับมาใช้ใหม่ ได้ เราจะเริ่มต้นด้วยการแทนที่ค่าฮาร์ดโค้ดของเราใน add_page() ด้วยวิธีการสองสามวิธี เช่น:

 public function add_page() { add_menu_page( $this->get_page_title(), // page_title $this->get_menu_title(), // menu_title $this->get_capability(), // capability $this->get_slug(), // menu_slug array( $this, 'render' ), // callback function $this->get_icon_url(), // icon_url $this->get_position() // position ); }

เราจะกำหนดเมธอดเหล่านี้ว่า protected เพื่อให้สามารถเข้าถึงได้ภายในคลาสเองและโดยคลาสย่อย/พาเรนต์เท่านั้น

 protected function get_page_title() { /* ... */ } protected function get_menu_title() { /* ... */ } protected function get_capability() { /* ... */ } protected function get_slug() { /* ... */ } protected function get_icon_url() { /* ... */ } protected function get_position() { /* ... */ }

ยอดเยี่ยม! ตอนนี้เราสามารถใช้คลาสนี้เป็นคลาสทั่วไปที่ใช้ซ้ำได้เพื่อขยายจาก

การออกแบบใหม่

เราบอกคุณแล้วว่าสิ่งนี้อาจเกิดขึ้นในที่สุด เรากำลังคิดใหม่เกี่ยวกับการออกแบบชั้นเรียนของเราขณะสร้างชั้นเรียน

เนื่องจากนี่จะเป็น คลาสพื้นฐาน ของเรา เราจะเปลี่ยนชื่อเป็นชื่อทั่วไป เช่น Admin_Page จนถึงตอนนี้ดูเหมือนว่า:

 class Admin_Page { /** * Admin_Page constructor. */ public function __construct() { add_action( 'admin_menu', array( $this, 'add_page' ) ); add_action( 'admin_init', array( $this, 'register_sections' ) ); } /** * Add this page as a top-level menu page. */ public function add_page() { add_menu_page( $this->get_page_title(), // page_title $this->get_menu_title(), // menu_title $this->get_capability(), // capability $this->get_slug(), // menu_slug array( $this, 'render' ), // callback function $this->get_icon_url(), // icon_url $this->get_position() // position ); } /** * Register sections. */ public function register_sections() { // TODO: Implement this method. } protected function get_page_title() { /* ... */ } protected function get_menu_title() { /* ... */ } protected function get_capability() { /* ... */ } protected function get_slug() { /* ... */ } protected function get_icon_url() { /* ... */ } protected function get_position() { /* ... */ } }

ตอนนี้ เราสามารถสร้าง Settings_Page แยกกัน ซึ่ง ขยาย คลาสพื้นฐานของ Admin_Page นั้น

 class Settings_Page extends Admin_Page { // ... }

นั่นเป็นตัวอย่างที่ดีของ inheritance ซึ่งเป็นหนึ่งในแนวคิดหลักของการเขียนโปรแกรมเชิงวัตถุ เมื่อขยายคลาส คลาสย่อย— Settings_Page ในกรณีนี้—รับช่วงต่อสาธารณะและเมธอด คุณสมบัติ และค่าคงที่ทั้งหมดที่ได้รับการป้องกันจากคลาสพาเรนต์

เราสามารถใช้ประโยชน์จากสิ่งนี้และตั้งค่าเริ่มต้นบางอย่างได้ ตัวอย่างเช่น เราจะตั้งค่าไอคอนทั่วไปสำหรับหน้าเมนูทั้งหมด โดยกำหนด get_icon_url() ของเราดังนี้:

 class Admin_Page { // ... /** * Return the menu icon to be used for this menu. * * @link https://developer.wordpress.org/resource/dashicons/ * * @return string */ protected function get_icon_url() { return 'dashicons-admin-generic'; } }

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

อย่างไรก็ตาม หากเราต้องการตั้งค่าไอคอนอื่นสำหรับหน้าเมนูเฉพาะ เราสามารถ แทนที่ get_icon_url() ในคลาสย่อยได้ดังนี้:

 class Settings_Page extends Admin_Page { protected function get_icon_url() { return 'dashicons-shield-alt'; } }

มีค่าบางอย่างที่ต้องแตกต่างกันสำหรับแต่ละชั้นเรียนเด็ก ตัวอย่างเช่น กระสุนเมนู—อาร์กิวเมนต์ที่สี่ของ add_menu_page() ควรจะไม่ซ้ำกันสำหรับแต่ละหน้าเมนู

หากเราจะกำหนดวิธีการนี้ในคลาสฐาน Admin_Page ของเรา เราต้องการวิธีที่จะทำให้แน่ใจว่าคลาสย่อยทุกคลาสจะแทนที่เมธอดนี้ ดีเราสามารถทำสิ่งที่ดียิ่งขึ้น เราสามารถประกาศลายเซ็นของเมธอดและข้ามการดำเนินการไปโดยสิ้นเชิง

ป้อนวิธีการนามธรรม!

คลาสนามธรรมและวิธีการ

วิธีการที่กำหนดให้เป็น นามธรรม เพียงแค่ประกาศลายเซ็นของวิธีการและไม่สามารถกำหนดการใช้งานได้

 /** * Return page slug. * * @return string */ abstract protected function get_slug();

คลาสใด ๆ ที่มีอย่างน้อยหนึ่งวิธีนามธรรม ต้อง เป็นนามธรรมด้วย นั่นหมายความว่า คลาส Admin_Page ของเราควรถูกกำหนดให้เป็นนามธรรมเช่นกัน

 abstract class Admin_Page { // ...

สิ่งสำคัญคือต้องชี้ให้เห็นว่าคลาสที่กำหนดเป็นนามธรรมไม่สามารถสร้างอินสแตนซ์ได้ ดังนั้นเราจึงไม่สามารถสร้างอินสแตนซ์ Admin_Page ได้โดยตรงอีกต่อไป

นี่คือการแสดงภาพของชั้นเรียนด้วย:

เมื่อสืบทอดจากคลาสนามธรรม คลาสย่อย ต้อง กำหนดวิธีการทั้งหมดที่ทำเครื่องหมายเป็นนามธรรมในการประกาศคลาสพาเรนต์ หมายความว่า Settings_Page ของเราต้องใช้ get_slug()

 class Settings_Page extends Admin_Page { // ... protected function get_slug() { return 'prsdm_limit_login_attempts_settings'; } // ... }

ในทำนองเดียวกัน เราควรใช้วิธีป้องกันที่เหลือตามที่ add_page() ต้องการ

ก่อนดำเนินการต่อเกี่ยวกับวิธีการลงทะเบียนส่วนและฟิลด์ของหน้าผู้ดูแลระบบและแสดงเนื้อหา เรามาพูดถึงการตั้งค่าใน WordPress กันก่อน

การตั้งค่า API

เราจะถือว่าคุณคุ้นเคยกับ Settings API อยู่แล้ว แต่ในกรณีที่นี่คือส่วนสำคัญของมัน:

  • settings_fields() — ส่งออกฟิลด์ nonce, action และ option_page สำหรับหน้าการตั้งค่า โดยทั่วไปฟิลด์แบบฟอร์มที่ซ่อนอยู่
  • do_settings_sections() — พิมพ์ส่วนการตั้งค่าทั้งหมด (และฟิลด์) ที่เพิ่มไปยังหน้าการตั้งค่าเฉพาะ
  • add_settings_section() — เพิ่มส่วนใหม่ในหน้าการตั้งค่า
  • add_settings_field() — เพิ่มฟิลด์ใหม่ให้กับส่วนของหน้าการตั้งค่า
  • register_setting() — ลงทะเบียนการตั้งค่าและข้อมูล

หากคุณไม่คุ้นเคยกับสิ่งนี้ คุณสามารถหยุดอ่านบทความนี้ชั่วคราว และดูบทความที่เกี่ยวข้องของเราเกี่ยวกับวิธีสร้างหน้าการตั้งค่าสำหรับปลั๊กอินที่กำหนดเอง

ตอนนี้เราอยู่ในหน้าเดียวกันแล้ว กลับไปที่เมธอด register_sections() ของเรา เป็นอีกครั้งที่เราต้องย้อนกลับไปคิดเกี่ยวกับ API ของเรา

เนื่องจากเราได้กำหนด add_page() ในคลาส Admin_Page เราจะกำหนดเมธอด render() ไว้ที่นั่นด้วย เราจะส่งคืนค่าของวิธีการอื่นๆ ของเราเป็นอาร์กิวเมนต์ไปยังฟังก์ชันของ WordPress

 abstract class Admin_Page { // ... /** * Render this admin page. */ public function render() { ?> <div class="wrap"> <form action="options.php" method="post"> <h1><?php echo esc_html( $this->get_page_title() ); ?></h1> <?php settings_fields( $this->get_slug() ); do_settings_sections( $this->get_slug() ); submit_button( __( 'Change Options', 'prsdm-limit-login-attempts' ) ); ?> </form> </div> <?php } }

ด้วยวิธีนี้ เราจะไม่ต้องกังวลกับฟังก์ชัน WordPress เหล่านี้โดยตรงอีกต่อไป นั่นเป็นเพราะว่าหน้าผู้ดูแลระบบใด ๆ ที่เราอาจเพิ่มในอนาคตจะถูกสร้างขึ้นผ่านคลาสย่อยเช่นเดียวกับ Settings_Page และการแสดงผลจะทำผ่านเมธอด render() ที่สืบทอดมาของ Admin_Page

บทสรุป

ยอดเยี่ยม! เราได้สร้างคลาสที่รับผิดชอบในการลงทะเบียนเมนูการดูแลระบบและเพิ่มหน้าการตั้งค่า

ในบทความถัดไปของซีรีส์ เราจะสร้างหน้าการตั้งค่าต่อไปและลงทะเบียนส่วน ฟิลด์ และองค์ประกอบต่างๆ

คลิกที่นี่เพื่ออ่านตอนที่ 6 ใน Objected Oriented Programming Series

ดูสิ่งนี้ด้วย

  • WordPress และการเขียนโปรแกรมเชิงวัตถุ – ภาพรวม
  • ส่วนที่ 2 – การเขียนโปรแกรม WordPress และ Object Oriented: ตัวอย่างในโลกแห่งความจริง
  • ส่วนที่ 3 – WordPress และการเขียนโปรแกรมเชิงวัตถุ: Α ตัวอย่าง WordPress – การกำหนดขอบเขต
  • ส่วนที่ 4 – การเขียนโปรแกรม WordPress และ Object Oriented: ตัวอย่าง WordPress – การออกแบบ