#444: Use matched incoming parameters for same route generation on a hierarchial
basis
---------------------+------------------------------------------------------
 Reporter:  david    |        Owner:  dominik  
     Type:  task     |       Status:  new      
 Priority:  highest  |    Milestone:  0.11     
Component:  routing  |      Version:  0.11.0RC2
 Severity:  blocker  |   Resolution:           
 Keywords:           |  
---------------------+------------------------------------------------------
Old description:

> [1714] implemented #434 which disabled the use of incoming request params
> as defaults when generating the same URL as the one that matched in the
> request.
>
> Well, turns out that was not such a good idea after all. While the issue
> described there is fixed, the new behavior might not be the most
> convenient or even intuitive.
>
> Never thought I'd say this, but we can copy an idea from Ruby on Rails
> here: assuming a hierarchy of values.
>
> The basic idea is that when generating a route, and that route was the
> one that matched during the current request, then the incoming values are
> used as defaults, but only those '''to the left of the leftmost supplied
> parameter'''. Example:
> {{{
> <route name="search" pattern="^/search/(term:[^/]+)/(page:\d+)/$">
>   <default for="page">1</default>
> </route>
> }}}
> Let's assume our incoming url is {{{/search/agavi/13/}}}, then
>
>  * {{{$ro->gen('search', array('page' => 14));}}} would produce
> {{{/search/agavi/14/}}}
>  * {{{$ro->gen('search', array('term' => 'snoopy'));}}} would produce
> {{{/search/snoopy/1/}}}
>
> That is fairly simple then. However, if we change our route example a
> little bit so the page is optional:
> {{{
> <route name="search" pattern="^/search/(term:[^/]+)/({page:\d+}/)?$">
>   <default for="page">1</default>
> </route>
> }}}
> then {{{$ro->gen('search', array('term' => 'snoopy'));}}} could produce
> two things:
>
>  * {{{/search/snoopy/1/}}}
>  * {{{/search/snoopy/}}}
>
> The issue here is the default. Without it, things would be simple. The
> page would be simply omitted. The problem is that you _want_ the page
> default because you then get a value in your action even if none was
> supplied in the URL.
>
> Rails doesn't have this problem. There, all URL chunks count as optional,
> and the {{{/}}} is "hardcoded" as a delimiter, they have no inline
> regular expressions. We have these, it's not as simple in our case, and
> we have to find a decent solution.
>
> One thing to keep in mind here is that it should still be optional to
> "skip" parameters by passing {{{null}}} for it in the gen() call (e.g.
> {{{$ro->gen('lala', array('foo' => null));}}}). Right now, this always
> omits the respective parameter portion in the pattern entirely. I have
> thought of the following change for when {{{null}}} is passed:
>
>  * If the parameter is an optional pattern, it is skipped entirely and
> not present in the route
>  * If the parameter is not an optional pattern, the default for the
> parameter is used
>
> We could establish that all parameters to the right side of the last
> portion of the pattern for which a parameter was given are assumed to be
> {{{null}}}. Applying that to Snoopy's problem outlined above, the result
> would be {{{/search/snoopy/}}} because the page parameter is optional and
> the default is not used.
>
> ----
>
> Also, something regarding parent routes. If we have this (yes, the actual
> date patterns would have to be more complex):
> {{{
> <route name="blog" pattern="^/blog/(name:[^/]+)" module="Blog">
>   <route name=".index" pattern="^/$" action="Index" />
>   <route name=".entry" pattern="^/(id:\d+).html$" action="Entry" />
>   <route name=".archive"
> pattern="^/(year:20\d{2})/({month:\d{2}/)?({day:\d{2}/)?"
> action="Archive">
>     <default for="year">2007</default>
>   </route>
> </route>
> <route name="profile" pattern="^/profile/(name:[^/]+)/$" ... />
> }}}
> And our current URL is {{{/blog/woodstock/2006/07/13}}}, then:
>
>  * {{{$ro->gen('blog.archive', array('month' => 11));}}} produces
> {{{/blog/woodstock/2006/11/}}}
>  * {{{$ro->gen('blog.entry', array('id' => 22));}}} produces
> {{{/blog/woodstock/22.html}}}
>  * {{{$ro->gen('blog.archive', array('name' => 'snoopy'));}}} produces
> {{{/blog/snoopy/2007/}}}
>
> That means parent routes are included in these rules, too.
>
> One thing I should mention: Rails doesn't require named routes. I'm not
> quite sure, but I ''think'' that in Rails {{{$ro->gen('profile');}}}
> would produce {{{/profile/woodstock/}}} whereas in Agavi you'd end up
> with {{{/profile//}}} since no {{{name}}} would be implied because the
> {{{profile}}} route isn't on "the way upwards". All of the above would
> only work for those generated routes that also matched during the current
> request.
>
> ----
>
> Is that all okay like that? Anything missing? Questions? Comments?

New description:

 [1714] implemented #434 which disabled the use of incoming request params
 as defaults when generating the same URL as the one that matched in the
 request.

 Well, turns out that was not such a good idea after all. While the issue
 described there is fixed, the new behavior might not be the most
 convenient or even intuitive.

 Never thought I'd say this, but we can copy an idea from Ruby on Rails
 here: assuming a hierarchy of values.

 The basic idea is that when generating a route, and that route was the one
 that matched during the current request, then the incoming values are used
 as defaults, but only those '''to the left of the leftmost supplied
 parameter'''. Example:
 {{{
 <route name="search" pattern="^/search/(term:[^/]+)/(page:\d+)/$">
   <default for="page">1</default>
 </route>
 }}}
 Let's assume our incoming url is {{{/search/agavi/13/}}}, then

  * {{{$ro->gen('search', array('page' => 14));}}} would produce
 {{{/search/agavi/14/}}}
  * {{{$ro->gen('search', array('term' => 'snoopy'));}}} would produce
 {{{/search/snoopy/1/}}}

 That is fairly simple then. However, if we change our route example a
 little bit so the page is optional:
 {{{
 <route name="search" pattern="^/search/(term:[^/]+)/({page:\d+}/)?$">
   <default for="page">1</default>
 </route>
 }}}
 then {{{$ro->gen('search', array('term' => 'snoopy'));}}} could produce
 two things:

  * {{{/search/snoopy/1/}}}
  * {{{/search/snoopy/}}}

 The issue here is the default. Without it, things would be simple. The
 page would be simply omitted. The problem is that you ''want'' the page
 default because you then get a value in your action even if none was
 supplied in the URL.

 Rails doesn't have this problem. There, all URL chunks count as optional,
 and the {{{/}}} is "hardcoded" as a delimiter, they have no inline regular
 expressions. We have these, it's not as simple in our case, and we have to
 find a decent solution.

 One thing to keep in mind here is that it should still be optional to
 "skip" parameters by passing {{{null}}} for it in the gen() call (e.g.
 {{{$ro->gen('lala', array('foo' => null));}}}). Right now, this always
 omits the respective parameter portion in the pattern entirely. I have
 thought of the following change for when {{{null}}} is passed:

  * If the parameter is an optional pattern, it is skipped entirely and not
 present in the route
  * If the parameter is not an optional pattern, the default for the
 parameter is used

 We could establish that all parameters to the right side of the last
 portion of the pattern for which a parameter was given are assumed to be
 {{{null}}}. Applying that to Snoopy's problem outlined above, the result
 would be {{{/search/snoopy/}}} because the page parameter is optional and
 the default is not used.

 ----

 Also, something regarding parent routes. If we have this (yes, the actual
 date patterns would have to be more complex):
 {{{
 <route name="blog" pattern="^/blog/(name:[^/]+)" module="Blog">
   <route name=".index" pattern="^/$" action="Index" />
   <route name=".entry" pattern="^/(id:\d+).html$" action="Entry" />
   <route name=".archive"
 pattern="^/(year:20\d{2})/({month:\d{2}/)?({day:\d{2}/)?"
 action="Archive">
     <default for="year">2007</default>
   </route>
 </route>
 <route name="profile" pattern="^/profile/(name:[^/]+)/$" ... />
 }}}
 And our current URL is {{{/blog/woodstock/2006/07/13}}}, then:

  * {{{$ro->gen('blog.archive', array('month' => 11));}}} produces
 {{{/blog/woodstock/2006/11/}}}
  * {{{$ro->gen('blog.entry', array('id' => 22));}}} produces
 {{{/blog/woodstock/22.html}}}
  * {{{$ro->gen('blog.archive', array('name' => 'snoopy'));}}} produces
 {{{/blog/snoopy/2007/}}}

 That means parent routes are included in these rules, too.

 One thing I should mention: Rails doesn't require named routes. I'm not
 quite sure, but I ''think'' that in Rails {{{$ro->gen('profile');}}} would
 produce {{{/profile/woodstock/}}} whereas in Agavi you'd end up with
 {{{/profile//}}} since no {{{name}}} would be implied because the
 {{{profile}}} route isn't on "the way upwards". All of the above would
 only work for those generated routes that also matched during the current
 request.

 ----

 Is that all okay like that? Anything missing? Questions? Comments?

-- 
Ticket URL: <http://trac.agavi.org/ticket/444#comment:3>
Agavi <http://www.agavi.org/>
An MVC Framework for PHP5


_______________________________________________
Agavi Tickets Mailing List
[email protected]
http://lists.agavi.org/mailman/listinfo/tickets

Reply via email to