分析 WordPress `register_meta()` 函数的源码:如何将自定义元数据暴露给 REST API。

各位听众朋友们,大家好!今天咱们不搞那些虚头巴脑的,直接进入正题,聊聊 WordPress 里一个相当重要但又容易被忽略的函数:register_meta()。重点是,我们要搞清楚它如何把咱们辛辛苦苦定义的自定义元数据,华丽丽地暴露给 WordPress REST API,让前端小伙伴们也能轻松获取。

一、元数据是个啥?为啥要用 register_meta()

想象一下,WordPress 的文章就像一个基本的“房子”,标题、内容、作者就是这房子的“砖头和瓦”。但是,如果我想给这个房子加点个性化的东西,比如“装修风格”、“设计师”、“完工日期”等等,这些就属于元数据,也就是“房子里的家具摆设和装饰”。

元数据能让我们给文章、用户、分类等等,添加各种各样的额外信息,让数据更加丰富多彩。

那么,为什么我们需要 register_meta() 呢?直接用 update_post_meta()get_post_meta() 不行吗?当然可以,但是 register_meta() 提供了以下几个关键优势:

  • 数据类型定义: 可以明确指定元数据的数据类型(string、number、boolean 等),确保数据的正确性和一致性。
  • 访问控制: 可以控制哪些用户可以读取和修改元数据,保证数据的安全性。
  • REST API 集成: 也是我们今天的主角,通过 register_meta(),可以将元数据自动暴露给 REST API,方便前端访问。
  • 模式验证: 如果你更高级,还可以定义元数据的 schema,做更严格的验证。

简单来说,register_meta() 让元数据管理更加规范、安全、并且方便对外提供数据。

二、register_meta() 的基本用法:像搭积木一样简单

register_meta() 函数的基本语法如下:

register_meta(
    string   $meta_type,
    string   $meta_key,
    array    $args = array()
);

参数解释:

  • $meta_type: 元数据所属的对象类型,例如 post(文章)、user(用户)、term(分类)等等。
  • $meta_key: 元数据的键名,也就是我们给这个元数据起的名字,例如 _my_custom_field。 记住,通常情况下,以下划线_开头的meta key是默认不显示在REST API中的,除非你明确指定了show_in_rest为true。
  • $args: 一个关联数组,用于配置元数据的各种属性,例如数据类型、访问权限、REST API 显示等等。

下面是一个简单的例子,注册一个文章的元数据,用于存储书籍的作者:

function register_book_author_meta() {
    register_meta( 'post', '_book_author', array(
        'type'          => 'string', // 数据类型是字符串
        'description'   => '书籍作者', // 描述信息
        'single'        => true,    // 是否只允许单个值
        'show_in_rest'  => true,    // 关键!暴露给 REST API
        'auth_callback' => function() { // 权限控制
            return current_user_can( 'edit_posts' );
        }
    ) );
}
add_action( 'init', 'register_book_author_meta' );

代码解释:

  1. register_meta( 'post', '_book_author', ... ): 告诉 WordPress,我们要注册一个文章类型的元数据,键名是 _book_author
  2. 'type' => 'string': 声明这个元数据的值是字符串类型。
  3. 'description' => '书籍作者': 给这个元数据添加一个描述,方便开发者理解。
  4. 'single' => true: 表示每篇文章只能有一个作者。如果设置为 false,则可以有多个作者(存储为数组)。
  5. 'show_in_rest' => true重点来了! 这个参数告诉 WordPress,要把这个元数据暴露给 REST API。 默认情况下,这个值是 false
  6. 'auth_callback' => function() { ... }: 定义一个回调函数,用于控制哪些用户可以读取和修改这个元数据。这里我们简单地使用了 current_user_can( 'edit_posts' ),表示只有具有编辑文章权限的用户才能访问。

三、show_in_rest 的各种姿势:让 REST API 为你所用

show_in_rest 参数是控制元数据是否暴露给 REST API 的关键。它有以下几种取值方式:

  • false(默认值): 不暴露给 REST API。
  • true: 简单粗暴地暴露给 REST API,使用默认的 schema。
  • array: 最灵活的方式,可以自定义 REST API 的行为,例如指定 schema、字段名称等等。

让我们分别来看一下这几种方式:

1. show_in_rest 设置为 true:最简单的暴露方式

就像上面的例子一样,直接设置 'show_in_rest' => true,WordPress 会自动将元数据暴露给 REST API。

