WordPress REST API的許多重要用途之一就是改善您的插件或主題設(shè)置屏幕。添加自定義REST API端點(diǎn)后,通過(guò)AJAX獲取保存的設(shè)置并通過(guò)AJAX(即無(wú)額外頁(yè)面刷新)保存設(shè)置將變得更加簡(jiǎn)單。
使用WordPress REST API代替admin-ajax不僅性能更高,而且還可以使WordPress核心在清理和驗(yàn)證方面完成大部分繁重的工作。

在本文中,我們將逐步完成創(chuàng)建設(shè)置表單頁(yè)面的每個(gè)步驟,并使用WordPress REST API處理該表單。
添加設(shè)置頁(yè)面
在開始設(shè)計(jì)設(shè)置頁(yè)面之前,我們需要向WordPress儀表板添加菜單或子菜單項(xiàng),您可以在其中放置設(shè)置表單。在此頁(yè)面上,您將需要加載CSS和JavaScript文件。
這是一個(gè)入門類:
<?php
class Apex_Menu {
/**
* Menu slug
*
* @var string
*/
protected $slug = 'apex-menu';
/**
* URL for assets
*
* @var string
*/
protected $assets_url;
/**
* Apex_Menu constructor.
*
* @param string $assets_url URL for assets
*/
public function __construct( $assets_url ) {
$this->assets_url = $assets_url;
add_action( 'admin_menu', array( $this, 'add_page' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'register_assets' ) );
}
/**
* Add CF Popup submenu page
*
* @since 0.0.3
*
* @uses "admin_menu"
*/
public function add_page(){
add_menu_page(
__( 'Apex Page', 'text-domain' ),
__( 'Apex Page', 'text-domain' ),
'manage_options',
$this->slug,
array( $this, 'render_admin' ) );
}
/**
* Register CSS and JS for page
*
* @uses "admin_enqueue_scripts" action
*/
public function register_assets()
{
wp_register_script( $this->slug, $this->assets_url . '/js/admin.js', array( 'jquery' ) );
wp_register_style( $this->slug, $this->assets_url . '/css/admin.css' );
wp_localize_script( $this->slug, 'APEX', array(
'strings' => array(
'saved' => __( 'Settings Saved', 'text-domain' ),
'error' => __( 'Error', 'text-domain' )
),
'api' => array(
'url' => esc_url_raw( rest_url( 'apex-api/v1/settings' ) ),
'nonce' => wp_create_nonce( 'wp_rest' )
)
) );
}
/**
* Enqueue CSS and JS for page
*/
public function enqueue_assets(){
if( ! wp_script_is( $this->slug, 'registered' ) ){
$this->register_assets();
}
wp_enqueue_script( $this->slug );
wp_enqueue_style( $this->slug );
}
/**
* Render plugin admin page
*/
public function render_admin(){
$this->enqueue_assets();
echo 'Put your form here!';
}
}
在此類中,我使用add_menu_page創(chuàng)建一個(gè)頂層菜單,但是您可能希望根據(jù)需要使用add_sub_menu創(chuàng)建子菜單。
這里有兩件事要注意。
首先是我們?nèi)绾问褂?code>wp_localize_script()。每當(dāng)加載第一個(gè)參數(shù)中指定的腳本時(shí),此函數(shù)都可以為您提供一種使用PHP創(chuàng)建全局作用域JavaScript變量的方法。最初是為了向?yàn)g覽器提供翻譯后的本地化字符串而設(shè)計(jì)的。這就是我們使用它的一部分-提供可翻譯的成功和錯(cuò)誤消息。但是,它也可以用于傳遞動(dòng)態(tài)值,例如當(dāng)前站點(diǎn)的URL,在這種情況下為REST API終結(jié)點(diǎn)和隨機(jī)數(shù)。我們將在JavaScript中需要所有這些功能,但是對(duì)于每個(gè)站點(diǎn),它都是不同的,因此我們必須使用PHP即時(shí)生成它。
另外,請(qǐng)注意,腳本的根URL作為對(duì)類的依賴關(guān)系而傳入。我喜歡這樣做,因?yàn)樵揢RL可能會(huì)在插件或主題的其他地方使用,并且我希望在一個(gè)地方更改或過(guò)濾整個(gè)插件。
實(shí)例化類時(shí),需要指定該URL。這樣做的一個(gè)好地方是在根插件文件中,使用plugin_dir_url()將生成正確的URL。讓我們看一下設(shè)置它的主要插件文件:
<?php
/**
* Plugin Name: Apex Plugin
*/
add_action( 'init', function(){
$assets_url = plugin_dir_url( __FILE__ );
//Setup menu
if( is_admin() ){
new Apex_Menu( $assets_url );
}
//Setup REST API
});
在此,我們使用“init”操作加載此類。我為REST API端點(diǎn)保留了一個(gè)占位符,一旦插件屏幕準(zhǔn)備就緒,我們將使用它來(lái)保存數(shù)據(jù)。
設(shè)置表單
我不會(huì)對(duì)設(shè)置表單本身太深入,我可以為此寫一整個(gè)系列。取而代之的是,我們僅添加兩個(gè)字段來(lái)查看一些重要的內(nèi)容。然后,我們可以編寫JavaScript將表單值發(fā)送回服務(wù)器。
這是“render_admin”方法,使用具有兩個(gè)字段的字段進(jìn)行了更新:
/**
* Render plugin admin page
*/
public function render_admin(){
$this->enqueue_assets();
?>
<form id="apex-form">
<div id="feedback">
</div>
<div>
<label for="industry">
<?php esc_html_e( 'Industry', 'text-domain' ); ?>
</label>
<input id="industry" type="text" />
</div>
<div>
<label for="amount">
<?php esc_html_e( 'Amount', 'text-domain' ); ?>
</label>
<input id="amount" type="number" min="0" max="100" />
</div>
<?php submit_button( __( 'Save', 'text-domain' ) ); ?>
</form>
<?php
}
我確保每個(gè)字段都有一個(gè)ID。這將允許我使用jQuery.val()定位每個(gè)對(duì)象以獲取其值。這對(duì)于保持我們的HTML語(yǔ)義也很重要,因?yàn)樽侄螛?biāo)簽的屬性必須與字段ID相對(duì)應(yīng)。我還給了表單一個(gè)ID,并添加了一個(gè)ID為“feedback”的空元素,我們可以在其中動(dòng)態(tài)放置保存或錯(cuò)誤消息。
同樣,您的表單可能會(huì)更復(fù)雜,但讓我們開始吧。
添加REST API路由
在編寫任何JavaScript通過(guò)AJAX將數(shù)據(jù)發(fā)送到服務(wù)器之前,我們需要為其提供REST API路由。我為此寫了很多,但是值得通過(guò)一個(gè)非常簡(jiǎn)單的帶有GET和POST端點(diǎn)的自定義路由。
分離業(yè)務(wù)邏輯
我堅(jiān)信REST API路由的類應(yīng)僅與處理請(qǐng)求有關(guān),而與這些請(qǐng)求所需的業(yè)務(wù)邏輯無(wú)關(guān)。在這種情況下,“業(yè)務(wù)邏輯”是指讀寫實(shí)際設(shè)置。首先,讓我們創(chuàng)建一個(gè)可以處理此問(wèn)題的類。
此類將是帶有一些基本驗(yàn)證的get_option()和update_option()的簡(jiǎn)單包裝。此類具有get_settings()方法,該方法獲取保存的值,然后使用wp_parse_args()填充我們希望它具有的已保存數(shù)組的所有缺失索引。它還具有一個(gè)save_settings()方法,該方法確保只有要保存的數(shù)組的白名單鍵才在要保存的最終數(shù)組中。
<?php
class Apex_Settings {
/**
* Option key to save settings
*
* @var string
*/
protected static $option_key = '_apex_settings';
/**
* Default settings
*
* @var array
*/
protected static $defaults = array(
'industry' => 'lumber',
'amount' => 42
);
/**
* Get saved settings
*
* @return array
*/
public static function get_settings(){
$saved = get_option( self::$option_key, array() );
if( ! is_array( $saved ) || ! empty( $saved )){
return self::$defaults;
}
return wp_parse_args( $saved, self::$defaults );
}
/**
* Save settings
*
* Array keys must be whitelisted (IE must be keys of self::$defaults
*
* @param array $settings
*/
public static function save_settings( array $settings ){
//remove any non-allowed indexes before save
foreach ( $settings as $i => $setting ){
if( ! array_key_exists( $setting, self::$defaults ) ){
unset( $settings[ $i ] );
}
}
update_option( self::$option_key, $settings );
}
}
REST API路由
現(xiàn)在,我們有了一種讀寫方法,可以通過(guò)任何方式尋址,讓我們創(chuàng)建一個(gè)REST API路由,以充當(dāng)其RESTful接口。該路由將具有GET和POST端點(diǎn)。
如果您從未創(chuàng)建過(guò)自定義的REST API終結(jié)點(diǎn),建議您閱讀文檔。我還介紹過(guò)與該主題相關(guān)的Torque文章和WordPress TV talks: 精選的鏈接列表 ?。
這是REST API路由類:
<?php
class Apex_API {
/**
* Add routes
*/
public function add_routes( ) {
register_rest_route( 'apex-api/v1', '/settings',
array(
'methods' => 'POST',
'callback' => array( $this, 'update_settings' ),
'args' => array(
'industry' => array(
'type' => 'string',
'required' => false,
'sanitize_callback' => 'sanitize_text_field'
),
'amount' => array(
'type' => 'integer',
'required' => false,
'sanitize_callback' => 'absint'
)
),
'permissions_callback' => array( $this, 'permissions' )
)
);
register_rest_route( 'apex-api/v1', '/settings',
array(
'methods' => 'GET',
'callback' => array( $this, 'get_settings' ),
'args' => array(
),
'permissions_callback' => array( $this, 'permissions' )
)
);
}
/**
* Check request permissions
*
* @return bool
*/
public function permissions(){
return current_user_can( 'manage_options' );
}
/**
* Update settings
*
* @param WP_REST_Request $request
*/
public function update_settings( WP_REST_Request $request ){
$settings = array(
'industry' => $request->get_param( 'industry' ),
'amount' => $request->get_param( 'amount' )
);
Apex_Settings::save_settings( $settings );
return rest_ensure_response( Apex_Settings::get_settings())->set_status( 201 );
}
/**
* Get settings via API
*
* @param WP_REST_Request $request
*/
public function get_settings( WP_REST_Request $request ){
return rest_ensure_response( Apex_Settings::get_settings());
}
}
看一下,回調(diào)函數(shù)非常簡(jiǎn)單,因?yàn)樗鼈冎话b了我在上一節(jié)中創(chuàng)建的設(shè)置類。重要的是要了解,根據(jù)設(shè)計(jì),設(shè)置類沒(méi)有權(quán)限檢查或清理操作。但是權(quán)限檢查和清理非常重要。
這些REST API端點(diǎn)提供了這一點(diǎn)。POST方法為每個(gè)字段指定一個(gè)“ sanitize_callback”參數(shù)。這樣,我可以相信數(shù)據(jù)在傳入之前是安全的。而且,這兩個(gè)路由都使用“permissions_callback”,因此只能通過(guò)具有“ manage_options”功能的路由來(lái)訪問(wèn)這些路由。跳過(guò)這些步驟中的任何一個(gè)都是危險(xiǎn)的。
現(xiàn)在我們只需要在“rest_api_init”操作上實(shí)例化此類,以便存在端點(diǎn)。這又是主要的插件文件,對(duì)其進(jìn)行了修改以實(shí)現(xiàn)此目的:
<?php
/**
* Plugin Name: Apex Plugin
*/
add_action( 'init', function(){
$assets_url = plugin_dir_url( __FILE__ );
//Setup menu
if( is_admin() ){
new Apex_Menu( $assets_url );
}
//Setup REST API
});
在“設(shè)置”頁(yè)面中使用REST API
現(xiàn)在我們有了端點(diǎn),讓我們?cè)谠O(shè)置頁(yè)面上使用它們。我們將編寫兩個(gè)AJAX調(diào)用。第一個(gè)將獲取保存的設(shè)置,并使用這些設(shè)置更新表單。這將由頁(yè)面加載觸發(fā)。第二個(gè)將由表單保存按鈕觸發(fā),并將用于更新設(shè)置。
在本教程的前面,我們告訴WordPress在此管理頁(yè)面上加載JavaScript文件。現(xiàn)在該使用該文件了。
這是第一個(gè)AJAX調(diào)用的樣子。它的工作是獲取保存的設(shè)置并使用以下內(nèi)容更新表單:
jQuery(function($) {
$.ajax({
method: 'GET',
url: APEX.api.url
beforeSend: function ( xhr ) {
xhr.setRequestHeader( 'X-WP-Nonce', APEX.api.nonce );
}
}).then( function ( r ) {
if( r.hasOwnProperty( 'industry' ) ){
$( '#industry' ).val( r.industry );
}
if( r.hasOwnProperty( 'amount' ) ){
$( '#amount' ).val( r.amount );
}
})
});
請(qǐng)注意此處的兩個(gè)重要事項(xiàng)。首先,我使用之前通過(guò)wp_localize_script()設(shè)置的APEX全局對(duì)象來(lái)告訴jQuery請(qǐng)求什么URL。另外,我正在使用beforeSend()方法添加包含該對(duì)象中隨機(jī)數(shù)的標(biāo)頭。否則,在API請(qǐng)求處理期間將不會(huì)認(rèn)為用戶已登錄,因此我們的權(quán)限檢查將失敗。
API請(qǐng)求完成后,使用jQuery.val()將設(shè)置添加到表單字段。為了安全起見(jiàn),我使用Object.hasOwnProperty()確保它們?cè)陧憫?yīng)中。這是很重要的驗(yàn)證,但是隨著設(shè)置數(shù)量的增長(zhǎng)而無(wú)法很好地?cái)U(kuò)展-這是我使用VueJS進(jìn)行這種事情的眾多原因之一。
現(xiàn)在,當(dāng)您加載頁(yè)面時(shí),它應(yīng)該獲取已保存的設(shè)置(此時(shí)可能是默認(rèn)值)并更新表單。很好,但是所有這些的真正意義在于能夠更新設(shè)置。因此,我們將需要第二個(gè)AJAX調(diào)用,該調(diào)用將在提交表單時(shí)運(yùn)行POST請(qǐng)求。
這是帶有保存AJAX調(diào)用的更新后的JavaScript:
jQuery(function($) {
$.ajax({
method: 'GET',
url: APEX.api.url,
beforeSend: function ( xhr ) {
xhr.setRequestHeader( 'X-WP-Nonce', APEX.api.nonce );
}
}).then( function ( r ) {
if( r.hasOwnProperty( 'industry' ) ){
$( '#industry' ).val( r.industry );
}
if( r.hasOwnProperty( 'amount' ) ){
$( '#amount' ).val( r.amount );
}
});
$( '#apex-form' ).on( 'submit', function (e) {
e.preventDefault();
var data = {
amount: $( '#amount' ).val(),
industry: $( '#industry' ).val()
};
$.ajax({
method: 'POST',
url: APEX.api.url,
beforeSend: function ( xhr ) {
xhr.setRequestHeader('X-WP-Nonce', APEX.api.nonce);
},
data:data
}).then( function (r) {
$( '#feedback' ).html( '<p>' + APEX.strings.saved + '</p>' );
}).error( function (r) {
var message = APEX.strings.error;
if( r.hasOwnProperty( 'message' ) ){
message = r.message;
}
$( '#feedback' ).html( '<p>' + message + '</p>' );
})
})
});
第二個(gè)調(diào)用非常相似,除了它使用POST并包裝在與表單的commit事件綁定的閉包中。這樣,它在表單提交時(shí)運(yùn)行,我們可以防止該事件的默認(rèn)操作發(fā)生。
您應(yīng)該在這里查看成功和錯(cuò)誤方法。它們用于將文本(位于APEX.string對(duì)象中)本地化的消息添加到#feedback元素中。該對(duì)象中的“錯(cuò)誤”消息非常籠統(tǒng)。但是大多數(shù)失敗的請(qǐng)求都會(huì)生成一條帶有消息的響應(yīng)。因此,如果已設(shè)置,我們將改用它。
自己去實(shí)踐吧
一旦有了一個(gè)良好的起點(diǎn),就可以更新表單中的字段以符合您的需求。另外,您可能應(yīng)該使用JavaScript框架來(lái)簡(jiǎn)化此過(guò)程,因?yàn)殡S著表單的復(fù)雜性增加,您將越來(lái)越難以使用jQuery進(jìn)行管理并且會(huì)變得更加簡(jiǎn)單,并且如果您使用VueJS或React ,會(huì)提供更好的用戶體驗(yàn)。
本文向您展示了使用WordPress REST API 添加 WordPress設(shè)置頁(yè)面的所有部分。我們添加了一個(gè)菜單頁(yè)面,將我們的JavaScript和CSS入隊(duì),添加了一個(gè)用于讀取和寫入設(shè)置的類,添加了兩個(gè)REST API端點(diǎn)作為這些設(shè)置的RESTful和安全接口,并使用jQuery AJAX根據(jù)我們的設(shè)置表單更新了設(shè)置。代碼量很多,但我希望您已經(jīng)了解了如何使用這些基礎(chǔ)知識(shí)來(lái)改進(jìn)自己的設(shè)置頁(yè)面,或者從頭開始構(gòu)建自己的設(shè)置頁(yè)面并從那里發(fā)展。
原文出自: https://torquemag.io/2017/06/creating-wordpress-settings-page-using-wordpress-rest-api/ ,由WordPress大學(xué)翻譯整理。
拓展閱讀:創(chuàng)建WordPress插件設(shè)置頁(yè)面的5種方法




