【第 20 天】WordPress 外掛開發者工具

今天也不寫 code ,來分享在 WordPress 的世界裡,提供了各式各樣的開發外掛,可以協助我們開發 WordPress 的時候,更正確的開發外掛和診斷問題。

Debug Bar

這個外掛會在 WordPress 上面的管理工具 bar 添加一個新的選單,用來顯示 WordPress 查詢、查詢時間這類的資訊,當 WP_DEBUG 設為 true 時,還會協助我們追蹤 PHP 警告和通知。而 SAVEQUERIES 也被設置為啟用時,SQL 查詢也會被追蹤並顯示。

Query Monitor

我們可以在通過這個外掛查看有關資料庫查詢、hook、函式、HTTP 請求這類的狀況和請求。

WP Crontrol

WP Crontrol 可以協助我們查看和控制 WP-Cron 系統中發生的事情。

在管理畫面中,我們可以:

  1. 查看所有 cron 事件及其參數,重複發生,呼叫函數以及查詢下一次到期的時間。
  2. 編輯,刪除並立即運行任何 cron 事件。
  3. 新增 cron 事件。
  4. 批量刪除 cron 事件。
  5. 新增、編輯和刪除自定義 cron 計劃。
  6. 如果您的 Cron 系統似乎無法正常工作,會向您顯示警告消息。

Debug Info

這個外掛會提供有關當前 WordPress 操作環境的資訊,主要提供了:

  1. 該站點上當前正在運行的 WordPress 版本
  2. 當前/活動的 WordPress 主題名稱,主題版本,主題作者和主題URI
  3. 執行中的外掛列表
  4. PHP 版本
  5. MySQL 版本
  6. Apache/Nginx 版本

Debug This

這個外掛也會提供了大量有關 WordPress 安裝的資訊給開發人員。

這邊就簡單分享幾個,大家常用的 WordPress 開發輔助工具,大家如果有推薦的也歡迎留言跟我分享。

我的外掛學習心得也差不多到這邊囉!我覺得無論是 WordPress 外掛開發,或者是學習其他任何知識都是,都不會是簡單 20 天或是 30 天就能學會,例如說光是一個 hook 就可以講得很深,絕對不是像我這樣一言兩語就能說完的,所以我每天的心得文章都僅僅只是淺談!在我這二十天的內容中,有興趣想學更深的話,不妨可以翻翻官方的文件,想必會有更多的收穫喔!

然後明天開始,我們會進入實戰篇,要來開發我們第一個外掛囉!這次的參賽主題是『WordPress 遇上 Chatbot,像極了愛情』,沒錯!接著我們要來開發一個 LINE chatbot 通知的外掛!

那我們明天見吧!

【第 19 天】上架第一個外掛到 WordPress.org

今天我們不寫任何的 code ,今天我們就很單純的聊聊如何上架一個外掛到 WordPress.org 。

WordPress.org 為每一個想要開發外掛的開發者免費提供託管的服務,通過這個服務,我們可以:

  1. 監控外掛被下載的次數
  2. 取得外掛各個版本的使用統計
  3. 得到使用者的回饋和評價

WordPress.org 提供了一個 WordPress 外掛的 API,供開發者監控外掛的統計數據。

規劃外掛

1、盡可能多的測試
我們要確保外掛已經得到了盡可能多的測試,測試涵蓋了各種情況。

2、選擇一個好名稱
選擇名字的時候,確保沒有侵犯商標或與其他的產品名稱產生衝突。例如,如果你不在 Facebook 工作,你就不應該命名你的外掛為 Facebook's Dancing Squirrels,命名為 Dancing Squirrels for Facebook 會更好一點。

3、撰寫優秀的程式碼
README.text 文件是最好的開始,因為這個文件是所有外掛的標準參考點,要確保該文件中包含:

  1. 簡要描述外掛的實際作用是什麼
  2. 安裝說明

4、在 WordPress.org 上發布第一個版本
WordPress.org 外掛目錄是潛在使用者下載和安裝外掛最簡單的方式,把外掛發佈到 WordPress.org 外掛目錄後,你的外掛可以讓使用者通過點擊來下載或更新。

5、擁抱開源
開放程式碼是這個時代最偉大的想法之一,因為他可以跨越國界進行協作。通過鼓勵人們貢獻,你可以讓別人可以和你一樣熱愛你的代碼,下面是幾個開放你源代碼的好地方。

6、傾聽你的使用者
你經常會發現,使用者經常會在比你想像得多得情況下使用你的程式碼,因此這會是個非常有價值的回饋。

7、定期推送新版本
最好的外掛是隨著時間的推移不斷更顯迭代的外掛,每次更新都提供一些小改進,而不用讓用戶等得太辛苦。但是,不斷的更新也會導致用戶很煩!因此,保持一個合適的更新頻率很重要,不要太多,也不要太少。

8、堅持工作
和生活中其他方面一樣,最好的事情來自良好的耐心和努力的工作。

如何上架?

註冊

想要上傳外掛到 WordPress 官方,首先你要先在 WordPress.org 註冊一個賬戶。點擊右上角的 Register 便可以開始註冊

https://ithelp.ithome.com.tw/upload/images/20200919/20121194qoU7nL7Urw.png

提交你的外掛

將外掛打包成為一個 zip 文件,點擊這這裡打開頁面,登入後會看到:

https://ithelp.ithome.com.tw/upload/images/20200919/20121194wr0nw1qKEv.png

點擊 Select File,選擇剛剛打包的 zip 文件,再點下 Upload 按鈕,就可以將你的外掛添加到審核隊列了。

提交完成後,會收到一封來自 WordPress 的信件,提醒你外掛已經成功提交了!

修改外掛

在大部分情況下,第一次提交都會有一些問題,這個時候會收到來自審核團隊發送的信件,告訴你你的外掛有什麼問題,然後根據信件的內容去更新程式碼,並將新的程式碼以附件的形式直接回覆給審核團隊即可。

順利完成的話,應該會在新的回復中,得到一個 SVN 倉庫的位址,我們可以使用 SVN 管理軟件將這個倉庫 clone 一份到本地使用。

上傳外掛

將倉庫 clone 一份到本地後,會發現外掛備放在 trunk 目錄下,還有其他目錄,例如: assets、branches 、tags 目錄,而且底下都沒有文件。

沒錯,我們需要準備一些資料放進去。

Banner

我們需要準備一張標準大小的 Banner (772×250),格式為 png/jpg。以及一張高清大小的 Banner(1544×500),格式為 png/jpg。

Banner 要放在 assets 目錄下,並且 Banner 需要命名為 banner-772x250.pngbanner-1544x500.png

Icon

圖標也需要準備 2 個 png 格式的,或者一個 SVN 格式。

如果要使用 png 格式的,需要準備一個 128×128 的和一個 256×256 的,並且命名為 icon-128x128.pngicon-256x256.png ,放在 assets 目錄下。

如果是 svg 格式的,只需準備一個,文件命名為 icon.svg 即可,一樣放在 assets 目錄下。

Screenshots

截圖文件也同樣要放在 assets 目錄下,並將其命名為 screenshot-1.png 、screenshot-2.png

使用 SVN 上傳

文件放置正確後,執行如下命令,就可以更新新的外掛資訊到官方倉庫了。

svn add .
svn commit -m 'upload assets'

上傳成功後,就可以在外掛頁面查看到外掛資訊囉。自此我們就順利的發佈第一個外掛了,後續就可以把外掛分享給別人、或者讓別人在 WordPress 後台外掛市集找到並且安裝。

【第 18 天】WordPress 操作資料庫

