CakePHP:带有可选参数的路由

时间:2016-06-11 08:14:41

标签: cakephp routes cakephp-3.0

假设我有这个动作:

public function index_by_date($year = NULL, $month = NULL, $day = NULL) {
    if($year && $month && $day) {
        //Posts of day
    }
    elseif($year && $month) {
        //Posts of month
    }
    elseif($year) {
        //Posts of year
    }
    else {
        //Exception...
    }
}

这显示了一天的帖子(06/11/2016的所有帖子):
mysite.com/posts/2016/06/11

这显示整个月的帖子(2016年6月的所有帖子):
mysite.com/posts/2016/06

这显示了一整年的帖子(2016年的所有帖子):
mysite.com/posts/2016

我认为这很清楚。

现在我想为所有这些案例写一条路线。然后,第二个和第三个参数应该是可选的。

我尝试过类似的东西,但这不起作用:

$routes->connect('/posts/:year/:month/:day',
    ['controller' => 'Posts', 'action' => 'index_by_date'], 
    [
        'year'  => '[12][0-9]{3}',
        'month' => '(0[1-9]|1[012])?',
        'day'   => '(0[1-9]|[12][0-9]|3[01])?',
        'pass'  => ['year', 'month', 'day']
    ]
);

怎么办?

修改 现在,这是我能做的最好的事情:

路线:

$routes->connect('/posts/:date',
    ['controller' => 'Posts', 'action' => 'index_by_date'], 
    [
        'date' => '\d{4}(\/\d{2}(\/\d{2})?)?',
        'pass' => ['date']
    ]
);

动作:

public function index_by_date($date = NULL) {
    list($year, $month, $day) = array_merge(explode('/', $date), [NULL, NULL, NULL]);

    if($year && $month && $day) {
        //Posts of day
    }
    elseif($year && $month) {
        //Posts of month
    }
    elseif($year) {
        //Posts of year
    }
    else {
        //Exception...
    }
}

有更好的方法吗?

2 个答案:

答案 0 :(得分:1)

在CakePHP 3中,您可以将'_name'选项添加到$ routes-> connect()并分隔三个不同的路由。

完整日期,routes.php:

$routes->connect('/posts/:year/:month/:day',
    ['controller' => 'Posts', 'action' => 'index_by_date'], 
    [
        'year'  => '[12][0-9]{3}',
        'month' => '(0[1-9]|1[012])?',
        'day'   => '(0[1-9]|[12][0-9]|3[01])?',
        'pass'  => ['year', 'month', 'day'],
        '_name' => 'postsFull'
    ]
);

用法(通话中):

['_name' => 'postsFull', 'year' => '2016', 'month' => '06', 'day' => '11']

每年的月份,routes.php:

$routes->connect('/posts/:year/:month',
    ['controller' => 'Posts', 'action' => 'index_by_date'], 
    [
        'year'  => '[12][0-9]{3}',
        'month' => '(0[1-9]|1[012])?',
        'pass'  => ['year', 'month'],
        '_name' => 'postsYearMonth'
    ]
);

用法(通话中):

['_name' => 'postsYearMonth', 'year' => '2016', 'month' => '06']

仅一年,routes.php:

$routes->connect('/posts/:year',
    ['controller' => 'Posts', 'action' => 'index_by_date'], 
    [
        'year'  => '[12][0-9]{3}',
        'pass'  => ['year'],
        '_name' => 'postsYear'
    ]
);

用法(通话中):

['_name' => 'postsYear', 'year' => '2016']

希望对您有帮助。

答案 1 :(得分:0)

不支持

最好放弃将这个定义为具有单独路由元素的单一路由的想法,路由编译器/匹配器既不支持也不支持,也不支持反向路由。

虽然您可以创建支持此功能的自定义路由类,但您必须完全重新实现路由编译,并且实现反向路由支持可能同样有趣 - 我个人认为这不值得麻烦。< / p>

使用多个路由或自定义路由类

所以要么写多个路由,这是我最有可能做的,要么像你在这里一样使用参数解析解决方案。

当选择后者时,最好在自定义路线类中完成,以使事情更加干燥。但是,在构建URL时,您仍需要将日期部分作为单个字符串传递,除非您还实现了一些自定义匹配,例如检查yearmonthday个密钥存在,并构建一个新的URL数组以匹配。

我现在有一点空闲时间,这样的解析/匹配通常很有趣,所以这里有一个快速的&amp;肮脏的例子:

<?php
namespace App\Routing\Route;

use Cake\Routing\Route\Route;

class CompactDateRoute extends Route
{
    public function parse($url)
    {
        $params = parent::parse($url);
        if (!$params) {
            return false;
        }

        $params['pass'] = explode('/', $params['pass'][0]) + [null, null, null];

        return $params;
    }

    public function match(array $url, array $context = [])
    {
        $dateParts = [];
        foreach (['year', 'month', 'day'] as $key) {
            if (!isset($url[$key])) {
                break;
            }
            $dateParts[] = $url[$key];
            unset($url[$key]);
        }
        if ($dateParts) {
            $date = implode('/', $dateParts);
            return parent::match($url + compact('date'), $context);
        }

        return false;
    }
}

解析部分应该是非常自我解释的,它只是将单个日期字符串解析为单独的参数,然后将其作为单独的参数传递给您的控制器操作。请注意+而不是array_merge()的使用,后者会追加 null值!

匹配部分也不过分复杂,它只是检查URL数组中的日期部分键,从中构建一个紧凑的日期值(year/month?/day?),它可以匹配您的路线,并与之匹配,即从像

这样的数组构建URL
[
    'controller' => 'Posts',
    'action' => 'index_by_date',
    'year' => '2014',
    'month' => '12',
    'day' => '04'
]

基本上与

相等
[
    'controller' => 'Posts',
    'action' => 'index_by_date',
    'date' => '2014/12/04'
]

然而,所有这一切,只是因为能够用一条路线来定义它?再说一次,我不确定这是值得的麻烦。

另见