搜索全站
插件 16 分钟阅读

Advanced Custom Fields 完整教程:字段组、字段类型与前台调用

文章只有标题和正文不够用?ACF 能把任意字段挂到文章、用户、分类甚至选项页上。本文从字段组与位置规则讲起,覆盖常用字段类型、前台调用写法、Repeater 循环与本地 JSON 版本化。

U
UFO_PF 2016-03-30 · 阅读 16 分钟
Ad 插件
阅读提示
本文约 6,375 字,预计阅读 16 分钟。文中命令与配置均经过实测,可直接参考执行。

引言:文章只有标题正文,业务数据往哪放

做企业站时客户会提一串需求:产品要有”型号、参数表、PDF 下载”,案例要有”客户名称、施工时间、现场图集”,首页要有”轮播图(图片+标题+链接)可增删改”。这些东西用正文编辑器硬写,客户改一次就得找你一次,而且数据结构混乱,没法按型号筛选。

WordPress 自带的”自定义字段”能解决这个问题,但原生界面是一张键值对的裸表,客户填的时候完全是猜。Advanced Custom Fields(简称 ACF)做的事就是把这张裸表包装成有类型、有校验、有布局的表单:图片字段给上传按钮,日期字段给日历,关联字段给下拉搜索。字段存进去的依然是 wp_postmeta,但填的人和读的人都舒服了。

ACF 分免费版和 PRO 版。免费版已经覆盖文本、图片、文件、选择、关联、Repeater、Gallery、Flexible Content 等核心类型,足够绝大多数站点;PRO 版多的是 Options Page、Repeater 之外的 Gallery 增强、Clone 字段、区块(ACF Blocks)等。下面以免费版为主,涉及 PRO 的地方会标注。

原理分析:字段组、位置规则与存储

ACF 的核心概念是三层嵌套:字段(Field)→ 字段组(Field Group)→ 位置规则(Location Rules)。

  • 字段定义”填什么”:name(存储用的键)、label(界面显示名)、type(类型)、instructions、required、default_value。
  • 字段组是一组字段的集合,也就是后台那张表单的容器,可以控制显示位置(normal / side / acf_after_title)、样式(盒装 / 无缝)和顺序。
  • 位置规则决定这个字段组出现在哪些编辑界面上。规则是”与/或”组合的,比如”文章类型 = 产品 且 产品分类 = 机械设备”。

存储层面要记住两点:

  1. 字段值存进 wp_postmeta,meta_key 就是字段的 name。所以 get_field( 'model' ) 本质上等价于 get_post_meta( get_the_ID(), 'model', true )。
  2. 字段组本身(配置)默认存在 wp_posts 里,post_type 为 acf-field-group,字段为 acf-field。这意味着字段配置是数据库数据,不是代码——换了环境就得同步,这是后面要讲本地 JSON 的原因。
常用字段类型 存储形态 典型用途
Text / Textarea / Number 字符串或数字 型号、简介、排序值
Image / File 附件 ID(整数) 轮播图、PDF 下载
Select / Checkbox / Radio 单个值或序列化数组 枚举选项
True / False 1 或空 布尔开关
Post Object / Relationship ID 或 ID 数组 关联文章、交叉推荐
Taxonomy 分类 ID 跨类型打标
Repeater / Gallery 行数 + 多个子键 参数表、图集
Group 子字段数组 结构化分组

保姆级实操

第一步:建字段组并设置位置规则

后台「Custom Fields → Field Groups → Add New」,填标题,加字段,然后在 Location 区域写规则。例如”只在产品类型下显示”:Post Type is equal to Product。规则保存后,去产品编辑页就能看到表单。

第二步:前台调用基础字段

<?php
// 单个字段:第二个参数不传时默认取当前文章 ID
$model = get_field( 'model' );        // 返回值
the_field( 'model' );                 // 直接 echo,等价于 echo get_field()

// 图片字段默认返回数组,也可指定返回格式
$image = get_field( 'cover_image' );
if ( $image ) {
    printf(
        '<img src="%s" width="%d" height="%d" alt="%s" loading="lazy">',
        esc_url( $image['url'] ),
        (int) $image['width'],
        (int) $image['height'],
        esc_attr( $image['alt'] )
    );
}

// 拿完整字段定义(含 choices 等配置)用 get_field_object
$field = get_field_object( 'model' );
echo esc_html( $field['label'] );

图片字段的返回格式可以在字段配置里选 Array / URL / ID。建议统一用 Array,前台能拿到 alt、width、height,对 SEO 和 CLS 都有好处。

第三步:Repeater 循环输出参数表

Repeater 是实际项目里用得最多、也最容易写错的类型:

<?php if ( have_rows( 'specs' ) ) : ?>
<table class="spec-table">
    <tbody>
    <?php while ( have_rows( 'specs' ) ) : the_row(); ?>
        <tr>
            <th><?php echo esc_html( get_sub_field( 'label' ) ); ?></th>
            <td><?php echo esc_html( get_sub_field( 'value' ) ); ?></td>
        </tr>
    <?php endwhile; ?>
    </tbody>
</table>
<?php endif; ?>

have_rows() / the_row() / get_sub_field() 这套函数操作的是 ACF 内部的行指针,不能在同一个页面里嵌套着乱调;Flexible Content 里则用 get_row_layout() 判断当前布局:

