WordPress 6.9 và Abilities API: Giải thích chi tiết qua ví dụ thực tế

WordPress 6.9 giới thiệu Abilities API, một lớp API nền tảng cho phép core, plugin và theme đăng ký và cung cấp các “khả năng” (abilities) theo một chuẩn thống nhất, có schema rõ ràng và có thể truy cập từ PHP, REST API và JavaScript. Bài viết này sẽ giải thích chi tiết cách Abilities API hoạt động, dựa trên ví dụ plugin List All URLs, và chỉ ra cách bạn có thể áp dụng nó vào kiến trúc plugin/theme đang dựa trên REST API.

WordPress 6.9 và Abilities API: Giải thích chi tiết qua ví dụ thực tế

Abilities API trong WordPress 6.9 là gì?

Abilities API là một hệ thống giúp bạn mô tả mỗi tính năng của WordPress như một “ability” có:

  • Tên định danh duy nhất (namespace/ability).
  • Mô tả thân thiện với con người (label, description).
  • Schema dữ liệu đầu vào và đầu ra dưới dạng mảng JSON Schema.
  • Hàm thực thi (execute_callback).
  • Hàm kiểm tra quyền (permission_callback).
  • Tùy chọn mở ra REST API và các lớp client khác.

Thay vì mỗi plugin tự tạo function, hook và REST route riêng, Abilities API cung cấp một registry tập trung, nơi các abilities được khai báo và có thể được khám phá, kiểm tra và gọi một cách thống nhất.

Xem bài gốc tại: Introducing the WordPress Abilities API.

Vấn đề trước đây: nhiều API, thiếu chuẩn chung

Trước khi có Abilities API, một plugin hoặc theme có thể expose chức năng theo nhiều con đường:

  • Function PHP toàn cục hoặc class method.
  • Hook action/filter để dev khác gắn vào.
  • REST API route tự định nghĩa bằng register_rest_route.
  • Các block tương tác trực tiếp qua JavaScript.

Điểm yếu là không có một chuẩn bắt buộc để mô tả: chức năng đó nhận input gì, trả output gì, yêu cầu quyền ra sao. Abilities API giải quyết vấn đề này bằng cách đưa mọi thứ về một mô hình thống nhất và có thể đọc được bởi cả con người lẫn máy.

Ví dụ nền: plugin List All URLs trước khi có Abilities

Plugin List All URLs là một ví dụ đơn giản: nó thêm một trang trong Tools, cho phép người quản trị chọn kiểu nội dung (post, page, custom post type) và hiển thị danh sách URL tương ứng. Trước khi dùng Abilities API, plugin này có một hàm core duy nhất xử lý toàn bộ việc lấy dữ liệu:

<?php
/**
 * Generate a list of URLs based on the provided arguments.
 * Optionally make them clickable links.
 *
 * @param array $arguments Arguments to customize the URL generation.
 * @param bool  $makelinks Whether to return clickable links or plain URLs (escaped).
 *
 * @return array List of generated URLs.
 */
function list_all_urls_generate_url_list( array $arguments = array(), bool $makelinks = false ): array {
    $default_args = array(
        'post_type'      => 'post',
        'posts_per_page' => -1,
        'post_status'    => 'publish',
    );

    $args  = wp_parse_args( $arguments, $default_args );
    $posts = get_posts( $args );

    $links = array();

    foreach ( $posts as $post ) {
        $permalink = get_permalink( $post );

        if ( $makelinks ) {
            $links[] = '<a href="' . esc_url( $permalink ) . '">' . esc_html( $permalink ) . '</a>';
        } else {
            $links[] = esc_html( $permalink );
        }
    }

    return $links;
}

Hàm này làm ba việc:

  1. Trộn tham số truyền vào với mặc định (post_type, posts_per_page, post_status).
  2. Dùng get_posts để lấy danh sách bài viết tương ứng.
  3. Format output thành danh sách URL thô hoặc thẻ liên kết HTML, tùy thuộc vào biến makelinks.

