WordPress插件開發(fā)教程手冊(cè) — 元數(shù)據(jù)
本文詳細(xì)介紹了WordPress插件開發(fā)中的元數(shù)據(jù)管理,包括如何添加、更新和刪除元數(shù)據(jù),以及處理字符轉(zhuǎn)義問題。還討論了自定義元數(shù)據(jù)盒子的使用,幫助開發(fā)者更好地利用WordPress數(shù)據(jù)結(jié)構(gòu)的靈活性。
元數(shù)據(jù)就是關(guān)于數(shù)據(jù)的信息,比如一張圖片的大小、種類、創(chuàng)建時(shí)間、尺寸等信息,在 WordPress 中,元數(shù)據(jù)指的是文章、用戶、評(píng)論和分類法項(xiàng)目的附加信息。
舉個(gè)例子,我們創(chuàng)建了類型為 “商品” 的自定義文章類型,其中包含一個(gè)價(jià)格元數(shù)據(jù)字段,這個(gè)字段存儲(chǔ)在 postmeta 文章元數(shù)據(jù)表中。
WordPress 的主要數(shù)據(jù)(文章、評(píng)論、用戶、分類法項(xiàng)目)和他們?cè)獢?shù)據(jù)是一對(duì)多關(guān)系,一個(gè)主要數(shù)據(jù)可以有很多元數(shù)據(jù),所以,我們可以在元數(shù)據(jù)中存儲(chǔ)很多附加數(shù)據(jù)。這就是 WordPress 數(shù)據(jù)結(jié)構(gòu)的靈活性所在。
本章節(jié)我們將討論 管理文章元數(shù)據(jù), 自定義元數(shù)據(jù)盒子, 渲染文章元數(shù)據(jù).
管理文章元數(shù)據(jù)
添加元數(shù)據(jù)
我們可以使用 add_post_meta() 函數(shù)添加元數(shù)據(jù),該函數(shù)接受一個(gè) post_id、一個(gè) meta_key、一個(gè) meta_value 和一個(gè) unique 標(biāo)志。
meta_key 是插件在其他地方引用 meta_value 的依據(jù),我們可以使用類似 ‘mycrazymetakeyname’ 或其他任意字符串作為名稱,但是為了避免沖突和語(yǔ)義化,建議在名稱前面加上前綴,并以下劃線分隔名稱中的單詞,如 wporg_featured_menu,需要注意的是,同一個(gè) meta_key 可以被多次使用來(lái)存儲(chǔ)各種各樣的元數(shù)據(jù)(見下面的唯一標(biāo)志)。
meta_value 可以是一個(gè)字符串、整數(shù)、數(shù)組或?qū)ο蟆H绻鹠eta_value是一個(gè)數(shù)組或?qū)ο?,WordPress 會(huì)在存儲(chǔ)到數(shù)據(jù)庫(kù)之前自動(dòng)序列化他們,并在從數(shù)據(jù)庫(kù)中獲取之前反序列化。
unique 標(biāo)志可以讓我們聲明某條元數(shù)據(jù)相對(duì)于一條主數(shù)據(jù)是否為唯一的,一個(gè)非唯一的 meta_key 相對(duì)于一條主數(shù)據(jù)可以有多個(gè)值,比如 ‘price’ 這個(gè) meta_key,一個(gè)產(chǎn)品的價(jià)格是唯一的,所以我們應(yīng)該為這條元數(shù)據(jù)添加 unique 標(biāo)記,以確保一個(gè)產(chǎn)品只有一個(gè)價(jià)格。
更新元數(shù)據(jù)
如果需要更新一條已經(jīng)存在的元數(shù)據(jù),我們可以使用 update_post_meta(), 如果我們使用這個(gè)函數(shù)更新了一條不存在的元數(shù)據(jù),該函數(shù)自動(dòng)調(diào)用 add_post_meta() 來(lái)幫我們添加這條數(shù)據(jù),該函數(shù)的參數(shù)和 add_post_meta() 一樣。
刪除元數(shù)據(jù)
如果我們需要?jiǎng)h除一條元數(shù)據(jù),使用 delete_post_meta() 函數(shù),該函數(shù)接受一個(gè) post_id,一個(gè) meta_key 和一個(gè)可選的 meta_value。
字符轉(zhuǎn)義
WordPress 在存儲(chǔ)文章元數(shù)據(jù)的時(shí)候會(huì)使用 stripslashes() 轉(zhuǎn)義數(shù)據(jù)中的某些字符串,因此在傳入可能包含 \ 轉(zhuǎn)義字符串的時(shí)候(例如 JSON )需要注意。比如一個(gè) JSON 字符串 {key:value with \escaped quotes\}:
$escaped_json = '{key:value with \escaped quotes\}';
update_post_meta($id, 'escaped_json', $escaped_json);
$broken = get_post_meta($id, 'escaped_json', true);
/*
$broken, after stripslashes(), ends up unparsable:
{key:value with escaped quotes}
*/
解決辦法
通過使用函數(shù) wp_slash() (在 WordPress 3.6 中引入),添加一個(gè)轉(zhuǎn)義級(jí)別,用來(lái)補(bǔ)償 stripslashes() 的轉(zhuǎn)義。
$escaped_json = '{key:value with \escaped quotes\}';
update_post_meta($id, 'double_escaped_json', wp_slash($escaped_json));
$fixed = get_post_meta($id, 'double_escaped_json', true);
/*
$fixed, after stripslashes(), ends up as desired:
{key:value with \escaped quotes\}
*/
隱藏的自定義字段
如果我們打算在插件或主題中使用自定義字段來(lái)存儲(chǔ)數(shù)據(jù),請(qǐng)務(wù)必注意,WordPress 不會(huì)在自定義字段編輯潔面顯示以下劃線 “_” 開頭的 meta_key。我們可以使用 add_meta_box() 函數(shù)來(lái)添加元數(shù)據(jù)盒子以顯示這些隱藏的自定義字段。
下面的例子將添加一個(gè) meta_key 為 “_color” 的隱藏自定義字段,該字段的值為 “red”,這個(gè)字段不會(huì)在 WordPress 默認(rèn)的自定義字段編輯界面中顯示。
add_post_meta( 68, '_color', 'red', true );
隱藏?cái)?shù)組
另外,如果 meta_value 是一個(gè)數(shù)組,即便 meta_key 沒有下劃線,這條元數(shù)據(jù)也不會(huì)在自定義字段編輯界面上顯示。因?yàn)閿?shù)據(jù)庫(kù)中保存的數(shù)據(jù)是被轉(zhuǎn)義過的,我們很難去編輯一條被轉(zhuǎn)義過的數(shù)據(jù)。
自定義元數(shù)據(jù)盒子
什么是元數(shù)據(jù)盒子
用戶編輯文章時(shí),編輯界面由多個(gè)默認(rèn)盒子組成:編輯器、發(fā)布、分類目錄、標(biāo)簽等,這些都是元數(shù)據(jù)盒子。主題和插件也可以添加自定義元數(shù)據(jù)盒子到任何一個(gè)文章類型編輯界面。自定義元數(shù)據(jù)盒子的內(nèi)容通常是 HTML 表單元素,用戶通過表單輸入主題或插件需要的數(shù)據(jù),除了表單,元數(shù)據(jù)盒子的內(nèi)容也可以是任何我們需要的 HTML。
為什么要使用元數(shù)據(jù)盒子?
元數(shù)據(jù)盒子是方便、靈活的模塊化文章數(shù)據(jù)編輯元素,可以讓用戶很方便的編輯當(dāng)前文章的相關(guān)信息,我們的自定義元數(shù)據(jù)盒子和默認(rèn)的文章信息顯示在一個(gè)界面上,他們之間的從屬和相關(guān)關(guān)系很清晰。
如果用戶不需要某個(gè)元數(shù)據(jù)盒子,他們可以很方便的將其隱藏,根據(jù)需要,用戶也可以對(duì)元數(shù)據(jù)盒子排序,把自己經(jīng)常使用的元數(shù)據(jù)盒子放在合適的位置。
添加元數(shù)據(jù)盒子
如果我們需要?jiǎng)?chuàng)建一個(gè)元數(shù)據(jù)盒子,調(diào)用 add_meta_box() 函數(shù)并將執(zhí)行該調(diào)用的函數(shù)掛載到 add_meta_boxes Action 鉤子中即可。下面的示例在文章和 wporg_cpt 自定義文章類型編輯界面上添加了一個(gè)元數(shù)據(jù)盒子。
function wporg_add_custom_box() {
$screens = ['post', 'wporg_cpt'];
foreach ($screens as $screen) {
add_meta_box(
'wporg_box_id', // Unique ID
'Custom Meta Box Title', // Box title
'wporg_custom_box_html', // Content callback, must be of type callable
$screen // Post type
);
}
}
add_action('add_meta_boxes', 'wporg_add_custom_box');
上面的 wporg_custom_box_html 是一個(gè)回調(diào)函數(shù),用來(lái)顯示元數(shù)據(jù)盒子需要的 HTML 表單,該函數(shù)示例代碼如下:
function wporg_custom_box_html($post) {
?>
<label for=wporg_field>Description for this field</label>
<select name=wporg_field id=wporg_field class=postbox>
<option value=>Select something...</option>
<option value=something>Something</option>
<option value=else>Else</option>
</select>
<?php
}
上面的示例只包一個(gè)下拉列表字段,在實(shí)際開發(fā)時(shí),我們可以根據(jù)需要添加任意類型的表單字段,如果需要添加的字段很多,可以考慮按照數(shù)據(jù)上的相似性把這些字段分組,這樣可以讓界面更加清晰和容易使用。
獲取自定義字段值作為表單默認(rèn)值
如果我們已經(jīng)保存了自定義字段,顯示表單的時(shí)候,我們需要把已經(jīng)保存的值作為表單的默認(rèn)值和表單一起顯示,我們可以使用 get_post_meta() 函數(shù)獲取這個(gè)值。下面的示例使用自定義表單的值為下拉選擇表單添加了已選擇的狀態(tài)。
function wporg_custom_box_html($post) {
$value = get_post_meta($post->ID, '_wporg_meta_key', true);
?>
<label for=wporg_field>Description for this field</label>
<select name=wporg_field id=wporg_field class=postbox>
<option value=>Select something...</option>
<option value=something <?php selected($value, 'something'); ?>>Something</option>
<option value=else <?php selected($value, 'else'); ?>>Else</option>
</select>
<?php
}
上面代碼中,selected() 是一個(gè)非常有用的輔助函數(shù),用來(lái)幫助我們?cè)O(shè)置已選中的選項(xiàng)數(shù)據(jù),關(guān)于該函數(shù)的詳細(xì)介紹請(qǐng)參考: selected() 函數(shù)文檔。
保存自定字段值
當(dāng)我們保存或更新一篇文章的時(shí)候,會(huì)觸發(fā)幾個(gè) Action,我們可以根據(jù)需要在任何一個(gè) Action 被觸發(fā)時(shí)保存自定義字段中輸入的值。在這個(gè)例子中,我們使用 save_post Action 鉤子,根據(jù)情況不同,有時(shí)候選擇其他鉤子可能更合適。需要注意的是,更新文章的時(shí)候 save_post 可能會(huì)被觸發(fā)多次,在保存數(shù)據(jù)的時(shí)候,我們需要照顧到這種情況。
我們可以把輸入的數(shù)據(jù)保存在任何地方,甚者 WordPress 之外,由于我們處理的是與 WordPress 文章相關(guān)的數(shù)據(jù),大多數(shù)情況下,保存在 post_meta 中都是一個(gè)不錯(cuò)的選擇。
下面的示例把保存文章時(shí)題傳遞過來(lái)的 wporg_field 數(shù)據(jù)存儲(chǔ)到了 _wporg_meta_key 這個(gè)隱藏 post_meta 中。
function wporg_save_postdata($post_id) {
if (array_key_exists('wporg_field', $_POST)) {
update_post_meta(
$post_id,
'_wporg_meta_key',
$_POST['wporg_field']
);
}
}
add_action('save_post', 'wporg_save_postdata');
上面的示例代碼沒有做必要的安全檢查,在實(shí)際的代碼中,千萬(wàn)不要忘記。
幕后發(fā)生的事情
我們通常不需要關(guān)心幕后發(fā)生的事情,為了內(nèi)容的完整,這里簡(jiǎn)單的說明一下。
WordPress 顯示文章編輯界面的時(shí)候,會(huì)調(diào)用 do_meta_boxes() 函數(shù)遍歷所有元數(shù)據(jù)盒子,并調(diào)用元數(shù)據(jù)盒子中 callback 參數(shù)指定的函數(shù)來(lái)顯示表單,顯示表單內(nèi)容之前,會(huì)加上需要的標(biāo)記(如 div titles 等)。
移除元數(shù)據(jù)盒子
如果我們需要從編輯界面移除現(xiàn)有元數(shù)據(jù)盒子,可以使用 remove_meta_box() 函數(shù),傳遞的參數(shù)需要和添加元數(shù)據(jù)盒子時(shí)候的對(duì)應(yīng)參數(shù)完全一致。如果需要?jiǎng)h除默認(rèn)的元數(shù)據(jù)盒子,可以查看 wp-includes/edit-form-advanced.php 文件中添加默認(rèn)元數(shù)據(jù)盒子的相關(guān)代碼獲取需要的參數(shù)。
其他實(shí)現(xiàn)方式
到目前為止,我們一直在使用面向過程的方法來(lái)添加元數(shù)據(jù)盒子,我們還可以使用其他編程方法來(lái)添加。
面向?qū)ο?/h4>
使用面向?qū)ο蟮姆椒▉?lái)添加元數(shù)據(jù)盒子非常簡(jiǎn)單,并且我們不用擔(dān)心在全局命名空間中的命名沖突。為了節(jié)省內(nèi)存并使實(shí)現(xiàn)更簡(jiǎn)單,下面示例中使用具有靜態(tài)方法的抽象類。
abstract class WPOrg_Meta_Box {
public static function add() {
$screens = ['post', 'wporg_cpt'];
foreach ($screens as $screen) {
add_meta_box(
'wporg_box_id', // Unique ID
'Custom Meta Box Title', // Box title
[self::class, 'html'], // Content callback, must be of type callable
$screen // Post type
);
}
}
public static function save($post_id) {
if (array_key_exists('wporg_field', $_POST)) {
update_post_meta(
$post_id,
'_wporg_meta_key',
$_POST['wporg_field']
);
}
}
public static function html($post) {
$value = get_post_meta($post->ID, '_wporg_meta_key', true);
?>
<label for=wporg_field>Description for this field</label>
<select name=wporg_field id=wporg_field class=postbox>
<option value=>Select something...</option>
<option value=something <?php selected($value, 'something'); ?>>Something</option>
<option value=else <?php selected($value, 'else'); ?>>Else</option>
</select>
<?php
}
}
add_action('add_meta_boxes', ['WPOrg_Meta_Box', 'add']);
add_action('save_post', ['WPOrg_Meta_Box', 'save']);
Ajax
由于元數(shù)據(jù)盒子的 HTML 元素位于編輯文章的表單之內(nèi),在默認(rèn)情況下,元數(shù)據(jù)盒子里面的數(shù)據(jù)會(huì)在保存文章的時(shí)候和文章數(shù)據(jù)一起存儲(chǔ)在 $_POST 超級(jí)變量中提交。根據(jù)需要,我們可以使用 AJAX 來(lái)提升用戶體驗(yàn),讓元數(shù)據(jù)盒子里面的數(shù)據(jù)單獨(dú)提交,不管用戶是否保存了文章。
定義一個(gè)觸發(fā)器
首先,我們需要定義一個(gè)觸發(fā)事情,這個(gè)事件可以是點(diǎn)擊了每個(gè)鏈接、改變了某個(gè)值或者其他任何 JavaScript 事件。在下面的例子中,我們將 change 定義為執(zhí)行 Ajax 請(qǐng)求的觸發(fā)事件。
(function ($, window, document) {
'use strict';
// execute when the DOM is ready
$(document).ready(function () {
// js 'change' event triggered on the wporg_field form field
$('#wporg_field').on('change', function () {
// our code
});
});
}(jQuery, window, document));
客戶端代碼
接下來(lái),我們需要定義觸發(fā)器,也就是說,我們需要編寫 AJAX 客戶端代碼。在下面的例子中,我們將發(fā)起 POST 請(qǐng)求,服務(wù)端響應(yīng)成功或者失敗,來(lái)判斷 wporg_field 是否有效。
(function ($, window, document) {
'use strict';
// execute when the DOM is ready
$(document).ready(function () {
// js 'change' event triggered on the wporg_field form field
$('#wporg_field').on('change', function () {
// jQuery post method, a shorthand for $.ajax with POST
$.post(wporg_meta_box_obj.url, // or ajaxurl
{
action: 'wporg_ajax_change', // POST data, action
wporg_field_value: $('#wporg_field').val() // POST data, wporg_field_value
}, function (data) {
// handle response data
if (data === 'success') {
// perform our success logic
} else if (data === 'failure') {
// perform our failure logic
} else {
// do nothing
}
}
);
});
});
}(jQuery, window, document));
下一步,我們將創(chuàng)建一個(gè)名為 wporg_meta_box_obj 的自定義 JavaScript 對(duì)象,這個(gè)對(duì)象中包含了用于自定義 WrodPress AJAX 操作的 URL。
插入客戶端代碼
為了讓上一步的代碼可以訪問到 wporg_meta_box_obj 這個(gè)JavaScript 對(duì)象,我們需要把這個(gè)對(duì)象加入到前端代碼中,在下面的示例中,我們將 AJAX 功能添加到了文章、wporg_cpt 這兩個(gè)文章類型的編輯界面。
JacaScript 文件所在的路徑為/plugin-name/admin/meta-boxes/js/admin.js,plugin-name 是插件文件夾。
function wporg_meta_box_scripts(){
// get current admin screen, or null
$screen = get_current_screen();
// verify admin screen object
if (is_object($screen)) {
// enqueue only for specific post types
if (in_array($screen->post_type, ['post', 'wporg_cpt'])) {
// enqueue script
wp_enqueue_script('wporg_meta_box_script', plugin_dir_url(__FILE__) . 'admin/meta-boxes/js/admin.js', ['jquery']);
// localize script, create a custom js object
wp_localize_script(
'wporg_meta_box_script',
'wporg_meta_box_obj',
[
'url' => admin_url('admin-ajax.php'),
]
);
}
}
}
add_action('admin_enqueue_scripts', 'wporg_meta_box_scripts');
服務(wù)端代碼
最后一步是編寫處理 AJAX 請(qǐng)求的服務(wù)端代碼。
function wporg_meta_box_ajax_handler() {
if (isset($_POST['wporg_field_value'])) {
switch ($_POST['wporg_field_value']) {
case 'something':
echo 'success';
break;
default:
echo 'failure';
break;
}
}
// ajax handlers must die
die;
}
// wp_ajax_ is the prefix, wporg_ajax_change is the action we've used in client side code
add_action('wp_ajax_wporg_ajax_change', 'wporg_meta_box_ajax_handler');
再提醒一次,本頁(yè)面的示例代碼中缺乏足夠的安全操作來(lái)保證安全,在實(shí)際的代碼中,一定要把安全代碼加進(jìn)去。
有關(guān) AJAX 的更多信息,請(qǐng)參閱 AJAX章節(jié) 和 Ajax 文檔。
更多信息
- WordPress 中的復(fù)雜元數(shù)據(jù)盒子
- 怎么在WordPress中創(chuàng)建自定義文章元數(shù)據(jù)盒子
- WordPress元數(shù)據(jù)盒子全面開發(fā)者指南
渲染文章元數(shù)據(jù)
WordPress 提供了兩個(gè)函數(shù)來(lái)幫助我們?cè)L問存儲(chǔ)在 postmeta 數(shù)據(jù)表中的數(shù)據(jù) get_post_meta(),get_post_custom()。
get_post_meta 函數(shù)獲取指定文章的某個(gè) meta_key 的數(shù)據(jù),使用方法如下。
get_post_meta(
int $post_id,
string $key = '',
bool $single = false
);
例如:
$wporg_meta_value = get_post_meta(get_the_ID(), 'wporg_meta_key');
get_post_custom 獲取某個(gè)文章的所有元數(shù)據(jù),使用方法如下:
get_post_custom(
int $post_id
);
例如:
$meta_array = get_post_custom(get_the_ID());