chimera/routing

一组可重用的PSR-15组件,将Chimera连接到任何框架

资助包维护!
lcobucci
Patreon


README

Total Downloads Latest Stable Version Unstable Version

Build Status Code Coverage

术语Chimera(/kɪˈmɪərə/ 或 /kaɪˈmɪərə/)用来描述任何由多种动物部分组成的神话或虚构动物,或用来描述由非常不同部分组成或被认为是异想天开、不可信或耀眼的事物。

PHP社区中有许多令人惊叹的库,随着PSRs的创建和采用,我们不一定需要依赖全栈框架来创建复杂且设计良好的软件。选择要使用的组件并将它们组合起来有时可能会有些挑战。

这些包的目的是使这变得更容易(而不牺牲质量),让您能够专注于软件的行为。

这个特定的包提供了PSR-15 中间件和可重用的 请求处理器,帮助您使用HTTP作为网络机制来公开命令和查询处理器。

安装

您可能不会直接依赖这个包,但它可以在 Packagist 上找到,并且可以使用 Composer 安装

composer require chimera/routing

PHP配置

为了确保我们处理的是正确的数据,我们使用assert(),这是PHP中一个非常有趣的功能,但不太常用。关于assert()的好之处在于我们可以(也应该)在生产模式下禁用它,这样我们就没有无用的语句。

因此,对于生产模式,我们建议您在您的php.ini中将zend.assertions设置为-1。对于开发,您应该将zend.assertions保留为1并将assert.exception设置为1,这样PHP在出现问题时会抛出一个AssertionError

有关更多信息,请参阅文档:https://secure.php.net/manual/en/function.assert.php

组件

扩展点

扩展此库的包应实现两个基本接口,它们用于抽象每个路由库的工作方式

  • Chimera\Routing\RouteParamsExtractor:返回匹配路由的参数列表
  • Chimera\Routing\UriGenerator:根据给定的参数生成路由

路由参数提取中间件

此中间件使用Chimera\Routing\RouteParamsExtractor的实现将匹配路由的参数放入标准属性中,以便其他组件可以检索它们。

请求处理器

  • Chimera\Handler\CreateAndFetch:执行创建资源的命令并立即执行查询,返回一个包含查询结果和位置头的未格式化响应 - 旨在用于处理 POST 请求
  • Chimera\Handler\CreateOnly:执行创建资源的命令,返回一个包含位置头的空响应 - 旨在用于处理 POST 请求(前一个的变体,但也可以用于异步API)
  • Chimera\Handler\ExecuteAndFetch:执行修改资源的命令并立即执行查询,返回一个包含查询结果的未格式化响应 - 旨在用于处理 PUTPATCH 请求
  • Chimera\Handler\ExecuteOnly:执行修改或删除资源的命令,返回空响应 - 用于处理 PUTPATCHDELETE 请求(也可用于异步API)
  • Chimera\Handler\FetchOnly:执行查询以获取资源,返回未格式化的查询结果响应 - 用于处理 GET 请求

用法

中间件管道

如上所述,内容协商不是请求处理器的责任。预期您将配置 lcobucci/content-negotiation-middleware 来处理此类任务,并且它应放在管道的最前面,以便它可以处理 任何 未格式化的响应。

Chimera\Routing\RouteParamsExtractor 中间件应放在负责匹配路由的中间件之后(每个实现都有所不同)。

因此,在 Zend Expressive v3 应用程序 中的中间件管道看起来像这样 - 假定所有服务都已在DI容器中正确配置

<?php
declare(strict_types=1);