<?php
while ( have_rows( 'page_blocks' ) ) : the_row();
    if ( 'hero' === get_row_layout() ) {
        get_template_part( 'blocks/hero' );
    } elseif ( 'faq' === get_row_layout() ) {
        get_template_part( 'blocks/faq' );
    }
endwhile;

第四步:用 PHP 注册字段组(团队协作推荐)

界面配置的字段组不在版本控制里,多人协作必然打架。改用 acf_add_local_field_group() 写在主题或插件里:

add_action( 'acf/init', function () {
    acf_add_local_field_group( array(
        'key'      => 'group_product',
        'title'    => '产品信息',
        'fields'   => array(
            array(
                'key'   => 'field_model',
                'label' => '产品型号',
                'name'  => 'model',
                'type'  => 'text',
            ),
            array(
                'key'           => 'field_specs',
                'label'         => '参数表',
                'name'          => 'specs',
                'type'          => 'repeater',
                'sub_fields'    => array(
                    array( 'key' => 'field_spec_label', 'label' => '项目', 'name' => 'label', 'type' => 'text' ),
                    array( 'key' => 'field_spec_value', 'label' => '数值', 'name' => 'value', 'type' => 'text' ),
                ),
            ),
        ),
        'location' => array(
            array(
                array( 'param' => 'post_type', 'operator' => '==', 'value' => 'product' ),
            ),
        ),
    ) );
} );

第五步:把界面配置导出为 JSON(ACF Local JSON)

如果坚持用界面配置,至少启用本地 JSON,让配置落到文件、进 Git:

add_filter( 'acf/settings/save_json', function () {
    return get_stylesheet_directory() . '/acf-json';
} );
add_filter( 'acf/settings/load_json', function ( $paths ) {
    $paths[] = get_stylesheet_directory() . '/acf-json';
    return $paths;
} );

启用后,每次在后台保存字段组,都会往主题目录下的 acf-json/ 写一个 JSON 文件;部署到新环境时 ACF 会自动从这些文件加载。

第六步:全局设置用选项页(ACF PRO)

add_action( 'acf/init', function () {
    if ( function_exists( 'acf_add_options_page' ) ) {
        acf_add_options_page( array(
            'page_title' => '站点全局设置',
            'menu_slug'  => 'site-settings',
            'capability' => 'manage_options',
        ) );
    }
} );

// 前台取值时第二个参数固定写 'option'
$icp = get_field( 'icp_number', 'option' );

常见踩坑

  1. get_field() 必须在循环内或显式传 ID。 在 WP_Query 自定义循环里,get_the_ID() 返回的是循环当前文章,不是主循环文章;不确定时永远写全 get_field( 'x', $post_id )。
  2. 字段 name 用了中文或连字符。 name 会成为 meta_key,建议只用小写字母和下划线,且不要用 wp_ 开头。
  3. the_field() 不做转义。 它直接 echo,字段内容含 HTML 时既可能破坏布局也可能引入 XSS。输出到前台优先 get_field() + esc_html() / esc_url() / wp_kses_post()。
  4. 改了字段 name 等于换了一个键,旧数据还在老键上,前台会突然全部取不到。改名前先想清楚要不要做数据迁移。
  5. Repeater 里套 Repeater 会显著拖慢列表页。 每一行每个子字段都对应一条 meta 记录,100 篇文章 × 20 行 × 3 子字段就是 6000 行。列表页不要渲染 Repeater,只渲染简单字段,具体优化思路见 ACF get_field 性能优化。
  6. 字段组配置丢了。 换服务器只导了 wp_posts 没导 acf-field-group,后台字段全空。启用本地 JSON 可以根治。
  7. 案例: 一个机械外贸站把产品参数做成 Repeater,后台填了三年的数据。后来同事在字段组里把子字段 value 改名成 spec_value(为了”更规范”),保存后前台参数表全空,客户以为数据丢了。恢复办法是写一次性脚本,用 $wpdb->query( "UPDATE {$wpdb->postmeta} SET meta_key='specs_%_spec_value' WHERE meta_key LIKE 'specs_%_value'" ) 批量改名——注意 Repeater 子字段的 meta_key 形如 specs_0_value,是带行号的。教训是:字段 name 一旦上线就等同于数据库表结构,改名必须配套迁移脚本。

总结

ACF 的正确用法可以浓缩成三条:字段组靠位置规则收敛到具体场景,避免”全站一个巨型字段组”;字段配置要么用 acf_add_local_field_group() 写成代码,要么启用本地 JSON,总之必须进版本控制;前台取值一律显式传 ID 并做转义。

上手顺序建议:先做两个字段的字段组跑通”后台填—前台读”闭环,再上 Repeater,再上 Flexible Content 做区块化页面。涉及全局配置时用 Options Page 而不是塞进某个文章;涉及大量重复数据时先评估性能再决定要不要 Repeater。更多关于字段读取的性能细节可以接着看 ACF 自定义字段性能优化,而产品类型本身如何优雅创建,可以参看 Custom Post Type UI 创建自定义文章类型。

U
UFO_PF作者

专注 WordPress 生态的实战作者,内容以「可复制、可验证」为第一原则。

留下你的想法

邮箱地址不会被公开。带 * 的为必填项。