首先让我澄清两件事:
ACF 建议向支持此属性的输入元素添加“disabled”或“readonly”,但这还不够:
并非所有 acf 字段都可以自定义为只读或禁用(当然,某些字段不需要此功能,但其他字段可以使用它,例如复选框或范围)
“禁用”不做这项工作:如果您提交一个禁用了复选框 acf 字段的表单,它将删除该字段的数据,因此该字段是可修改的,但方式很糟糕。您只能将其添加到以下字段:
$field['disabled']=1;
:“文本”、“文本区域”、“数字”、“电子邮件”、“网址”、“密码”、“日期选择器”、“时间选择器”、“选择”$field['disabled']=array(<elements to disable>)
:'复选框','单选按钮'“只读”在更少的字段上可用,并且不会从发布请求中删除值,因此,如果包含该字段的页面打开并且该字段在其他地方被修改,如果您提交此页面是因为您在其他的,它也会覆盖这个“只读”字段的更改
所以,我尝试了一个解决方案:我在字段更新时进行挂钩,并通过查看 $_POST['acf'] 是否设置来检查它是否来自前面,但我不知道它是否可靠?
function prevent_acf_field_from_updating($null, $value, $post_id, $field) {
/*
* if $_POST['acf'] is not set, the update is from server side, so I don't prevent it
* I suppose ?
*
*/
if (!isset($_POST['acf'])) {
return $null;
}
/*
* to make a field disabled, I use a class
*
*/
if (!str_contains($field['wrapper']['class'], 'read_only_acf')) {
return $null;
}
return false;
}
add_filter('acf/pre_update_value', 'prevent_acf_field_from_updating', 10, 4);
我还使用过滤器将
inert
属性添加到包装器 acf/field_wrapper_attributes
但我真的不确定这是一个好方法,检查 $_POST['acf'] 是否可靠?
经过更多调查,我找到了一个我认为不错的方法,所以我会回答我自己的问题(如果你不想阅读所有推理,请跳到答案末尾的代码)。
正如CBroe在其评论中指出的那样,acf字段仅呈现到管理面板,而不是前端(在wordpress中,“前端”并不意味着通过网络浏览器访问的内容,而是指非管理员访问的内容网络浏览器。管理部分称为后端)。
因此,有 2 个地方可以防止特定的 acf 字段从管理员更新:
acf/pre_update_value
,正如我在问题中尝试做的那样acf/pre_update_value
过滤器防止更新:为了防止此过滤器进行更新,我们需要一种可靠的方法来检测字段更新操作是在管理端还是在服务器端启动。我可以想到 3 种方法来做到这一点:
通过检查
$_POST['acf']
是否已设置,正如我在答案中建议的那样
通过检测操作是否在管理员中,可以简单地使用
is_admin()
完成,但 CBroe 发布的链接显示了更好、更完整的方法:https://florianbrinkmann.com/en/wordpress-backend-request -3815/
通过将服务器端调用包装在函数中,例如
my_plugin_acf_field_update()
,然后使用函数debug_backtrace()
检查调用是否来自这个函数
update_field()
的服务器端调用,这可能会很痛苦,具体取决于项目的状态和结构(在我的情况下这很好,而且我几乎用过)防止更新的另一种方法是首先防止在 html 中提交字段。
为此,html 为表单元素提供了
disabled
属性,acf 提出了一种将它们添加到某些字段的方法,但效果不佳:
对于某些领域,它似乎有效:
['text', 'textarea', 'number', 'email', 'url', 'password', 'date_picker', 'time_picker', 'select']
对于其他一些领域它不起作用:
['checkbox', 'radio']
。为什么 ?实际上,因为 acf 添加了一个带有字段 ref 的隐藏字段,如下所示:<input type="hidden" name="acf[field_6617daff359b2]">
,因此,如果某个复选框因禁用而未提交,则 acf 无论如何都会接收隐藏输入,然后检查任何复选框值,找不到任何值并推断它们都未被选中,因此它会相应地更新该字段
最后,对于所有其他字段,acf 没有提出任何禁用它们的方法,即使它们由输入元素组成,如范围字段。
但是还有另一种方法,这就是我将在我的项目中使用的方法:您可以使用 acf 内部使用的操作挂钩来渲染每个字段,并在 acf 之前和之后挂钩它,以添加一个
fieldset
元素使用 disabled
属性 -> 它将禁用其中的任何元素,包括隐藏字段。我希望这应该有效并且稳健。
我就是这样做的:
/*
* there is one action hook suitable to open and close the fieldset element :
* 'acf/render_field' -> line 807 in advanced-custom-fields/includes/acf-field-functions.php
*
* acf uses priority 9 for its 'acf/render_field' hook -> see line 64 in advanced-custom-fields/includes/fields/class-acf-field.php
* so we hook before and after
*
* side note : we use a class, 'read_only_acf', to detect if a field should be disabled
*
*/
function open_fieldset_acf_disabled($field) {
if (str_contains($field['wrapper']['class'], 'read_only_acf')) {
echo "<fieldset disabled class='acf_disabled_fieldset' style='border:none; margin:0px; padding:0px;'>\n";
}
}
function close_fieldset_acf_disabled($field) {
if (str_contains($field['wrapper']['class'], 'read_only_acf')) {
echo "</fieldset>\n";
}
}
add_action('acf/render_field', 'open_fieldset_acf_disabled', 8);
add_action('acf/render_field', 'close_fieldset_acf_disabled', 10);