向 WordPress 插件添加可配置字段的 3 种方法
已发表: 2022-03-10任何创建过 WordPress 插件的人都知道需要创建可配置字段来修改插件的工作方式。 插件中的可配置选项有无数种用途,实现上述选项的方法也几乎一样多。 你看,WordPress 允许插件作者在他们的设置页面中创建他们自己的标记。 作为副作用,插件之间的设置页面可能会有很大差异。
在本文中,我们将介绍使插件可配置的三种常用方法。 我们将首先创建一个设置页面并使用默认的 WordPress 设置 API 创建我们的字段。
关于 SmashingMag 的进一步阅读:
- 使用自定义字段扩展 WordPress
- WordPress的自定义字段黑客
- 使用您自己的控件扩展高级自定义字段
- 自定义帖子类型的完整指南
然后,我将引导您了解如何使用自定义处理程序设置您的字段。 最后,我将向您展示如何将出色的可配置字段插件高级自定义字段(ACF)集成到您自己的插件中。
由于这篇文章很长,这里有一个目录,其中包含指向每个主要部分的链接:
- 创建我们的插件和设置页面
- 方法 1:使用内置的 WordPress 功能
- 方法 2:设置自定义表单和处理程序
- 方法 3:将 ACF(高级自定义字段)集成到您的插件中
有关代码示例,请参阅我为这篇文章设置的存储库。
创建我们的插件和设置页面
我们需要做的第一件事是设置我们的插件并创建一个设置页面。 本文中概述的所有三种方法都从下面的插件结构开始。 此插件结构是面向对象的,因此如果您的插件是按程序编写的,您自己的代码可能会有一些差异。 特别注意动作和过滤器中的回调函数格式。
/* Plugin Name: Smashing Fields Plugin description: >- Setting up configurable fields for our plugin. Author: Matthew Ray Version: 1.0.0 */ class Smashing_Fields_Plugin { // Our code will go here } new Smashing_Fields_Plugin();
在我们的类中,我们将添加一个动作挂钩来添加设置页面:
public function __construct() { // Hook into the admin menu add_action( 'admin_menu', array( $this, 'create_plugin_settings_page' ) ); }
您可以看到我们的操作的回调是create_plugin_settings_page
,所以让我们创建该方法。 注意:我已将参数设置为单独的命名变量,以便为我们的代码提供一些上下文,但您可以简单地将值直接放在函数中以节省内存。
public function create_plugin_settings_page() { // Add the menu item and page $page_title = 'My Awesome Settings Page'; $menu_title = 'Awesome Plugin'; $capability = 'manage_options'; $slug = 'smashing_fields'; $callback = array( $this, 'plugin_settings_page_content' ); $icon = 'dashicons-admin-plugins'; $position = 100; add_menu_page( $page_title, $menu_title, $capability, $slug, $callback, $icon, $position ); }
查看add_menu_page
的 WP codex 以获取更多信息。
这个函数将创建我们的页面以及菜单项。 这里的重要部分是 slug、capability 和 callback 参数。 我们稍后将使用 slug 来注册我们的字段,所以把它写下来。 您可以更改功能以允许不同用户级别访问您的设置页面。 至于回调,我们将很快创建该方法。 请注意,您还可以将 dashicon 类直接放入函数中以更改菜单的图标。 最后一个参数是菜单项在菜单中的位置; 玩弄这个数字,在菜单中找到您希望设置下降的位置。 注意:您可以使用十进制值来避免与其他菜单项发生冲突。
我们的下一步是为我们的设置页面创建回调方法plugin_settings_page_content
。
public function plugin_settings_page_content() { echo 'Hello World!'; }
如果您保存插件并刷新 WordPress 管理面板,您应该会看到以下内容:

您可以看到您的设置页面是一个顶级菜单项。 根据设置页面的需要,您可能更愿意以这种方式保留它。 但是,您可能还希望将插件设置放在另一个菜单项下。 在这种情况下,您只需将create_plugin_settings_page
方法的最后一行更改为以下内容:
add_submenu_page( 'options-general.php', $page_title, $menu_title, $capability, $slug, $callback );
在这里,我们将函数add_menu_page
更改为add_submenu_page
并添加一个新参数。 该参数是新设置页面所在的父菜单项。 在add_submenu_page
文档中,您可以看到一个非常好的父菜单项及其 slug 列表。 您可能还会看到我们不再有最后两个参数$icon
和$position
。 由于我们现在在子菜单部分,我们不再控制元素的位置。 此外,子菜单没有可用的图标,因此不需要该参数。
如果您保存此新代码,您将看到我们的设置页面将显示在“设置”菜单项下:

在许多情况下,添加插件设置页面最合适的位置是在设置项下。 在 WordPress 法典中,它解释说“设置”部分用于“显示只有管理员才能查看的插件选项”。 但是,这只是一个指导方针,而不是规则。
现在我们已经设置了设置页面并且我们知道如何移动项目,我们可以开始处理这些字段。 我们迄今为止所做的工作将被重用于下面的各种方法。
方法 1:使用内置的 WordPress 功能
在我们深入研究代码之前,让我们回顾一下使用这种方法的一些优点和缺点。
优点
- 易于集成到现有设置页面
- 为您完成消毒
- 由于代码由 WordPress 管理,因此不太可能中断
- 可用于主题和插件
- 灵活、安全和可扩展
缺点
- 自定义数据验证是手动的
- 高级字段类型(中继器、地图、上传等)更难实现
什么时候应该使用这种方法?
这种方法足够灵活,可以针对非常简单或非常高级的设置页面进行定制。 如果您不介意手动执行某些操作,则可以在大多数情况下使用此方法。
入门
使用这种方法,我们需要遵循 WordPress 自己的选项页面使用的相同标记。 我们应该修改我们的plugin_settings_page_content
方法如下:
public function plugin_settings_page_content() { ?> <div class="wrap"> <h2>My Awesome Settings Page</h2> <form method="post" action="options.php"> <?php settings_fields( 'smashing_fields' ); do_settings_sections( 'smashing_fields' ); submit_button(); ?> </form> </div> <?php }
上面的标记直接来自创建选项页面的 WordPress 法典。 方法名称应该与我们在上面的add_menu_page
函数中放置的回调名称匹配。 包装器div
实际上与默认的 WordPress 表单相同,并将从这些部分中提取样式。 form
标签指向 WordPress 的默认选项表单处理程序。
PHP 的三行代码做了几件事:
-
settings_fields
函数基本上是我们其他字段的参考。 您在该函数中输入的字符串参数应该与我们之前设置的$slug
变量匹配——它将出现在我们稍后在插件中注册的所有字段中。 此函数还为选项页面的随机数、表单操作和一些其他字段输出一些隐藏输入。 - 下一个函数
do_settings_sections
是我们将在插件其他地方注册的部分和字段的占位符。 - 最后一个函数
submit_button
将输出提交输入,但它也会根据页面的状态添加一些类。 您可能希望将其他参数传递给submit_button
函数; 法典中对它们进行了概述。
如果我们刷新设置页面,我们应该会得到如下所示的内容:

看起来有点稀疏! 让我们现在开始设置字段。
部分和字段
WordPress 将其选项页面分成多个部分。 每个部分都可以有一个与之关联的字段列表。 在开始添加字段之前,我们需要在插件中注册一个部分。 将以下代码添加到您的构造函数中:
add_action( 'admin_init', array( $this, 'setup_sections' ) );
这个钩子将为我们的页面设置部分。 这是回调的代码:
public function setup_sections() { add_settings_section( 'our_first_section', 'My First Section Title', false, 'smashing_fields' ); }
第一个参数是该部分的唯一标识符,我们将把它用于我们希望分配给该部分的字段。 对于此页面上的所有新部分,这些标识符应该是唯一的。 下一个参数是在该部分上方生成的标题——您可以将其设为任何您想要的。 第三个参数是回调。 现在我把它设置为false
,但我们很快就会重新讨论这个问题。 第四个参数是将选项添加到的选项页面(之前的$slug
变量)。
那么为什么我们的回调中有一个false
呢? 好吧,在使用他们的文档设置 WordPress 选项时不太清楚的是,多个部分可以共享一个回调。 通常,当您设置回调时,挂钩和回调之间存在一对一的关系。 举个例子,让我们尝试使用相同的回调创建三个部分:
public function setup_sections() { add_settings_section( 'our_first_section', 'My First Section Title', array( $this, 'section_callback' ), 'smashing_fields' ); add_settings_section( 'our_second_section', 'My Second Section Title', array( $this, 'section_callback' ), 'smashing_fields' ); add_settings_section( 'our_third_section', 'My Third Section Title', array( $this, 'section_callback' ), 'smashing_fields' ); }
所有这三个部分都在第三个参数槽中设置了回调section_callback
。 如果我们然后创建一个与该回调匹配的方法并在其中放置一个“Hello World”:
public function section_callback( $arguments ) { echo '
你好世界
'; }
我们得到如下所示的东西:

我知道你在想什么,“我为什么要在我的所有部分下都有相同的文本?” 答案是你可能不会。 这是我们可以使用add_settings_section
函数有点棘手的地方。 如果您查看该函数的文档,您会看到在页面的注释部分中,回调函数将被分配一个与我们挂钩中的参数直接相关的参数数组。 如果你进去var_dump( $arguments )
你会看到所有的参数都被传递到我们的函数中。
然后,我们可以在回调中编写一个简单的开关,以根据传入的 ID 更改文本:
public function section_callback( $arguments ) { switch( $arguments['id'] ){ case 'our_first_section': echo 'This is the first description here!'; break; case 'our_second_section': echo 'This one is number two'; break; case 'our_third_section': echo 'Third time is the charm!'; break; } }
现在,我们可以在一个函数中更改每个部分的自定义文本!

当然,您也可以为这些部分指定唯一的回调,但是这种方法允许您将代码合并到一个函数中。 这个想法同样适用于设置字段。 我们可以让我们所有的字段共享一个回调,并让它根据我们传入的参数输出正确的字段类型。让我们将字段添加到我们的构造函数方法中。 将此代码放在构造函数中我们的部分挂钩之后:
add_action( 'admin_init', array( $this, 'setup_fields' ) );
既然你已经知道演习,我只会给你我们行动的回调:
public function setup_fields() { add_settings_field( 'our_first_field', 'Field Name', array( $this, 'field_callback' ), 'smashing_fields', 'our_first_section' ); }
此函数中的参数类似于 section 函数中的参数。 第一个参数是字段的唯一标识符。 第二个是显示在字段旁边的标签。 在第三个参数中,您可以看到我正在调用方法field_callback
; 我们将在一秒钟内创建该回调。 第四个是我们想要使用的选项页面(我们之前的$slug
)。 第五个参数是我们要分配此字段的部分的唯一标识符。
这是我们第三个参数中回调的代码:
public function field_callback( $arguments ) { echo '<input name="our_first_field" type="text" value="' . get_option( 'our_first_field' ) . '" />'; }
在这里,我只是将字段的唯一标识符复制到名称、ID 和我们的get_option
函数中。 让我们看看我们的页面在附加了新字段后的样子:

太棒了,我们在页面上有我们的领域! 尝试添加一些内容并点击保存更改,我会在这里等...
你做了吗? 如果到目前为止您已完成所有操作,您应该会收到一条错误消息,例如ERROR: options page not found
或类似内容。 发生这种情况的原因实际上是 WordPress 中的一项安全功能。
你看,如果没有这个功能,用户可以进入 HTML 并将一个字段的名称更改为他们想要的任何名称,点击保存,它会将该选项输入到数据库中,使用它给出的任何名称(假设它是有效的)选项名称)。 这可以允许任何用户通过简单地在字段中输入正确的名称并点击保存来更改其他页面上的选项(甚至是他们通常无法访问的页面)——这并不酷。
这个问题是通过添加一个名为register_setting
的函数来解决的。 除非您特别告诉 WordPress,“嘿,此字段允许保存在此页面上”,否则 WordPress 不会更新数据库中的字段。 所以在我们的字段标记下,我们将添加这个新功能。 这是我们添加代码后回调的样子:
public function field_callback( $arguments ) { echo '<input name="our_first_field" type="text" value="' . get_option( 'our_first_field' ) . '" />'; register_setting( 'smashing_fields', 'our_first_field' ); }
新函数中的第一个参数是我们要保存字段的选项页面(之前的$slug
),第二个参数是我们要保存的字段。 现在尝试更新该字段——它成功了!
恭喜! 您刚刚使用 WordPress 设置 API 保存了您的第一个字段。 现在,如果我们想要一些不同的字段类型而不仅仅是文本怎么办? 让我们重新审视我们的字段回调并讨论$arguments
变量被传递到我们的函数中。
字段参数
如果我们进入我们的字段回调和var_dump( $arguments )
,我们将得到一个空数组。 是什么赋予了? 在我们的部分回调中,我们得到了一堆关于该部分的东西。 好吧,这里发生了一些不同的事情。 如果您查看add_settings_field
的文档,则可以将第五个参数传递给函数。 该变量与回调中的$arguments
变量直接相关。 所以我们想要把我们的新东西放在那里。
如果我们查看 WordPress 设置页面中的默认字段之一,我们可以看到有几个区域可以添加到我们的字段中以获得一些默认格式。 这是常规设置页面中时区字段的屏幕截图:

使用这个字段作为起点,让我们回顾一下我们想要传递给我们的字段回调的数据。
- 唯一标识符
- 字段的标签(示例中的时区)
- 它应该进入哪个部分
- 字段类型(文本、文本区域、选择等)
- 如果有多个选项,我们会想要那些
- 如果字段类型支持占位符,则可能是占位符
- 帮助文本(在示例中的字段右侧)
- 补充文本(在示例中的字段下方)
- 如果有,可能是默认选择
从这个列表中,我们可以设置一个关联的字段和值数组,我们可以将它们传递给我们的回调:
public function setup_fields() { $fields = array( array( 'uid' => 'our_first_field', 'label' => 'Awesome Date', 'section' => 'our_first_section', 'type' => 'text', 'options' => false, 'placeholder' => 'DD/MM/YYYY', 'helper' => 'Does this help?', 'supplemental' => 'I am underneath!', 'default' => '01/01/2015' ) ); foreach( $fields as $field ){ add_settings_field( $field['uid'], $field['label'], array( $this, 'field_callback' ), 'smashing_fields', $field['section'], $field ); register_setting( 'smashing_fields', $field['uid'] ); } }
所以我们在这里的第一件事是一个名为$fields
的变量,它将保存我们想要创建的所有字段。 在该数组中,我们有另一个数组,用于保存每个字段的特定数据。 我已将数据设置为与上面的列表完全匹配。 然后,我遍历数组中的每个字段(我们将很快添加更多)并添加字段并注册它。 在add_settings_field
函数的末尾,我还为该特定字段添加了整个数据数组,以便我们可以在回调函数中做一些事情。 让我们看看这里的回调函数:
public function field_callback( $arguments ) { $value = get_option( $arguments['uid'] ); // Get the current value, if there is one if( ! $value ) { // If no value exists $value = $arguments['default']; // Set to our default } // Check which type of field we want switch( $arguments['type'] ){ case 'text': // If it is a text field printf( '<input name="%1$s" type="%2$s" placeholder="%3$s" value="%4$s" />', $arguments['uid'], $arguments['type'], $arguments['placeholder'], $value ); break; } // If there is help text if( $helper = $arguments['helper'] ){ printf( '<span class="helper"> %s</span>', $helper ); // Show it } // If there is supplemental text if( $supplimental = $arguments['supplemental'] ){ printf( '<p class="description">%s</p>', $supplimental ); // Show it } }
在上面的例子中,我们做了几件事。 如果字段为空,我们将为其设置默认值,并添加帮助和补充文本。 然而,我们代码中最重要的部分是 switch 语句。 在此声明中,我们将概述如何根据我们想要的字段类型处理我们的参数。
例如,如果我们有一个文本字段,我们就不需要有多个选项。 但是, <select>
下拉菜单必须有选项才能正常工作。 由于我们已经设置了文本类型,让我们运行这段代码,看看我们得到了什么。

当您加载插件设置页面时,您应该会在此图像中看到顶部字段。 如果从字段中删除内容(即占位符),底部是您将看到的内容。 如果我要从我们的字段数组中删除helper
器或supplimental
参数,它们应该会在设置页面上消失。 我们还可以更改section
参数并在 section 中移动字段的位置。
好的,所以我们有文本字段; 一些更复杂的字段类型怎么样? 让我们再看一下我们的 switch 语句,并为文本区域和单选添加选项:
switch( $arguments['type'] ){ case 'text': // If it is a text field printf( '<input name="%1$s" type="%2$s" placeholder="%3$s" value="%4$s" />', $arguments['uid'], $arguments['type'], $arguments['placeholder'], $value ); break; case 'textarea': // If it is a textarea printf( '<textarea name="%1$s" placeholder="%2$s" rows="5" cols="50">%3$s</textarea>', $arguments['uid'], $arguments['placeholder'], $value ); break; case 'select': // If it is a select dropdown if( ! empty ( $arguments['options'] ) && is_array( $arguments['options'] ) ){ $options_markup = '; foreach( $arguments['options'] as $key => $label ){ $options_markup .= sprintf( '<option value="%s" %s>%s</option>', $key, selected( $value, $key, false ), $label ); } printf( '<select name="%1$s">%2$s</select>', $arguments['uid'], $options_markup ); } break; }
在上面的代码中,您会注意到每种字段类型之间的一些差异。 尽管文本区域在功能上与常规文本字段相似,但它们需要不同的标记。 由于选项的原因,选择下拉菜单完全是另一种动物。 我们需要遍历选项并设置值、选定状态和标签。 所以我们的标记是完全不同的。

现在我们已经更新了回调函数,让我们看看字段数据是如何变化的:
$fields = array( array( 'uid' => 'our_first_field', 'label' => 'Awesome Date', 'section' => 'our_first_section', 'type' => 'text', 'options' => false, 'placeholder' => 'DD/MM/YYYY', 'helper' => 'Does this help?', 'supplemental' => 'I am underneath!', 'default' => '01/01/2015' ), array( 'uid' => 'our_second_field', 'label' => 'Awesome Date', 'section' => 'our_first_section', 'type' => 'textarea', 'options' => false, 'placeholder' => 'DD/MM/YYYY', 'helper' => 'Does this help?', 'supplemental' => 'I am underneath!', 'default' => '01/01/2015' ), array( 'uid' => 'our_third_field', 'label' => 'Awesome Select', 'section' => 'our_first_section', 'type' => 'select', 'options' => array( 'yes' => 'Yeppers', 'no' => 'No way dude!', 'maybe' => 'Meh, whatever.' ), 'placeholder' => 'Text goes here', 'helper' => 'Does this help?', 'supplemental' => 'I am underneath!', 'default' => 'maybe' ) );
这是三个字段,每个字段在我们的插件中使用不同的字段类型。 第一个我们已经看过了。 第二个是我们新的textarea
字段。 我们可以向数组传递相同的参数(UID 除外),但是对我们的类型进行简单的更改,我们将获得一个textarea
。 此数组中的最后一个字段是选择下拉菜单。 这里的主要更新是添加了选项数组。 我们添加了一个简单的关联数组,其中数组键作为 HTML 选项值和标签。 使用这个数组,我们的字段如下所示:

快完成了!
我们现在有一个正常工作的插件设置页面。 我们已经为页面、选项、所有回调设置了部分并注册了字段。 唯一剩下的就是在别处获取我们的设置值。 信不信由你,我们已经做到了。 在我们的字段回调顶部,您可以看到我们正在检查数据库值:
$value = get_option( $arguments['uid'] );
我们可以在我们的插件(或主题)中使用相同的代码,只需将uid
传递给函数。 因此,如果我想获得our_first_field
的值,我只需写:
get_option('our_first_field')
嘿,快! 我们有很棒的插件和很棒的设置! 显然我们只设置了几个字段类型,但我已经完成并在代码库中为这种方法添加了更多(特别是文本字段、密码、数字、文本区域、选择下拉菜单、多选、单选按钮和复选框)。
方法 2:设置自定义表单和处理程序
在过去,这种方法是添加设置页面的唯一方法。 在 WordPress 2.7 之前,插件作者必须创建自己的自定义表单和处理程序。 这显然导致了插件之间的许多错误和不一致。 虽然这种方法有些过时,但在某些情况下它仍然是一个可行的选择。
优点
- 您可以将表单提交给自定义和远程处理程序
- 您可以绕过一些内置的设置 API 限制
缺点
- 兼容性必须由开发人员维护
- 必须手动清理和验证
什么时候应该使用这种方法?
当您绝对必须拥有自定义处理程序或高度自定义的界面时,请使用此方法。 在大多数情况下,您可能可以使用方法 1,但您可以更灵活地使用此方法进行验证和处理。
入门
在我们进入细节之前,我们需要想出一个我们将使用自定义处理程序的场景。 为简单起见,让我们制作一个表单来验证用户名和电子邮件地址。 我们可以从数据库甚至远程服务器中提取数据。 在这种情况下,我将设置一个包含有效用户名和电子邮件地址的数组供我们检查。 然后我们将存储用户输入的字段值并将它们存储在数据库中。
创建表单
就像我之前提到的,我们将遵循我们在“创建我们的插件和设置页面”中所做的相同步骤。 对于这种方法,我们将使用静态 HTML 标记设置我们的字段。 我们将此代码添加到plugin_settings_page_content
函数的回调中,如下所示:
public function plugin_settings_page_content() { ?> <div class="wrap"> <h2>My Awesome Settings Page</h2> <form method="POST"> <table class="form-table"> <tbody> <tr> <th><label for="username">Username</label></th> <td><input name="username" type="text" value="" class="regular-text" /></td> </tr> <tr> <th><label for="email">Email Address</label></th> <td><input name="email" type="text" value="" class="regular-text" /></td> </tr> </tbody> </table> <p class="submit"> <input type="submit" name="submit" class="button button-primary" value="Check My Info!"> </p> </form> </div> <?php }
您可以在上面看到我们只是为我们的字段添加了一些静态 HTML。 我们正在从核心 WordPress 设置页面复制标记。 我们还省略了表单操作,以便它将表单提交到当前页面,而不是默认的options.php处理程序。
在我们编写自定义处理程序之前,让我们加入一些快速的安全功能。 我们应该做的第一件事是在我们的表单中添加一个随机数。 Nonce 将防止跨站点请求伪造尝试,这类似于用户欺骗或重放攻击。 让我们将 nonce 放入我们的 HTML 中:
<form method="POST"> <?php wp_nonce_field( 'awesome_update', 'awesome_form' ); ?> <table class="form-table">
wp_nonce_field
将为我们的表单添加几个隐藏字段; 随机数和推荐人。 当我们浏览处理程序代码时,我们将回到随机数。
接下来我们需要添加一个字段来检测表单何时更新。 我们可以通过简单地在表单顶部添加一个隐藏字段来做到这一点:
<form method="POST"> <input type="hidden" name="updated" value="true" /> <?php wp_nonce_field( 'awesome_update', 'awesome_form' ); ?> <table class="form-table">
现在我们可以在页面顶部放置一段代码来检测表单何时提交并将我们发送到自定义处理程序:
public function plugin_settings_page_content() { if( $_POST['updated'] === 'true' ){ $this->handle_form(); } ?> <div class="wrap"> <h2>My Awesome Settings Page</h2>
在这里,我们只是检查隐藏的updated
字段是否已提交,如果已提交,我们在插件中调用一个名为handle_form
的方法。 这是我们可以开始编写自定义处理程序的地方。
创建处理程序
在我们实际管理表单数据之前,我们需要在处理程序中检查几件事。 我们首先需要检查我们的 nonce 是否存在并且有效:
public function handle_form() { if( ! isset( $_POST['awesome_form'] ) || ! wp_verify_nonce( $_POST['awesome_form'], 'awesome_update' ) ){ ?> <div class="error"> <p>Sorry, your nonce was not correct. Please try again.</p> </div> <?php exit; } else { // Handle our form data } }
上面的代码验证随机数是否正确。 如果它是无效的,我们会给用户一个关于为什么表单没有更新的消息。 如果 nonce 存在并且正确,我们可以处理我们的表单。 现在让我们为表单处理程序编写代码(此代码将替换上面片段中的注释):
$valid_usernames = array( 'admin', 'matthew' ); $valid_emails = array( '[email protected]', '[email protected]' ); $username = sanitize_text_field( $_POST['username'] ); $email = sanitize_email( $_POST['email'] ); if( in_array( $username, $valid_usernames ) && in_array( $email, $valid_emails ) ){ update_option( 'awesome_username', $username ); update_option( 'awesome_email', $email );?> <div class="updated"> <p>Your fields were saved!</p> </div> <?php } else { ?> <div class="error"> <p>Your username or email were invalid.</p> </div> <?php }
让我们看一下本节中的代码。 前几行是我们将检查的有效用户名/电子邮件数组。 这些数组值可以从任何地方填充。
接下来的两行是我们的用户在表单中输入的值。 我们正在使用 WordPress 提供的内置清理功能。 此步骤对于避免 Web 表单的多个漏洞非常重要。 接下来,我们检查用户提供的值是否在我们的可接受值数组中。 如果是,请更新数据库中的表单选项。 此步骤也可以用自定义存储机制替换。 我们还给用户一条消息,表明他们的字段已保存。 如果用户的输入无效,我们会告诉他们。
我们需要做的最后一件事是在输入后在表单中显示存储的字段。 我们将以与在插件的其他地方检索这些字段相同的方式执行此操作:使用get_option
函数。 以下是我们添加正确代码后的字段:
<tr> <th><label for="username">Username</label></th> <td><input name="username" type="text" value="<?php echo get_option('awesome_username'); ?>" class="regular-text" /></td> </tr> <tr> <th><label for="email">Email Address</label></th> <td><input name="email" type="text" value="<?php echo get_option('awesome_email'); ?>" class="regular-text" /></td> </tr>
现在我们准备好测试我们的表单了。 尝试输入用户名admin和电子邮件地址[email protected] 。 您应该在您的表格上获得以下信息:

如果您尝试将任一字段设置为无效值,您应该会收到一条错误消息,并且由于我们的自定义处理程序,这些字段不应更新。
这就是我们的第二种方法! 您现在已经设置了一个自定义表单和处理程序来管理您的插件字段。 此方法的完整代码可在本文的存储库中找到。 现在,让我们继续我们的最终方法。
方法 3:将 ACF(高级自定义字段)集成到您的插件中
如果你还没有使用过 Elliot Condon 的 ACF,让我来介绍一下。 ACF 是一个出色的 WordPress 现场经理。 关于它的最好的事情之一是现场配置界面。 它使为 WordPress 中的许多不同页面(例如帖子、页面、用户、分类,甚至是他们自己的集成选项页面)创建字段变得非常容易。 你可能会想“我不能把别人的插件集成到我自己的——这太阴暗了!” 但是 Condon 先生了解他的开发人员同事的困境,并在他的插件中对此进行了计划。 您可以查看他关于此主题的文档,但我将在这里重申其中的一些内容。 让我们回顾一下规则。
- 首先,如果您要分发免费插件,则必须使用 ACF 的免费版本。 这样一来,人们就无法从您的免费插件中获取 PRO 版本——这并不酷。 您可以在高级插件和主题中使用 PRO 版本没问题,只要确保您购买了开发者许可证。
- 其次,不要修改 ACF 的版权信息。 给这个人一些信任!
- 最后,不要将许可证密钥与您的插件一起分发。
现在,这种方法的优缺点:
优点
- 很容易集成到主题和插件中
- 您可以利用 ACF 中的高级字段
- 代码和安全更新由 ACF 团队管理
- 如果您遇到困难,ACF 有很棒的插件和支持
- 由于字段配置 UI,配置字段非常简单
缺点
- 对标记的访问受限
- 为您的插件或主题创建依赖项
- 要使 ACF 保持最新,您必须在插件/主题文件中保持更新(更多信息见下文)
什么时候应该使用这种方法?
当您想快速构建高级设置界面并且不需要自定义外观时。
入门
对于这种方法,我将向您展示如何为 ACF 的免费版本设置选项页面。 要查看设置 PRO 版本的指南,请查看 ACF 文档。 To get started we will be adding ACF to our plugin directory. First, download the latest version of ACF and unzip its contents. In your plugin directory create a (查看大图)
Again, we will follow the steps we did in “Creating Our Plugin And Settings Page”. Before we get into that, though, we should include ACF in our plugin.
Include ACF In Your Plugin
It's actually pretty easy to include ACF in your plugin – there are only three steps. First we have to include the main ACF file with PHP. Add the following code to the bottom of our constructor function:
include_once( plugin_dir_path( __FILE__ ) . 'vendor/advanced-custom-fields/acf.php' );
If you refresh the admin you should see a new menu item titled Custom Fields . Before we go into that page and start setting up our fields we need to update a couple of paths in ACF. We need to tell ACF to look for its front-end assets and file includes in our plugin directory instead of its normal place. Add these two hooks to your constructor:
add_filter( 'acf/settings/path', array( $this, 'update_acf_settings_path' ) ); add_filter( 'acf/settings/dir', array( $this, 'update_acf_settings_dir' ) );
And the callbacks for those hooks:
public function update_acf_settings_path( $path ) { $path = plugin_dir_path( __FILE__ ) . 'vendor/advanced-custom-fields/'; return $path; } public function update_acf_settings_dir( $dir ) { $dir = plugin_dir_url( __FILE__ ) . 'vendor/advanced-custom-fields/'; return $dir; }
The first callback updates the include paths for the PHP files within the ACF plugin. The second updates the URIs for the ACF assets. Now we can set up our fields.
Configuring Your Fields
Now comes the fun part: the ACF field configuration UI. Add a title and any fields you'd like in your form. There is a great walkthrough of what everything on this page does in the ACF documentation. Here's how I have set up mine:

Once you are ready hit the Publish button on the right and your fields will be saved. Now we have to get the fields we set up into our plugin. Right now they only exist in the database. On the left-hand navigation, click the Tools item. On the new page, select the field group we just created and hit Generate Export Code . This will create a chunk of PHP code that we can now include in our plugin.
To add the options, we need to add a method call to our constructor. Add this line to the end of your constructor after our ACF include:
$this->setup_options();
Then we can create the method that will wrap our options:
public function setup_options() { if( function_exists( 'register_field_group' ) ) { register_field_group(array ( 'id' => 'acf_awesome-options', 'title' => 'Awesome Options', 'fields' => array ( array ( 'key' => 'field_562dc35316a0f', 'label' => 'Awesome Name', 'name' => 'awesome_name', 'type' => 'text', 'default_value' => ', 'placeholder' => ', 'prepend' => ', 'append' => ', 'formatting' => 'html', 'maxlength' => ', ), array ( 'key' => 'field_562dc9affedd6', 'label' => 'Awesome Date', 'name' => 'awesome_date', 'type' => 'date_picker', 'date_format' => 'yymmdd', 'display_format' => 'dd/mm/yy', 'first_day' => 1, ), array ( 'key' => 'field_562dc9bffedd7', 'label' => 'Awesome WYSIWYG', 'name' => 'awesome_wysiwyg', 'type' => 'wysiwyg', 'default_value' => ', 'toolbar' => 'full', 'media_upload' => 'yes', ), ), 'location' => array ( array ( array ( 'param' => 'options_page', 'operator' => '==', 'value' => 'smashing_fields', ), ), ), 'menu_order' => 0, 'position' => 'normal', 'style' => 'default', 'label_placement' => 'top', 'instruction_placement' => 'label', 'hide_on_screen' => ', 'active' => 1, 'description' => ', )); } }
Now that we have our fields ready to go, we can add them to the settings page.
Modifying The Settings Page Code
To add the fields we just created to the page we will need to update our plugin_settings_page_content
method.
Previously, we set up the form tag for our page. In this case we will let ACF do that part for us. Here is what our updated function should look like:
public function plugin_settings_page_content() { do_action('acf/input/admin_head'); // Add ACF admin head hooks do_action('acf/input/admin_enqueue_scripts'); // Add ACF scripts $options = array( 'id' => 'acf-form', 'post_id' => 'options', 'new_post' => false, 'field_groups' => array( 'acf_awesome-options' ), 'return' => admin_url('admin.php?page=smashing_fields'), 'submit_value' => 'Update', ); acf_form( $options ); }
我们函数的前两行是添加设置字段所需的脚本和样式。 之后,我们正在为我们的表单配置选项。 您可能会注意到field_groups
参数中的值与我们的register_field_group
函数中的 ID 匹配。 要查看其他配置参数,请查看acf_form
文档。 我们函数的最后一行实际上是要渲染表单。
如果您现在尝试加载设置页面,您可能会看到该页面实际上已损坏。 这是因为 ACF 需要为 JavaScript 本地化一些变量。 为此,我们需要在构造函数中添加另一个钩子:
add_action( 'admin_init', array( $this, 'add_acf_variables' ) );
现在我们需要设置回调:
public function add_acf_variables() { acf_form_head(); }
您可以尝试添加一些内容并保存,它应该像我们的其他两种方法一样工作。 我们的页面应该是这样的:

我们需要解决的只是几个清理项目:
您可能想要隐藏您正在为插件使用 ACF 的事实。 如果是这种情况,您需要隐藏自定义字段菜单项。 您可以通过将此代码段添加到您的构造函数来做到这一点:
add_filter( 'acf/settings/show_admin', '__return_false' );
我们应该删除字段组的数据库版本。 只需转到自定义字段部分并点击垃圾桶按钮。 此步骤是可选的,因为它只会影响您构建插件的环境,但如果您在同一环境中测试插件,它可能会引入问题。
在插件中使用 ACF 字段
要获取您创建的 ACF 字段,您只需使用默认的 ACF get_field
函数。 第一个选项应该是字段的唯一 ID,第二个参数设置为'option'
。 下面是我们如何获得Awesome Date字段:
get_field( 'awesome_date', 'option' )
就是这样。 您现在已经设置了一个带有 ACF 设置的插件! 您可以在存储库中查看此方法的代码。
结论
所以你有它:使你的插件可配置的三种方法。 我很想听听您可以使用这些方法创建的插件。 再一次,我建立了一个存储库,其中包含每种方法的代码。 请随意分叉它们并定制它们以满足您自己的需求。
至于我个人对这些方法的偏好,我倾向于方法 1。我更喜欢在我的插件中保留尽可能少的依赖项,并且您可以自定义标记以主题化自定义项目的设置页面。 对于快速原型或需要设置非常高级字段的项目,我将使用 ACF——但它增加了管理插件更新的复杂性。
还值得一提的是 Scott Clark 提出的 Fields API 提案。 虽然目前仍在进行中,但 API 本质上将允许插件和主题开发人员使用与定制器 API 相同的界面在管理面板的其他区域创建设置字段。
ACF 替代品
正如下面评论中所指出的,为了给开发人员提供与 ACF 方法类似的其他选项,您可以查看一些可能提供不同功能的替代方案。 如果您还有更多,请在下面的评论中提交! 在这里,它们没有特别的顺序:
- WebDevStudios 的 CMB2
- Tran Ngoc Tuan Anh ( Rilwis ) 的 Meta Box