使CakePHP考虑相同路径的两个不同路由/模型?

时间:2017-08-04 16:38:26

标签: cakephp routes

我有一个包含用户和条目的网站,这两个用户和条目都存储在数据库中。每个条目都有自己的页面,使用其slug,每个用户都有一个公共配置文件,使用其用户名。它们各自的URL可能如下所示:

  • 参赛作品:https://example.com/hello-world
  • 用户:https://example.com/test-user

使用CakePHP 3.4(原始使用“vanilla”PHP构建)重构网站,我实现了以下路线:

$routes->connect('/:slug',
    ['controller' => 'Entries', 'action' => 'view'],
    ['pass' => ['slug']]
);

$routes->connect('/:username',
    ['controller' => 'Users', 'action' => 'view'],
    ['pass' => ['username']]
);

条目页面就像一个魅力 - 没有问题 - 但是当我尝试访问用户个人资料时,Cake会抛出一个RecordNotFoundException。这是有道理的,因为它正在寻找一个不存在的条目。

我希望在firstOrFail中从find切换到EntriesController会允许应用程序继续使用下一个路由(因为不会抛出任何异常),但是结果实际上更糟糕的是:它试图在没有对象的情况下渲染条目视图,从而在其他空布局上导致PHP通知。

我已经阅读了CakePHP文档(“Book”),但找不到解决方案(我会假设相当通用)问题。我也尝试了许多其他(通常不太明显的)路线设置,但也没有运气。

现在我的思维一直像EntryOrUserController那样,但我怀疑这将是最好的解决方案,甚至是一个好的解决方案。坦率地说,我认为这很愚蠢。我想我真的希望有一些控制器或中间件功能完全符合我想要的功能,但任何优雅的解决方案都能做到。

P.S。我确实意识到,默认的CakePHP / MVC方式是让网址更像这样:

  • 参赛作品:https://example.com/entries/hello-world
  • 用户:https://example.com/users/test-user

...但在这种情况下,这不是一个选项,所以,谢谢,但没有。 ☺

1 个答案:

答案 0 :(得分:1)

感谢ndm让我指向正确的方向。经过一些修补,我有一个很好的解决方案。在此发布,因为它可能对其他人有用。有三个步骤。

1。创建自定义路由类

/src/Routing/Route/SlugRoute.php

namespace App\Routing\Route;

use Cake\Routing\Route\Route;
use Cake\ORM\Locator\LocatorAwareTrait;

class SlugRoute extends Route
{
    use LocatorAwareTrait;

    public function parse($url, $method = '')
    {
        $params = parent::parse($url, $method);

        if (!$params ||
            !isset($this->options['model']) ||
            !isset($this->options['pass'][0])
        ) {
            return false;
        }

        $count = $this
            ->tableLocator()
            ->get($this->options['model'])
            ->find()
            ->where([
                $this->options['pass'][0] => $params['pass'][0]
            ])
            ->count();

        if ($count !== 1) {
            return false;
        }

        return $params;
    }
}

2。将新路由类应用于相关路由

$routes->connect('/:slug',
    ['controller' => 'Entries', 'action' => 'view'],
    ['pass' => ['slug', 'name'], 'routeClass' => 'SlugRoute', 'model' => 'Entries']
);

$routes->connect('/:username',
    ['controller' => 'Users', 'action' => 'view'],
    ['pass' => ['username'], 'routeClass' => 'SlugRoute', 'model' => 'Users']
);

3。考虑一些事情

  • 我也在告诉路线使用哪种型号。如果路由类自己解决这个问题,那可能会很好。
  • 路由类假设我们要对pass数组的第一个值进行查找。在这种情况下这很好(仅传递slugusername),但它不是非常透明且容易被破坏。