自定义Meta Box

什么是Meta Box

当用户编辑文章时,编辑界面由几个默认框组成:编辑器、发布、类别、标签等。这些框就是元框(meta box)。插件可以将自定义元框添加到任何文章类型的编辑界面。

自定义meta box的内容通常是HTML表单元素,用户在其中输入与插件用途相关的数据,但内容实际上可以是您想要的任何HTML。

 

为什么使用Meta Box

元框是方便、灵活、模块化的编辑界面元素,可用于收集与正在编辑的文章相关的信息。您的自定义meta box将与所有其他文章相关信息位于同一界面上,因此,建立了明确的关系。

元框很容易对不需要查看的用户隐藏,并显示给那些需要查看的用户。用户可以在编辑屏幕上排列元框。可以自由地以适合自己的方式排列编辑屏幕,让用户可以控制自己的编辑环境。

Alert:本页上的所有示例仅用于说明作用。该代码不适用于生产环境。

有意省略了安全输入用户能力nonces国际化等操作。务必始终解决这些重要操作。

 

添加Meta Box

要创建meta box,请使用add_meta_box()函数并将其执行插入add_meta_boxes动作钩子。

下面的示例将向post编辑屏幕和wporg_cpt编辑屏幕添加一个元框。

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函数将保存元框的HTML。

下面的示例添加表单元素、标签和其他HTML元素。

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
}

Note:注意,元框中没有提交按钮。元框HTML包含在编辑屏幕的表单标签中,当用户单击发布或更新按钮时,包括元框值在内的所有post数据都通过POST传输。

此处显示的示例仅包含一个表单字段,即下拉列表。您可以根据需要在任何特定的meta box中创建任意多个。如果要显示很多字段,请考虑使用多个元框,将每个元框中的类似字段分组在一起。这有助于保持页面更有组织性和视觉吸引力。

 

获取值

要检索并使用保存的用户数据,您需要从最初保存的位置获取数据。如果它存储在postmeta表中,则可以使用get_post_meta()获取数据。

下面的示例使用基于保存的元框值的预填充数据来增强前面的表单元素。在下一部分中,您将学习如何保存元框值。

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()函数的更多信息。

 

保存值

保存或更新文章类型时,会触发多个动作,其中任何动作都可能适合挂接以保存输入的值。在本例中,我们使用save_post动作挂钩,但其他钩子可能更适合某些情况。请注意,save_post可能会为单个更新事件触发多次。要相应地构建保存数据的方法。

你可以将输入的数据保存在任何你想要的地方,甚至在WordPress之外。由于您可能正在处理与post相关的数据,postmeta表通常是存储数据的好地方。

以下示例将wporg_field字段值保存在_wporg_meta_key元键中,该键是隐藏的。

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' );

在生产代码中,请记住遵循信息框中概述的安全措施!

 

幕后

你通常不需要关心幕后发生的事情。增加本节是为了完整性。

当文章编辑界面想要显示添加到其中的所有元框时,它调用do_meta_boxes()函数。该函数循环遍历所有元框,并调用与每个元框相关的callback
在每个调用之间,添加了中间标记(例如div、标题等)。

 

删除Meta Box

要从编辑屏幕中删除现有的元框,请使用remove_meta_box()函数。传递的参数必须与用于添加元框的add_meta_box()的参数完全匹配。

要删除默认的元框,请检查所用参数的源代码。默认的add_meta_box()调用来自wp-includes/edit-form-advanced.php

 

实现方式的变化

到目前为止,我们一直在使用程序化的技术来实现元框。许多插件开发者发现需要使用其他各种技术来实现元框。

 

面向对象

使用面向对象添加元框很容易,并且可以避免您担心全局命名空间中的命名冲突。
为了节省内存并允许更容易的实现,下面的示例使用了带有静态方法的抽象类。

abstract class WPOrg_Meta_Box {


	/**
	 * Set up and add the 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
			);
		}
	}


	/**
	 * Save the meta box selections.
	 *
	 * @param int $post_id  The post ID.
	 */
	public static function save( int $post_id ) {
		if ( array_key_exists( 'wporg_field', $_POST ) ) {
			update_post_meta(
				$post_id,
				'_wporg_meta_key',
				$_POST['wporg_field']
			);
		}
	}


	/**
	 * Display the meta box HTML to the user.
	 *
	 * @param \WP_Post $post   Post object.
	 */
	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

由于元框的HTML元素位于编辑屏幕的form标记内,因此默认行为是在用户提交页面后从$_POST超全局解析元框值。

您可以增强AJAX的默认体验,这允许根据用户输入和行为执行操作,不管他们是否提交了页面。

 

定义触发器

首先,您必须定义触发器,这可以是链接点击、值更改或任何其他JavaScript事件。

在下面的示例中,我们将定义change作为执行AJAX请求的触发器。

/*jslint browser: true, plusplus: true */
(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));

 

客户端代码

接下来,需要定义我们希望触发器做什么,换句话说,我们需要编写客户端代码。

在下面的示例中,我们将发出一个POST请求,响应将是成功或失败,这将表明wporg_field的值有效。

/*jslint browser: true, plusplus: true */
(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
                       post_ID: jQuery('#post_ID').val()           // The ID of the post currently being edited
                   }, 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));

我们从我们将在下一步中创建的JavaScript自定义对象wporg_meta_box_obj中动态获取WordPress AJAX文件URL。

Note:如果您的元框只需要WordPress AJAX文件URL;您可以使用ajaxurl预定义的JavaScript变量,而不是创建新的自定义JavaScript对象。
仅在WordPress管理中可用。在执行任何逻辑之前,确保它不是空的。

 

引入客户端代码

下一步是将代码放入脚本文件中,并在编辑屏幕上列队引入。

在下面的示例中,我们将向以下post类型的编辑屏幕添加AJAX功能:post,wporg_cpt。

脚本文件将位于/plugin-name/admin/meta-boxes/js/admin.js
plugin-name为主插件文件夹,
/plugin-name/plugin.php调用函数的文件。

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');

 

服务器端代码

最后一步是编写处理请求的服务器端代码。

// The piece after `wp_ajax_`  matches the action argument being sent in the POST request.
add_action( 'wp_ajax_wporg_ajax_change', 'my_ajax_handler' );
 
/**
 * Handles my AJAX request.
 */
function my_ajax_handler() {
    // Handle the ajax request here
    if ( array_key_exists( 'wporg_field_value', $_POST ) ) {
        $post_id = (int) $_POST['post_ID'];
        if ( current_user_can( 'edit_post', $post_id ) ) {
            update_post_meta(
                $post_id,
                '_wporg_meta_key',
                $_POST['wporg_field_value']
            );
        }
    }
 
    wp_die(); // All ajax handlers die when finished
}

最后提醒一下,本页所示的代码缺少重要的安全操作。确保生产代码包含此类操作。

有关AJAX的更多信息,请参阅手册的AJAX章节Codex

 

更多信息