use Chimera\Routing\RouteParamsExtraction;
use Lcobucci\ContentNegotiation\ContentTypeMiddleware;
use Psr\Container\ContainerInterface;
use Zend\Expressive\Application;
use Zend\Expressive\Handler\NotFoundHandler;
use Zend\Expressive\Helper\BodyParams;
use Zend\Expressive\Helper\ServerUrlMiddleware;
use Zend\Expressive\Helper\UrlHelperMiddleware;
use Zend\Expressive\MiddlewareFactory;
use Zend\Expressive\Router\Middleware\DispatchMiddleware;
use Zend\Expressive\Router\Middleware\ImplicitHeadMiddleware;
use Zend\Expressive\Router\Middleware\ImplicitOptionsMiddleware;
use Zend\Expressive\Router\Middleware\MethodNotAllowedMiddleware;
use Zend\Expressive\Router\Middleware\RouteMiddleware;
use Zend\Stratigility\Middleware\ErrorHandler;

return function (Application $app, MiddlewareFactory $factory, ContainerInterface $container) : void {
    $app->pipe(ErrorHandler::class);
    $app->pipe(ServerUrlMiddleware::class);

    // Handles content negotiation, ensuring that the response format is the best one
    // according to what was requested via the `Accept` header
    $app->pipe(ContentTypeMiddleware::class);

    $app->pipe(RouteMiddleware::class);

    // Puts the matched arguments in a standard place (must be executed after the
    // `Zend\Expressive\Router\Middleware\RouteMiddleware` middleware, otherwise
    // matched routed info is not available)
    $app->pipe(RouteParamsExtraction::class);

    // It's quite important to add this one to the list, so that we ensure
    // that the request body is properly parsed and can be retrieved via
    // `ServerRequestInterface#getParsedBody()` - used by the `HttpRequest` input
    $app->pipe(BodyParams::class);

    $app->pipe(ImplicitHeadMiddleware::class);
    $app->pipe(ImplicitOptionsMiddleware::class);
    $app->pipe(MethodNotAllowedMiddleware::class);
    $app->pipe(UrlHelperMiddleware::class);
    $app->pipe(DispatchMiddleware::class);
    $app->pipe(NotFoundHandler::class);
};

路由

此包的主要思想是将应用程序的行为移动到命令和查询处理器,这样它们就可以通过不同的交付机制进行重用。这意味着请求处理器的逻辑几乎是可重用的,因此您最终会拥有更少的代码需要维护。

考虑到您已使用正确的处理器配置了命令和查询总线,并且您已在依赖注入容器中映射了请求处理器的实例,您只需配置PSR-15路由器以将处理器添加到正确的端点。

Zend Expressive v3 应用程序 中,config/routes.php 的外观如下

<?php

declare(strict_types=1);

use Psr\Container\ContainerInterface;
use Zend\Expressive\Application;
use Zend\Expressive\MiddlewareFactory;

/**
 * Considering you have the following services in your DI container:
 *
 * - `album.list` => new FetchOnly(
 *     new ExecuteQuery(**query bus**, **message creation strategy**, MyApi\FetchAlbumList::class),
 *     ResponseInterface::class
 * );
 * - `album.find_one` => new FetchOnly(
 *     new ExecuteQuery(**query bus**, **message creation strategy**, MyApi\FindAlbum::class),
 *     ResponseInterface::class
 * );
 * - `album.create` => new CreateOnly(
 *     new ExecuteCommand(**command bus**, **message creation strategy**, MyApi\CreateAlbum::class),
 *     ResponseInterface::class,
 *     'album.find_one',
 *     UriGenerator::class,
 *     IdentifierGenerator::class,
 *     201
 * );
 * - `album.update_album` => new ExecuteAndFetch(
 *     new ExecuteCommand(**command bus**, **message creation strategy**, MyApi\UpdateAlbum::class),
 *     new ExecuteQuery(**query bus**, **message creation strategy**, MyApi\FindAlbum::class),
 *     ResponseInterface::class
 * );
 */
return function (Application $app, MiddlewareFactory $factory, ContainerInterface $container) : void {
    $app->get('/album', 'album.list', 'album.list');
    $app->post('/album', 'album.create', 'album.create');
    $app->get('/album/{id}', 'album.find_one', 'album.find_one');
    $app->patch('/album/{id}', 'album.update_album', 'album.update_album');
};

许可证

MIT,请参阅 LICENSE