动作
在 Web 应用程序中,请求 URL 决定了要执行的内容。匹配由配置了多条路由的路由器完成。每条路由可以绑定一个中间件,该中间件接收请求并产生响应。由于中间件可以链式调用并将实际处理传递给下一个中间件,我们将真正执行任务的中间件称为动作。
描述动作有多种方式,最简单的是使用闭包:
php
use \Psr\Http\Message\ServerRequestInterface;
use \Psr\Http\Message\ResponseInterface;
use Yiisoft\Router\Route;
Route::get('/')->action(function (ServerRequestInterface $request) use ($responseFactory): ResponseInterface {
$response = $responseFactory->createResponse();
$response->getBody()->write('You are at homepage.');
return $response;
});对于简单的处理,这种方式完全可以,但更复杂的处理通常需要获取依赖,因此将处理逻辑移至类方法是一个好主意。为此可以使用回调中间件:
php
use Yiisoft\Router\Route;
Route::get('/')->action(FrontPageAction::class),该类的结构如下:
php
use \Psr\Http\Message\ServerRequestInterface;
use \Psr\Http\Message\ResponseInterface;
final readonly class FrontPageAction
{
public function __invoke(ServerRequestInterface $request): ResponseInterface
{
// build response for a front page
}
}在许多情况下,将多条路由的处理逻辑归入一个类是合理的:
php
use Yiisoft\Router\Route;
Route::get('/post/index')->action([PostController::class, 'actionIndex']),
Route::get('/post/view/{id:\d+}')->action([PostController::class, 'actionView']),该类的结构如下所示:
php
use \Psr\Http\Message\ServerRequestInterface;
use \Psr\Http\Message\ResponseInterface;
final readonly class PostController
{
public function actionIndex(ServerRequestInterface $request): ResponseInterface
{
// render posts list
}
public function actionView(ServerRequestInterface $request): ResponseInterface
{
// render a single post
}
}我们通常将这样的类称为“控制器”。
自动注入
动作类的构造函数和动作方法都可以自动从 依赖注入容器中获取服务:
php
use \Psr\Http\Message\ServerRequestInterface;
use \Psr\Http\Message\ResponseInterface;
use Psr\Log\LoggerInterface;
final readonly class PostController
{
public function __construct(
private PostRepository $postRepository
)
{
}
public function actionIndex(ServerRequestInterface $request, LoggerInterface $logger): ResponseInterface
{
$logger->debug('Rendering posts list');
// render posts list
}
public function actionView(ServerRequestInterface $request): ResponseInterface
{
// render a single post
}
}在上面的示例中,PostRepository 通过构造函数自动注入,因此在每个动作中都可用。而 Logger 仅注入到 index 动作中。