ส่วนที่ 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
การห่อหุ้ม/สิ่งที่เป็นนามธรรม
การห่อหุ้มทุกอย่างไว้ในชั้นเดียว ห่อหุ้มอวัยวะภายใน (ราวกับอยู่ในแคปซูล) โดยพื้นฐานแล้ว "การซ่อน" สิ่งเหล่านี้จากโลกภายนอก คือสิ่งที่เราเรียกว่าการ ห่อหุ้ม การห่อหุ้มเป็นแนวคิดหลักอีกประการหนึ่งของการเขียนโปรแกรมเชิงวัตถุ
เมื่อใช้อินเทอร์เฟซ 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