WordPress REST API创建WordPress设置页面

WordPress REST API的许多重要用途之一就是改善您的插件或主题设置屏幕。添加自定义REST API端点后,通过AJAX获取保存的设置并通过AJAX(即无额外页面刷新)保存设置将变得更加简单。

使用WordPress REST API代替admin-ajax不仅性能更高,而且还可以使WordPress核心在清理和验证方面完成大部分繁重的工作。

在本文中,我们将逐步完成创建设置表单页面的每个步骤,并使用WordPress REST API处理该表单。

添加设置页面

在开始设计设置页面之前,我们需要向WordPress仪表板添加菜单或子菜单项,您可以在其中放置设置表单。在此页面上,您将需要加载CSS和JavaScript文件。

这是一个入门类:

  1. <?php
  2. class Apex_Menu {
  3. /**
  4. * Menu slug
  5. *
  6. * @var string
  7. */
  8. protected $slug = 'apex-menu';
  9. /**
  10. * URL for assets
  11. *
  12. * @var string
  13. */
  14. protected $assets_url;
  15. /**
  16. * Apex_Menu constructor.
  17. *
  18. * @param string $assets_url URL for assets
  19. */
  20. public function __construct( $assets_url ) {
  21. $this->assets_url = $assets_url;
  22. add_action( 'admin_menu', array( $this, 'add_page' ) );
  23. add_action( 'admin_enqueue_scripts', array( $this, 'register_assets' ) );
  24. }
  25. /**
  26. * Add CF Popup submenu page
  27. *
  28. * @since 0.0.3
  29. *
  30. * @uses "admin_menu"
  31. */
  32. public function add_page(){
  33. add_menu_page(
  34. __( 'Apex Page', 'text-domain' ),
  35. __( 'Apex Page', 'text-domain' ),
  36. 'manage_options',
  37. $this->slug,
  38. array( $this, 'render_admin' ) );
  39. }
  40. /**
  41. * Register CSS and JS for page
  42. *
  43. * @uses "admin_enqueue_scripts" action
  44. */
  45. public function register_assets()
  46. {
  47. wp_register_script( $this->slug, $this->assets_url . '/js/admin.js', array( 'jquery' ) );
  48. wp_register_style( $this->slug, $this->assets_url . '/css/admin.css' );
  49. wp_localize_script( $this->slug, 'APEX', array(
  50. 'strings' => array(
  51. 'saved' => __( 'Settings Saved', 'text-domain' ),
  52. 'error' => __( 'Error', 'text-domain' )
  53. ),
  54. 'api' => array(
  55. 'url' => esc_url_raw( rest_url( 'apex-api/v1/settings' ) ),
  56. 'nonce' => wp_create_nonce( 'wp_rest' )
  57. )
  58. ) );
  59. }
  60. /**
  61. * Enqueue CSS and JS for page
  62. */
  63. public function enqueue_assets(){
  64. if( ! wp_script_is( $this->slug, 'registered' ) ){
  65. $this->register_assets();
  66. }
  67. wp_enqueue_script( $this->slug );
  68. wp_enqueue_style( $this->slug );
  69. }
  70. /**
  71. * Render plugin admin page
  72. */
  73. public function render_admin(){
  74. $this->enqueue_assets();
  75. echo 'Put your form here!';
  76. }
  77. }

在此类中,我使用add_menu_page创建一个顶层菜单,但是您可能希望根据需要使用add_sub_menu创建子菜单。

这里有两件事要注意。

首先是我们如何使用wp_localize_script()。每当加载第一个参数中指定的脚本时,此函数都可以为您提供一种使用PHP创建全局作用域JavaScript变量的方法。最初是为了向浏览器提供翻译后的本地化字符串而设计的。这就是我们使用它的一部分-提供可翻译的成功和错误消息。但是,它也可以用于传递动态值,例如当前站点的URL,在这种情况下为REST API终结点和随机数。我们将在JavaScript中需要所有这些功能,但是对于每个站点,它都是不同的,因此我们必须使用PHP即时生成它。

另外,请注意,脚本的根URL作为对类的依赖关系而传入。我喜欢这样做,因为该URL可能会在插件或主题的其他地方使用,并且我希望在一个地方更改或过滤整个插件。

