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

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

จนถึงตอนนี้ เราจำเป็นต้องจัดเก็บตัวเลือกที่ผู้ใช้กำหนดเท่านั้น ดังนั้นเราจึงใช้ API การตั้งค่า อย่างไรก็ตาม ปลั๊กอินของเราต้องสามารถอ่าน/เขียนตัวเลือกได้เองเพื่อ "จดจำ" จำนวนครั้งที่ที่อยู่ IP พยายามเข้าสู่ระบบไม่สำเร็จ หากถูกล็อกอยู่ในปัจจุบัน ฯลฯ

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

นอกจากนี้เรายังจะจัดเรียงตัวเลือก "กลุ่ม" ร่วมกันตามส่วนต่างๆ เพื่อจัดระเบียบ นั่นขึ้นอยู่กับความชอบส่วนบุคคลล้วนๆ

มาเปลี่ยนสิ่งนี้เป็นอินเทอร์เฟซ:

 interface Options { /** * Return the option value based on the given option name. * * @param string $name Option name. * @return mixed */ public function get( $name ); /** * Store the given value to an option with the given name. * * @param string $name Option name. * @param mixed $value Option value. * @param string $section_id Section ID. * @return bool Whether the option was added. */ public function set( $name, $value, $section_id ); /** * Remove the option with the given name. * * @param string $name Option name. * @param string $section_id Section ID. */ public function remove( $name, $section_id ); }

ตามหลักการแล้ว เราสามารถโต้ตอบกับ WordPress Options API โดยทำสิ่งนี้:

 $options = new WP_Options(); $options->get( 'retries' );

ณ จุดนี้ คุณอาจสงสัยว่าทำไมเราไม่เพียงแค่ใช้ฟังก์ชัน WordPress get_option() แทนที่จะสร้างอินเทอร์เฟซและคลาสของเราเอง แม้ว่าการใช้ฟังก์ชันของ WordPress โดยตรงจะเป็นวิธีที่ยอมรับได้อย่างสมบูรณ์ในการพัฒนาปลั๊กอินของเรา แต่ด้วยการก้าวไปอีกขั้นและสร้างอินเทอร์เฟซที่ต้องพึ่งพา เรายังคงมีความยืดหยุ่น

คลาส WP_Options ของเรากำลังใช้อินเทอร์เฟซ Options ของเรา ด้วยวิธีนี้ เราจะพร้อมหากความต้องการของเราเปลี่ยนแปลงในอนาคต ตัวอย่างเช่น เราอาจจำเป็นต้องจัดเก็บตัวเลือกของเราในตารางแบบกำหนดเอง ในฐานข้อมูลภายนอก ในหน่วยความจำ (เช่น Redis) ที่คุณตั้งชื่อไว้ โดยขึ้นอยู่กับสิ่งที่เป็นนามธรรม (เช่นอินเทอร์เฟซ) การเปลี่ยนแปลงบางอย่างในการนำไปใช้นั้นง่ายพอ ๆ กับการสร้างคลาสใหม่ที่ใช้อินเทอร์เฟซเดียวกัน

WP_Options

มาเริ่มเขียนคลาส WP_Options โดยการดึงตัวเลือกทั้งหมดโดยใช้ฟังก์ชัน WordPress get_option() ในตัวสร้าง

 class WP_Options { /** * @var array Stored options. */ private $options; /** * WP_Options constructor. */ public function __construct() { $this->options = get_option( Plugin::PREFIX ); } }

เนื่องจากคุณสมบัติ $options จะถูกใช้เป็นการภายใน เราจะประกาศให้เป็น private ดังนั้นคลาส WP_Options จึงสามารถเข้าถึงได้โดยคลาสที่กำหนดไว้เท่านั้น

