tenjuu99/wp-resta

WordPress REST Api development framework using DI

Maintainers

Package info

github.com/amashigeseiji/wp-resta

Type:wordpress-plugin

pkg:composer/tenjuu99/wp-resta

Statistics

Installs: 329

Dependents: 0

Suggesters: 0

Stars: 5

Open Issues: 3


README

Continuous Integration

Wp\Resta

WordPress 上で REST API 開発をするためのプラグインです。

アイデアやインターフェースは BEAR.Sunday から影響を受けています。

How to install

前提: WordPress 管理画面からパーマリンク設定を「投稿名」などにしておいてください。

自作テーマで利用する

$ cd /path/to/theme
$ composer require tenjuu99/wp-resta

functions.php で初期化

以下は、サンプルのディレクトリにあるAPIを読みこむ設定です。

<?php
require_once __DIR__ . '/vendor/autoload.php';

(new Wp\Resta\Resta)->init([
    'routeDirectory' => [
        [__DIR__ . '/vendor/wp/resta/src/REST/Example/Routes', 'Wp\\Resta\\REST\\Example\\Routes\\', 'example']
    ],
    'schemaDirectory' => [
        [__DIR__ . '/vendor/wp/resta/src/REST/Example/Schemas', 'Wp\\Resta\\REST\\Example\\Schemas\\'],
    ],
    'hooks' => [
        \Wp\Resta\Hooks\SwaggerHook::class,  // Swagger UI を有効化
    ],
    'adapters' => [
        \Wp\Resta\Kernel\WpKernelAdapter::class,  // WordPress フック統合(必須)
    ],
]);

プラグインで利用する場合

WordPress のプラグインとしても利用できます。

次の例は composer/installers を利用して WordPress プラグインを wp-content/plugins/ 以下に配置しています。

$ composer config "extra.installer-paths.wp-content/plugins/{\$name}/" "['type:wordpress-plugin']"
$ composer require composer/installers tenjuu99/wp-resta

無事 wp-content/plugins 以下に展開できたら、管理画面からプラグインを有効化してください。

Example

インストールしたら、管理画面に REST API doc というメニューが追加されます。

このページでは、 Swagger UI を使ってAPI定義をドキュメント化しています。

このサンプル実装は src/REST/Example/Routes/ 以下にあります。

How to develop

自分のルーティング定義を追加するためには、 functions.php での初期化時のコードにルーティング用ディレクトリの設定を記述してください。

routeDirectory に渡す配列は、 ['ディレクトリ名', 'php namespace', 'api namespace'] となっています。 schemaDirectory['ディレクトリ名', 'php namespace'] です。

(new Wp\Resta\Resta)->init([
    'routeDirectory' => [
        ['wp-content/themes/mytheme/vendor/wp/resta/src/REST/Example/Routes', 'Wp\\Resta\\REST\\Example\\Routes\\', 'example'],
+       ['src/Routes', 'MyREST\\Routes\\', 'myroute']
    ],
    'schemaDirectory' => [
        ['wp-content/themes/mytheme/vendor/wp/resta/src/REST/Example/Schemas', 'Wp\\Resta\\REST\\Example\\Schemas\\'],
+       ['src/Schemas', 'MyREST\\Schemas\\']
    ],
]);

必要に応じて composer.json にも autoload 設定を追加してください。

  "autoload": {
+     "psr-4": {
+         "MyREST\\": "src/"
+     }
  }

src/Routes/HelloWorld.php を作ります。

<?php
namespace MyREST\Routes;

use Wp\Resta\REST\AbstractRoute;

class HelloWorld extends AbstractRoute
{
    public $body = 'Hello, world!';
}

次のURLが生成されます。

$ curl http://example.com/wp-json/myroute/helloworld

"Hello, world!" と値が返ってきていることを確かめてください。

URL定義とURL変数

myroutefunctions.php で定義した namespace ですが、 helloworld にはクラス名がそのまま利用されています。

URL 定義は ROUTE 定数を定義することで変更できます。ついでにURL変数も定義してみます。

<?php
namespace MyREST\Routes;

use Wp\Resta\REST\AbstractRoute;

class HelloWorld extends AbstractRoute
{
    protected const ROUTE = 'hello/[name]';
    protected const URL_PARAMS = [
        'name' => 'string',
    ];

    public function callback(string $name) : string
    {
        return "Hello, ${name}!";
    }
}

次のURLが生成されるとおもいます。

$ curl http://example.com/wp-json/myroute/hello/amashige

"Hello, amashige!" と返ってくるとおもいます。

ROUTE定数のなかに [var][] で囲えばパスパラメータとして扱うことができます。

変数は次のようなパターンを許容します。

    protected const URL_PARAMS = [
        'id' => 'integer',
        'id_not_required' => '!integer',
        'name' => 'string',
        'name_not_required' => '!string',
        'ok_or_ng' => '(ok|ng)',
        'first_name' => [
            'type' => 'string',
            'required' => false,
            'regex' => '[a-z]+'
        ],
    ];