实例化类时,需要指定该URL。这样做的一个好地方是在根插件文件中,使用plugin_dir_url()将生成正确的URL。让我们看一下设置它的主要插件文件:

  1. <?php
  2. /**
  3. * Plugin Name: Apex Plugin
  4. */
  5. add_action( 'init', function(){
  6. $assets_url = plugin_dir_url( __FILE__ );
  7. //Setup menu
  8. if( is_admin() ){
  9. new Apex_Menu( $assets_url );
  10. }
  11. //Setup REST API
  12. });

在此,我们使用“init”操作加载此类。我为REST API端点保留了一个占位符,一旦插件屏幕准备就绪,我们将使用它来保存数据。

设置表单

我不会对设置表单本身太深入,我可以为此写一整个系列。取而代之的是,我们仅添加两个字段来查看一些重要的内容。然后,我们可以编写JavaScript将表单值发送回服务器。

这是“render_admin”方法,使用具有两个字段的字段进行了更新:

  1. /**
  2. * Render plugin admin page
  3. */
  4. public function render_admin(){
  5. $this->enqueue_assets();
  6. ?>
  7. <form id="apex-form">
  8. <div id="feedback">
  9. </div>
  10. <div>
  11. <label for="industry">
  12. <?php esc_html_e( 'Industry', 'text-domain' ); ?>
  13. </label>
  14. <input id="industry" type="text" />
  15. </div>
  16. <div>
  17. <label for="amount">
  18. <?php esc_html_e( 'Amount', 'text-domain' ); ?>
  19. </label>
  20. <input id="amount" type="number" min="0" max="100" />
  21. </div>
  22. <?php submit_button( __( 'Save', 'text-domain' ) ); ?>
  23. </form>
  24. <?php
  25. }

我确保每个字段都有一个ID。这将允许我使用jQuery.val()定位每个对象以获取其值。这对于保持我们的HTML语义也很重要,因为字段标签的属性必须与字段ID相对应。我还给了表单一个ID,并添加了一个ID为“feedback”的空元素,我们可以在其中动态放置保存或错误消息。

同样,您的表单可能会更复杂,但让我们开始吧。

添加REST API路由

在编写任何JavaScript通过AJAX将数据发送到服务器之前,我们需要为其提供REST API路由。我为此写了很多,但是值得通过一个非常简单的带有GETPOST端点的自定义路由。

分离业务逻辑

我坚信REST API路由的类应仅与处理请求有关,而与这些请求所需的业务逻辑无关。在这种情况下,“业务逻辑”是指读写实际设置。首先,让我们创建一个可以处理此问题的类。

此类将是带有一些基本验证的get_option()update_option()的简单包装。此类具有get_settings()方法,该方法获取保存的值,然后使用wp_parse_args()填充我们希望它具有的已保存数组的所有缺失索引。它还具有一个save_settings()方法,该方法确保只有要保存的数组的白名单键才在要保存的最终数组中。

  1. <?php
  2. class Apex_Settings {
  3. /**
  4. * Option key to save settings
  5. *
  6. * @var string
  7. */
  8. protected static $option_key = '_apex_settings';
  9. /**
  10. * Default settings
  11. *
  12. * @var array
  13. */
  14. protected static $defaults = array(
  15. 'industry' => 'lumber',
  16. 'amount' => 42
  17. );
  18. /**
  19. * Get saved settings
  20. *
  21. * @return array
  22. */
  23. public static function get_settings(){
  24. $saved = get_option( self::$option_key, array() );
  25. if( ! is_array( $saved ) || ! empty( $saved )){
  26. return self::$defaults;
  27. }
  28. return wp_parse_args( $saved, self::$defaults );
  29. }
  30. /**
  31. * Save settings
  32. *
  33. * Array keys must be whitelisted (IE must be keys of self::$defaults
  34. *
  35. * @param array $settings
  36. */
  37. public static function save_settings( array $settings ){
  38. //remove any non-allowed indexes before save
  39. foreach ( $settings as $i => $setting ){
  40. if( ! array_key_exists( $setting, self::$defaults ) ){
  41. unset( $settings[ $i ] );
  42. }
  43. }
  44. update_option( self::$option_key, $settings );
  45. }
  46. }