ตอนนี้ เรามาใช้งานอินเทอร์เฟซ Options ของเราโดยใช้ตัว implements การ

 class WP_Options implements Options { // ...

IDE ของเรากำลังตะโกนใส่เราให้ประกาศ class abstract หรือใช้เมธอด get() set() และ remove() ที่กำหนดไว้ในอินเทอร์เฟซ

มาเริ่มใช้วิธีการเหล่านี้กัน!

รับออปชั่น

เราจะเริ่มต้นด้วยเมธอด get() ซึ่งจะมองหาชื่อตัวเลือกที่ระบุในคุณสมบัติ $options ของเรา และส่งคืนค่าหรือ false หากไม่มีอยู่

 class WP_Options implements Options { private $options; public function __construct() { $this->options = get_option( Plugin::PREFIX ); } /** * Return the option value based on the given option name. * * @return mixed */ public function get( $option_name ) { if ( ! isset( $this->options[ $option_name ] ) ) { return false; } return $this->options[ $option_name ]; } }

ตอนนี้เป็นเวลาที่ดีที่จะคิดถึงตัวเลือกเริ่มต้น

ตัวเลือกเริ่มต้น

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

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

หมายเหตุ: ชื่อของค่าคงที่เป็นตัวพิมพ์ใหญ่ทั้งหมดตามแบบแผน

 const DEFAULT_OPTIONS = array( 'general_options' => array( 'allowed_retries' => 4, 'normal_lockout_time' => 1200, // 20 minutes 'max_lockouts' => 4, 'long_lockout_time' => 86400, // 24 hours 'hours_until_retries_reset' => 43200, // 12 hours 'site_connection' => 'direct', 'handle_cookie_login' => 'yes', 'notify_on_lockout_log_ip' => true, 'notify_on_lockout_email_to_admin' => false, 'notify_after_lockouts' => 4 ), 'state' => array( 'lockouts' => array(), 'retries' => array(), 'lockout_logs' => array(), 'total_lockouts' => 0 ) );

ในอาร์เรย์ที่ซ้อนกัน DEFAULT_OPTIONS เราได้ตั้งค่าเริ่มต้นสำหรับตัวเลือกทั้งหมดของเรา

สิ่งที่เราจะทำต่อไปคือเก็บค่าตัวเลือกเริ่มต้นไว้ในฐานข้อมูลเมื่อปลั๊กอินเริ่มทำงาน โดยใช้ add_option() WordPress

 class WP_Options { public function __construct() { $all_options = array(); foreach ( self::DEFAULT_OPTIONS as $section_id => $section_default_options ) { $db_option_name = Plugin::PREFIX . '_' . $section_id; $section_options = get_option( $db_option_name ); if ( $section_options === false ) { add_option( $db_option_name, $section_default_options ); $section_options = $section_default_options; } $all_options = array_merge( $all_options, $section_options ); } $this->options = $all_options; } }

มาดูส่วนย่อยนี้กันดีกว่า ขั้นแรก เราวนซ้ำอาร์เรย์ตัวเลือกเริ่มต้นและดึงข้อมูลตัวเลือกโดยใช้ฟังก์ชัน WordPress get_option()

 foreach ( self::default_options as $section_id => $section_default_options ) { $db_option_name = Plugin::PREFIX . '_' . $section_id; $section_options = get_option( $db_option_name ); // ...

จากนั้น เราตรวจสอบว่าแต่ละตัวเลือกมีอยู่แล้วในฐานข้อมูลหรือไม่ และถ้าไม่มี เราจะเก็บตัวเลือกเริ่มต้นไว้

 if ( $section_options === false ) { add_option( $db_option_name, $section_default_options ); $section_options = $section_default_options; }

สุดท้าย เรารวบรวมตัวเลือกของทุกส่วน

 $all_options = array_merge( $all_options, $section_options );

และเก็บไว้ในคุณสมบัติ $options เพื่อให้เราสามารถเข้าถึงได้ในภายหลัง

 $this->options = $all_options;

ตารางตัวเลือกของ WordPress ในฐานข้อมูลจะมีสองสามแถว โดยที่ option_name ประกอบด้วยคำนำหน้าของปลั๊กอินที่ต่อเข้ากับชื่อส่วน

มาดูวิธีการที่เหลือที่เราจำเป็นต้องใช้กัน

การจัดเก็บตัวเลือก

ในทำนองเดียวกัน เราต้องการเก็บตัวเลือกใหม่ในฐานข้อมูลอย่างง่ายดาย และเขียนทับค่าก่อนหน้าใดๆ เช่นนี้

 $options = new Options(); $options->set( 'retries', 4 );

ลองใช้เมธอด set() ซึ่งจะใช้ update_option() ของ WordPress

 /** * Store the given value to an option with the given name. * * @param string $name Option name. * @param mixed $value Option value. * @param string $section_id Section id. Defaults to 'state'. * @return bool Whether the option was added. */ public function set( $name, $value, $section_ ) { $db_option_name = Plugin::PREFIX . '_' . $section_id; $stored_option = get_option( $db_option_name ); $stored_option[ $name ] = $value; return update_option( $db_option_name, $stored_option ); }

การลบตัวเลือก

สุดท้าย เราจะใช้เมธอด remove() ซึ่งจะตั้งค่าตัวเลือกให้เป็นค่าเริ่มต้น:

 /** * Remove the option with the given name. * * @param string $name Option name. * @param string $section_id Section id. Defaults to 'state'. * @return bool Whether the option was removed. */ public function remove( $name, $section_ ) { $initial_value = array(); if ( isset( self::DEFAULT_OPTIONS[ $section_id ][ $name ] ) ) { $initial_value = self::DEFAULT_OPTIONS[ $section_id ][ $name ]; } return $this->set( $name, $initial_value, $section_id ); }

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

การห่อหุ้ม/สิ่งที่เป็นนามธรรม

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

โฮสต์เว็บไซต์ของคุณด้วย Pressidium

รับประกันคืนเงิน 60 วัน

ดูแผนของเรา

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

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

การพึ่งพา

ลองพิจารณาสถานการณ์สมมติต่อไปนี้:

มีคลาส Lockouts ที่รับผิดชอบในการกำหนดว่า IP address ควรถูกล็อกหรือไม่ ระยะเวลาของการล็อกควรเป็นเท่าใด หากการล็อกแอ็คทีฟยังคงใช้ได้หรือหมดอายุ ฯลฯ คลาสนั้นประกอบด้วย should_get_locked_out() รับผิดชอบในการกำหนด ว่าที่อยู่ IP ควรถูกล็อคหรือไม่ วิธีการนั้นจะต้องอ่านจำนวนการลองใหม่สูงสุดที่อนุญาตก่อนที่ที่อยู่ IP จะถูกล็อก ซึ่งเป็นค่าที่กำหนดค่าได้ ซึ่งหมายความว่าเก็บไว้เป็น ตัวเลือก

ดังนั้น โค้ดที่เราเพิ่งอธิบายจะมีลักษณะดังนี้:

 class Lockouts { // ... /** * @var WP_Options An instance of `WP_Options`. */ private $options; /** * Lockouts constructor */ public function __construct() { $this->options = new WP_Options(); } /** * Return the number of retries. * * @return int */ private function get_number_of_retries() { // ... } /** * Check whether this IP address should get locked out. * * @return bool */ public function should_get_locked_out() { $retries = $this->get_number_of_retries(); $allowed_retries = $this->options->get( 'allowed_retries' ); return $retries % $allowed_retries === 0; } // ... }

โดยพื้นฐานแล้ว เรากำลังสร้างอินสแตนซ์ใหม่ของ WP_Options ในตัวสร้าง จากนั้นใช้อินสแตนซ์นั้นเพื่อดึงค่าของตัวเลือกที่อนุญาต allowed_retries

ไม่เป็นไร แต่เราต้องจำไว้ว่าคลาส Lockouts ของเราตอนนี้ขึ้นอยู่กับ WP_Options เราเรียก WP_Options การ พึ่งพา

หากความต้องการของเราเปลี่ยนแปลงในอนาคต เช่น เราจำเป็นต้องอ่าน/เขียนตัวเลือกบนฐานข้อมูลภายนอก เราจำเป็นต้องแทนที่ WP_Options ด้วยคลาส DB_Options ดูเหมือนจะไม่เลวร้ายนัก หากเราจำเป็นต้องดึงตัวเลือกในคลาสเดียวเท่านั้น อย่างไรก็ตาม อาจมีเรื่องยุ่งยากเล็กน้อยเมื่อมีคลาสจำนวนมากที่มีการพึ่งพาหลายรายการ การเปลี่ยนแปลงใด ๆ ในการพึ่งพาเดียวมีแนวโน้มที่จะกระเพื่อมข้าม codebase บังคับให้เราแก้ไขคลาสหากการขึ้นต่อกันอย่างใดอย่างหนึ่งเปลี่ยนแปลง

เราสามารถขจัดปัญหานี้ได้โดยการเขียนโค้ดใหม่เพื่อให้เป็นไปตาม หลักการผกผันการพึ่งพา

ดีคัปปลิ้ง

หลักการผกผันการพึ่งพา (DIP) ตัว “D” ใน SOLID ระบุว่า:

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

ในกรณีของเราคลาส Lockouts คือ "โมดูลระดับสูง" และขึ้นอยู่กับ "โมดูลระดับต่ำ" ซึ่งเป็นคลาส WP_Options

เราจะเปลี่ยนสิ่งนั้นโดยใช้ Dependency Injection ซึ่งง่ายกว่าที่คิด คลาส Lockouts ของเราจะได้รับออบเจ็กต์ที่ขึ้นอยู่กับ แทนที่จะสร้างมันขึ้นมา

 class Lockouts { // ... /** * Lockouts constructor. * * @param WP_Options $options */ public function __construct( WP_Options $options ) { $this->options = $options; } // ... }

ดังนั้นเราจึง ใส่ การพึ่งพา:

 $options = new WP_Options(); $lockouts = new Lockouts( $options );

เราเพิ่งทำให้คลาส Lockouts ของเราดูแลรักษาได้ง่ายขึ้น เนื่องจากตอนนี้มีการผนวกกับการพึ่งพา WP_Options อย่างหลวมๆ นอกจากนี้ เราจะสามารถจำลองการขึ้นต่อกัน ทำให้โค้ดของเราทดสอบได้ง่ายขึ้น การแทนที่ WP_Options ด้วยอ็อบเจ็กต์ที่เลียนแบบพฤติกรรมจะทำให้เราสามารถทดสอบโค้ดของเราโดยไม่ต้องดำเนินการค้นหาใดๆ บนฐานข้อมูลจริงๆ

 /** * Lockouts constructor. * * @param WP_Options $options */ public function __construct( WP_Options $options ) { $this->options = $options; }

แม้ว่าเราจะให้การควบคุมการพึ่งพาของ Lockouts กับคลาสอื่น (ซึ่งต่างจาก Lockouts ที่ควบคุมการพึ่งพานั้นเอง) Lockouts ยังคงคาดหวังวัตถุ WP_Options หมายความว่ายังคงขึ้นอยู่กับคลาส WP_Options ที่เป็นรูปธรรม แทนที่จะเป็นสิ่งที่เป็นนามธรรม ตามที่กล่าวไว้ก่อนหน้านี้ ทั้งสองโมดูล ควรขึ้นอยู่กับ abstractions

มาแก้ไขกันเถอะ!

 /** * Lockouts constructor. * * @param Options $options */ public function __construct( Options $options ) { $this->options = $options; }

และเพียงแค่เปลี่ยนประเภทของอาร์กิวเมนต์ $options จากคลาส WP_Options เป็นอินเทอร์เฟซ Options คลาส Lockouts ของเราจะขึ้นอยู่กับสิ่งที่เป็นนามธรรมและเราสามารถส่งออบเจ็กต์ DB_Options หรืออินสแตนซ์ของคลาสใดๆ ที่ใช้อินเทอร์เฟซเดียวกันได้ ให้กับตัวสร้าง

ความรับผิดชอบเดียว

เป็นที่น่าสังเกตว่าเราใช้วิธีการที่เรียกว่า should_get_locked_out() เพื่อตรวจสอบว่าที่อยู่ IP ควรถูกล็อคหรือไม่

 /** * Check whether this IP address should get locked out. * * @return bool */ public function should_get_locked_out() { $retries = $this->get_number_of_retries(); $allowed_retries = $this->options->get( 'allowed_retries' ); return $retries % $allowed_retries === 0; }

เราสามารถเขียน one-liner ได้ง่ายๆ ดังนี้

 if ( $this->get_number_of_retries() % $this->options->get( 'allowed_retries' ) === 0 ) {

อย่างไรก็ตาม การย้ายตรรกะนั้นไปใช้วิธีการเล็กๆ น้อยๆ ของมันเองนั้นมีประโยชน์มากมาย

  • หากเงื่อนไขในการพิจารณาว่าที่อยู่ IP ควรถูกล็อคหรือไม่มีการเปลี่ยนแปลง เราจะต้องแก้ไขวิธีการนี้เท่านั้น (แทนที่จะค้นหาคำสั่ง if ของเราทั้งหมด)
  • การเขียนแบบทดสอบหน่วยจะง่ายขึ้นเมื่อ "หน่วย" แต่ละหน่วยมีขนาดเล็กลง
  • ปรับปรุงความสามารถในการอ่านโค้ดของเรา อย่างมาก

กำลังอ่านสิ่งนี้:

 if ( $this->should_get_locked_out() ) { // ...

ดูเหมือนว่าเราจะง่ายกว่าการอ่านว่า:

 if ( $this->get_number_of_retries() % $this->options->get( 'allowed_retries' ) === 0 ) { // ...

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

หลักการความรับผิดชอบเดียว (SRP) ตัว “S” ใน SOLID ระบุว่า:

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

หรืออย่างที่โรเบิร์ต ซี. มาร์ติน (“ลุงบ๊อบ”) พูดว่า:

“ชั้นเรียนควรมีเพียงหนึ่งเหตุผลในการเปลี่ยนแปลง”

ทบทวนไฟล์ปลั๊กอินหลักอีกครั้ง

ในขณะนี้ ไฟล์ปลั๊กอินหลักของเรามีเพียงสิ่งนี้:

 /** * 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 ( ! defined( 'ABSPATH' ) ) { exit; }

อีกครั้ง เราจะรวมทุกอย่างไว้ในคลาส Plugin คราวนี้เพื่อหลีกเลี่ยงการตั้งชื่อที่ขัดแย้งกัน

 namespace Pressidium\Limit_Login_Attempts; if ( ! defined( 'ABSPATH' ) ) { exit; } class Plugin { /** * Plugin constructor. */ public function __construct() { // ... } }

เราจะยกตัวอย่างคลาส Plugin นี้ที่ส่วนท้ายของไฟล์ ซึ่งจะรันโค้ดในตัวสร้าง

 new Plugin();

ใน Constructor เราจะพูดถึงการทำงานของ plugins_loaded ซึ่งจะเริ่มทำงานเมื่อโหลดปลั๊กอินที่เปิดใช้งานแล้ว

 public function __construct() { add_action( 'plugins_loaded', array( $this, 'init' ) ); } public function init() { // Initialization }

นอกจากนี้ เราจะเรียกเมธอด require_files() เพื่อโหลดไฟล์ PHP ทั้งหมดของเรา

 public function __construct() { $this->require_files(); add_action( 'plugins_loaded', array( $this, 'init' ) ); } private function require_files() { require_once __DIR__ . '/includes/Sections/Section.php'; require_once __DIR__ . '/includes/Pages/Admin_Page.php'; require_once __DIR__ . '/includes/Pages/Settings_Page.php'; // ... }

สุดท้าย เราจะเริ่มต้นปลั๊กอินของเราโดยสร้างบางอ็อบเจ็กต์ในเมธอด init() ของเรา

หมายเหตุ: ตัวอย่างต่อไปนี้มีเพียงส่วนเล็ก ๆ ของไฟล์ปลั๊กอินหลัก คุณสามารถอ่านไฟล์จริงได้ที่ที่เก็บ GitHub ของปลั๊กอิน

 public function init() { $options = new Options(); $hooks_manager = new Hooks_Manager(); $settings_page = new Settings_Page( $options ); $hooks_manager->register( $settings_page ); // ... }

การจัดระเบียบไฟล์

การจัดระเบียบไฟล์ของคุณเป็นสิ่งสำคัญ โดยเฉพาะอย่างยิ่งเมื่อทำงานกับปลั๊กอินขนาดใหญ่ที่มีโค้ดจำนวนมาก โครงสร้างโฟลเดอร์ของคุณควรจัดกลุ่มไฟล์ที่คล้ายกันเข้าด้วยกัน ช่วยให้คุณและเพื่อนร่วมทีมจัดระเบียบได้

เราได้กำหนดเนมสเปซแล้ว ( Pressidium\Limit_Login_Attempts ) ที่มีเนมสเปซย่อยหลายรายการสำหรับ Pages , Sections , Fields , Elements ฯลฯ หลังจากลำดับชั้นในการจัดระเบียบไดเร็กทอรีและไฟล์ของเรา เราก็ได้โครงสร้างที่คล้ายกับสิ่งนี้:

 . ├── includes │ ├── Hooks │ │ ├── Actions.php │ │ ├── Filters.php │ │ └── Hooks_Manager.php │ ├── Pages │ │ ├── Admin_Page.php │ │ └── Settings_Page.php │ ├── Sections │ │ ├── Fields │ │ │ ├── Elements │ │ │ │ ├── Checkbox_Element.php │ │ │ │ ├── Custom_Element.php │ │ │ │ ├── Element.php │ │ │ │ ├── Number_Element.php │ │ │ │ └── Radio_Element.php │ │ │ └── Field.php │ │ └── Section.php │ └── WP_Options.php ├── prsdm-limit-login-attempts.php └── uninstall.php

แต่ละไฟล์มีคลาสเดียว ไฟล์ถูกตั้งชื่อตามคลาสที่มีอยู่ และไดเร็กทอรีและไดเร็กทอรีย่อยจะถูกตั้งชื่อตามเนมสเปซ (sub-)

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

บทสรุป

ยินดีด้วย! คุณได้ทำชุดบทความของเราเกี่ยวกับ WordPress และการเขียนโปรแกรมเชิงวัตถุเสร็จแล้ว

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

นี่คือบทสรุปโดยย่อของสิ่งที่เรากล่าวถึงในซีรีส์นี้:

  • การรวบรวมความต้องการ: เราตัดสินใจว่าปลั๊กอินควรทำอย่างไร
  • การออกแบบ: เราคิดว่าปลั๊กอินจะมีโครงสร้างอย่างไร ความสัมพันธ์ระหว่างคลาสที่มีศักยภาพของเรา และภาพรวมระดับสูงของนามธรรมของเรา
  • การใช้งาน: เราเขียนโค้ดจริงของส่วนสำคัญของปลั๊กอิน ขณะทำเช่นนั้น เราได้แนะนำให้คุณรู้จักกับแนวคิดและหลักการหลายประการ

อย่างไรก็ตาม เราแทบไม่ได้ขีดข่วนพื้นผิวของสิ่งที่ OOP เป็นและนำเสนอ การฝึกฝนทักษะใหม่ให้เก่งนั้นต้องอาศัยการฝึกฝน ดังนั้นให้เริ่มสร้างปลั๊กอิน WordPress เชิงวัตถุของคุณเอง มีความสุขในการเข้ารหัส!

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

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