Đây chính là “business logic” cốt lõi mà sau đó sẽ được nhiều lớp khác nhau tái sử dụng (admin page, REST, block, v.v.).

Cách triển khai bằng REST API và Block Editor theo cách truyền thống

Để cho phép hệ thống bên ngoài truy cập danh sách URL và hiển thị trong Block Editor, cách làm cũ thường là:

  • Đăng ký một REST route mới.
  • Viết callback cho route này, gọi lại list_all_urls_generate_url_list.
  • Tạo một block, dùng apiFetch để gọi REST endpoint và render danh sách URL trong Editor.
  • Nếu muốn block hiển thị động trên frontend, dùng dynamic block với file render.php.

Một ví dụ REST route tối giản:

<?php
add_action( 'rest_api_init', 'list_all_urls_register_rest_route' );

function list_all_urls_register_rest_route(): void {
    register_rest_route(
        'list-all-urls/v1',
        '/urls',
        array(
            'methods'  => 'GET',
            'callback' => 'list_all_urls_rest_fetch_all_urls',
            'args'     => array(
                'type' => array(
                    'validate_callback' => function( $param ) {
                        return is_string( $param );
                    },
                ),
            ),
        )
    );
}

function list_all_urls_rest_fetch_all_urls( $arguments ) {
    if ( isset( $arguments['type'] ) ) {
        $post_type = sanitize_text_field( wp_unslash( $arguments['type'] ) );
    } else {
        $post_type = 'any';
    }

    $args = array(
        'post_type' => $post_type,
    );

    return list_all_urls_generate_url_list( $args );
}

Ở phía Block Editor, một block đơn giản có thể dùng apiFetch để lấy dữ liệu:

import { useEffect, useState } from '@wordpress/element';
import apiFetch from '@wordpress/api-fetch';
import { __ } from '@wordpress/i18n';
import { useBlockProps } from '@wordpress/block-editor';

export default function Edit() {
    const [ urls, setUrls ] = useState( [] );

    useEffect( () => {
        apiFetch( { path: '/list-all-urls/v1/urls' } ).then( ( response ) => {
            setUrls( response );
        } );
    }, [] );

    if ( ! urls ) {
        return (
            <div { ...useBlockProps() }>
                <p>{ __( 'Loading...', 'list-all-urls' ) }</p>
            </div>
        );
    }

    const urlsList = urls.map( ( url ) => {
        return <li><a href={ url }>{ url }</a></li>;
    } );

    return (
        <div { ...useBlockProps() }>
            <ul>{ urlsList }</ul>
        </div>
    );
}

Để block hiển thị đúng trên frontend, bạn có thể dùng dynamic block với file render.php:

<?php
/**
 * Render file for the List All URLs block.
 */
$block_attributes = get_block_wrapper_attributes();
$urls             = list_all_urls_generate_url_list( array( 'post_type' => 'any' ), true );

$url_list = '';

foreach ( $urls as $url ) {
    $url_list .= '<li>' . wp_kses_post( $url ) . '</li>';
}
?>

<div <?php echo $block_attributes; ?>>
    <ul>
        <?php echo $url_list; ?>
    </ul>
</div>

Cách làm này hoạt động tốt nhưng phải lặp lại nhiều bước cấu hình và code: REST route, block, validation input, xử lý quyền, v.v. Đây chính là nơi Abilities API có thể thay thế hầu hết phần “keo dán” này.

Cài đặt Abilities API để thử nghiệm

Trước khi WordPress 6.9 phát hành ổn định, Abilities API tồn tại như một plugin và gói Composer, cho phép bạn thử nghiệm sớm. Có ba cách phổ biến:

  • Clone trực tiếp repository vào wp-content/plugins và build.
  • Tải bản phát hành mới nhất dạng zip và cài như plugin thông thường.
  • Thêm gói Composer wordpress/abilities-api vào plugin/theme của bạn.

Ví dụ clone mã nguồn plugin:

cd wp-content/plugins
git clone [email protected]:WordPress/abilities-api.git
cd abilities-api
composer install
npm install
npm run build