REST API路由

现在,我们有了一种读写方法,可以通过任何方式寻址,让我们创建一个REST API路由,以充当其RESTful接口。该路由将具有GET和POST端点。

如果您从未创建过自定义的REST API终结点,建议您阅读文档。我还介绍过与该主题相关的Torque文章和WordPress TV talks: 精选的链接列表  。

这是REST API路由类:

  1. <?php
  2. class Apex_API {
  3. /**
  4. * Add routes
  5. */
  6. public function add_routes( ) {
  7. register_rest_route( 'apex-api/v1', '/settings',
  8. array(
  9. 'methods' => 'POST',
  10. 'callback' => array( $this, 'update_settings' ),
  11. 'args' => array(
  12. 'industry' => array(
  13. 'type' => 'string',
  14. 'required' => false,
  15. 'sanitize_callback' => 'sanitize_text_field'
  16. ),
  17. 'amount' => array(
  18. 'type' => 'integer',
  19. 'required' => false,
  20. 'sanitize_callback' => 'absint'
  21. )
  22. ),
  23. 'permissions_callback' => array( $this, 'permissions' )
  24. )
  25. );
  26. register_rest_route( 'apex-api/v1', '/settings',
  27. array(
  28. 'methods' => 'GET',
  29. 'callback' => array( $this, 'get_settings' ),
  30. 'args' => array(
  31. ),
  32. 'permissions_callback' => array( $this, 'permissions' )
  33. )
  34. );
  35. }
  36. /**
  37. * Check request permissions
  38. *
  39. * @return bool
  40. */
  41. public function permissions(){
  42. return current_user_can( 'manage_options' );
  43. }
  44. /**
  45. * Update settings
  46. *
  47. * @param WP_REST_Request $request
  48. */
  49. public function update_settings( WP_REST_Request $request ){
  50. $settings = array(
  51. 'industry' => $request->get_param( 'industry' ),
  52. 'amount' => $request->get_param( 'amount' )
  53. );
  54. Apex_Settings::save_settings( $settings );
  55. return rest_ensure_response( Apex_Settings::get_settings())->set_status( 201 );
  56. }
  57. /**
  58. * Get settings via API
  59. *
  60. * @param WP_REST_Request $request
  61. */
  62. public function get_settings( WP_REST_Request $request ){
  63. return rest_ensure_response( Apex_Settings::get_settings());
  64. }
  65. }

看一下,回调函数非常简单,因为它们只包装了我在上一节中创建的设置类。重要的是要了解,根据设计,设置类没有权限检查或清理操作。但是权限检查和清理非常重要。

这些REST API端点提供了这一点。POST方法为每个字段指定一个“ sanitize_callback”参数。这样,我可以相信数据在传入之前是安全的。而且,这两个路由都使用“permissions_callback”,因此只能通过具有“ manage_options”功能的路由来访问这些路由。跳过这些步骤中的任何一个都是危险的。

现在我们只需要在“rest_api_init”操作上实例化此类,以便存在端点。这又是主要的插件文件,对其进行了修改以实现此目的:

  1. <?php
  2. /**
  3. * Plugin Name: Apex Plugin
  4. */
  5. add_action( 'init', function(){
  6. $assets_url = plugin_dir_url( __FILE__ );
  7. //Setup menu
  8. if( is_admin() ){
  9. new Apex_Menu( $assets_url );
  10. }
  11. //Setup REST API
  12. });

在“设置”页面中使用REST API

现在我们有了端点,让我们在设置页面上使用它们。我们将编写两个AJAX调用。第一个将获取保存的设置,并使用这些设置更新表单。这将由页面加载触发。第二个将由表单保存按钮触发,并将用于更新设置。

在本教程的前面,我们告诉WordPress在此管理页面上加载JavaScript文件。现在该使用该文件了。

