o/wp-rest-router

此包的最新版本(dev-master)没有可用的许可信息。

dev-master 2019-12-11 21:25 UTC

This package is not auto-updated.

Last update: 2024-09-27 18:37:46 UTC


README

WP Rest Router是WordPress中注册自定义REST路由的抽象。它的目标是通过提供类似LaravelExpress等框架的开发者体验,简化并增强原生WordPress API。

要求

  • PHP >= 7.1
  • WordPress >= 4.7

创建路由的方式与大多数流行框架中创建路由的方式相同。

use O\WordPress\Rest\Router;

// create a new router, you'll need to provide it a namespace to use when registering routes with WordPress
// @see https://developer.wordpress.org/reference/functions/register_rest_route/#parameters
$router = new Router('my-namespace');

// unlike regular WordPress endpoint callbacks, you can always expect
// to receive a request AND response object in your route callback
$router->get('my-route', function (WP_REST_Request $req, WP_REST_Response $res) {
  // endpoint logic here
  return $res;
});

// POST
$router->post('my-route', ...);

// PUT
$router->put('my-route', ...);

// PATCH
$router->patch('my-route', ...);

// DELETE
$router->delete('my-route', ...);

// registers routes with WordPress
// listen attempts to register routes at the right time
// so you can skip hooking into the rest_api_init action if you’d like
// listen will throw an exception if its detected that its being called too late in the request
$router->listen();

可以使用route方法将路由范围或分组到特定的路径。

use O\WordPress\Rest\Router;

$router = new Router('my-namespace');

// route will accept a callback as its second argument
$router->route('foo', function ($scope) {
  // wp-json/my-namespace/foo/bar
  $scope->get('bar', ...);
  // wp-json/my-namespace/foo/baz
  $scope->get('baz', ...);
});

// you may also choose to omit providing a callback and simply use the returned scoped router
$fooScope = $router->route('foo');
$fooScope->get('bar', ...);

// route may also be used to easily define multiple methods for a single route
$router->route('foo', function ($scope) {
  // GET wp-json/my-namespace/foo
  $scope->get('', ...);
  // POST wp-json/my-namespace/foo
  $scope->post('', ...)
});

类似于Laravel使用控制器类,可以使用由类名和方法名组成的字符串(由@字符分隔)作为回调来创建路由。路由器将负责在后台实例化类并调用给定方法。

use O\WordPress\Rest\Router;

class MyController
{
  /**
   * Lists some items
   *
   * @param WP_REST_Request $req
   * @param WP_REST_Response $res
   * @return WP_REST_Response
   */
  public function list(WP_REST_Request $req, WP_REST_Response $res): WP_REST_Response
  {
    // endpoint logic here
    return $res;
  }
}

$router = new Router('my-namespace');

// the router will take care of instantiating MyController and invoking the list method under the hood
$router->get('my-route', 'MyController@list');
// or if you're using namespaces
$router->get('my-route', 'MyNamespace\Controllers\MyController@list');

请注意,路由器不知道如何解析控制器类上的构造函数参数。如果您发现自己处于这种情况或者使用依赖注入容器,则需要向路由器提供一个解析函数。

use O\WordPress\Rest\Router;

class SomeRepository
{
  /**
   * Queries the database
   *
   * @param string $id
   * @return array
   */
  public function find(string $id): array
}

class MyController
{

  /**
   * @var SomeRepository
   */
  protected $repo;

  /**
   * @param SomeRepository $repo
   */
  public function __construct(SomeRepository $repo)
  {
    $this->repo = $repo;
  }

  /**
   * Lists some items
   *
   * @param WP_REST_Request $req
   * @param WP_REST_Response $res
   * @return WP_REST_Response
   */
  public function list(WP_REST_Request $req, WP_REST_Response $res): WP_REST_Response
  {
    $id = $req->get_param('id');
    $items = $this->repo->find($id);
    $res->set_data(json_encode($items));
    return $res;
  }
}

$router = new Router('my-namespace');

// provide the router with a function that resolves classes through a DI container
$router->setResolver(function (string $classname) {
  $container = SomeDIContainer::getInstance();
  return $container->create($classname);
});

