Search code examples
phpregexzend-frameworkzend-route

Zend route regex, optional parameters


I need some help about my routes in Zend (Zend1) ...

I need that I can use my route like that :

http://mywebsite.com/myfolder/region/job/   
http://mywebsite.com/myfolder/region/job/add  
http://mywebsite.com/myfolder/region/job/add/page  
http://mywebsite.com/myfolder/region/job/page   

Parameters add and page are optional ...

This is what I did

$route = new Zend_Controller_Router_Route_Regex(
          'myfolder/([^/]+)/([^/]+)/?([^/]+)?/?([0-9]+)?',
          array('controller'    => 'myfolder','action'  => 'search'), 
          array(1 => 'region',2 => 'job', 3 => 'add', 4 => 'page'), 
          'myfolder/%s/%s/%s/%s'
         ); 

Obviously, it doesn't work ... What I want? I want that last the two parameters (add and page) are optional ...

Can you help me? what's wrong with my regex?

EDIT 1:

Ok, so I tried it, but isn't ok ...
I need that parameters add and page are optional ...

    $route = new Zend_Controller_Router_Route(
'myfolder/:region/:job/:add/:page',
array(
    'controller'    => 'myfolder',
    'action'        => 'search',
    'region'        => 'XX',
    'job'        => '',
    'add'    => '',
    'page'      => 1
),
array(
    'region' => '[a-zA-Z-_0-9-]+',
    'job' => '[a-zA-Z-_0-9-]+',
    'add' => '[a-zA-Z-_]+',
    'page' => '\d+'
)
);  

With that, this one http://mywebsite.com/myfolder/region/job/page doesn't work ...

EDIT 2:

I also tried with 'myfolder/:region/:job/*', but the result is same, doesn't work as I want ...
I really wonder if it is possible ...

EDIT 3:##

$route = new Zend_Controller_Router_Route_Regex('myfolder/([^/]+)/([^/]+)(?:/|$)(?:(?!\d+(?:/|$))([^/]+)(?:/|$))?(?:(\d+)(?:/|$))?$',
      array('controller'    => 'myfolder', 'action' => 'recherche', 'presta' => ''), 
      array(1 => 'region',2 => 'job', 3 => 'presta', 4 => 'page'), 
      'myfolder/%s/%s/%s/%s');

Solution

  • Prepare yourself.

    The RegEx

    myfolder/([^/]+)/([^/]+)(?:/|$)(?:(?!\d+(?:/|$))([^/]+)(?:/|$))?(?:(\d+)(?:/|$))?$
    

    See it working on RegExr (on RegExr, I had to add \n\r to one of the negated classes so it didn't match all my line breaks, in practice you probably won't be dealing with line breaks though.)

    The important thing to note on RegExr is that in the 4th case, the page number is in the 4th capture group, with nothing in the 3rd group.

    Explanation

    myfolder/([^/]+)/([^/]+) All looking good up to here, no changes yet.

    (?:/|$) Match a / or end of input.

    Next, overall we have a non-capturing group that is optional. This would be the add section.

    (?:(?!\d+(?:\|$))([^/]+)(?:/|$))?

    Now lets break it down further:

    (?!\d+(?:/|$)) Make sure its not a page number - digits only followed by / or end of input. (Negative lookahead)

    ([^/]+) Our capture group - add in the example.

    (?:/|$) Match a / or end of input.

    Now for our page number group, again it's optional and non-capturing:

    (?:(\d+)(?:/|$))? Captures the numbers, then matches / or end of input again.

    $ And just in case it tries to match substrings of actual matches, I threw in another end of input anchor (since you can match as many in a row as you like), although the regex functions without it.


    Generating The Path

    What you basically want is a way of doing this:

    At the moment the 2nd and 3rd parameters are:

    array(1 => 'region',2 => 'job', 3 => 'add', 4 => 'page'), 
    'myfolder/%s/%s/%s/%s'
    

    You want them to be something like:

    array(1 => 'region',2 => 'job', 3 => '/'+'add', 4 => '/'+'page'), 
    'myfolder/%s/%s%s%s'
    

    Where you only add the / if the optional group is present. The code above won't work but perhaps there is some way you could implement that.