我必须支持项目的url友好结构。
有多个带有段塞列的表,在cakepp中,我如何才能以最有效的方式将段塞路由到控制器。
起初,我在检查表中是否存在鼻涕虫,如果鼻涕虫存在,请使用以下路线:
$c = TableRegistry::get('cateogories');
$result= $c->find()->select(['id'])->where(['url'=>$slug])->toArray();
if(count($result) > 0) {
$routes->connect(
'/:slug',
['controller' => 'Categories', 'action' => 'index', 'id' => $result[0]['id']]
);
}
问题是,我有多个像上面这样的检查,即使路由之前匹配,每个检查都在运行(不需要运行,所以会调用额外的查询)。
那么,我如何添加某种条件语句,以便它只检查路由是否匹配(如果之前的路由都不匹配)。
我建议使用一个处理此问题的自定义路由类。虽然你可以查询路线文件中的数据,但这是
- 对测试不太友好
- 不太干燥
- 反向路由不安全
后一点意味着,当没有连接所有路由时,试图从路由数组中为未连接的路由生成URL可能会触发异常,或者匹配错误的路由。
使用自定义路由类,您只需在连接路由时在选项中传递模型,在解析URL后的路由类中,查询给定段塞的模型,并相应地返回false
或解析的数据。这真的很简单,只要看看现有的路由类是做什么的。
这是一个非常基本的例子,应该是非常自我解释的。
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)
{
$params = parent::parse($url);
if (!$params ||
!isset($this->options['model'])
) {
return false;
}
$count = $this
->tableLocator()
->get($this->options['model'])
->find()
->where([
'slug' => $params['slug']
])
->count();
if ($count !== 1) {
return false;
}
return $params;
}
}
本例假设在控制器中,您将使用段塞来检索记录。如果您想要传递ID,那么您可以获取ID并将其传递到解析的数据中,而不是使用count()
,例如:
$params['pass'][] = $id;
然后,它将作为控制器操作的第二个参数传递。
routes.php
$routes->connect(
'/:slug',
['controller' => 'Articles', 'action' => 'view'],
[
'pass' => ['slug'],
'routeClass' => 'SlugRoute',
'model' => 'Articles'
]
);
$routes->connect(
'/:slug',
['controller' => 'Categories', 'action' => 'view'],
[
'pass' => ['slug'],
'routeClass' => 'SlugRoute',
'model' => 'Categories'
]
);
// ...
这将首先检查Articles
模型,然后检查Categories
模型等,并在其中一条路由找到给定段塞的记录后停止。
另请参见
- 食谱>路线>自定义路线类别
- API>''Cake''Routing''Route::parse()
- 源>''Cake''Routing''Route