例如,注册了上面的 _book_author 元数据后,你就可以通过以下 REST API 端点来获取文章的作者信息:

/wp-json/wp/v2/posts/{文章ID}

在返回的 JSON 数据中,你会看到类似这样的内容:

{
    "id": 123,
    "title": {
        "rendered": "我的第一本书"
    },
    "content": {
        "rendered": "<p>这是一本好书!</p>n"
    },
    "meta": {
        "_book_author": "老王"
    }
}

2. show_in_rest 设置为 array:自定义 REST API 行为

如果仅仅暴露元数据还不够,你想要更精细地控制 REST API 的行为,例如自定义字段名称、指定 schema 等等,就可以将 show_in_rest 设置为一个数组。

function register_book_info_meta() {
    register_meta( 'post', '_book_info', array(
        'type'          => 'object', // 数据类型是对象
        'description'   => '书籍信息',
        'single'        => true,
        'show_in_rest'  => array(
            'name'   => 'book_info', // 自定义字段名称
            'schema' => array(      // 自定义 schema
                'type'       => 'object',
                'properties' => array(
                    'title'  => array(
                        'type'        => 'string',
                        'description' => '书籍标题'
                    ),
                    'author' => array(
                        'type'        => 'string',
                        'description' => '书籍作者'
                    ),
                    'price'  => array(
                        'type'        => 'number',
                        'description' => '书籍价格'
                    )
                ),
            ),
        ),
        'auth_callback' => function() {
            return current_user_can( 'edit_posts' );
        }
    ) );
}
add_action( 'init', 'register_book_info_meta' );

代码解释:

  • 'show_in_rest' => array( ... ): 将 show_in_rest 设置为一个数组,用于自定义 REST API 行为。
  • 'name' => 'book_info': 指定 REST API 中显示的字段名称为 book_info,而不是默认的 _book_info
  • 'schema' => array( ... ): 定义元数据的 schema,用于描述数据的结构和类型。这里我们定义了一个包含 titleauthorprice 三个属性的对象。

有了这个配置,REST API 返回的数据会变成这样:

{
    "id": 123,
    "title": {
        "rendered": "我的第一本书"
    },
    "content": {
        "rendered": "<p>这是一本好书!</p>n"
    },
    "book_info": {
        "title": "Python 从入门到放弃",
        "author": "张三",
        "price": 99.9
    }
}

可以看到,字段名称变成了 book_info,并且数据的结构也符合我们定义的 schema。

四、auth_callback:保护你的元数据

auth_callback 参数用于定义一个回调函数,用于控制哪些用户可以读取和修改元数据。这个回调函数应该返回 truefalse,表示用户是否有权限访问元数据。

例如,我们之前的例子中使用了 current_user_can( 'edit_posts' ),表示只有具有编辑文章权限的用户才能访问元数据。

你也可以根据自己的需求,定义更复杂的权限控制逻辑。例如,可以根据用户的角色、用户 ID 等等来判断用户是否有权限访问元数据。

function check_book_author_permission( $allowed, $meta_key, $post_id, $user_id, $cap, $caps ) {
    // 只允许作者本人或者管理员才能编辑书籍作者信息
    $post = get_post( $post_id );
    if ( $post->post_author == $user_id || current_user_can( 'administrator' ) ) {
        return true;
    }
    return false;
}

function register_book_author_meta() {
    register_meta( 'post', '_book_author', array(
        'type'          => 'string',
        'description'   => '书籍作者',
        'single'        => true,
        'show_in_rest'  => true,
        'auth_callback' => 'check_book_author_permission' // 使用自定义权限控制函数
    ) );
}
add_action( 'init', 'register_book_author_meta' );

五、sanitize_callbackvalidate_callback:保证数据的质量

除了权限控制,我们还可以使用 sanitize_callbackvalidate_callback 参数来保证数据的质量。

  • sanitize_callback: 用于对数据进行清理和转换,例如去除空格、转换为小写等等。
  • validate_callback: 用于验证数据的有效性,例如检查是否是有效的邮箱地址、是否在指定的范围内等等。
function sanitize_book_price( $value ) {
    // 将价格转换为浮点数,并保留两位小数
    return floatval( number_format( floatval( $value ), 2, '.', '' ) );
}

function validate_book_price( $value ) {
    // 验证价格是否大于 0
    return is_numeric( $value ) && floatval( $value ) > 0;
}

