我有一个包含用户和条目的网站,这两个用户和条目都存储在数据库中。每个条目都有自己的页面,使用其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
...但在这种情况下,这不是一个选项,所以,谢谢,但没有。 ☺
答案 0 :(得分:1)
感谢ndm让我指向正确的方向。经过一些修补,我有一个很好的解决方案。在此发布,因为它可能对其他人有用。有三个步骤。
/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;
}
}
$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']
);
pass
数组的第一个值进行查找。在这种情况下这很好(仅传递slug
和username
),但它不是非常透明且容易被破坏。