これらは、ROUTE定数に user/[id] のような形でURL埋め込み変数として定義されていればパスパラメータとして機能します。

パスパラメータとして利用されていないが URL_PARAMS に定義されている変数はクエリパラメータとして利用されます。

次のような例は、 http://example.com/wp-json/myroute/user/2?name=tenjuu99 として展開されます。

    protected const ROUTE = 'user/[id]';
    protected const URL_PARAMS = [
        'id' => 'integer',
        'name' => '?string',
    ];

これらの変数は callback メソッドで受け取ることができます。

コールバック

AbstractRoute を継承したクラスが callback という名のメソッドを持っている場合、このメソッドを呼びだしてレスポンスの body にします。body として返してよいのは、配列、文字列、オブジェクトなど、WordPress REST API がレスポンスとして扱える任意のデータです。また、Wp\Resta\REST\Http\RestaResponseInterface を返した場合にはそのまま利用されます。

callback メソッドの引数は、URL変数を受けとることができます。 URL変数id を定義していれば callback(int $id) と定義して問題ありません。

また、簡易な DI があるため、解決可能なクラスを引数に定義すると受け取ることができます。ランタイムに値が決まるもの(例えば WP_REST_Request )などはコンストラクタインジェクションでは値が決まっていませんが、コールバックが呼び出される時点では確定しているので、利用できます。

DI

簡易なDIを用意しています。

基本的には autorwiring で、ほとんどの場合に設定なしで利用できます。 また、基本的にはコンストラクタインジェクションのみ対応しています(AbstractRoute::callback は例外)。

// src/Lib/Foo.php
namespace MyREST\Lib;

class Foo
{
    private Bar $bar;

    public function __construct(Bar $bar)
    {
        $this->bar = $bar;
    }

    public function getBarString(): string
    {
        return $this->bar->get();
    }
}

// src/Lib/Bar.php
namespace MyREST\Lib;

class Bar
{
    public function get(): string
    {
        return 'bar';
    }
}

// src/Routes/Sample.php

namespace MyREST\Routes;

use MyREST\Lib\Foo;

class Sample extends AbstractRoute
{
    private Foo $foo;
    public function __construct(Foo $foo)
    {
        $this->foo = $foo;
    }

    public function callback()
    {
        return $this->foo->getBarString();
    }
}

Bar::get の返り値が解決されます。

上記は DI の autowiring で解決可能ですが、interface を注入する場合は自動で解決できません。この場合は設定が必要になります。 設定は、初期化コードに dependencies を渡すことができます。

(new Wp\Resta\Resta)->init([
    'routeDirectory' => [
    ...
    ],
    'schemaDirectory' => [
    ...
    ],
+   'dependencies' => [
+       PSR\Log\LoggerInterface::class => MonoLog\Logger::class,
+   ],
]);

また、autowiring 機構はクラスやインターフェースの依存しか解決しないため、コンストラクタがクラスやインターフェース以外の値を受けている場合は解決できません。 この場合は関数を使ってください。

(new Wp\Resta\Resta)->init([
    'routeDirectory' => [
    ...
    ],
    'schemaDirectory' => [
    ...
    ],
    'dependencies' => [
        PSR\Log\LoggerInterface::class => MonoLog\Logger::class,
+       WP_Query::class => function () {
+           return new WP_Query(['post_type' => 'post']);
+       }
    ],
]);

Testing Your Routes

wp-resta を使って作成した Route クラスは、WordPress 環境なしでテストできます。

基本的なテスト

<?php
use PHPUnit\Framework\TestCase;
use Wp\Resta\REST\Http\TestRestaRequest;
use MyREST\Routes\HelloWorld;

class HelloWorldTest extends TestCase
{
    public function testHelloWorld()
    {
        $route = new HelloWorld();

        // TestRestaRequest でシンプルにテスト
        $request = new TestRestaRequest('/helloworld', $route);
        $response = $route->invoke($request);

        $this->assertEquals(200, $response->getStatusCode());
        $this->assertEquals('Hello, world!', $response->getData());
    }
}

URL パラメータを使ったテスト

URL パラメータが定義されている Route では、TestRestaRequest が自動的にパラメータをパースします:

<?php
use PHPUnit\Framework\TestCase;
use Wp\Resta\REST\Http\TestRestaRequest;
use MyREST\Routes\HelloWorld;

class HelloWorldWithParamTest extends TestCase
{
    public function testHelloWithName()
    {
        // ROUTE = 'hello/[name]' の Route
        $route = new HelloWorld();
        $route->setNamespace('myroute');  // namespace を設定

        // URL パラメータは自動的にパースされる
        $request = new TestRestaRequest('/myroute/hello/amashige', $route);
        $response = $route->invoke($request);

        $this->assertEquals(200, $response->getStatusCode());
        $this->assertEquals('Hello, amashige!', $response->getData());
    }
}