我們寫外掛的時候,會要存資料到資料庫去,一般來說我們就存在 wp_options 表中,可以參考我在第十天分享的內容:如何修改 WordPress 的後台設置選項。或者就是可以存到 postmeta 或 usermeta 表中,可以參考第十一天的文章:如何管理 WordPress metadata

不過還是會有些時候,這些不能滿足我們的需求,那我們就得需要單獨操作資料表了。這篇文章會來講講如何在寫外掛的時候去操作資料表。

創建資料表

一般在寫外掛時,創建資料表有如下三個步驟:

  1. 寫一個 PHP 函式負責建立資料表。
  2. 外掛啟動的時候去呼叫這個函式。

一個 PHP 函式負責建立資料表

我們定義這個函式叫做 eric_crate_table() ,會長成這樣:

function eric_crate_table() {
    global $wpdb;
    $table_name = $wpdb->prefix . "eric";
    if($wpdb->get_var("show tables like '$table_name'") != $weixin_robot_texts_table) {
        $sql = "CREATE TABLE " . $table_name . " (
              `id` bigint(20) NOT NULL AUTO_INCREMENT,
              `title` varchar(255) CHARACTER SET utf8 NOT NULL,
              `description` text CHARACTER SET utf8 NOT NULL,
              PRIMARY KEY (`id`),
              UNIQUE KEY `keyword` (`keyword`)
            ) ENGINE=MyISAM  DEFAULT CHARSET=utf8;
        ";
        require_once(ABSPATH . 'wp-admin/includes/upgrade.php');

        dbDelta($sql);
    }
}

接著我們逐一解釋。首先,預設 WordPress 資料表都是以 wp_ 開頭,但也是會有例外,所以我們向 WordPress 資料庫建立表的時候,要先找找到資料庫表開頭。我們可以在變量 $wpdb->prefix 找到,如下:

global $wpdb;
$table_name = $wpdb->prefix . "eric";

接著,我們得去判斷表是否存在,我們可以通過一條 SHOW TABLES SQL 查詢來判斷。

if($wpdb->get_var("SHOW TABLES LIKE '$table_name'") != $table_name)

再來就是實際來創建一個資料表了。這裡不用直接的 SQL 查詢來創建,而是使用在 wp-admin/upgrade-functions.php 中的 dbDelta() 來建立,所以我們需要載入這個文件。 dbDelta() 函式很方便,他可以用來協助我們檢查當前的表結構。

$sql = "CREATE TABLE " . $tablename . " (
      `id` bigint(20) NOT NULL AUTO_INCREMENT,
      `title` varchar(255) CHARACTER SET utf8 NOT NULL,
      `description` text CHARACTER SET utf8 NOT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=MyISAM  DEFAULT CHARSET=utf8;
";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');

dbDelta($sql);

外掛啟動的時候呼叫創建資料表的函示

我們使用 register_activation_hook() hook 去掛上剛剛我們建立的 eric_crate_table() 函式。關於 register_activation_hook() 這個 hook 的介紹,可以參考我在第六天的介紹:WordPress 啟用、停用、刪除外掛 。

register_activation_hook( __FILE__,'eric_crate_table');

操作資料表

對於 WordPress 核心的文章、頁面、使用者…這類的資料,我們都可以可以通過 WordPress 內建的函式來完成,例如:我們可以透過 query_posts 來查詢文章。

但是,在很多時候,我們可能會定義自己的資料結構,這時,我們就需要了解 WordPress 的 WPDB,並嘗試借助 WPDB 來完成資料的操作。

在使用 WPDB 時,會需要掌握一定的 SQL 基礎,不過 SQL 基礎已經超過這篇文章的範圍了,我就不贅述。如果還不會使用 SQL 對資料庫進行增刪查改,那麼可能要先去學習一下 SQL 的基礎內容,再來繼續看下去。

初識 WPDB

WPDB 讓我們可以輕鬆的實現對 WordPress 資料庫的操作,讓我們可以完成自定義的資料操作需求。在使用 WPDB 時,我們可以直接使用 WordPrss 提供的 $wpdb 全局變數即可。

global $wpdb;

執行查詢

我們也可以使用 WPDB 進行 SQL 語句的查詢。我們會使用 $wpdb 的 query()

global $wpdb;
$wpdb->query('query');

這個方法將會返回所執行的 SQL 語句影響的行數。如果回傳值是 0 或 false ,表示語句有問題。

獲取一行資料

在執行資料庫時,大部分時候需要查詢某筆資料。這個時候,就可以使用 get_row() 來取得。

global $wpdb;
$wpdb->get_row('query', output_type, row_offset);

get_row() 有三個參數:

  1. 需要執行的 SQL 語句。
  2. 輸出結果的類型,我們可以將其設置為 object ,就是輸出一個物件。將其設置為 ARRAY_A,會回傳一個 Key-Value 形式的陣列;設置為 ARRAY_N,回傳一個排序過的陣列。
  3. 設置我們的資料是否要跳過多少行,默認為 0,即取第一個資料。

取得查詢結果

剛剛我們去抓單個資料,但在某些情況下,我們需要取得某一個類型的所有資料。在這個時候,可以使用 get_results()

global $wpdb;
$wpdb->get_results('query', output_type);

插入資料

除了查詢以外,插入新的資料對於我們來說,也是非常重要的。 WordPress 同樣提供了插入的方法 insert()

global $wpdb;
$wpdb->insert( $table, $data, $format );

執行 insert() 時,需要設置表名,並設置要添加的資料(預設需要是一個資料陣列)。 $format 是插入的類型,預設是 array ,不需要做修改。

在執行完成 insert 後,我們可以通過 $wpdb->insert_id 獲得插入後在表內的 id。

更新資料

我們會查詢和新增了,接著我們還會遇到需要更新的時候,此時,我們可以執行 update() 來更新。

global $wpdb;
$wpdb->update( $table, $data, $where, $format = null, $where_format = null );

update() 的參數和前面幾個比,多了很多,但是其實也不難,我們來一一拆解。

在使用 update() 時,需要 $table 設置資料表名。 $data 則需要傳入我們要更改的資料,傳入的值為也是陣列類型。$where 則是傳入我們要抓來更新的條件陣列。$format 則是我們輸入的 data 的類型,預設是 array ,一般來說不需要動到。$where_format 則是是我們查詢的 where 的類型,預設也是 array,一般來說也不太會去修改他。

刪除資料

剩下最後一個操作了,就是刪除。我們可以使用 delete() 來刪除。

global $wpdb;
$wpdb->delete($table, $where, $where_format = null );

在使用 delete() 時,我們一樣需要 $table 設置資料表名。$where 則是傳入我們要抓來刪除的條件陣列。$where_format 則是是我們查詢的 where 的類型,預設 array,一般來說不太會去修改他。

WordPress SQL 查詢語句安全檢查

WordPress 外掛如果想要上架到 WordPress.org 的話,會需要通過官方的安全檢查。

WordPress 的外掛審核團隊在審核外掛的時候,最主要的就是審查安全問題(通常也不會太在意你的外掛具體功能是什麼)。因此,在開發時,我們需要檢查我們的 SQL 語句安全,不會出現安全問題。

我們最怕的就是使用者在我們前台表單輸入資料的時候,做一些壞壞的事情,可能是 SQL injection 之類的(關於 SQL injection 可以上網 Google ,這也超出本篇範圍了,所以也不贅述。)。

因此, WordPress 官方提供了 prepare() 函式來協助我們預防使用者做壞事。該函式可以將使用者的輸入進行安全的轉換,從而確保進入資料庫的語句都是安全的。

$sql = $wpdb->prepare( 'query' [, value_parameter, value_parameter ... ] );

