Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Define Cakephp Route to call specific controller if given action not exists

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();
like image 824
Aman Rawat Avatar asked May 19 '17 07:05

Aman Rawat


Video Answer


1 Answers

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.

like image 173
Cholthi Paul Ttiopic Avatar answered Oct 05 '22 23:10

Cholthi Paul Ttiopic