Welcome to Abdul Malik Ikhsan's Blog

Using Router match Check and Diactoros’s RedirectResponse in Zend\Expressive

Posted in Tutorial PHP, Zend Framework by samsonasik on September 17, 2015

If we use Zend\Expressive middleware, and want to handle redirection using Diactoros’s RedirectResponse, we may need to check whenever the Uri that we want to redirect is a valid and match against registered Router, if not valid, then back to home/other handling. Zend\Expressive gives us a choice to use ZF2 Router, Aura Router, or FastRoute. They have a way to check Uri is on registered Router, and they return different value when not valid (null for ZF2 Router, false for Aura Router, and FastRoute check with Dispatcher FOUND and NOT_FOUND constants). Zend\Expressive facilitating it by provide Router ‘adapter’ for each of them, and they have match method that return RouteResult instance as Value Object. They RouteResult has a $success property that will be setted as false when not match, and true when matched via following functions:

namespace Zend\Expressive\Router;

class RouteResult
{
    // (new self())->success = true;
    public static function fromRouteMatch($name, $middleware, array $params){}
    // (new self())->success = false;
    public static function fromRouteFailure($methods = null){} 
}

We have another methods to check $success property that have setted:

namespace Zend\Expressive\Router;

class RouteResult
{
    // return $this->success
    public static function isSuccess(){}
    // return (! $this->success);
    public static function isFailure(){} 
}

We can use one of the methods above to check $success property value.

Ok, let’s create an example! For example, we have a SamplePageAction as middleware:

namespace App\Action;

use Zend\Diactoros\Response\RedirectResponse;

class SamplePageAction
{
    public function __invoke($request, $response, $next = null)
    {
        return new RedirectResponse('/a-sample-uri');
    }
}

We need to ‘validate’ if ‘/a-sample-uri’ is a valid uri for our Zend\Expressive application. We can do:

  1. Inject the middleware with Zend\Expressive\Router\RouterInterface
    class SamplePageAction
    {
        private $router;
    
        public function __construct(RouterInterface $router)
        {
            $this->router = $router;
        }
    
        public function __invoke($request, $response, $next = null)
        { /** **/ }
    }
    

  2. Make factory for it

    namespace App\Action;
    
    use Interop\Container\ContainerInterface;
    use Zend\Expressive\Router\RouterInterface;
    
    class SamplePageActionFactory
    {
        public function __invoke(ContainerInterface $container)
        {
            $router   = $container->get(RouterInterface::class);
            return new SamplePageAction($router);
        }
    }
    

  3. If we are using Zend\ServiceManager as the Container, we can register inside our config at ‘factories’

    use App\Action\SamplePageAction;
    use App\Action\SamplePageActionFactory;
    
    //...
            'factories' => [
                SamplePageAction::class => SamplePageActionFactory::class,
            ]
    // ...
    

    if we use other container, we can follow the docs.

  4. Now, we can use it to check and handle route match the request uri

    class SamplePageAction
    {
        public function __construct(RouterInterface $router){ /** **/ }
        public function __invoke($request, $response, $next = null)
        {
            $uri         =  '/a-sample-uri';
    
            // save current path as variable
            $currentPath = $request->getUri()->getPath();
            // set request with Uri
            $request     = $request->withUri(new Uri($uri));
    
            // check match
            $match = $this->router->match($request);
            // check failure and if request uri path is not same with current
            // path to avoid infinite redirection 
            if (! $match->isFailure() 
                && $currentPath !== $request->getUri()->getPath()
            ) {   
                return new RedirectResponse($uri);
            }
    
            // redirect to uri with router name = home 
            // when router is not match  
            return new RedirectResponse($this->router->generateUri('home'));
        }
    }
    

    In real world app, the redirect check usually come from a variable, for example, a Query param(for ex: ‘redirect’ query param). For that case, we can do check like this:

    class SamplePageAction
    {
        public function __construct(RouterInterface $router){ /** **/ }
        public function __invoke($request, $response, $next = null)
        {
            // get arrays of Query params
            $queryParams = $request->getQueryParams();
            
            // check if 'redirect' Query param is set
            // and not equals than ''
            if (isset($queryParams['redirect']) 
                && trim($queryParams['redirect']) !== ''
            ) {  
                // save current path as variable
                $currentPath = $request->getUri()->getPath();
                // set request with Uri
                $request = $request->withUri(new Uri($queryParams['redirect']));
                 
                 // check match
                 $match = $this->router->match($request);
                 // check failure and if request uri path is not same with
                 // current path to avoid infinite redirection 
                 if (! $match->isFailure() 
                     && $currentPath !== $request->getUri()->getPath()
                 ) {
                     return new RedirectResponse($queryParams['redirect']);
                 }
            }
    
            return new RedirectResponse($this->router->generateUri('home'));
        }
    }
    

Update: There is a package for that that I created, go grab it here: https://github.com/samsonasik/ExpressiveRedirectHandler 😉

Advertisements

2 Responses

Subscribe to comments with RSS.

  1. seyferx said, on September 17, 2015 at 4:44 pm

    In last example

    if (! $match->isFailure()
    && $currentPath !== $request->getUri()->getPath()
    ) {
    return new RedirectResponse($uri);
    }

    There should be

    return new RedirectResponse($queryParams[‘redirect’]);


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: