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

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

ถึงจุดนี้ การโต้ตอบกับ Plugin API หมายถึงการเรียก add_action() และ add_filters() ในตัวสร้างของแต่ละคลาส

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

หากวัตถุลงทะเบียน hooks ทั้งหมดของมันเมื่อสร้างขึ้น สิ่งต่างๆ เช่น การทดสอบหน่วยจะกลายเป็นเรื่องยุ่งยาก

หมายเหตุ: การทดสอบหน่วยควรทดสอบแต่ละ "หน่วย" แยกกัน แม้ว่าคุณจะไม่ได้เขียนการทดสอบหน่วยในขณะนี้ การเขียนโค้ดที่ทดสอบได้จะช่วยคุณประหยัดเวลาในการจัดโครงสร้างใหม่ในภายหลัง หากคุณตัดสินใจที่จะเขียนการทดสอบ

The Hooks Manager

ก้าวไปอีกขั้นแล้วแนะนำคลาสใหม่เพื่อจัดการ hooks ของเรา เราจะเรียกว่า Hooks_Manager ชั้นเรียนนี้จะต้องรับผิดชอบในการลงทะเบียนขอเกี่ยวทั้งหมดของเรา ดังนั้น เราจะสร้างคลาสใหม่โดยใช้เมธอด register()

 class Hooks_Manager { /** * Register the hooks of the given object. * * @param object $object */ public function register( $object ) { // Register the hooks the specified object needs } }

เราจำเป็นต้องมีอินเทอร์เฟซสำหรับแต่ละคลาสที่จำเป็นต้องลงทะเบียน hooks เพื่อใช้งาน

 interface Hooks { /** * Return the actions to register. * * @return array */ public function get_actions(); }

คุณสามารถคิดว่าอินเทอร์เฟซเป็น สัญญา โดยที่คลาสที่ใช้อินเทอร์เฟซนั้น "ผูกพันตามสัญญา" เพื่อใช้วิธีการทั้งหมดที่กำหนดไว้ในอินเทอร์เฟซนั้น

ตัวอย่างเช่น คลาส Login_Error ที่เชื่อมต่อกับการดำเนินการ login_head ต้อง ใช้ get_actions() ของอินเทอร์เฟซ Hooks ของเรา

 class Login_Error implements Hooks { public function get_actions() { return array( 'login_head' => array( 'add_errors', 10, 1 ), ); } }

เมธอด register() ของ Hooks_Manager ยอมรับอ็อบเจ็กต์ เรียก get_actions() และลงทะเบียนการกระทำทั้งหมด

 public function register( $object ) { $actions = $object->get_actions(); foreach ( $actions as $action_name => $action_details ) { $method = $action_details[0]; $priority = $action_details[1]; $accepted_args = $action_details[2]; add_action( $action_name, array( $object, $method ), $priority, $accepted_args ); } }

มาเพิ่ม get_filters() ให้กับอินเทอร์เฟซของเรากันเถอะ เพื่อให้เราสามารถลงทะเบียนทั้งการดำเนินการและตัวกรอง

 interface Hooks { /** * Return the actions to register. * * @return array */ public function get_actions(); /** * Return the filters to register. * * @return array */ public function get_filters(); }

กลับไปที่คลาส Login_Error ของเรา เราจำเป็นต้องใช้ get_filters() ใหม่นี้

 class Login_Error implements Hooks { public function get_actions() { return array( 'login_head' => array( 'add_errors', 10, 1 ), ); } public function get_filters() { return array( 'authenticate' => array( 'track_credentials', 10, 3 ), 'shake_error_code' => array( 'add_error_code', 10, 1 ), 'login_errors' => array( 'format_error_message', 10, 1 ), ); } }

เราจะเปลี่ยนชื่อเมธอด register() ของ Hooks_Manager เป็น register_actions() เราจะเพิ่มเมธอด register_filters() ด้วย ทั้งสองวิธีนี้จะรับผิดชอบในการลงทะเบียนการดำเนินการและตัวกรองตามลำดับ

 class Hooks_Manager { /** * Register the actions of the given object. * * @param object $object */ private function register_actions( $object ) { $actions = $object->get_actions(); foreach ( $actions as $action_name => $action_details ) { $method = $action_details[0]; $priority = $action_details[1]; $accepted_args = $action_details[2]; add_action( $action_name, array( $object, $method ), $priority, $accepted_args ); } } /** * Register the filters of the given object. * * @param object $object */ private function register_filters( $object ) { $filters = $object->get_filters(); foreach ( $filters as $filter_name => $filter_details ) { $method = $filter_details[0]; $priority = $filter_details[1]; $accepted_args = $filter_details[2]; add_filter( $filter_name, array( $object, $method ), $priority, $accepted_args ); } } }

ตอนนี้เราสามารถเพิ่มเมธอด register() อีกครั้ง ซึ่งจะเป็นการเรียกทั้ง register_actions() และ register_filters()

 class Hooks_Manager { /** * Register an object. * * @param object $object */ public function register( $object ) { $this->register_actions( $object ); $this->register_filters( $object ); } // ...

จะเกิดอะไรขึ้นถ้าคลาสไม่จำเป็นต้องลงทะเบียนทั้งการกระทำและตัวกรอง อินเทอร์เฟซของ Hooks มีสองวิธี: get_actions() และ get_filters() คลาสทั้งหมดที่ใช้อินเทอร์เฟซนั้นจะถูกบังคับให้ใช้ ทั้งสอง วิธี

 class Cookie_Login implements Hooks { public function get_actions() { return array( 'auth_cookie_bad_username' => array( 'handle_bad_username', 10, 1 ), 'auth_cookie_bad_hash' => array( 'handle_bad_hash', 10, 1 ), 'auth_cookie_valid' => array( 'handle_valid', 10, 2 ), ); } public function get_filters() { return array(); } }

ตัวอย่างเช่น คลาส Cookie_Login ต้องลงทะเบียนเฉพาะการกระทำ แต่ตอนนี้ถูกบังคับให้ใช้ get_filters() เพื่อส่งคืนอาร์เรย์ว่าง

Interface Segregation Principle (ISP) ซึ่งเป็น “I” ใน SOLID ระบุว่า:

“ลูกค้าไม่ควรถูกบังคับให้พึ่งพาวิธีการที่ไม่ได้ใช้”

หมายความว่าสิ่งที่เรากำลังทำอยู่ตอนนี้คือสิ่งที่เราไม่ควรทำอย่างยิ่ง

การแยกส่วนต่อประสาน

เราสามารถแก้ไขได้โดยแบ่งอินเทอร์เฟซของเราให้เล็กลงและเฉพาะเจาะจงมากขึ้น เพื่อให้ชั้นเรียนของเราต้องรู้เฉพาะเกี่ยวกับวิธีการที่พวกเขาสนใจเท่านั้น

 interface Actions { /** * Return the actions to register. * * @return array */ public function get_actions(); }
 interface Filters { /** * Return the filters to register. * * @return array */ public function get_filters(); }

เราไม่ต้องการทั้ง get_actions() และ get_filters() อีกต่อไป เราสามารถใช้เฉพาะอินเทอร์เฟซ Actions และกำจัด get_filters()

 class Cookie_Login implements Actions { public function get_actions() { return array( 'auth_cookie_bad_username' => array( 'handle_bad_username', 10, 1 ), 'auth_cookie_bad_hash' => array( 'handle_bad_hash', 10, 1 ), 'auth_cookie_valid' => array( 'handle_valid', 10, 2 ), ); } }

ในทางกลับกัน Login_Error ซึ่งต้องการการดำเนินการ และ ตัวกรอง เพียงแค่ใช้อินเทอร์เฟซทั้งสอง คลาสอาจใช้อินเทอร์เฟซมากกว่าหนึ่งโดยคั่นด้วยเครื่องหมายจุลภาค

 class Login_Error implements Actions, Filters { public function get_actions() { return array( 'login_head' => array( 'add_errors', 10, 1 ), ); } public function get_filters() { return array( 'authenticate' => array( 'track_credentials', 10, 3 ), 'shake_error_code' => array( 'add_error_code', 10, 1 ), 'login_errors' => array( 'format_error_message', 10, 1 ), ); } }

ตอนนี้เราได้แยกอินเทอร์เฟซของเราแล้ว เราแค่ต้องอัปเดตเมธอด register() ของ Hooks_Manager เพื่อให้สอดคล้องกับการเปลี่ยนแปลงของเรา

 class Hooks_Manager { /** * Register an object. * * @param object $object */ public function register( $object ) { if ( $object instanceof Actions ) { $this->register_actions( $object ); } if ( $object instanceof Filters ) { $this->register_filters( $object ); } } // ...

ด้วยวิธีนี้ เราเรียกตามเงื่อนไขเฉพาะ register_actions() เท่านั้น register_filters() หรือทั้งสองอย่าง ตามอินเทอร์เฟซที่วัตถุที่ระบุนำไปใช้

ในการใช้ตัวจัดการ hooks จริง:

 $hooks_manager = new Hooks_Manager(); $hooks_manager->register( $login_error ); $hooks_manager->register( $cookie_login );

แค่นั้นแหละ! ตอนนี้เราสามารถใช้อ็อบเจกต์นั้นเพื่อจัดการ hooks ใน codebase ทั้งหมดได้

บทสรุป

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

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

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

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

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