使用WordPress REST API

WordPress REST API 提供了一个统一的接口,用于与 WordPress 站点中的数据进行交互。

在本课中,你将学习如何使用 WP REST API 从你的 WordPress 站点获取数据。

你将发现三种进行 REST API 请求的内部选项,然后使用它们执行 GET 请求来获取一些公开的自定义文章类型数据。

Bookstore 插件

如果你完成了“WordPress 插件入门”模块,你应该已经构建了一个注册名为 book 的自定义文章类型的插件。

如果你跳过了那个模块,你可以通过点击 Bookstore 插件链接,从 GitHub 仓库下载主插件代码。

安装并激活插件后,在你的代码编辑器中打开主插件文件。

你会注意到传递给 register_post_type 函数的参数之一是 show_in_rest

register_post_type( 'book',
    array(
        'labels'      => array(
            'name'          => __( 'Books', 'textdomain' ),
            'singular_name' => __( 'Book', 'textdomain' ),
        ),
        'public'      => true,
        'has_archive' => true,
        'show_in_rest' => true,
    )
);

此参数设置为 true,这意味着该自定义文章类型在 REST API 中可用。

这意味着如果你浏览到 wp-json/wp/v2/book 路由,你将在响应中看到自定义文章类型的数据。

如果你查看 register_post_type 函数参考,你会发现一些额外的参数可用于控制 REST API 响应。

例如,你可以更改 rest_base 参数来改变自定义文章类型数据可用的路由。

考虑到你期望能够从 book 路由获取多本书,将 rest_base 改为 books 是个好主意。

register_post_type( 'book',
    array(
        'labels'      => array(
            'name'          => __( 'Books', 'textdomain' ),
            'singular_name' => __( 'Book', 'textdomain' ),
        ),
        'public'      => true,
        'has_archive' => true,
        'show_in_rest' => true,
        'rest_base'   => 'books',
    )
);

这样做将允许你向 wp-json/wp/v2/books 路由发出请求,通过 REST API 访问书籍。

发起 REST API 请求

假设你想在 WordPress 仪表盘中添加一个页面,该页面获取书籍并以逗号分隔的列表形式显示书名和永久链接。

首先,你可以使用 admin_menu 钩子和 add_submenu_page 函数,在“书籍”菜单下添加一个管理子菜单页面。

add_action( 'admin_menu', 'bookstore_add_admin_menu' );
function bookstore_add_admin_menu() {
    add_submenu_page(
        'edit.php?post_type=book',
        __( 'Book List', 'textdomain' ),
        __( 'Book List', 'textdomain' ),
        'manage_options',
        'book-list',
        'bookstore_render_booklist'
    );
}

然后,你需要创建 bookstore_render_booklist 回调函数,该函数将输出管理页面的 HTML。

function bookstore_render_booklist() {
    ?>
    <div class="wrap">
        <h1>Book List</h1>
        <button id="load-books" class="button button-primary">Load Books</button>
        <textarea id="book-list" rows="10" cols="50"></textarea>
    </div>
    <?php
}

如果你浏览到仪表盘并点击“书籍”菜单,你会看到一个名为“Book List”的新子菜单页面。

点击该链接将带你进入一个包含“Load Books”按钮和一个文本区域的页面。

现在,你可以向 bookstore_render_booklist 函数添加功能,通过 PHP 获取书籍列表,并使按钮触发页面刷新。

然而,为了获得更流畅的用户体验,你希望使用 JavaScript 和 REST API 异步获取书籍数据并填充书籍列表,而无需等待整个页面刷新。

加载管理 JavaScript

在“WordPress 插件入门”模块中,你学习了如何在插件中加载 JavaScript 文件。

由于此功能被添加到管理仪表盘中,你需要设置一个单独的 wp_enqueue_script 函数调用,并将其挂载到 admin_enqueue_scripts 钩子上,这样 JavaScript 文件就只在管理仪表盘中加载。

首先,在插件目录中创建一个新的 JavaScript 文件,命名为 admin_bookstore.js

然后,将以下代码添加到主插件文件中,以在仪表盘中加载该 JavaScript 文件:

add_action( 'admin_enqueue_scripts', 'bookstore_enqueue_admin_scripts' );
function bookstore_enqueue_admin_scripts() {
    wp_enqueue_script(
        'bookstore-admin',
        plugin_dir_url( __FILE__ ) . 'admin_bookstore.js',
        array(),
        '1.0.0',
        true
    );
}