这是第一个AJAX调用的样子。它的工作是获取保存的设置并使用以下内容更新表单:

  1. jQuery(function($) {
  2. $.ajax({
  3. method: 'GET',
  4. url: APEX.api.url
  5. beforeSend: function ( xhr ) {
  6. xhr.setRequestHeader( 'X-WP-Nonce', APEX.api.nonce );
  7. }
  8. }).then( function ( r ) {
  9. if( r.hasOwnProperty( 'industry' ) ){
  10. $( '#industry' ).val( r.industry );
  11. }
  12. if( r.hasOwnProperty( 'amount' ) ){
  13. $( '#amount' ).val( r.amount );
  14. }
  15. })
  16. });

请注意此处的两个重要事项。首先,我使用之前通过wp_localize_script()设置的APEX全局对象来告诉jQuery请求什么URL。另外,我正在使用beforeSend()方法添加包含该对象中随机数的标头。否则,在API请求处理期间将不会认为用户已登录,因此我们的权限检查将失败。

API请求完成后,使用jQuery.val()将设置添加到表单字段。为了安全起见,我使用Object.hasOwnProperty()确保它们在响应中。这是很重要的验证,但是随着设置数量的增长而无法很好地扩展-这是我使用VueJS进行这种事情的众多原因之一。

现在,当您加载页面时,它应该获取已保存的设置(此时可能是默认值)并更新表单。很好,但是所有这些的真正意义在于能够更新设置。因此,我们将需要第二个AJAX调用,该调用将在提交表单时运行POST请求。

这是带有保存AJAX调用的更新后的JavaScript:

  1. jQuery(function($) {
  2. $.ajax({
  3. method: 'GET',
  4. url: APEX.api.url,
  5. beforeSend: function ( xhr ) {
  6. xhr.setRequestHeader( 'X-WP-Nonce', APEX.api.nonce );
  7. }
  8. }).then( function ( r ) {
  9. if( r.hasOwnProperty( 'industry' ) ){
  10. $( '#industry' ).val( r.industry );
  11. }
  12. if( r.hasOwnProperty( 'amount' ) ){
  13. $( '#amount' ).val( r.amount );
  14. }
  15. });
  16. $( '#apex-form' ).on( 'submit', function (e) {
  17. e.preventDefault();
  18. var data = {
  19. amount: $( '#amount' ).val(),
  20. industry: $( '#industry' ).val()
  21. };
  22. $.ajax({
  23. method: 'POST',
  24. url: APEX.api.url,
  25. beforeSend: function ( xhr ) {
  26. xhr.setRequestHeader('X-WP-Nonce', APEX.api.nonce);
  27. },
  28. data:data
  29. }).then( function (r) {
  30. $( '#feedback' ).html( '<p>' + APEX.strings.saved + '</p>' );
  31. }).error( function (r) {
  32. var message = APEX.strings.error;
  33. if( r.hasOwnProperty( 'message' ) ){
  34. message = r.message;
  35. }
  36. $( '#feedback' ).html( '<p>' + message + '</p>' );
  37. })
  38. })
  39. });

第二个调用非常相似,除了它使用POST并包装在与表单的commit事件绑定的闭包中。这样,它在表单提交时运行,我们可以防止该事件的默认操作发生。

您应该在这里查看成功和错误方法。它们用于将文本(位于APEX.string对象中)本地化的消息添加到#feedback元素中。该对象中的“错误”消息非常笼统。但是大多数失败的请求都会生成一条带有消息的响应。因此,如果已设置,我们将改用它。

自己去实践吧

一旦有了一个良好的起点,就可以更新表单中的字段以符合您的需求。另外,您可能应该使用JavaScript框架来简化此过程,因为随着表单的复杂性增加,您将越来越难以使用jQuery进行管理并且会变得更加简单,并且如果您使用VueJS或React ,会提供更好的用户体验。

本文向您展示了使用WordPress REST API 添加 WordPress设置页面的所有部分。我们添加了一个菜单页面,将我们的JavaScript和CSS入队,添加了一个用于读取和写入设置的类,添加了两个REST API端点作为这些设置的RESTful和安全接口,并使用jQuery AJAX根据我们的设置表单更新了设置。代码量很多,但我希望您已经了解了如何使用这些基础知识来改进自己的设置页面,或者从头开始构建自己的设置页面并从那里发展。

THE END