Human readable URLs with nested categories like /category/subcategory/n-subcategories/article. I'm using CakePHP 2.2.3 and can't find a proper solution for a routing problem. Using 2 Tables:
A very common example I guess. But how do I have to define the router now, to get URL-paths with the nested categories like this:
/categoryname1 (showing category view)
/categoryname1/articlename1 (showing article view)
/categoryname2/articlename2 (showing article view)
/categoryname2/subcategoryname1 (showing category view)
/categoryname2/subcategoryname2/articlename4 (showing article view)
/n-categoryname/././...n-subcategoryname (showing category view)
/n-categoryname/././...n-subcategoryname/n-articlename (article view)
I tried to make all routes fix in the routes.php, but that is not very comfortable and I think there should be a dynamic solution.
I also tried to automatically generate all routes out of category- and article-alias and save them in a separate "routes" database table - it worked, but I don't think it's really necessary to define hunderets of single routes?!
I also tried just to define all the categories fix in router, like
Router::connect(
'/any-category-name',
array('controller' => 'categories', 'action' => 'view', 1)
);
and then for the articles
Router::connect(
'/any-category-name/:slug',
array('controller' => 'articles', 'action' => 'view'),
array('pass' => array('slug'))
);
But with this method, all articles are available in all categories, which isn't a good solution. And I thought about using
Router::connect(
'/:slug', ...
but I don't know how to go on, because there are two different controllers and two different views possible (also I don't know if Pagination will still work in this case and what will happen, if I also want to use more controllers/actions in the installation).
I think it shouldn't be so difficult to get nested urls with two controllers (categories and articles) in Cake?! Thanks for any helpful advise!
I think you have to check for two things:
1) Check the number of categories in the url and
2) Check if the last parameter is a category or an article
Handle both checks within a (dynamic) route may be very difficult. I would suggest to create just one route for all these requests and do the checks for 1) and 2) in a controller.
The route may be something like this:
Router::connect(
'/*',
array('controller' => 'outputs', 'action' => 'index')
);
I called the controller for this route OutputController because this will be the controller that handles the output for all these urls.
class OutputController extends AppController
{
public $uses = array('Article', 'Category');
public function index()
{
// Get n parameters from url (1)
$args = func_get_args();
$last_arg = $args[count($args) - 1];
// Check if this is an article (2)
$article = $this->Article->find('first', array(
'conditions' => array('Article.slug' => $last_arg
));
if (!empty($article)) {
$this->set('article', $article);
$this->render('article');
}
// Check if this is an category (2)
$category = $this->Category->find('first', array(
'conditions' => array('Category.slug' => $last_arg
));
if (!empty($category)) {
$this->set('category', $category);
$this->render('category');
}
// Page not found
if (empty($article) and empty($category)) {
throw new NotFoundException();
}
}
// ...
To display an article, the view 'Output/article.ctp' is used. For a category, CakePHP renders 'Output/category.ctp'. In addition you can use the parameters in $args to fetch all the necessary data for your (sub-) categories.