Hoặc dùng Composer trong plugin List All URLs:

cd wp-content/plugins/list-all-urls
composer require wordpress/abilities-api

Khi WordPress 6.9 được cài đặt, các phần server-side của Abilities API sẽ có sẵn trong core, nhưng việc dùng Composer vẫn hữu ích nếu bạn muốn đảm bảo tương thích với các phiên bản API mới nhất hoặc dùng trên môi trường chưa nâng cấp.

Đăng ký một Ability trong PHP với wp_register_ability

Sau khi Abilities API sẵn sàng, bước đầu tiên là đăng ký một ability mới. Việc đăng ký luôn phải diễn ra trên hook wp_abilities_api_init, đảm bảo registry đã sẵn sàng.

<?php
add_action( 'wp_abilities_api_init', 'list_all_urls_register_abilities' );

/**
 * Register the ability to list all URLs.
 *
 * @return void
 */
function list_all_urls_register_abilities(): void {
    wp_register_ability(
        'list-all-urls/urls',
        array(
            'label'       => __( 'Get All URLs', 'list-all-urls' ),
            'description' => __( 'Retrieves a list of URLs from the WordPress site, optionally as clickable anchor links.', 'list-all-urls' ),
            'category'    => 'site',
            'input_schema'  => array(
                'type'       => 'object',
                'properties' => array(
                    'post_type' => array(
                        'type'        => 'string',
                        'description' => 'The post type to retrieve URLs from (e.g., post, page, custom post type).',
                    ),
                    'posts_per_page' => array(
                        'type'        => 'integer',
                        'description' => 'Number of posts to retrieve. Use -1 to retrieve all posts.',
                    ),
                    'post_status' => array(
                        'type'        => 'string',
                        'description' => 'The status of the posts to retrieve (e.g., publish, draft).',
                    ),
                    'makelinks' => array(
                        'type'        => 'boolean',
                        'description' => 'Whether to return URLs as clickable anchor links.',
                    ),
                ),
            ),
            'output_schema' => array(
                'type'       => 'object',
                'properties' => array(
                    'url' => array(
                        'type'        => 'string',
                        'description' => 'URL or clickable link to the URL.',
                    ),
                ),
            ),
            'execute_callback'    => 'list_all_urls_generate_url_list',
            'permission_callback' => '__return_true',
            'meta' => array(
                'show_in_rest' => true,
            ),
        )
    );
}

Trong ví dụ trên, ability có tên định danh list-all-urls/urls, mô tả rõ ràng chức năng, chỉ rõ input mà nó chấp nhận và cấu trúc output mà nó trả về. Hàm list_all_urls_generate_url_list được dùng lại làm execute_callback, tức là business logic không cần thay đổi.

Giải phẫu chi tiết tham số wp_register_ability

Khi đăng ký một ability, bạn cần chú ý đến các trường sau:

  • label: Tên hiển thị thân thiện, dùng cho giao diện hoặc công cụ khám phá.
  • description: Mô tả ngắn gọn, giúp dev và công cụ hiểu ability làm gì.
  • category: Nhóm phân loại ability. Bạn có thể dùng category sẵn có (ví dụ site) hoặc tự đăng ký category riêng cho plugin.
  • input_schema: Mô tả input theo JSON Schema. Ở ví dụ trên, input là object với các thuộc tính post_type, posts_per_page, post_status, makelinks.
  • output_schema: Mô tả cấu trúc dữ liệu trả về, giúp hệ thống validate và giúp AI/tool hiểu output.
  • execute_callback: Hàm sẽ được gọi khi ability thực thi. Trong ví dụ, đây chính là hàm core đã tồn tại từ trước.
  • permission_callback: Hàm dùng để kiểm tra quyền. Nếu trả về false, ability sẽ không được phép chạy.
  • meta.show_in_rest: Khi đặt true, ability sẽ tự động được expose qua REST API trong namespace wp-abilities.

