問題描述
我必須支持項(xiàng)目的 url 友好結(jié)構(gòu).
有多個(gè)帶有 slug 列的表,在 cakephp 中如何以最有效的方式將 slug 路由到控制器.
起初我正在檢查表中是否存在 slug,如果存在 slug 使用路由:
$c = TableRegistry::get('cateogories');$result= $c->find()->select(['id'])->where(['url'=>$slug])->toArray();如果(計(jì)數(shù)($ 結(jié)果)> 0){$routes->connect('/:slug',['控制器' =>'類別', '動作' =>'索引', 'id' =>$result[0]['id']]);}
問題是我有多個(gè)檢查,如上面的一個(gè),即使路由先前匹配(不需要運(yùn)行,因此調(diào)用額外的查詢),每個(gè)檢查都在運(yùn)行.
那么我如何添加某種條件語句,以便它只檢查路由是否匹配,如果之前的路由都不匹配.
我建議使用處理此問題的自定義路由類.雖然您可以查詢路由文件中的數(shù)據(jù),但這是
- 不要過度測試友好
- 不太干
- 反向路由不安全
后一點(diǎn)意味著當(dāng)沒有連接所有路由時(shí),嘗試從路由數(shù)組中為未連接的路由生成 URL 可能會觸發(fā)異常,或者匹配錯誤的路由.
使用自定義路由類,您可以在連接路由時(shí)簡單地在選項(xiàng)中傳遞模型,并在 解析 URL,查詢給定 slug 的模型,并返回 false
或相應(yīng)地解析數(shù)據(jù).真的很簡單,看看現(xiàn)有的路由類是做什么的.
這是一個(gè)非常基本的例子,應(yīng)該是不言自明的.
src/Routing/Route/SlugRoute.php
命名空間 AppRoutingRoute;使用 CakeRoutingRouteRoute;使用 CakeORMLocatorLocatorAwareTrait;類 SlugRoute 擴(kuò)展 Route{使用 LocatorAwareTrait;公共函數(shù)解析($url){$params = parent::parse($url);如果 (!$params ||!isset($this->options['model'])){返回假;}$count = $this->tableLocator()->get($this->options['model'])-> 查找()->哪里(['slug' =>$params['slug']])-> 計(jì)數(shù)();如果 ($count !== 1) {返回假;}返回 $params;}}
此示例假設(shè)在控制器中,您將使用 slug 來檢索記錄.如果您希望傳遞 ID,則可以不使用 count()
,而是獲取 ID 并將其傳遞到已解析的數(shù)據(jù)中,例如:
$params['pass'][] = $id;
它最終會作為控制器動作的第二個(gè)參數(shù)被傳遞.
routes.php
$routes->connect('/:slug',['控制器' =>'文章', '動作' =>'看法'],['通過' =>['蛞蝓'],'routeClass' =>'SlugRoute','模型' =>'文章']);$routes->connect('/:slug',['控制器' =>'類別', '動作' =>'看法'],['通過' =>['蛞蝓'],'routeClass' =>'SlugRoute','模型' =>'類別']);//...
這將首先檢查 Articles
模型,然后是 Categories
模型等,并在其中一條路線找到給定 slug 的記錄后停止.><小時(shí)>
另見
- 食譜 > 路由 > 自定義路由課程
- API > CakeRoutingRoute::parse()
- 來源> CakeRoutingRoute
I have to support url friendly structure for a project.
There is multiple tables with a slug column, in cakephp how can I route the slug to a controller in the most efficient way.
At first I was checking if slug exist in a table, if slug exist use the route:
$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']]
);
}
The problem being that I have multiple checks like the one above and each one is being ran even if a route prior matches (doesn't need to be ran so extra querys are being called).
So how can I add a conditional statement of some sort so that it only checks if the route matches if none of the prior ones have.
I'd suggest to go for a custom route class that handles this. While you could query the data in your routes files, this is
- not overly test friendly
- not very DRY
- not safe for reverse routing
The latter point means that when not connecting all routes, trying to generate a URL from a route array for a non-connected route might trigger an exception, or match the wrong route.
With a custom route class you could simply pass the model in the options when connecting the routes, and in the route class after parsing the URL, query that model for the given slug, and return false
or the parsed data accordingly.It's really simple, just have a look at what the existing route classes do.
Here's a very basic example which should be pretty self-explantory.
src/Routing/Route/SlugRoute.php
namespace AppRoutingRoute;
use CakeRoutingRouteRoute;
use CakeORMLocatorLocatorAwareTrait;
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;
}
}
This example assumes that in the controller, you'd use the slug to retrieve the record. If you'd wanted to have the ID passed, then instead of using count()
, you could fetch the ID and pass it along in the parsed data, like:
$params['pass'][] = $id;
It would then end up being passed as the second argument of the controller action.
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'
]
);
// ...
This would first check the Articles
model, then the Categories
model, etc., and stop once one of the routes finds a record for the given slug.
See also
- Cookbook > Routing > Custom Route Classes
- API > CakeRoutingRoute::parse()
- Source > CakeRoutingRoute
這篇關(guān)于在路由中從數(shù)據(jù)庫映射 slug的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網(wǎng)!