在使用時,我們需要使用 %s ,這樣的方式來替換 SQL 語句中的值,然後在第二個參數中的數組中傳入對應的值,進而進行 SQL 的處理,確保 SQL 語句的安全,就像這樣:

$wpdb->prepare( "
    INSERT INTO $wpdb->postmeta( post_id, meta_key, meta_value ) VALUES ( %d, %s, %s )", 
        10, $metakey, $metavalue )

對於 WordPress 的資料庫操作介紹差不多就到這邊,wpdb class 底下還有很多好用的方法,可以直接去官方文件參考:wpdb

【第 17 天】WP-Cron

Cron 是基於時間的工作排程系統,而 WP-Cron 是 WordPress 中處理工作的基於時間的工作排程系。WordPress 的有很多的核心功能,例如說:檢查更新、排程發布文章這類的功能,都是使用了 WP-Cron。

WP-Cron 的工作原理是,在頁面被讀取的時候,才會開始檢查工作排程列表以查看需要運行的工作。 也因此 WP-Cron 和 Unix 系統的 Cron 運行方式是不一樣的。舉例:如果我們安排了一個工作在早上 10:00 運行,而在這之前都沒有發生任何頁面加載,則該排程也就都不會運行。

為什麼使用 WP-Cron

那為什麼我們要使用 WP-Cron 呢?

因為,很多虛擬主機不允許用戶訪問系統 Cron,但 WordPress 核心和許多外掛還是得需要一個 Cron 系統來執行一些排程工作呀!因此 WordPress 提供了這種通過頁面載入後去觸發 WP-Cron 系統的機制。

雖然這種運作方式,不一定會在特定的時間『準時』運行,但是 WP-Cron 也有一定的機率去完成我們的排程工作,所以還算是可以用。

想較於系統 Cron ,WP-Cron 系統也是有好處的,今天如果是 Unix 系統的工作排程,假設說沒有在特定的時間執行,他就永遠真的不會執行了。而 WP-Cron ,工作將一直在佇列中,直到有一個頁面加載時觸發他們。也就是說每一次頁面載入的時候,會去檢查並且執行過期而沒有運行的工作(不管這個工作過期過久),因此,通過 WP-Cron 設置的排程工作不會沒執行到。

理解 WordPress 工作排程

Unix 系統的 Cron 是會在特定的時間去運行,而 WP-Cron 則是使用時間間隔來模擬系統 Cron。

什麼意思?首先給第一個工作一個運行時間,然後設置一個時間間隔,以秒為單位,之後按照時間間隔重複工作。例如,我們安排了一個 早上 10:00 開始,間隔 300 秒 的排程工作。

會發生什麼事情?該工作首先會在這個 早上 10:00 被運行一次,然後在 早上 10:05 再被運行一次,然後每隔 300 秒就會被再運行一次。

創建一個時間間隔

WordPress 提供了 cron_schedules hook 讓我們可以創建一個過濾器來添加自定義時間間隔,如下:

add_filter( 'cron_schedules', 'example_add_cron_interval' );

function example_add_cron_interval( $schedules ) {
   $schedules[ 'every-5-minutes' ] = array(
       'interval' => 5 * MINUTE_IN_SECONDS,
       'display' => __( 'Every 5 minutes', 'devhub' ) );
    return $schedules;
}

我們就創建了一個新的時間間隔,允許我們每 5 分鐘運行一次 Cron 工作。

啟用 WP Cron 事件

排程週期性工作

為了讓我們的工作執行,我們需要創建一個自定義 hook,並掛載一個 callback function 到這個 hook 上,這是非常重要的一步,忘了這一步,我們的工作永遠不會被執行。

add_action( 'bl_cron_hook', 'bl_cron_exec' );

現在,我們來進行實際的工作排程。另外, WordPress 提供了一個 wp_next_scheduled() 函式來幫助我們檢查一個特定的工作是否已經被排程。

wp_next_scheduled() 函式很簡單,只接收一個參數 hook 的名稱,使用的時候如果有抓到工作排程,就會回傳一個包含下一個執行時間的字串,否則就會回傳 false ,使用方法如下:

wp_next_scheduled( 'bl_cron_hook' );

重複工作

我們使用 wp_schedule_event() 來完成重複工作的呼叫,該函式需要三個必需的參數,參數如下:

  1. $timestamp:首次執行此工作的 Unix timestamp
  2. $recurrence:執行工作的重複時間間隔的名稱
  3. $hook:我們自定義 hook 名稱

我們將使用 5 秒的時間間隔和我們之前創建的 hook 來演示一下:

wp_schedule_event( time(), 'five_seconds', 'bl_cron_hook' );

記住,我們需要首先確保該工作排程尚未被安排,程式碼如下:

if ( ! wp_next_scheduled( 'bl_cron_hook' ) ) {
   wp_schedule_event( time(), 'five_seconds', 'bl_cron_hook' );
}

取消工作排程

當我們不再需要某個排程工作時,我們也可以取消,我們使用 wp_unschedule_event() 函式來取消工作排程,該函式有兩個參數:

  1. $timestamp:下一次工作的 timestamp
  2. $hook:我們自定義 hook 名稱

這個函式不僅可以取消在每個 timestamp 執行的排程工作,還可以取消未來所有的工作排程。

得到下一個工作的 timestamp

如果不知道下一個工作的 timestamp,我們可以使用 wp_next_scheduled() 函式來查找,該函式需要一個參數:

  1. $hook:我們自定義的 hook 名稱

把上面的程式碼組合在一起使用,看起來應該像下面這樣:

$timestamp = wp_next_scheduled( 'bl_cron_hook' );
wp_unschedule_event( $timestamp, 'bl_cron_hook' );

當我們不再需要某些工作排程的時候,取消這些工作排程非常重要,不然 WordPress 會繼續嘗試執行他們。

取消排程工作的重要時機就是禁用外掛的時候,很多外掛都忘記了這一點,忘記清理不再需要的工作排程。 而 WordPress 也為我們提供了一個名為 register_deactivation_hook() 函式,允許開發人員在外掛停用的時候使用,詳細可以參考我第六天分享的內容:WordPress 啟用、停用、刪除外掛

程式碼如下:
PHP

register_deactivation_hook( __FILE__, 'bl_deactivate' );

function bl_deactivate() {
   $timestamp = wp_next_scheduled( 'bl_cron_hook' );
   wp_unschedule_event( $timestamp, 'bl_cron_hook' );
}

掛載 WP-Cron 到系統 Corn

如我們上面介紹的,WP-Cron 的執行依賴於頁面加載,不一定會嚴格『準時』執行。但是如果我們真的有那種必需『準時』執行的工作,這會是一個問題。

還好,我們有一個簡單的解決辦法來避免這個問題。我們只需將系統的工作排程設置為按我們所需的時間間隔執行即可。什麼意思?

就是我們直接使用工具向 wp-cron.php 文件發送請求。

關掉 WP-Corn

在這之前,因為我們會直接在系統上安排工作排程,也就不需要 WordPress 在每個頁面載入的時候,幫我們執行工作排程,所以我們可以先禁用掉這個功能,來節約這個不必要的伺服器負擔。

我們可以在 wp-config.php 中設定開關,在 wp-config.php 中加上以下程式碼即可。

define('DISABLE_WP_CRON', true);

設定系統工作排程

Mac OS X 和 Linux

因為我手邊沒有 Windows ,所以這邊我就只有介紹 Mac OS X 和 Linux 。用 Windows 的朋友可能要另外爬文了。Mac OS X 和 Linux 的部分都是使用 Cron 作為基於時間的排程系統,可以直接從 terminal 設定。

crontab -e

Cron 有一個特定的語法。

https://ithelp.ithome.com.tw/upload/images/20200917/20121194ht7Y6g7FDK.png

