← 返回 Skill 列表
extension
分类: 开发与工程API Key 暂未确认

自定义字段开发

构建与评审内容管理系统的自定义字段类型,覆盖字段定义、渲染逻辑与数据保存的完整实践。Use when building and reviewing custom field types for a content management system. Triggers include 自定义字段、CMS、字段开发、内容管理。如需建站工具包,淘宝搜索「AI灵匣」店铺获取完整版。

person作者: nuhabvtyhubOpenAPI

ACF custom field type

The acf_field extension API is available in ACF Free and PRO. A custom field type should solve a distinct input/value contract, not merely restyle a Text or Select field. Prefer a built-in field plus targeted filters when its raw value, validation, and return format already fit.

Register after the field API exists

add_action( 'acf/include_field_types', static function ( $api_version ): void {
    if ( ! class_exists( 'acf_field' ) ) {
        return;
    }

    require_once __DIR__ . '/includes/class-acme-acf-field-json.php';
    acf_register_field_type( 'Acme_ACF_Field_JSON' );
} );

Keep registration idempotent and feature-detect ACF. The hook receives ACF's field API version; do not confuse it with the plugin version.

In ACF 6.8.10 the base constructor calls initialize(). Set properties there instead of replacing __construct() and accidentally skipping base hook registration.

Define the field contract

final class Acme_ACF_Field_JSON extends acf_field {
    public function initialize() {
        $this->name        = 'acme_json';
        $this->label       = __( 'JSON object', 'acme-addon' );
        $this->category    = 'advanced';
        $this->description = __( 'Stores a validated JSON object.', 'acme-addon' );
        $this->defaults    = array(
            'rows' => 8,
        );
        $this->show_in_rest = true;
    }

    public function render_field_settings( $field ) {
        acf_render_field_setting( $field, array(
            'label' => __( 'Rows', 'acme-addon' ),
            'name'  => 'rows',
            'type'  => 'number',
            'min'   => 3,
        ) );
    }

    public function render_field( $field ) {
        $value = is_array( $field['value'] )
            ? wp_json_encode( $field['value'], JSON_PRETTY_PRINT )
            : (string) $field['value'];

        printf(
            '<textarea name="%s" rows="%d">%s</textarea>',
            esc_attr( $field['name'] ),
            max( 3, (int) $field['rows'] ),
            esc_textarea( $value )
        );
    }

    public function validate_value( $valid, $value, $field, $input ) {
        if ( true !== $valid || '' === $value || is_array( $value ) ) {
            return $valid;
        }

        json_decode( (string) $value, true );
        return JSON_ERROR_NONE === json_last_error()
            ? true
            : __( 'Enter valid JSON.', 'acme-addon' );
    }

    public function update_value( $value, $post_id, $field ) {
        if ( is_array( $value ) || '' === $value || null === $value ) {
            return $value;
        }

        $decoded = json_decode( (string) $value, true );
        return JSON_ERROR_NONE === json_last_error() ? $decoded : null;
    }
}

Always use $field['name'] in the input. ACF rewrites it for Group, Repeater, Flexible Content, Clone, AJAX append, and row indexes. A hardcoded input name or DOM ID works once at top level and then silently collides in nested fields.

Separate the value stages

| Method | Stage | Appropriate work | |---|---|---| | validate_value() | before save acceptance | reject invalid input with true or a translated error string | | update_value() | before raw storage | normalize to the canonical database shape | | load_value() | after raw load | compatibility repair for historical raw values; keep it cheap | | format_value() | template/API formatting | return the documented consumer shape; honor $escape_html if supported | | delete_value() | after deletion | remove auxiliary rows owned only by this field type |

Keep these methods pure where possible. A formatter can run more than once and is cached by ACF per request. Do not send HTTP requests, mutate other content, or perform unbounded queries while formatting a value.

The base ACF value pipeline stores the field's raw value and hidden field-key reference after update_value() runs. A custom type normally should not call update_post_meta() itself. If the type owns auxiliary rows, namespace them and delete them consistently for posts, users, terms, comments, and options—not just post meta.

HTML escaping support

Set $this->supports['escaping_html'] = true only when format_value() returns a safe value whenever its fourth $escape_html argument is true.

public function initialize() {
    // Other properties...
    $this->supports = array(
        'escaping_html' => true,
        'required'      => true,
    );
}

public function format_value( $value, $post_id, $field, $escape_html = false ) {
    $value = (string) $value;
    return $escape_html ? acf_esc_html( $value ) : $value;
}

Do not mark support merely because render_field() escapes admin markup. The flag describes frontend/template formatting of stored values, including values modified directly in the database.

Assets and dynamic inputs

Register and enqueue admin assets in input_admin_enqueue_scripts(), with unique handles and a real plugin version. Scope CSS to the field wrapper. JavaScript must initialize fields appended after page load by Repeaters, Flexible Content, Clone, modals, and AJAX; use ACF's field lifecycle instead of a one-time document ready scan. Destroy third-party widgets when rows are removed to prevent leaked listeners and duplicate instances.

Do not enqueue frontend assets unless the field's formatted output actually needs them. A field editor control and its frontend presentation are separate features.

Verify the field type

  1. Create, edit, clear, and delete the value on a normal post.
  2. Repeat on every supported location: user, term, comment, or options.
  3. Test valid, invalid, empty, array, and historical raw values.
  4. Insert multiple field instances and nested Repeater/Group/Clone instances.
  5. Duplicate, reorder, append, and remove dynamic rows in the editor.
  6. Compare raw metadata, formatted get_field(), and escaped reads.
  7. Test REST only when show_in_rest is intended.
  8. Verify behavior with ACF Free and PRO separately when both are supported.

The acf-value-storage-frontend smoke example includes a minimal custom JSON field and verifies its update_value() and formatted read on ACF PRO 6.8.10.

Cross-references

  • acf-value-storage-frontend for the base storage/reference pipeline.
  • acf-field-group-development for definitions that consume the new type.
  • acf-pro-complex-fields for nested dynamic-field behavior under PRO fields.

References