skip to Main Content

Using CakePHP v3.3.16

I want to write a fallback route in such a way that if URL is not connected to any action then it should go to that fallback.

Created routes for SEO friendly URL like this

   $routes->connect(
        ':slug',
        ['prefix'=>'website','controller' => 'Brands', 'action' => 'index'],
        ['routeClass' => 'DashedRoute']
    );
    $routes->connect(
        ':slug/*',
        ['prefix'=>'website','controller' => 'Products', 'action' => 'index'],
        ['routeClass' => 'DashedRoute']
    );

But it’s also inclute all the controller actions in it so if i try to call a controller ex: cart/index it’s going to website/brands/index/index

If I have to remove exclude it, I have to create a route like this/

$routes->connect('/cart',['controller' => 'Cart'], ['routeClass' => 'DashedRoute']);

And so on to the other controller to access.

Example:
I have a controller CartController action addCart

CASE 1

if I access URL my_project/cart/addCart/ It should go to cart controller action

CASE 2

if I access URL my_project/abc/xyz/ and there is no controller named abc so it should go to BrandsController action index

My Current routes.php looks like this

Router::defaultRouteClass(DashedRoute::class);

Router::scope('/', function (RouteBuilder $routes) {

    $routes->connect('/', ['prefix'=>'website','controller' => 'Home', 'action' => 'index']);
    $routes->connect('/trending-brands', ['prefix'=>'website','controller' => 'Brands', 'action' => 'trending']);
    $routes->connect('/users/:action/*',['prefix'=>'website','controller' => 'Users'], ['routeClass' => 'DashedRoute']);

    $routes->connect('/cart',['prefix'=>'website','controller' => 'Cart'], ['routeClass' => 'DashedRoute']);
    $routes->connect('/cart/:action/*',['prefix'=>'website','controller' => 'Cart'], ['routeClass' => 'DashedRoute']);

    $routes->connect(
        ':slug',
        ['prefix'=>'website','controller' => 'Brands', 'action' => 'index'],
        ['routeClass' => 'DashedRoute']
    );
    $routes->connect(
        ':slug/*',
        ['prefix'=>'website','controller' => 'Products', 'action' => 'index'],
        ['routeClass' => 'DashedRoute']
    );
    $routes->connect(':controller', ['prefix'=>'website'], ['routeClass' => 'DashedRoute']);
    $routes->connect(':controller/:action/*', ['prefix'=>'website'], ['routeClass' => 'DashedRoute']);


    $routes->fallbacks(DashedRoute::class);
});

Router::prefix('website', function (RouteBuilder $routes) {
    $routes->fallbacks(DashedRoute::class);
});


Plugin::routes();

2

Answers


  1. If I have to remove exclude it, I have to create a route like this/ And so on to the other controller to access.

    There is no need to specify every controller/name in the pattern, instead you can create all your specify routes first and then place the following route below them to catch all unmatched routes.

    $routes->connect(':/prefix/:controller/:action/*', [], ['routeClass' => 'DashedRoute']);
    

    And maybe another one without prefix

    $routes->connect('/:controller/:action/*', [], ['routeClass' => 'DashedRoute']);
    

    And since you mentioned that you are using 3.3.* cake version, Your routes can be written taking advatage of routes scoping

    Router::prefix('website', function ($routes) {
     // All routes here will be prefixed with `/prefix`
     // And have the prefix => website route element added.
     $routes->fallbacks(DashedRoute::class);
       $routes->connect(
        '/:slug',
        ['controller' => 'Brands', 'action' => 'index']
    );
    $routes->connect(
        ':slug/*',
        ['controller' => 'Products', 'action' => 'index']
     }); 
    
    Login or Signup to reply.
  2. Your edits totally invalidate my first answer, so I decided to just post another answer.

    What you want to achieve can not be done by the router because there is no way for router to tell if the controller/action exists for a particular route. This is because the router just work with url templates and delegates the task of loading Controllers up in the request stack.

    You can emulate this feature though by using a Middleware
    that check if the request attribute params is set and then validate them as appropriate and changing them to your fallback controller if the target controller doesn’t exist.

    Just make sure to place the RoutingMiddleware execute before your middleware otherwise you will have no params to check because the routes wouldn’t have been parsed.

    Middlewares implement one method __invoke().

    use PsrHttpMessageResponseInterface;
    use PsrHttpMessageServerRequestInterface;
    use CakeHttpControllerFactory;
    use CakeRoutingExceptionMissingControllerException;
    
    class _404Middleware {
    
    public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next)
    {
            //$params = (array)$request->getAttribute('params', []);
    
            try {
                $factory = new ControllerFactory();
                $controller = $factory->create($request, $respond);
            }
         catch (CakeRoutingExceptionMissingControllerException $e) {
          // here you fallback to your controllers/action or issue app level redirect
          $request = $request->withAttribute('params',['controller' =>'brands','action' => 'index']);
          }
        return $next($request, $response);
    }
    
    }
    

    Then attach in your AppApplication see also attaching middlewares

    $middlewareStack->add(new _404Middleware());
    

    please remmber to clean up the code as I didn’t test it under real dev’t enviroment.

    After thought: if your were looking to create an error page for all not found resources, then you don’t need all that, instead you would just customise the error template in Template/Error/error404.ctp.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search