In a previous post I explained a basic approach to make URLs more SEO-friendly. That approach works well if URLs can contain the controller name. But sometimes that's not desirable. What's needed then is a Catch All Route.
Again the CUrlManager shines with it's flexibility: Creating a last resort, catch all route is as simple as adding the following to your routes in protected/config/main.php
:
<?php //... 'rules'=>array( '<controller:[\w-]+>/<id:\d+>'=>'<controller>/view', '<controller:[\w-]+>/<action:[\w-]+>/<id:\d+>'=>'<controller>/<action>', '<controller:[\w-]+>/<action:[\w-]+>'=>'<controller>/<action>', // the last resort, catch all route '<slug:.*>' => 'slug/index', ), //...
This new route matches all requests(!). Since the routes are processed in the order they appear inside the config, it will only be reached if no other route matched. In the example, it takes the complete request path, puts it inside the slug
parameter and passes that to the SlugController::actionIndex
method.
<?php class SlugController extends Controller { public function actionIndex($slug) { // do something useful } }
Assuming the model Product
from the previous post - with a table column slug
containing a urlized string based on the product name - here is what I would do in SlugController::actionIndex
<?php class SlugController extends Controller { public function actionIndex($slug) { // try to find a product with $slug and redirect to it if ($model = Product::model()->findByAttributes(array('slug'=>$slug))) $this->redirect(array('product/view') + $model->routeParams); throw new CHttpException(404, 'Page not found.'); } }
SlugController::actionIndex
now tries to find and redirect to a product with a matching slug. If you had more models with slugs - say a model Category
, you could extend the action to also check for these.
<?php class SlugController extends Controller { public function actionIndex($slug) { // try to find a product with $slug and redirect to it if ($model = Product::model()->findByAttributes(array('slug'=>$slug))) $this->redirect(array('product/view') + $model->routeParams); // no product? // try to find a category with $slug and redirect to it if ($model = Category::model()->findByAttributes(array('slug'=>$slug))) $this->redirect(array('category/view') + $model->routeParams); throw new CHttpException(404, 'Page not found.'); } }
It's a really quick and simple approach, but not something I would do if I had more than 10 such models with slugs. Let's say there were 10 models I wanted to check, each having 100000 entries in the database, in the worst case this would end up scanning 1000000 entries! Creating a unique index on the slug
columns would help, but a different approach should be used here. (To be continued)
No comments:
Post a Comment