Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
123 lines (90 sloc) 3.41 KB

Actions

In a web application what is executed is determined by request URL. Matching is made by router that is configured with multiple routes. Each route can be attached a middleware that, given request, produces a response. Since middleware overall could be chained and can pass actual handling to next middleware, we call the middleware actually doing the job an action.

There are multiple ways to describe an action. Simplest one is using a closure:

use \Psr\Http\Message\ServerRequestInterface;
use \Psr\Http\Message\ResponseInterface;
use Yiisoft\Router\Route;

Route::get('/')->to(function (ServerRequestInterface $request) use ($responseFactory): ResponseInterface {
    $response = $responseFactory->createResponse();
    $response->getBody()->write('You are at homepage.');
    return $response;
}));

It is fine for very simple handling since any more complicated one would require getting dependencies so a good idea would be moving the handling to a class method. Callback middleware could be used for the purpose:

use Yiisoft\Router\Route;

Route::get('/')->to(new ActionCaller(FrontPageAction::class, 'run', $container)),

The class itself would like:

use \Psr\Http\Message\ServerRequestInterface;
use \Psr\Http\Message\ResponseInterface;

class FrontPageAction
{
    public function run(ServerRequestInterface $request): ResponseInterface
    {
        // render front page    
    }
}

For many cases it makes sense to group handling for multiple routes into a single class:

use Yiisoft\Router\Route;

Route::get('/post/index')->to(new ActionCaller(PostController::class, 'actionIndex', $container)),
Route::get('/post/view/{id:\d+}')->to(new ActionCaller(PostController::class, 'actionView', $container)),

The class itself would look like the following:

use \Psr\Http\Message\ServerRequestInterface;
use \Psr\Http\Message\ResponseInterface;

class PostController
{
    public function actionIndex(ServerRequestInterface $request): ResponseInterface
    {
        // render posts list
    }
    
    
    public function actionView(ServerRequestInterface $request): ResponseInterface
    {
        // render a single post      
    }
}

This class is usually called "controller". Above code is quite repetitive so you can use WebActionsCaller middleware:

use Yiisoft\Router\Route;
use Yiisoft\Yii\Web\Middleware\WebActionsCaller;

Route::anyMethod('/post/{action:\w+}')->to(new WebActionsCaller(PostController::class, $container)),

Autowiring

Both constructors of action-classes and action-methods are automatically getting services from dependency injection container:

use \Psr\Http\Message\ServerRequestInterface;
use \Psr\Http\Message\ResponseInterface;
use Psr\Log\LoggerInterface;

class PostController
{
    private $postRepository;

    public function __construct(PostRepository $postRepository)
    {
        $this->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      
    }
}

In the above example PostRepository is injected automatically via constructor. That means it is available in every action. Logger is injected into index action only.

You can’t perform that action at this time.