配列データのテスト

レスポンスが配列の場合も、getData() で直接アクセスできます:

<?php
public function testJsonResponse()
{
    $route = new MyApiRoute();
    $request = new TestRestaRequest('/api/user/123', $route);
    $response = $route->invoke($request);

    $data = $response->getData();
    $this->assertIsArray($data);
    $this->assertEquals(123, $data['user']['id']);
    $this->assertEquals('John', $data['user']['name']);
}

必要なパッケージ

composer require --dev phpunit/phpunit

Docker で動作確認する

Docker を使って簡単に動作確認環境をセットアップできます。

# セットアップ(WordPress インストール + プラグイン有効化)
./docker/setup.sh

# API の動作確認
./docker/test.sh

セットアップ完了後、以下の URL にアクセスできます:

環境を停止するには:

docker compose down

テスト

Unit/Integration テスト

composer test

E2E テスト

Docker 環境で実際の WordPress REST API に対してテストを実行:

# Docker 環境を起動(初回のみ)
./docker/setup.sh

# E2E テスト実行
composer test:e2e
# または
./docker/e2e-test.sh

Schema

wp-resta は OpenAPI 3.0 によるスキーマ定義をサポートしています。ルートクラスで定義したスキーマは自動的に OpenAPI 仕様として出力され、Swagger UI でドキュメント化されます。

スキーマ定義の詳細: wp-resta では複数の方法でスキーマを定義できます。詳しくは docs/SCHEMA.md を参照してください。

  • ObjectType クラスを使った型安全なスキーマ定義(推奨)
  • PHPDoc の @return アノテーションによる自動推論
  • SCHEMA 定数による完全な制御(複雑なスキーマ向け)

Envelope Pattern

wp-resta では、REST API レスポンスを統一的な構造でラップするEnvelope Patternをサポートしています。

Envelope Patternは、すべての API レスポンスを以下の構造で統一します:

{
  "data": {
    // 実際のレスポンスデータ
  },
  "meta": {
    // メタデータ(オプション)
  }
}

エンベロープパターンの使い方

エンベロープパターンを使うには、#[Envelope] 属性をルートクラスに追加します:

<?php
namespace MyREST\Routes;

use Wp\Resta\REST\AbstractRoute;
use Wp\Resta\REST\Attributes\Envelope;

#[Envelope]
class Posts extends AbstractRoute
{
    protected const ROUTE = 'posts';

    // スキーマはデータ部分のみ定義
    public const SCHEMA = [
        'type' => 'array',
        'items' => [
            '$ref' => '#/components/schemas/Post'
        ],
    ];

    // 普通に配列を返すだけ(自動的にエンベロープでラップされる)
    public function callback(): array
    {
        $posts = get_posts(['post_type' => 'post']);
        return $posts;
    }
}

EnvelopeHook は自動的に有効です

EnvelopeHook はフレームワークに組み込まれており、設定不要で常に有効です。#[Envelope] 属性のないルートには適用されません(素通り)。

レスポンス:

{
  "data": [
    { "ID": 1, "post_title": "Hello World" },
    { "ID": 2, "post_title": "Sample Post" }
  ],
  "meta": {
    "processed_at": "2026-02-17 12:00:00",
    "plugin_version": "0.8.4",
    "request_route": "/myroute/posts"
  }
}

#[Envelope] 属性を追加すると、callback() メソッドで返した値が自動的に data プロパティにラップされます。OpenAPI スキーマもエンベロープ構造として自動生成されます。

メタデータのカスタマイズ

meta プロパティは RouteInvocationEvent のリスナーで追加できます:

<?php
namespace MyREST\Listeners;

use Wp\Resta\REST\Http\EnvelopeResponse;
use Wp\Resta\REST\RouteInvocationEvent;

class CustomMetaListener
{
    // RouteInvocationEvent は NamedEvent のサブクラスなので #[Listen] 不要
    public function addCustomMeta(RouteInvocationEvent $event): void
    {
        if (!$event->response instanceof EnvelopeResponse) {
            return;
        }

        $event->response = $event->response
            ->addMeta('processed_at', current_time('mysql'))
            ->addMeta('plugin_version', '0.8.4');
    }
}

設定ファイルに追加:

(new Wp\Resta\Resta)->init([
    'routeDirectory' => [ /* ... */ ],
    'listeners' => [
        \MyREST\Listeners\CustomMetaListener::class,
    ],
]);

EventDispatcher と listeners config の詳細は docs/extending.md を参照してください。

OpenAPI スキーマの確認

生成された OpenAPI スキーマは以下の URL で確認できます:

http://localhost:8080/rest-api/schema

Swagger UI で視覚的に確認できます:

http://localhost:8080/wp-admin/admin.php?page=wp-resta

エンベロープパターンを使用したルートは、OpenAPI スキーマでも自動的にエンベロープ構造(type: object with data and meta properties)として定義されます。