設置工作排程的時候,如果不考慮的時間段,我們會使用 * 號代替。例如,如果我們需要每隔 15 分鐘運行一次命令,而不考慮小時、日期和月份,排程工作設置如下:

15 * * * * command

許多伺服器都自帶了 wget 工具,我們可以直接使用這個工具來訪問 WordPress 的 Cron 文件。

wget http://YOUR_SITE_URL/wp-cron.php

搭配上剛剛學到的 corn ,假設我們每天凌晨觸發網站的 WordPress Cron ,最後的 Cron 設置會變成這樣:

0 0 * * * wget http://YOUR_SITE_URL/wp-cron.php

【第 16 天】JavaScript、Ajax 和 jQuery 應用到 WordPress

不知不覺也玩了 WordPress 的各種 hook 和 function 半個月了,也差不多剩下幾個章節就要告一個段落了,接著就會開始實際開發一個外掛,並且記錄下來。

那們我們今天就要來看看 JavaScript、Ajax 和 jQuery 如何應用到 WordPress 上面。至於 JavaScript、Ajax 和 jQuery 是什麼?不是我這次的重點,有興趣的朋友,可以自行去 Google 一下,會很多資料可以參考。

PHP 後端和插入前端文件

我們目的是為了在伺服器端可以處理 Ajax 請求,我們來先想想邏輯。

我們需要在 PHP 中做兩個工作:

  1. 把 jQuery 放到前端,並且轉換 PHP 變數成全局 JavaScript 變數再放入頁面。
  2. 編寫處理 Ajax 請求的函式。

跟前端打交道

插入 JavaScript 腳本

我們需要以 <meta> 鏈接的形式顯示在頁面的 <head> 部分。

插入

在 WordPress 中,我們可以直接使用 wp_enqueue_script() 函式在頁面的 <head> 中插入一個 meta js 連結,而不是在 <head> 中寫死這些連結。

然而 wp_enqueue_script() 函式可以接受 3 個參數

  1. 其他函式中引用此 JavaScript 的名稱。
  2. JavaScript 的 URL,我們可以使用 plugins_url() 函式建立正確的 URL。
  3. JavaScript 所依賴的其他 JavaScript 的名稱陣列。由於我們使用 jQuery 發送 Ajax 請求,因此,一定要在陣列中放上 jquery

大概會是長這樣:

wp_enqueue_script( 'ajax-script',
   plugins_url( '/js/myjquery.js', __FILE__ ),
   array('jquery')
);

WordPress 載入後,我們必須從幾個 action hook 中把腳本插入頁面中,我們可以用下面幾個 hook 去插入。

  1. 後台頁面,使用 admin_enqueue_scripts
  2. 前台頁面,使用 wp_enqueue_scripts
  3. 登入頁面,使用 login_enqueue_scripts

我們先拿 admin_enqueue_scripts hook 為例。我們會把當前頁面的文件名傳來遞給,我們可以使用這些資料在需要的頁面上插入我們的 JavaScript。範例如下:

add_action( 'admin_enqueue_scripts', 'my_enqueue' );
function my_enqueue( $hook ) {
   if( 'myplugin_settings.php' != $hook ) return;
   wp_enqueue_script( 'ajax-script',
      plugins_url( '/js/myjquery.js', __FILE__ ),
      array( 'jquery' )
   );
}

這樣我們就可以順利的插入我們自己設計的 JavaScript 了。

驗證請求

順帶一提,為了可以驗證 jQuery Ajax 發送的請求是不是合法的,我們需要創建一個 nonce 隨機數。如此以來就只有我們的 PHP 和 jQuery 可以使用這個隨機數。收到請求後,我們可以驗證這個隨機數和我們創建的值是否一樣,確保請求是安全的!下面示範如何創建了一個隨機數:

$title_nonce = wp_create_nonce( 'title_example' );

參數 title_example 可以是任何字符串

轉換 PHP 變數成為 JavaScript 全域變數

我們回想一下前面 jQuery 的部分,由 PHP 創建的供 jQuery 使用的資料被傳入名稱為 my_ajax_obj 的全局物件中。

在我們的例子中,這個數據是一個隨機數和 admin-ajax.php 的完整 URL。分配物件屬性和創建全局 JavaScript 物件的過程,我們會使用 wp_localize_script() 來處理:

wp_localize_script( 'ajax-script', 'my_ajax_obj', array(
   'ajax_url' => admin_url( 'admin-ajax.php' ),
   'nonce'    => $title_nonce, // It is common practice to comma after
) );

最後透過一個 hook 完成所有處理,代碼如下:

add_action( 'admin_enqueue_scripts', 'my_enqueue' );
function my_enqueue( $hook ) {
   if( 'myplugin_settings.php' != $hook ) return;
   wp_enqueue_script( 'ajax-script',
      plugins_url( '/js/myjquery.js', __FILE__ ),
      array( 'jquery' )
   );
   $title_nonce = wp_create_nonce( 'title_example' );
   wp_localize_script( 'ajax-script', 'my_ajax_obj', array(
      'ajax_url' => admin_url( 'admin-ajax.php' ),
      'nonce'    => $title_nonce,
   ) );
}

跟後端打交道

後端 PHP 代碼的另一個主要組成部分,是處理 Ajax 提交資料的程序。這個處理函式接收 POST 資料,執行某些操作,然後將處理的結果返回給瀏覽器。

函式掛載到哪個 hook 上決定了 Ajax 請求處理函式是否會要求用戶登錄,使用哪個程序處理 Ajax 請求取決於 jQuery 的 action 參數傳遞了什麼值。

因為我們的 Ajax 交互是在外掛的設置頁面使用的,所以必須要求用戶登錄才可以發送 Ajax 請求,範例如下。

add_action( 'wp_ajax_my_tag_count', 'my_ajax_handler' );
function my_ajax_handler() {
   // Handle the ajax request
   wp_die(); // All ajax handlers die when finished
}

我們的 Ajax 處理程序需要做的第一件事就是使用 check_ajax_referer() 函式去驗證 Ajax 請求發送的隨機數,就是我們上面創建的隨機數的值。

check_ajax_referer( 'title_example' );

上面函式的參數必須於前面 wp_create_nonce 的參數相同,如果 Nonce 不一致,該處理函式就會自動結束,如果這是一個真正的隨機數,這裡的驗證就通不過了。

資料

下一步,我們就需要處理 jQuery 發送過來的 $_POST[‘title’] 數據了,我們可以使用 update_user_meta() 函式保存為用戶的元數據。

update_user_meta( get_current_user_id(), 'title_preference', $_POST['title']);

然後,我們創建一個查詢,取得標籤為所選標題的文章數。

$args = array(
   'tag' => $_POST['title'],
);
$the_query = new WP_Query( $args );

太棒了,我們終於可以將回應發回給 jQuery 腳本了,我們有幾種方法傳輸數據,我們先來看一下這幾種格式。

XML

WordPress 提供了 WP_Ajax_Response。WP_Ajax_Response 會生成一個 XML 格式的回應。

JSON

這種格式輕量且易用,WordPress 提供了 wp_send_json 函式來幫我們把數據轉換成 JSON ,發送給客戶端,然後結束。

總結

這裡是我們前面討論的所有程式碼總結,一個用於 jQuery ,一個用於 PHP 。

PHP
下面程式碼應該位於外掛的某個 PHP 文件中。

<?php add_action('admin_enqueue_scripts', 'my_enqueue');
function my_enqueue($hook) {
   if( 'myplugin_settings.php' != $hook) return;
   wp_enqueue_script( 'ajax-script',
      plugins_url( '/js/myjquery.js', __FILE__ ),
      array('jquery')
   );
   $title_nonce = wp_create_nonce('title_example');
   wp_localize_script('ajax-script', 'my_ajax_obj', array(
      'ajax_url' => admin_url( 'admin-ajax.php' ),
      'nonce'    => $title_nonce,
   ));
}

