如果给定的操作不存在,则定义 Cakephp 路由以调用特定的控制器

Posted

技术标签:

【中文标题】如果给定的操作不存在,则定义 Cakephp 路由以调用特定的控制器【英文标题】:Define Cakephp Route to call specific controller if given action not exists 【发布时间】:2017-10-19 05:55:01 【问题描述】:

使用 Cakephp v3.3.16

我想以这样一种方式编写一个后备路由,如果 URL 未连接到任何操作,那么它应该转到该后备。

为这样的 SEO 友好 URL 创建路由

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

但它也包含所有控制器操作,所以如果我尝试调用控制器 ex: cart/index 它会转到 website/brands/index/index

如果我必须删除排除它,我必须创建这样的路线/

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

等到其他控制器去访问。

示例: 我有一个控制器 CartController 动作 addCart

案例 1

如果我访问 URL my_project/cart/addCart/ 它应该转到购物车控制器操作

案例 2

如果我访问 URL my_project/abc/xyz/ 并且没有名为 abc 的控制器,那么它应该转到 BrandsController 操作 index

我当前的 routes.php 看起来像这样

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();

【问题讨论】:

您确实应该将无效的 URL 发送到 404 页面。 请问您的问题好吗?你的意思是如果 domain.comcart/any 动作然后它会去购物车控制器,否则它会去麸皮控制器? 是的,但如果购物车控制器存在。例如:如果我们点击 domain.com/cart/any 并且购物车控制器不存在,它将转到品牌,否则它将转到品牌控制器,购物车被视为 slug 【参考方案1】:

您的编辑完全使我的第一个答案无效,所以我决定只发布另一个答案。

路由器无法完成您想要实现的目标,因为路由器无法判断特定路由是否存在控制器/动作。这是因为路由器只使用 url 模板并委托在请求堆栈中加载控制器的任务。

您可以使用Middleware 来模拟此功能 检查是否设置了请求属性params,然后适当地验证它们,如果目标控制器不存在,则将它们更改为您的备用控制器。

只需确保将 RoutingMiddleware 执行放在中间件之前,否则您将没有要检查的参数,因为路由不会被解析。

中间件实现一种方法__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);



然后附上你的App\Application,另见attaching middlewares

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

请记住清理代码,因为我没有在真实的开发环境下测试它。

经过深思熟虑:如果您希望为所有未找到的资源创建一个错误页面,那么您不需要所有这些,而是​​只需自定义 Template/Error/error404.ctp 中的错误模板。

【讨论】:

你能举个例子吗?会更合适【参考方案2】:

如果我必须删除排除它,我必须创建一个这样的路由/等到另一个控制器访问

不需要在模式中指定每个控制器/名称,而是可以先创建所有指定的路由,然后在它们下面放置以下路由以捕获所有不匹配的路由。

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

也许还有一个不带前缀的

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

并且由于您提到您使用的是3.3.* cake 版本,您的路线可以利用路线范围

来编写
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']
 ); 

【讨论】:

尝试无法正常工作,因为我的 url 与第一条规则匹配,但它不会与其他规则匹配。 4 或 5,但我想概括一下,因为它可以根据需要扩展。 你所有的控制器都加前缀了吗?给我看看你所有的自定义路由。

以上是关于如果给定的操作不存在,则定义 Cakephp 路由以调用特定的控制器的主要内容,如果未能解决你的问题,请参考以下文章

Cakephp 检查模型是不是存在

Key操作命令

默认路由(控制器/动作)和 css/js 的链接不起作用 cakephp

CakePHP - 从插件加载路由文件

CakePHP 高级路由

Cakephp:在分页器链接中保留自定义路由参数