请注意,这段代码不仅加载了 JavaScript 文件,还指定了一个空的依赖项数组、一个版本号,并通过将最后一个参数设置为 true,指定它应在 HTML 页面的页脚中加载。

你可以在 wp_enqueue_script 函数参考的“参数”部分阅读有关这些参数的更多信息。

你可以通过在 admin_bookstore.js 文件中添加一个简单的 alert,然后刷新管理页面来测试它是否正确加载。

alert( 'Hello, World!' );

一旦你确认它正常工作,就可以从文件中删除该行。

选项 1:使用 Backbone.js 客户端

自从 REST API 被添加到 WordPress 以来,它就包含了一个 Backbone.js REST API JavaScript 客户端,用于直接向 WP REST API 发出请求。

它通过为所有通过 API 暴露的端点提供模型和集合,提供了一个使用 WP REST API 的接口。

为了确保你的 JavaScript 代码能够使用 REST API 客户端,你需要将其添加为已加载 JavaScript 的依赖项。

wp_enqueue_script 的第三个参数是一个包含所有依赖项的数组,你可以将 wp-api 作为依赖项添加到你的 wp_enqueue_script 函数调用中。

add_action( 'admin_enqueue_scripts', 'bookstore_enqueue_admin_scripts' );
function bookstore_enqueue_admin_scripts() {
    wp_enqueue_script(
        'bookstore-admin',
        plugin_dir_url( __FILE__ ) . 'admin_bookstore.js',
        array( 'wp-api' ),
        '1.0.0',
        true
    );
}

这将确保你的插件 JavaScript 代码仅在 REST API JavaScript 客户端加载完成后才加载,从而你可以在插件中使用它。

你需要先为新按钮注册一个点击事件处理器。

const loadBooksByRestButton = document.getElementById( 'bookstore-load-books' );
if ( loadBooksByRestButton ) {
    loadBooksByRestButton.addEventListener( 'click', function () {
        //do somthing
    } );
}

然后,在事件处理函数中,你可以通过全局对象 wp 访问 WP API 客户端,来创建一个新的书籍集合。

    const allBooks = new wp.api.collections.Books();

此时,allBooks 只是一个空集合,因此你需要通过调用集合的 fetch 方法来获取文章。

   allBooks.fetch();

fetch 方法返回一个 Promise,因此你可以链式调用 done 方法来处理响应,并实现一个回调函数来接收 API 请求的响应。

你可以在该回调函数中指定一个 books 参数,用于接收 API 请求的响应。

    allBooks.fetch().done(
        function ( books ) {
            // do something with books
        }
    );

现在,你可以使用类似 forEach 的方法遍历 books 对象,并逐一访问每本书。

    allBooks.fetch().done(
        function ( books ) {
            books.forEach( function ( book ) {
              // do something with book
            } );
        }
    );

最后,你可以将书名和永久链接添加到文本区域中。

首先,你需要在 forEach 循环之前创建一个文本区域的实例,然后在 forEach 循环内部将值追加到文本区域的 value 属性中。

        allBooks.fetch().done(
            function ( books ) {
                const textarea = document.getElementById( 'bookstore-booklist' );
                books.forEach( function ( book ) {
                    textarea.value += book.title.rendered + ',' + book.link + ',\n'
                });
            }
        );

你的最终代码将类似这样。

const loadBooksByRestButton = document.getElementById( 'bookstore-load-books' );
if ( loadBooksByRestButton ) {
    loadBooksByRestButton.addEventListener( 'click', function () {
        const allBooks = new wp.api.collections.Books();
        allBooks.fetch().done(
            function ( books ) {
                const textarea = document.getElementById( 'bookstore-booklist' );
                books.forEach( function ( book ) {
                    textarea.value += book.title.rendered + ',' + book.link + ',\n'
                });
            }
        );
    });
}

切换回自定义的“书籍列表”管理页面,点击“加载书籍”按钮,即可看到书籍列表出现在文本区域中。

选项 2:使用 @wordpress/fetch-api

自 WordPress 5.0 引入区块编辑器以来,@wordpress/fetch-api 包也已可用,用于发起 REST API 请求。

该包是浏览器 fetch API 的封装,提供了一种更现代、更灵活的方式来向 REST API 发起请求。

要使用 fetch API,你可以更新插件的 JavaScript 依赖项,加入 wp-api-fetch

    wp_enqueue_script(
        'bookstyle-script',
        plugins_url() . '/bookstore/admin_bookstore.js',
        array( 'wp-api', 'wp-api-fetch' ),
        '1.0.0',
        true
    );

你可以移除 wp-api 依赖,或者将 wp-api-fetch 作为额外依赖添加。

