Mapping slugs from database in routing

徘徊边缘 提交于 2019-11-29 18:17:24
ndm

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 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;
    }
}

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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!