add_action('wp_ajax_my_tag_count', 'my_ajax_handler');
function my_ajax_handler() {
   check_ajax_referer('title_example');
   update_user_meta( get_current_user_id(), 'title_preference', $_POST['title']);
   $args = array(
      'tag' => $_POST['title'],
   );
   $the_query = new WP_Query( $args );
   echo $_POST['title'].' ('.$the_query->post_count.') ';
   wp_die(); // all ajax handlers should die when finished
}

jQuery

下面的程式碼位於外掛文件夾下的文件 js/myjquery.js 中。

jQuery(document).ready(function($) {       //wrapper
    $(.pref).change(function() {         //event
        var this2 = this;                  //use in callback
        $.post(my_ajax_obj.ajax_url, {     //POST request
            _ajax_nonce: my_ajax_obj.nonce, //nonce
            action: my_tag_count,        //action
            title: this.value              //data
        }, function(data) {                //callback
            this2.nextSibling.remove();    //remove the current title
            $(this2).after(data);          //insert server response
        });
    });
});

【第 15 天】WordPress 的 HTTP API

HTTP 是一種用於分佈式、協作式和超媒體訊息系統的應用層協定,有興趣的可以參考維基百科對於 HTTP 的介紹,這裡就不贅述了

寫過 PHP 的朋友,應該知道有很多方法可以發送 HTTP 請求。而 WordPress 也提供了許多好用的 HTTP API 讓我們可以對第三方發送 HTTP 請求。

從 API 獲取數據

因為 GitHub 提供了一個很完整的 API ,所以我們就先從打 GitHub 的 API 開始做學習。我們先挑一隻不需要權限就能直接使用的 API 來打打看。

GET Method

首先我們可以使用 wp_remote_get() 函式來發送一個 GET 請求。

wp_remote_get()的參數如下:

  1. $url – URL ,必須是使用標準的 HTTP URL 格式
  2. $args – 選填,下面有 $args 參數的一些默認值,我們可以通過 $args 參數設置這些值。
  3. method:HTTP 發送方法
  4. timeout:等待時間
  5. redirection:跳轉之前的等待時間
  6. httpversion:HTTP 版本
  7. blocking:頁面的其他部分是否需要等待此操作完成之前才能載入
  8. headers:放 header 資訊,是的陣列
  9. body:放 body 資訊,是的陣列
  10. cookies:放 cookies 資訊,是的陣列

下面,讓我們打 GitHub 賬戶的 URL,看看可以得到什麼樣的信息。

$response = wp_remote_get( 'https://api.github.com/users/eric0324' );

$response 將包含我們請求的資訊後回傳的,所有 headers 資訊、内容和 metadata。

獲取 Body 信息

通常我們只需要使用回傳的 body,所以 WordPress 也提供我們一個很好用的函式 wp_remote_retrieve_body(),我們可以使用 wp_remote_retrieve_body() 來獲取這個信息。這個函數只需要一個參數,就是把剛剛的 response 丟進去就可以囉!

$response = wp_remote_get( 'https://api.github.com/users/eric0324' );
$body = wp_remote_retrieve_body( $response );

這樣我們就可以得到 body

string(1246) "{"login":"eric0324","id":3984670,"node_id":"MDQ6VXNlcjM5ODQ2NzA=","avatar_url":"https://avatars0.githubusercontent.com/u/3984670?v=4","gravatar_id":"","url":"https://api.github.com/users/eric0324","html_url":"https://github.com/eric0324","followers_url":"https://api.github.com/users/eric0324/followers","following_url":"https://api.github.com/users/eric0324/following{/other_user}","gists_url":"https://api.github.com/users/eric0324/gists{/gist_id}","starred_url":"https://api.github.com/users/eric0324/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/eric0324/subscriptions","organizations_url":"https://api.github.com/users/eric0324/orgs","repos_url":"https://api.github.com/users/eric0324/repos","events_url":"https://api.github.com/users/eric0324/events{/privacy}","received_events_url":"https://api.github.com/users/eric0324/received_events","type":"User","site_admin":false,"name":"Eric Wu","company":"Sat. Knowledge","blog":"https://www.ericwu.asia/","location":"Taiwan","email":null,"hireable":true,"bio":"Web developer / Chatbot developer / ♥ PHP","twitter_username":null,"public_repos":32,"public_gists":2,"followers":22,"following":18,"created_at":"2013-03-27T12:06:21Z","updated_at":"2020-09-01T07:44:05Z"}"

獲取回傳碼

為了確保我們的請求是成功的,我們有時候可能會想要檢查回傳碼,我們可以使用:wp_remote_retrieve_response_code() 來取得。

會回傳一個 int

int(200)

獲取特定的 Header 信息

有時候,我們需要拿到一個特定的 header ,我們可以使用 wp_remote_retrieve_header() 函式來獲取,這個函式和上面幾個比較不一樣的是,他會需要兩個參數。

$response:來自請求的回應信息
$header:想要取得 header 哪個 key 的 value

舉個例子,例如我想拿到 header 中的 最後修改時間

$response = wp_remote_get( 'https://api.github.com/users/eric0324' );
$last_modified = wp_remote_retrieve_header( $response, 'last-modified' );

我們得到了

string(29) "Tue, 01 Sep 2020 07:44:05 GMT"

使用 GET 進行基本身份驗證

有些 API 為了安全,會提供身份驗證的機制,這種情況非常的常見,所以我們也來看一下,怎麼把我們的授權資訊傳遞給 wp_remote_get() 函數,還記得剛剛說的第二個參數嗎?就是放進去即可,其他 HTTP 方法是也一樣的。

$token = 'i_am_token'
$args = array(
   'headers' => array(
      'Authorization' => 'Bearer ' . $token
   )
);
wp_remote_get( $url, $args );

POST Method

有些情況,我們會要送資料給第三方的 API ,我們就會用 POST Method 。WordPress 也提供了 wp_remote_post() 函式讓我們輕鬆完成他。

該函式的參數和 wp_remote_get() 完全一樣。比較不一樣的是,我們可以把需要發送的所有資料放到第二個參數裡面。接著我們就來看看怎麼 POST 資料到第三方 API~

通常第三方 API POST 資料都需要憑證,所以我們只能先想像一下有隻 API 了。假設我們想像的這隻 API 讓我們可以提交一個表單,這表單包含 name, email, subject, comment 這幾個欄位。我們首先需要設置 POST 請求的 body ,建立一個名為 body 的陣列:

$body = array(
   'name' => 'Eric',
   'email' => '[email protected]',
   'subject' => 'Hello World!',
   'comment' => 'Hello World!'
);

然後,把我們設置的 $body 陣列和其他可選的參數設置為 wp_remote_post() 的第二個參數。

$args = array(
   'body' => $body,
   'timeout' => '5',
   'redirection' => '5',
   'httpversion' => '1.0',
   'blocking' => true,
   'headers' => array(),
   'cookies' => array()
);

然後發送 POST 請求:

$response = wp_remote_post( 'http://ericwu.asia/hello', $args );

這樣就送出資料給表單囉!

其他 Method

大家可能會好奇,那如果今天用到其他 method 呢?別怕,WordPress 肯定也考慮到了這一點,所以提供了wp_remote_request() 函式來幫助我們,這個函式的參數和 wp_remote_get() 一樣,差別在可以讓我們指定 HTTP 的方法。比如,我們需要發送一個 DELETE 方法的請求。

