ellipse / handlers-adr
实现 ADR 模式的 Psr-15 请求处理器
Requires
- php: >=7.0
- ellipse/adr: ^1.0
- ellipse/type-errors: ^1.0
- psr/container: ^1.0
- psr/http-message: ^1.0
- psr/http-server-handler: ^1.0
- psr/http-server-middleware: ^1.0
Requires (Dev)
- eloquent/phony-kahlan: ^1.0
- kahlan/kahlan: ^4.0
This package is auto-updated.
Last update: 2024-08-25 23:41:51 UTC
README
此包提供了一种实现 Psr-15 请求处理器,并遵循 Action-Domain-Responder 模式的包。
要求 php >= 7.0
安装 composer require ellipse/handlers-adr
运行测试 ./vendor/bin/kahlan
使用 ADR 模式的请求处理器
Action-Domain-Responder (ADR) 模式用于分离应用程序的领域和展示逻辑。可以概括为使用 Action 对象将一对 Domain 和 Responder 对象粘合在一起,以从传入的请求生成响应。因此,可以将 Action 视为 Psr-15 请求处理器,此包的目标是提供一个可用于任何 Psr-15 分发系统的 ADR 实现。
Ellipse\Handlers\ActionRequestHandler
代表一个通用的 Action,实现了 Psr\Http\Server\RequestHandlerInterface
。其第一个构造函数参数是一个实现了 Ellipse\ADR\DomainInterface
的 Domain 对象,第二个参数是一个实现了 Ellipse\Handler\ResponderInterface
的 Responder 对象。以下是当使用 Psr-7 请求调用 ActionRequestHandler
实例的 ->handle()
方法时发生的操作:
- 从 Psr-7 请求中提取一个输入数组
- 通过调用 Domain 的
->payload()
方法并传入输入数组来生成一个负载 - 通过调用 Responder 的
->response()
方法并传入 Psr-7 请求和负载来生成一个 Psr-7 响应 - 返回 Psr-7 响应
默认情况下,输入数组是通过合并请求属性、查询参数、解析的正文参数和上传的文件获得的。它们按此顺序合并,这意味着具有相同键的请求属性会被查询参数覆盖,而查询参数会被解析的正文参数覆盖,最终由上传的文件覆盖。可以通过将可调用对象作为 ActionRequestHandler
第三个构造函数参数传递来指定 Action 特定的请求解析逻辑。此请求解析可调用对象以请求作为参数执行,并必须返回一个数组。当返回的不是数组时,会抛出 Ellipse\Handlers\Exceptions\InputTypeException
。
DomainInterface
定义了一个 ->payload()
方法,该方法接受一个输入数组作为参数,并返回一个实现了 Ellipse\ADR\PayloadInterface
的实现。
PayloadInterface
定义了两个方法:->status()
返回负载状态作为字符串,->data()
返回负载数据作为数组。可以使用 Ellipse\ADR\Payload
类作为 PayloadInterface
的默认实现。它将状态字符串和数据数组作为构造函数参数。
最后,ResponseInterface
定义了一个 ->response()
方法,该方法接受一个请求和一个实现了 PayloadInterface
的实现作为参数,并返回一个响应。
<?php namespace App\Domain; use Ellipse\ADR\Payload; use Ellipse\ADR\PayloadInterface; use Ellipse\ADR\DomainInterface; use App\SomeService; class SomeDomain implements DomainInterface { private $service; public function __construct(SomeService $service) { $this->service = $service; } public function payload(array $input): PayloadInterface { // perform domain logic... // This payload will be passed to the responder ->response() method. return new Payload('FOUND', ['k1' => $v1, 'k2' => $v2]); } }
<?php namespace App\Responder; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ResponseInterface; use Ellipse\ADR\PayloadInterface; use Ellipse\Handlers\ResponderInterface; use App\ResponseFactory; class SomeResponder implements ResponderInterface { private $factory; public function __construct(ResponseFactory $factory) { $this->factory = $factory; } public function response(ServerRequestInterface $request, PayloadInterface $payload): ResponseInterface { // Different Psr-7 responses can be produced according to the given Psr-7 request and // the given payload. if ($payload->status() === 'FOUND') { return $this->factory->createFoundResponse('template', $payload->data()); } return $this->factory->createNotFoundResponse(); } }
<?php namespace App; use App\Domain\SomeDomain; use App\Responder\SomeResponder; use Ellipse\Handlers\ActionRequestHandler; // Create an action request handler using SomeDomain and SomeResponder. $domain = new SomeSomain(new SomeService); $responder = new SomeResponder(new ResponseFactory); $handler = new ActionRequestHandler($domain, $responder); // A specific request parsing callable can be specified. $handler = new ActionRequestHandler($domain, $responder, function ($request) { $attributes = $request->getAttributes(); return [ 'key' => explode(' ', $attributes['key']), ]; }); // Action request handler instances work like any Psr-15 request handler. $response = $handler->handle($request);
与 Psr-11 容器一起使用
在实际应用中,通常从容器中检索 Domain 和 Responder 实例。
此包提供了实现 DomainInterface
和 ResponderInterface
的代理,这些代理 Psr-11 容器条目。
Ellipse\Handlers\ContainerDomain
类接受一个容器和一个 Domain 对象的容器 ID 作为构造函数参数。当调用其 ->payload()
方法时,将从容器中检索 Domain 对象,并返回其 ->payload()
方法产生的负载。如果容器条目不是 DomainInterface
的实现,将抛出 Ellipse\Handlers\Exceptions\ContainedDomainTypeException
异常。
同样地,Ellipse\Handlers\ContainerResponder
类接受一个容器和一个 Responder 对象的容器 ID 作为构造函数参数。容器条目的 ->response()
方法被代理,如果它不是 ResponderInterface
的实现,将抛出 Ellipse\Handlers\Exceptions\ContainedResponderTypeException
异常。
最后,可以使用 Ellipse\Handlers\ContainerRequestParser
类通过容器和可调用的容器 ID 作为构造函数参数检索请求解析的可调用对象。如果容器条目不是一个可调用对象,将抛出 Ellipse\Handlers\Exceptions\ContainedRequestParserTypeException
异常。
<?php namespace App; use SomePsr11Container; use App\Domain\SomeDomain; use App\Responder\SomeResponder; use Ellipse\Handlers\ContainerDomain; use Ellipse\Handlers\ContainerResponder; use Ellipse\Handlers\ContainerRequestParser; use Ellipse\Handlers\ActionRequestHandler; // Register SomeDomain, SomeResponder and a request parser into a Psr-11 container. $container = new SomePsr11Container; $container->set(SomeDomain::class, function ($container) { $service = $container->get(SomeService::class); return new SomeDomain($service); }); $container->set(SomeResponder::class, function ($container) { $factory = $container->get(ResponseFactory::class); return new SomeResponder($factory); }); $container->set('adr.parser', function () { return function ($request) { $attributes = $request->getAttributes(); return [ 'key' => explode(' ', $attributes['key']), ]; }; }); // Create an action using domain, responder and request parser proxying container entries. $domain = new ContainerDomain($container, SomeDomain::class); $responder = new ContainerResponder($container, SomeResponder::class); $parser = new ContainerRequestParser($container, 'adr.parser'); $handler = new ActionRequestHandler($domain, $responder, $parser); // Actual domain, responder and request parser are retrieved from the container when the request is handled // by the action. $response = $handler->handle($request);
当然,可以使用来自 ellipse/container-reflection 包的 Ellipse\Container\ReflectionContainer
类自动装配 Domain 和 Responder 类。
<?php namespace App; use SomePsr11Container; use App\Domain\SomeDomain; use App\Responder\SomeResponder; use Ellipse\ADR\DomainInterface; use Ellipse\Handlers\ResponderInterface; use Ellipse\Handlers\ActionRequestHandler; use Ellipse\Container\ReflectionContainer; // Get some Psr-11 container. $container = new SomePsr11Container; // Decorate the container with a reflection container. // Specify the domain and responder implementations can be auto wired. $reflection = new ReflectionContainer($container, [ DomainInterface::class, ResponderInterface::class, ]); // Create an action using domain and responder proxying container entries. $domain = new ContainerDomain($reflection, SomeDomain::class); $responder = new ContainerResponder($reflection, SomeResponder::class); $handler = new ActionRequestHandler($domain, $responder); // Instances of SomeDomain and SomeResponder are built using auto wiring. $response = $handler->handle($request);