CakePHP: route with optional parameters -
suppose have action:
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... } }
this shows posts of single day (all posts of 06/11/2016):
mysite.com/posts/2016/06/11
this shows post of full month (all posts of june 2016):
mysite.com/posts/2016/06
this shows post of full year (all posts of 2016):
mysite.com/posts/2016
i think clear.
now want write single route these cases. then, second , third parameter should optional.
i have tried this, not work:
$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'] ] );
how do?
edit now, best do:
route:
$routes->connect('/posts/:date', ['controller' => 'posts', 'action' => 'index_by_date'], [ 'date' => '\d{4}(\/\d{2}(\/\d{2})?)?', 'pass' => ['date'] ] );
action:
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... } }
is there better way this?
not supported
better drop idea of defining single route separate route elements, that's not supported, neither route compiler/matcher, nor reverse routing.
while create custom route class supports this, you'd have reimplement route compiling, , implementing reverse routing support fun - don't think it's worth hassle.
use multiple routes, or custom route classes
so either write multiple routes, i'd do, or go parameter parsing solution showing here.
when chosing latter, should better done in custom route class make things more dry. there still disadvantage you'd need pass date parts single string when building urls, unless you'd implement custom matching, checking whether year
, month
, day
keys present, , build new url array match against.
i have little free time right now, , such parsing/matching might interesting, here's quick & dirty example:
<?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'] $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; } }
the parsing part should pretty self-explantory, parses single date string separate parameters, passed controller action separate arguments. note usage of +
instead of array_merge()
, latter append null
values!
the matching part isn't overly complicated, checks date part keys in url array, builds compact date value (year/month?/day?
) match route, , matches against that, ie building url array like
[ 'controller' => 'posts', 'action' => 'index_by_date', 'year' => '2014', 'month' => '12', 'day' => '04' ]
will equal matching against
[ 'controller' => 'posts', 'action' => 'index_by_date', 'date' => '2014/12/04' ]
however, that, being able define single route? again, i'm not sure it's worth hassle.
Comments
Post a Comment