$args = array(
   'method' => 'DELETE'
);
$response = wp_remote_request( 'https://ericwu.asia/hello/delete', $args);

快取

我們經常會把比較耗時、常用的資料保存起來,以便在以後的請求中快速取得。這樣可以避免服務器去浪費大量不必要的時間重新生成重複的資料。對於外部 API 的回應,我們也都可以做快取!因此有時候外部 API 太慢,會拖垮我們網站的速度,讓使用者體驗變差,所以我們也可以來做個快取。

WordPress 的快取 API

WordPress 快取 API 為我們提供了一種方便的方式來存儲和使用快取。

設置快取

我們使用 set_transient() 函式來快取一個物件,該函數接受以下三個參數:

  1. $transient – 快取的名稱
  2. $value – 快取的值
  3. $expiration – 快取的到期時間(單位是秒)

下面來試試看把我們剛剛從 Github API 中獲取的使用者資訊快取一個小時。

$response = wp_remote_get( 'https://api.github.com/users/eric0324' );

set_transient( 'eric_github_userinfo', $response, 60*60 );

取得快取

在取得快取之前,我們會需要先檢查快取是否已經過期?如果過期,我們需要從 API 拿到新的資訊,然後重新設置快取。所以通常都是 set_transient() 和 get_transient() 一起使用。繼續延續剛剛的例子:

$github_userinfo = get_transient( 'eric_github_userinfo' );

if( !$github_userinfo ) {
   $response = wp_remote_get( 'https://api.github.com/users/eric' );
   set_transient( 'eric_github_userinfo', $response, 60*60 );
}

刪除快取

刪除快取是最簡單的,只需要把快取的名稱傳給 delete_transient() 函式就可以了。繼續延續剛剛的例子:

delete_transient( 'eric_github_userinfo' );

【第 14 天】WordPress 使用者角色

在 WordPress 的世界中,每個使用者至少有一個使用者名、密碼和電子郵件。一旦創建了使用者帳號,使用者就可的登錄後台來使用 WordPress 的功能。而 WordPress 使用者的資料,會被存在 wp_user 資料表中。每個使用者都會被分配一個角色,每個角色都會有一些能力。所以,我們也可以自定義能力,我們可以利用角色來限制使用者是否可以執行哪些操作。

操作 WordPress 使用者

新增使用者

我們可以使用 wp_create_user() 或 wp_insert_user() 這兩個函式來辦到。

那這兩個差別在哪裡呢?wp_create_user() 是拿一個使用者名稱、密碼和電子郵件作為參數來建立新的使用者,而 wp_insert_user() 則是可以接受拿描述使用者及和他的屬性組成的陣列或對象來建立使用者。

用 wp_create_user() 來建立

wp_create_user(
   string $username,
   string $password,
   string $email = ''
);

這個函式的邏輯是,首先會使用 wp_slash() 轉換參數,再使用 compact() 函式把上面的值創建為陣列,然後再使用 wp_insert_user() 函式將使用者的資料插入到到資料庫中。有了概念後,我們來新增看看吧!

// 先確認看看試用者是否存在
$user_id = username_exists($user_name);

// 確認信箱也沒被用過
if (!$user_id && email_exists($user_email) === false) {
   // 建立隨機密碼
   $random_password = wp_generate_password(
      $length = 12,
      $include_standard_special_chars = false
   );
   // 建立使用者
   $user_id = wp_create_user(
      $user_name,
      $random_password,
      $user_email
   );
}

用 wp_insert_user() 來建立

wp_insert_user(
   array|object|WP_User $userdata
);

此函式創建使用者的時候,會去執行 user_register action hook,更新使用者的時候,會去執行 profile_update action hook。下面也來試試看新增使用者,並添加使用者資料的過程。

$username = $_POST['username'];
$password = $_POST['password'];
$user_data = [
   'user_login' => $username,
   'user_pass'  => $password,
];

$user_id = wp_insert_user($user_data);

更新使用者

如果需要更新使用者的 metadata,我們可以使用 update_user_meta() 

wp_update_user(
   mixed $userdata
);

下面我們也來看看怎麼更新使用者的 metadata

$user_id = 1;
$website = 'https://ericwu.asia/';

$user_id = wp_update_user(
   [
      'ID'       => $user_id,
      'user_url' => $website,
   ]
);

順帶一提,如果更新了某個使用者的密碼,Cookies 也會被清除,這個被更新的使用者會需要重新登錄。

删除用户

如果需要刪除使用者,我們可以使用 wp_delete_user()

wp_delete_user(
   int $id,
   int $reassign = null
);

第一個參數顯而易懂,就是刪除哪個使用者的 ID ,第二個參數則是,在我們刪除使用者時,我們還可以選擇要把使用者曾經創建的內容,要重新分配給哪個使用者?

當使用者被刪除後,deleted_user hook 會被執行。有一點要注意一下,如果沒有設置 $reassign 參數,或者該參數不是一個有效的使用者 ID,被刪除使用者創建的對應所有內容也都會被刪除喔!

操作使用者 Metadata

WordPress 的 user 資料中只有包含使用者的基本信息:

  1. ID
  2. user_login
  3. user_pass
  4. user_nicename
  5. user_email
  6. user_url
  7. user_registered
  8. user_activation_key
  9. user_status
  10. display_name

我們可以增加自己想要的附加資料,這些資料會被存到 wp_usermeta 資料表中,而這個表使用使用者 ID 去和 wp_user 資料表做關聯。

操作使用者 Metadata

在 WordPress 中,有兩種方法可以操作使用者的 metadata。

  1. 通過個人資料編輯界面中的表單去修改
  2. 不通過個人資料編輯界面中的表單去修改

通過個人資料編輯界面中的表單去修改

我們先來看看怎麼使用表單來更新使用者 metadata 。簡單來說就是我們讓使用者可以去 WordPress 管理界面查看和編輯使用者資料。

在這之前,我們先了解一下這個過程中使用的 hook 有哪些?

  1. edit_user_profile: 使用者編輯了自己的配置文件,就會觸發。
  2. show_user_profile: 使用者編輯了其他人的使用者資料,就會觸發。

下面,我們將在使用者資料編輯介面中,新增一個生日欄位。

<?php
/*
Plugin Name: eric-plugin
*/

<?php

// 輸出 HTML 表單
function eric_usermeta_form_field_birthday($user)
{
    ?>
    <h3>It's Your Birthday</h3>
    <table class=form-table>
        <tr>
            <th>
                <label for=birthday>Birthday</label>
            </th>
            <td>
                <input type=date
                       class=regular-text ltr
                       id=birthday
                       name=birthday
                       value=<?= esc_attr(get_user_meta($user->ID, 'birthday', true)); ?>
                       title=Please use YYYY-MM-DD as the date format.
                       pattern=(19[0-9][0-9]|20[0-9][0-9])-(1[0-2]|0[1-9])-(3[01]|[21][0-9]|0[1-9])
                       required>
                <p class=description>
                    Please enter your birthday date.
                </p>
            </td>
        </tr>
    </table>
    <?php

}

// 處理這個 metadata
function eric_usermeta_form_field_birthday_update($user_id)
{
    // 確認是否有這權限去編輯
    if (!current_user_can('edit_user', $user_id)) {
        return false;
    }

    // 更新 metadata
    return update_user_meta(
        $user_id,
        'birthday',
        $_POST['birthday']
    );
}

// 新增編輯欄位到使用者管理介面
add_action(
    'edit_user_profile',
    'eric_usermeta_form_field_birthday'
);

// 新增顯示欄位到使用者管理介面
add_action(
    'show_user_profile',
    'eric_usermeta_form_field_birthday'
);