// when the router needs to create a new instance of MyController it will first check
// if a resolver function has been provided and invoke it with the name of the class
// its trying to create, your resolver function should return a new instance of that class
$router->get('my-route', 'MyController@list');

WP Rest Router的一个很酷的功能是它使得定义和使用类似Express的中间件函数变得容易。请注意,与Express不同,路由回调将在所有中间件函数之后始终被调用。

use O\WordPress\Rest\Router;

$router = new Router('my-namespace');

// similar to route callbacks, middleware functions will always be provided with
// a request and response object, the only difference here is that middleware functions
// are also provided with a next function as their last argument - just like Express middlewares
// calling $next() at the end of your middleware allows the chain to continue
function fooMiddleware(WP_REST_Request $req, WP_REST_Response $res, callable $next)
{
  $res->header('x-foo', true);
  $next();
}

// you may however choose to conditionally not call $next
// this will short circuit the chain and prompt the router to return the current
// response object as-is, skipping any remaining middleware and the route callback
function barMiddleware(WP_REST_Request $req, WP_REST_Response $res, callable $next)
{
  // continue the chain if my-param is truthy
  if ($req->get_param('my-param')) {
    $next();
  }
  // otherwise stop the chain and return the response as-is
}

// any callable can be added as a middleware with the use method
// middleware functions are called in the order that they are added
$router->use('fooMiddleware');
$router->use('barMiddleware');

中间件也可以应用于范围路由。

use O\WordPress\Rest\Router;

$router = new Router('my-namespace');

function fooMiddleware(WP_REST_Request $req, WP_REST_Response $res, callable $next)
{
  $res->header('x-foo', true);
  $next();
}

function barMiddleware(WP_REST_Request $req, WP_REST_Response $res, callable $next)
{
  $res->header('x-bar', true);
  $next();
}

// apply the foo middleware to all incoming requests
$router->use('fooMiddleware');

$router->route('scoped-route', function ($scope) {
  // apply the barMiddleware to all requests within this scope only
  // middleware applied to all requests will run first
  $scope->use('barMiddleware');
});

WP Rest Router主要保留了原生WordPress API注册路由的一些功能,包括权限回调和查询参数模式,几乎没有改变。

use O\WordPress\Rest\Router;

$router = new Router('my-namespace');

// get, post, put, patch, and delete methods return a Route object
// Routes have two chainable methods, setPermission and setArg
$router
  ->get('foo', 'MyController@foo')
  // set a permission callback on the route
  // @see https://developer.wordpress.org/rest-api/extending-the-rest-api/adding-custom-endpoints/#permissions-callback
  ->setPermission(function () {
    return current_user_can('edit_others_posts');
  });

$router
  ->get('bar', 'MyController@bar')
  // set query parameter schemas on routes
  // @see https://developer.wordpress.org/rest-api/extending-the-rest-api/adding-custom-endpoints/#arguments
  ->setArg('my-param', [
    'required' => true,
    'type' => 'integer',
  ]);

WP Rest Router将所有中间件和路由回调包装在单个try-catch中,将异常转换为WP_Error对象。这使得您可以安全地抛出常规异常并从客户端获得有效的响应。

use O\WordPress\Rest\Router;

$router = new Router('my-namespace');

$router->get('foo', function (WP_REST_Request $req, WP_REST_Response $res) {
  // if nothing else catches this exception, wp rest router will catch it
  // and convert it to a WP_Error object which will be returned to the client by WordPress
  throw new \Exception('oops!');
});

开发

要求

  • composer
  • docker
  • docker-compose
  • php 7.1

安装依赖项

$ composer install

代码风格检查

$ composer lint
$ composer analyse

测试

$ composer test:unit

集成测试需要WordPress和MySql docker容器正在运行

$ composer:docker-up
$ composer test:integration

贡献

所有更改都需要有自己的分支,拉取请求应简洁且范围有限。CI作业由github actions运行,并在推送提交时自动触发。PR必须通过CI中运行的测试,并获得批准后才能合并。