Việc định nghĩa input_schema và output_schema không chỉ để mô tả, mà còn kích hoạt validation tự động. Nếu giá trị truyền vào không đúng kiểu hoặc thiếu thuộc tính bắt buộc, ability có thể từ chối thực thi và trả về lỗi.

Sử dụng Ability trong PHP: wp_get_ability và execute

Sau khi đăng ký, ability có thể được truy xuất và thực thi trong PHP thông qua các hàm do Abilities API cung cấp.

Ví dụ trong admin page của plugin List All URLs, thay vì gọi trực tiếp list_all_urls_generate_url_list, bạn có thể sử dụng ability:

<?php
$input = array(
    'post_type'      => $post_type,
    'posts_per_page' => -1,
    'post_status'    => 'publish',
    'makelinks'      => $makelinks,
);

$urls_ability = wp_get_ability( 'list-all-urls/urls' );

if ( $urls_ability ) {
    $urls = $urls_ability->execute( $input );
}

Điều này mang lại một số lợi ích:

  • Logic thực thi được chuẩn hóa và có schema rõ ràng.
  • Các lớp khác (REST, JavaScript, AI agents) có thể tái sử dụng cùng một ability mà không cần viết lại logic.
  • Bạn có thể dễ dàng hoán đổi execute_callback mà không phải sửa mọi nơi đang gọi.

Khám phá và kiểm tra Abilities bằng WP-CLI

Abilities API hỗ trợ tốt cho việc khám phá trong quá trình phát triển. Bạn có thể dùng WP-CLI shell để kiểm tra nhanh các ability đã đăng ký.

Ví dụ liệt kê tất cả abilities hiện có:

$ wp shell
wp> $abilities = wp_get_abilities();
wp> var_dump( array_keys( $abilities ) );

Kiểm tra một ability cụ thể có tồn tại hay không:

$ wp shell
wp> $found = wp_has_ability( 'list-all-urls/urls' );
wp> var_dump( $found ); // bool(true) nếu ability đã đăng ký

Lấy đầy đủ thông tin của một ability:

$ wp shell
wp> $ability = wp_get_ability( 'list-all-urls/urls' );
wp> var_dump( $ability );

Đối tượng ability trả về chứa đủ thông tin về tên, mô tả, category, input_schema, output_schema, execute_callback, permission_callback và meta. Điều này rất hữu ích khi debug hoặc viết tài liệu cho plugin.

Tích hợp REST API tự động với Abilities

Khi meta.show_in_rest được đặt là true trong lúc đăng ký ability, WordPress sẽ tự động cung cấp REST endpoint tương ứng trong namespace wp-abilities. Một số hành động REST mặc định gồm:

  • Liệt kê tất cả abilities: gửi GET đến /wp-json/wp-abilities/v1/abilities.
  • Lấy thông tin một ability cụ thể: GET đến /wp-json/wp-abilities/v1/{namespace/ability}.
  • Thực thi một ability: gửi GET hoặc POST (tùy thuộc cấu hình readonly và schema) đến /wp-json/wp-abilities/v1/{namespace/ability}/run kèm input.

Trong ngữ cảnh plugin List All URLs, điều này có nghĩa là chỉ cần đặt meta.show_in_rest là true, bạn đã có thể gọi ability list-all-urls/urls từ bất kỳ client nào hỗ trợ REST, mà không cần tự định nghĩa register_rest_route như trước.

Sử dụng Ability trong JavaScript với client Abilities API

Abilities API cũng cung cấp một client JavaScript để thực thi abilities trực tiếp từ browser hoặc block. Thay vì gọi apiFetch một REST route tự định nghĩa, bạn có thể sử dụng hàm executeAbility từ gói @wordpress/abilities (dự kiến được phân phối qua Gutenberg hoặc package chính thức).

Một ví dụ dùng executeAbility trong block Edit component:

import { useEffect, useState } from '@wordpress/element';
import { useBlockProps } from '@wordpress/block-editor';
import { executeAbility } from '@wordpress/abilities';
import { __ } from '@wordpress/i18n';