// 在 show_user_profile_update hook 插入剛剛我們定義的 function
add_action(
    'show_user_profile_update',
    'eric_usermeta_form_field_birthday_update'
);

// 在 edit_user_profile_update hook 插入剛剛我們定義的 function
add_action(
    'edit_user_profile_update',
    'eric_usermeta_form_field_birthday_update'
);

太好了!我們現在多了一個生日欄位!
https://ithelp.ithome.com.tw/upload/images/20200914/20121194f20kypaaBJ.png

不通過個人資料編輯界面中的表單去修改

這種方法適合用在我們不打算給使用者訪問 WordPress 管理界面的情況下使用。

可用於操作使用者 metadata 的函式包含:add_user_meta(), [update_user_meta()](https://developer.wordpress.org/reference/functions/update_user_meta/, delete_user_meta() 和 get_user_meta()

在這函式中,我們可以只傳遞 $user_id 參數,這樣我們就可以獲取該使用者的所有 meatadata 陣列。

角色和能力

角色和能力是 WordPress 中重要的兩個概念,我們可以用這個來控制使用者的權限。 WordPress 將角色及其能力存在 wp_options 資料表中的 user_roles 欄位中。

角色

角色為使用者定義了一組能力,定義了使用者可以在看到什麼和可以操作什麼。WordPress 的世界中預設有 6 個角色,分別是:

  1. 超級管理員
  2. 管理員
  3. 編輯
  4. 作者
  5. 投稿者
  6. 訂閱使用者

我們可以新增刪除自定義角色(也可以刪除預設的角色)。

新增角色

我們可以使用 add_role() 新增新角色並為其分配能力。

function eric_simple_role() {
   add_role(
      'simple_role',
      'Simple Role',
      [
         'read'         => true,
         'edit_posts'   => true,
         'upload_files' => true,
      ]
   );
}

// add the simple_role
add_action( 'init', 'eric_simple_role' );

我們現在多了一個新的角色叫做 simple role
https://ithelp.ithome.com.tw/upload/images/20200914/20121194YqwLfsjYaf.png

删除角色

如果要刪除角色,我們可以使用 remove_role() 。

function eric_simple_role_remove() {
   remove_role( 'simple_role' );
}

// remove the simple_role
add_action( 'init', 'eric_simple_role_remove' );

能力

能力可以用來定義角色可以做什麼,不可以做什麼。

新增能力

我們可以為角色定義新能力。使用 get_role() 來獲取要新增能力的角色,然後再透過使用 add_cap() 方法,來為角色添加新能力。

function eric_simple_role_caps() {
   // gets the simple_role role object
   $role = get_role( 'simple_role' );

   // add a new capability
   $role->add_cap( 'edit_others_posts', true );
}

// add simple_role capabilities, priority must be after the initial role definition
add_action( 'init', 'eric_simple_role_caps', 11 );

刪除能力

我們可以從角色中移除某個能力,我們使用 remove_cap() 。

使用角色和能力

獲取角色

我們可以通過 get_role() 函式來取得角色和它的所有能力。

get_role(
   string $role
);
判斷使用者能力

我們可以使用 user_can() 函式來檢查使用者是否有指定的角色或是能力。

user_can(
   int|object $user,
   string $capability
);
判斷當前登錄使用者的能力

我們可以直接用 current_user_can() 來判斷當前登錄使用者的能力。

current_user_can() 是 user_can() 的進階版,讓我們可以直接使用當前使用者作為 $user 參數。例如我們要確認現在的這個使用者有沒有權限:

if (current_user_can('edit_posts')) {
   edit_post_link('Edit', '<p>', '</p>');
}

【第 13 天】自定義 WordPress 分類方法

在 WordPress 的世界中,我們可以註冊自定義分類法,分類的方法會被存儲在 term_taxonomy 資料表中。分類方法中有一些分類項目,這些項目則會被存儲在 wp_terms 資料表中。什麼意思呢?我們舉個例,名為 程式設計 的分類方法底下會有多個分類項目,例如說:PHP 和 Python ,大概就是這種關係。

所以接著,我們會來學習怎麼註冊一個自定義的分類方法,以及如何從我們的資料庫中去取得自定義的分類方法內容,然後顯示給用戶。

使用自定義分類法

自定義分類方法

WordPress 預設的分類方法有 分類目錄 和 標籤,在很多時候可能不能滿足我們的需求。所以這個時候,我們就會需要創建自定義分類方法。自定義分類方法通常和自定義文章類型一起使用,為我們自定義的某種內容按照我們自定義的條件分類。

為什麼使用自定義分類方法

有些人可能會好奇,我們什麼時候使用需要分類方法來組織我們在 WordPress 的內容?

假設有一個客戶是美髮店,他需要我們幫他使用 WordPress 創建一個網站,用來展示他的作品。

為了避免 作品集 這種類型的內容和其他內容混在一起,引起管理上的混亂,我們可能就會需要創建一個自定義文章類型來存儲這個設計師的作品。這時候,我們需要創建一個名為 髮型 的分類方法來把 長髮 和 中長髮 這兩種不同的髮型分開。獨立出來後,我們的自定義分類方法還可以在 WordPress 後台擁有自己獨立的選單,來方便我們去做管理,整個很方便!

課程分類方法

剛剛說了很多,所以接著我們就直接來實作看看

我們到寫文章的地方,可以看到預設有 分類 和 標籤 這兩種分類方法。

https://ithelp.ithome.com.tw/upload/images/20200913/20121194ea1rH7LCwC.png

所以我們先來用 init action hook 為內容類型 文章 註冊一個自定義分類方法 作品集

function eric_register_taxonomy_collection() {
    $labels = [
        'name'              => _x( '作品集', 'taxonomy general name' ),
        'singular_name'     => _x( '作品集', 'taxonomy singular name' ),
        'search_items'      => __( '搜尋作品集' ),
        'all_items'         => __( '所有作品集' ),
        'parent_item'       => __( '作品集' ),
        'parent_item_colon' => __( '作品集:' ),
        'edit_item'         => __( '編輯作品集' ),
        'update_item'       => __( '更新作品集' ),
        'add_new_item'      => __( '新增作品集' ),
        'new_item_name'     => __( '新作品集名稱' ),
        'menu_name'         => __( '作品集' ),
    ];
    $args   = [
        'hierarchical'      => true, // make it hierarchical (like categories)
        'labels'            => $labels,
        'show_ui'           => true,
        'show_admin_column' => true,
        'query_var'         => true,
        'show_in_rest'      => true,
        'rewrite'           => [ 'slug' => 'collection' ],
    ];
    register_taxonomy( '作品集', [ 'post' ], $args );
}

add_action( 'init', 'eric_register_taxonomy_collection' );

我們在函示eric_register_taxonomy_collection() 來自定義文章類型。

在函式的最後我們使用了 register_taxonomy() 來註冊上去。其中 我們先定義了自定義分類方法的標籤,也就是我們在後台看到的分類方法的名稱。一個陣列放了一個 post 內容類型,來告訴 WordPress 分類的方法。以及最後一個參數用來指示 WordPress 怎麼設置自定義分類方法。

最後用 add_action() 函式把創建分類方法的操作綁定到了 init action hook 上。

如此一來,WordPress 將自動為 作品集 分類方法創建存檔頁面及其子頁面。

我們自定義分類方法存檔頁面的形式會是 /collection/%%term-slug%%/,其中 /collection/ 是我們自定義的分類方法的標識符,/%%term-slug%%/ 則是我們自定義分類方法中分類項目的別名。

我們將看到一個新的的分類方法。

https://ithelp.ithome.com.tw/upload/images/20200913/20121194cpPAfHXi6K.png

https://ithelp.ithome.com.tw/upload/images/20200913/201211947ODGsHOtlV.png

【第 12 天】自定義 WordPress 文章類型

我們有時候會想自己定義的文章的類型,所以接著,我們就來看看如何註冊自定義文章類型,以及如何從資料庫中取得自定義文章類型內容並且把這些內容顯示給使用者。

自定義文章類型

WordPress 預設有五種文章類型,分別是 post, page, attachment, revision 和 menu。我們有時候,可能需要創建自定義文章類型,比如說:購物網站的商品、遊戲網站的遊戲。

註冊自定義文章類型

因此,我們可以使用 register_post_type() 函式來註冊一個自定義的文章類型。註冊之後,WordPress 會自動幫我們添加一個新文章類型菜單到 wp_psots 資料表中,我們就可以通過這個分類來創建文章。下面做個簡單的示範:

function eric_custom_post_type() {
   register_post_type( 'eric_type',
      [
         'labels'      => [
            'name'          => __( 'Types' ),
            'singular_name' => __( 'Type' ),
         ],
         'public'      => true,
         'has_archive' => true,
      ]
   );
}

add_action( 'init', 'eric_custom_post_type' );

我們多了一個新的文章類型了!
https://ithelp.ithome.com.tw/upload/images/20200912/20121194UkPhcpcEOC.png

網址

如果我們需要為自定義文章類型設置一個別名,可以在 register_post_type() 的 rewrite 參數中添加一個 key value ,如下:

function eric_custom_post_type() {
   register_post_type( 'eric_type',
      [
         'labels'      => [
            'name'          => __( 'Types' ),
            'singular_name' => __( 'Type' ),
         ],
         'public'      => true,
         'has_archive' => true,
         'rewrite'     => [ 'slug' => 'types' ], // my custom slug
      ]
   );
}

add_action( 'init', 'eric_custom_post_type' );

如此一來,我們就可以為我們的自定義文章類型設置一個別名囉!

【第 11 天】如何管理 WordPress metadata

metadata 就是關於資料的資料。簡單來說就是我們創建了類型為商品的自定義文章類型,其中價格就是 metadata 的一種,這個字串就會被存儲在 postmeta 文章 metadata 中。

WordPress 的主要資料(文章、評論、用戶、分類)和他們 metadata 的關係是一對多,也就是一個主要資料可以有很多 metadata 。所以,我們可以在 metadata 中存儲很多附加資訊。

管理文章 metadata

新增 metadata

我們可以使用 add_post_meta() 函式添加 metadata ,該函式接受一個 post_id、一個 meta_key、一個 meta_value 和一個 unique 布林值。

add_post_meta(
    int $post_id,
    string $meta_key,
    mixed $meta_value,
    bool $unique = false
)

我們來依序看每個參數的用途:

meta_key:是外掛在其他地方引用 meta_value 的依據,我們可以使用任意字符串作為名稱,但是為了避免衝突,通常會建議在名稱前面加上前綴,並以下劃線分隔名稱中的單詞,如 eric_featured_menu。

還有一種比較特別的命名方式,就是以底線 _ 開頭的 meta_key 。如果用了這個的話,WordPress 將不會在自定義字串編輯介面顯示。

meta_value:可以是一個字串、一個整數、一個陣列或是一個物件。

unique:可以讓我們聲明某條 metadata 是否為唯一的。什麼意思?比如 price 這個 meta_key,一個產品應該只能有一個價格(對,是唯一的!)。所以我們應該為這條 metadata 的 unique 設定為 true(預設是 false) ,來確保一個產品只有一個價格。

更新 metadata

如果需要更新一個已經存在的 metadata ,我們可以使用 update_post_meta(),用法和 add_post_meta() 差不多。

順帶一提,如果我們使用這個函式更新了一個不存在的 metadata ,該函式將會自動呼叫 add_post_meta() 來新增這個不存在的 metadata 。

刪除 metadata

如果我們需要刪除一個 metadata ,可以使用 delete_post_meta() 函式。

字串轉換

這裡有一個小小的坑,WordPress 在儲存文章的 metadata 的時候,會使用 stripslashes() 轉換資料中的某些字串,比如一個 JSON 串 {key:value with \escaped quotes}:,之後就可能出現無法解析的問題!

因此,我們可以通過使用在 WordPress 3.6 中引入的函式 wp_slash() (),用來修正 stripslashes() 的轉換。使用方法如下:

$escaped_json = '{key:value with \escaped quotes\}';
update_post_meta($id, 'double_escaped_json', wp_slash($escaped_json));

自定義 metadata box

什麼是 metadata box

用戶在編輯文章的時候,編輯界面是由許多個 box 組成:編輯器、發布、分類目錄、標籤等,這些都是 metadata box。因此,我們的外掛理所當然也可以新增自定義的 metadata box 到任何一個文章類型編輯界面。

那麼 WordPress 是怎麼做到的?在顯示文章編輯介面的時候,WordPress 就會自動調用 do_meta_boxes() 函式來掃過一遍所有的 metadata box,並調用 metadata box 中的 callback 參數指定的函式來顯示表單。

為什麼要使用 metadata box?

metadata box 是方便、靈活的模組化文章資料編輯元素,可以讓用戶很方便的編輯當前文章的相關信息。

如果用戶不需要某個 metadata box,他們也可以很方便的將其隱藏,或者是根據需要,也可以對 metadata box 排序,把自己經常使用的 metadata box 放在合適的位置。

新增 metadata box

如果我們需要創建一個 metadata box,必須呼叫 add_meta_box() 函式。下面示範如何在文章編輯界面上添加了一個 metadata box。


function eric_add_custom_box() {
   $screens = ['post', 'eric_cpt'];
   foreach ($screens as $screen) {
      add_meta_box(
         'eric_box_id',           // Unique ID
         'Custom Meta Box Title',  // Box title
         'eric_custom_box_html',  // Content callback, must be of type callable
         $screen                   // Post type
      );
   }
}
add_action('add_meta_boxes', 'eric_add_custom_box');

上面用到了一個 eric_custom_box_html ,這個 callback function 是用來顯示 metadata box 需要的 HTML 表單,程式碼如下:

function eric_custom_box_html($post) {
   ?>
   <label for=eric_field>Description for this field</label>
   <select name=eric_field id=eric_field class=postbox>
      <option value=>Select something...</option>
      <option value=something>Something</option>
   </select>
   <?php
}

這樣我們的文章編輯器下面就多了一個我們自己定義的表單可以給使用者選擇了!

https://ithelp.ithome.com.tw/upload/images/20200911/20121194Er0Aqu23Os.png

上面的範例只包一個下拉列表字段,可能沒什麼意義,所以我們在實際開發時,可以根據需要放上任意類型的表單。

保存自定字段值

我們可以根據需要,在任何一個 action 被觸發的時候,來去保存自定義欄位中輸入的值。在這個例子中,我們使用 save_post action hook。

我們可以把輸入的資料保存在任何地方,甚者 WordPress 之外,由於我們處理的是與 WordPress 文章相關的資料,所以我就很理所當然地把他保存到 post_meta 中。

這個例子,我把保存文章時題傳遞過來的 eric_field 資料存儲到了 _eric_meta_key 這個隱藏 post_meta 中。

function eric_save_postdata($post_id) {
   if (array_key_exists('eric_field', $_POST)) {
      update_post_meta(
         $post_id,
         '_eric_meta_key',
         $_POST['eric_field']
      );
   }
}
add_action('save_post', 'eric_save_postdata');

移除 metadata box

我們也可以從編輯界面移除現有 metadata box,可以使用 remove_meta_box() 函式,這裡有一點要注意的是,傳遞的參數需要和新增 metadata box時候的對應的參數完全一致。