8
votes

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
You should really be sending invalid URL's to a 404 page.delboy1978uk
Could you plz clr your question ? You mean if domain.comcart/any action then it will go to cart controller or otherwise it will go to brans controller ?Akshay Sharma
Yes, but if cart Controller exists. Ex: IF we hit domain.com/cart/any and cart controller does not exist it will go to brands otherwise it will go to brands controller where cart is treated as slugAman Rawat

2 Answers

3
votes

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 Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Cake\Http\ControllerFactory;
use Cake\Routing\Exception\MissingControllerException;

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 (\Cake\Routing\Exception\MissingControllerException $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 App\Application 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.

1
votes

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']
 });