function register_book_price_meta() {
    register_meta( 'post', '_book_price', array(
        'type'              => 'number',
        'description'       => '书籍价格',
        'single'            => true,
        'show_in_rest'      => true,
        'sanitize_callback' => 'sanitize_book_price', // 使用数据清理函数
        'validate_callback' => 'validate_book_price', // 使用数据验证函数
        'auth_callback'     => function() {
            return current_user_can( 'edit_posts' );
        }
    ) );
}
add_action( 'init', 'register_book_price_meta' );

六、一个更完整的例子:书籍信息管理系统

为了更好地理解 register_meta() 的用法,我们来构建一个简单的书籍信息管理系统。

  1. 注册元数据:
function register_book_meta() {
    register_meta( 'post', '_book_title', array(
        'type'          => 'string',
        'description'   => '书籍标题',
        'single'        => true,
        'show_in_rest'  => true,
        'auth_callback' => function() {
            return current_user_can( 'edit_posts' );
        }
    ) );

    register_meta( 'post', '_book_author', array(
        'type'          => 'string',
        'description'   => '书籍作者',
        'single'        => true,
        'show_in_rest'  => true,
        'auth_callback' => function() {
            return current_user_can( 'edit_posts' );
        }
    ) );

    register_meta( 'post', '_book_price', array(
        'type'              => 'number',
        'description'       => '书籍价格',
        'single'            => true,
        'show_in_rest'      => true,
        'sanitize_callback' => 'sanitize_book_price',
        'validate_callback' => 'validate_book_price',
        'auth_callback'     => function() {
            return current_user_can( 'edit_posts' );
        }
    ) );

    register_meta( 'post', '_book_category', array(
        'type'          => 'string', // 可以是字符串,也可以是数组
        'description'   => '书籍分类',
        'single'        => false, // 允许有多个分类
        'show_in_rest'  => true,
        'auth_callback' => function() {
            return current_user_can( 'edit_posts' );
        }
    ) );

    register_meta( 'post', '_book_description', array(
        'type'          => 'string',
        'description'   => '书籍描述',
        'single'        => true,
        'show_in_rest'  => array(
            'name'   => 'book_description',
            'schema' => array(
                'type' => 'string',
                'format' => 'html' // 允许 HTML
            ),
        ),
        'sanitize_callback' => 'wp_kses_post', // 使用 WordPress 内置的 HTML 清理函数
        'auth_callback'     => function() {
            return current_user_can( 'edit_posts' );
        }
    ) );
}
add_action( 'init', 'register_book_meta' );
  1. 使用 REST API 获取数据:

注册了这些元数据后,你就可以通过 REST API 获取书籍信息了。

/wp-json/wp/v2/posts/{文章ID}

返回的 JSON 数据会包含你注册的所有元数据:

{
    "id": 123,
    "title": {
        "rendered": "《深入理解 PHP 高级特性》"
    },
    "content": {
        "rendered": "<p>一本深入讲解 PHP 高级特性的好书!</p>n"
    },
    "meta": {
        "_book_title": "深入理解 PHP 高级特性",
        "_book_author": "老李",
        "_book_price": 129.9,
        "_book_category": [
            "编程",
            "PHP"
        ]
    },
    "book_description": "<p>本书详细介绍了 PHP 的各种高级特性,包括命名空间、Trait、生成器等等。</p>"
}

七、注意事项:避免踩坑

  • 元数据键名以下划线开头: 以下划线 (_) 开头的元数据键名默认不会暴露给 REST API,除非你显式地设置 show_in_resttrue
  • 权限控制: 一定要设置 auth_callback,防止未授权用户访问敏感数据。
  • 数据类型: 根据实际情况选择合适的数据类型,例如 stringnumberbooleanobjectarray
  • 数据清理和验证: 使用 sanitize_callbackvalidate_callback 保证数据的质量。
  • 缓存: 如果元数据经常被访问,可以考虑使用缓存来提高性能。

八、总结:register_meta(),让你的数据更强大

register_meta() 函数是 WordPress 中管理元数据的利器。通过它可以规范地定义元数据,控制访问权限,并且方便地将元数据暴露给 REST API。掌握了 register_meta() 的用法,你就可以构建更加强大、灵活的 WordPress 应用。

希望今天的讲座对大家有所帮助!下次有机会再和大家分享更多 WordPress 开发技巧。 谢谢大家!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注