Mapping slugs from database in routing

后端 未结 1 1459
灰色年华
灰色年华 2020-12-22 08:43

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

相关标签:
1条回答
  • 2020-12-22 09:13

    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

    • Cookbook > Routing > Custom Route Classes
    • API > \Cake\Routing\Route::parse()
    • Source > \Cake\Routing\Route
    0 讨论(0)
提交回复
热议问题