接下来,在表单的“操作”区域添加一个按钮,用于触发 fetch 请求。

<button id="bookstore-fetch-books">Fetch Books</button>

和之前一样,在你的 bookstore.js 文件中,为新按钮的点击事件设置一个事件监听器,但这次使用 apiFetch 方法来向 REST API 发起请求。

const fetchBooksByRestButton = document.getElementById( 'bookstore-fetch-books' );
if ( fetchBooksByRestButton ) {
    fetchBooksByRestButton.addEventListener( 'click', function () {
        wp.apiFetch( { path: '/wp/v2/books' } ).then( ( books ) => {
            const textarea = document.getElementById( 'bookstore-booklist' );
            books.map( ( book ) => {
                textarea.value += book.title.rendered + ',' + book.link + ',\n'
            });
        } );
    });
}

注意,你将指向书籍端点的路径作为一个对象传递给 wp.apiFetch 函数。这比使用 Backbone.js 客户端更灵活,因为后者需要使用特定的集合来访问书籍。

你可以链式调用 then 方法来处理响应。这与 Backbone 示例中使用 done 方法类似,因为它返回一个 Promise,等待 REST API 请求完成,然后将结果返回给回调函数。

在回调函数内部,你可以访问 books 对象,并使用 map 方法遍历它,将书名和永久链接追加到文本区域。

你还会注意到,这段代码使用了箭头函数语法作为接收响应的回调,这是一种更现代的 JavaScript 函数编写方式。

刷新管理页面,点击“获取书籍”按钮,即可看到书籍列表出现在文本区域中。

选项 3:使用 @wordpress/core-data

如果你正在开发区块,还有一个 core-data 包可用,用于从 REST API 访问数据。

core-data 旨在简化对核心 WordPress 实体的访问和操作。它注册了自己的存储,并提供了许多选择器,可自动从 WordPress REST API 解析数据,同时提供派发动作创建器来操作数据。

core-data 使用了大量 React 功能,因此最适合在区块上下文中使用。

让我们看看如何在区块中使用 core-data 模块从 REST API 获取书籍。

首先,使用你在“区块开发入门”模块中学到的 create-block 工具,创建一个名为 bookstore-block 的新区块。

cd path/to/local/site/wp-content/plugins
npx @wordpress/create-block bookstore-block

这将搭建新区块的脚手架,并生成一些代码供你编辑。

在区块的 edit.js 文件中,从 @wordpress/data 包导入 useSelect 钩子,以及从 @wordpress/core-data 包导入 store

import { useSelect } from '@wordpress/data';
import { store as bookStore } from '@wordpress/core-data';

然后,你可以使用这些来从 REST API 获取书籍。

    const books = useSelect(
        select =>
            select( bookStore ).getEntityRecords( 'postType', 'book' ),
        []
    );

useSelect 是一个钩子,允许你从已注册的选择器中检索数据。

useSelect 接受一个回调函数作为第一个参数,在该函数中你可以使用 bookStore 存储的 getEntityRecords 选择器从 REST API 中检索书籍。这些书籍随后会被存储在 books 变量中。

最后,你可以更新代码,使其在没有返回书籍时返回一个空组件,或者遍历书籍对象并输出书名和链接。

    if ( ! books ) {
        return (
            <div { ...useBlockProps() }></div>
        )
    }

    return (
        <div { ...useBlockProps() }>
            { books.map( ( book ) => (
                <p>
                    <a href={ book.link }>{book.title.rendered}</a>
                </p>
            ) ) }
        </div>
    );

现在,运行区块构建步骤,激活插件,并将书店区块添加到文章或页面中。

你将看到区块输出从 REST API 获取的书名和链接。

不同选项之间的区别

Backbone 客户端是三个选项中最古老的,但它也是与 REST API 集成最紧密的。如果你需要使用 WP REST API 构建管理仪表板页面,它是一个不错的选择,并且远优于使用传统的 admin-ajax.php 端点。

apiFetch 是一个出色的全能解决方案,因为它既可用于管理仪表板页面,也可用于编辑器中的区块。它也是一种更现代的向 REST API 发起请求的方式,并且比 Backbone 客户端更灵活。

core-data 最适合在区块上下文中使用,因为它使用了在区块编辑器上下文之外不可用的 React 功能。

进一步阅读

有关在 WordPress 中使用 REST API 的各种方式的更多信息,请查看 REST API 手册中的 Backbone JavaScript 客户端部分、区块编辑器手册中的 api-fetch 包,以及区块编辑器手册中的 core-data 包。