Skip to content

动作

在 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 动作中。