第 5 部分 – WordPress 和麵向對象編程:一個 WordPress 示例 – 實現:管理菜單

已發表: 2022-02-04

在我們之前關於面向對象編程的文章中,我們討論了我們最終為我們的面向對象插件提出的設計。

現在,我們將進入最激動人心的部分,我們將更深入地研究我們是如何實現它的!

我們將一步一步地引導您完成實現的某些部分,討論面向對象編程的基礎知識、PHP 語法、一些核心概念,我們甚至會瀏覽 SOLID 原則。

在本文結束時,您有望對 OOP 有更好的理解,並對編寫自己的面向對象插件感到興奮!

入門

我們假設您通常熟悉 WordPress 插件開發,因此我們將專注於插件的面向對象方面。 如果您不熟悉插件開發或需要復習,您應該首先學習如何構建您的第一個 WordPress 插件。

讓我們像往常一樣開始,在我們的插件目錄(即/wp-content/plugins/prsdm-limit-login-attempts)下創建一個新的prsdm-limit-login-attempts.php 文件。

主插件文件將包含您已經熟悉的插件標頭:

 /** * 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 設置 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' ) );

您可能會對這裡使用數組感到困惑。 每當我們想將實例化對象的方法作為回調/可調用對像傳遞時,我們可以使用一個數組,該數組在索引 0 處包含一個對象,在索引 1 處包含一個方法名稱。

$this 是什麼?

它是一個偽變量,當從對像上下文中調用方法時可用。 $this是調用對象的值。 在這種情況下, $thisPressidium_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 { // ...

另一個包含Settings_Page類的 WordPress 插件不再是問題,因為它的類和我們的類將不在同一個命名空間中。

在同一個命名空間中實例化我們的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();

添加掛鉤回調

現在,讓我們聲明這些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 { // ... }

這是繼承的一個很好的例子,它是面向對象編程的核心概念之一。 擴展類時,子類(在本例中為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'; } }

但是,對於每個子類,有些值必須不同。 例如,菜單 slug( 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。

由於我們已經在Admin_Page類中定義了add_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一樣通過子類構建,並且其渲染將通過Admin_Page父類的繼承的render()方法完成。

結論

偉大的! 我們創建了負責註冊管理菜單和添加設置頁面的類。

在本系列的下一篇文章中,我們將繼續構建我們的設置頁面並註冊其部分、字段和元素。

單擊此處閱讀面向對象編程系列的第 6 部分

也可以看看

  • WordPress 和麵向對象的編程——概述
  • 第 2 部分 – WordPress 和麵向對象編程:一個真實世界的示例
  • 第 3 部分 – WordPress 和麵向對象編程:A WordPress 示例 – 定義範圍
  • 第 4 部分 – WordPress 和麵向對象編程:一個 WordPress 示例 – 設計