export default function Edit( { attributes } ) {
    const [ urls, setUrls ] = useState( null );

    useEffect( () => {
        executeAbility( 'list-all-urls/urls', {
            post_type: 'any',
            makelinks: attributes.makeLinks,
        } ).then( ( result ) => {
            setUrls( result );
        } );
    }, [ attributes.makeLinks ] );

    if ( ! urls ) {
        return (
            <div { ...useBlockProps() }>
                <p>{ __( 'Loading URLs...', 'list-all-urls' ) }</p>
            </div>
        );
    }

    const urlsList = urls.map( ( url ) => {
        return <li><a href={ url }>{ url }</a></li>;
    } );

    return (
        <div { ...useBlockProps() }>
            <ul>{ urlsList }</ul>
        </div>
    );
}

Ưu điểm của cách tiếp cận này:

  • Block không cần biết REST endpoint cụ thể, chỉ cần biết tên ability và format dữ liệu.
  • Có thể tái sử dụng cùng một ability cho nhiều block hoặc UI khác nhau.
  • Cấu trúc input/output đã được chuẩn hóa nên rất thuận tiện cho tự động hóa và AI agents.

Áp dụng Abilities API vào kiến trúc plugin và theme hiện có

Nếu bạn đã có một plugin hoặc theme với kiến trúc xoay quanh REST API, bạn không cần phải viết lại toàn bộ. Cách áp dụng thực tế thường là:

  1. Tách business logic vào các service function rõ ràng, ví dụ get_trending_posts, get_recommendations_for_user, calculate_optimal_release_time.
  2. Đăng ký abilities tương ứng cho các service quan trọng, dùng service function làm execute_callback.
  3. Giữ nguyên REST route cũ, nhưng thay vì gọi trực tiếp logic, hãy gọi ability hoặc ít nhất là dùng chung service function với ability.
  4. Dần dần di chuyển block và UI mới sang dùng executeAbility hoặc REST endpoint của Abilities API.

Cách này giúp bạn vừa giữ được tính tương thích ngược, vừa từng bước chuẩn hóa kiến trúc theo Abilities API và sẵn sàng tích hợp AI trong tương lai.

Lợi ích và tác động thực tế của Abilities API

Abilities API mang lại nhiều lợi ích rõ ràng cho nhà phát triển:

  • Chuẩn hóa cách mô tả và expose tính năng giữa core, plugin, theme.
  • Giảm trùng lặp code khi phải viết riêng REST route, JS client và logic xử lý.
  • Tăng khả năng khám phá chức năng qua registry và WP-CLI.
  • Tự động có REST endpoint khi cần, với permission và validation nhất quán.
  • Mở đường cho việc tích hợp AI agents và công cụ tự động hóa dựa trên schema ability.

Đổi lại, lập trình viên cần làm quen với một khái niệm mới và cấu hình chi tiết hơn (schema input/output, permission, category). Tuy nhiên với các plugin có quy mô vừa và lớn, lợi ích về lâu dài là rất đáng kể.

Kết luận

WordPress 6.9 với Abilities API không chỉ thêm một API mới, mà còn tạo ra một lớp nền tảng giúp toàn bộ hệ sinh thái WordPress chuyển từ tập hợp các function rời rạc sang một hệ thống khả năng có cấu trúc, có thể được khám phá và tự động hóa. Thông qua ví dụ plugin List All URLs, có thể thấy rõ cách một chức năng quen thuộc được “nâng cấp” thành một ability chuẩn hóa, có thể gọi từ PHP, REST API, JavaScript và thậm chí bởi các AI agents.

Việc sớm học và áp dụng Abilities API sẽ giúp plugin và theme của bạn sẵn sàng cho thế hệ tiếp theo của WordPress và các ứng dụng tích hợp xung quanh nó.

Bình luận


  • Không có bình luận.

Init Toolbox

Nhấn Ctrl + \ trên máy tính, hoặc vuốt sang trái ở bất kỳ đâu trên mobile.

Đăng nhập





Đang tải...