ZF2: One Route to rule them all

classic Classic list List threaded Threaded
11 messages Options
Reply | Threaded
Open this post in threaded view
|

ZF2: One Route to rule them all

Ralf Eggert
Hi again,

when I work through the user guide of the manual I noticed the different
ways of handling the routing.

In the /module/Application/config/module.config.php there are two routes
defined:

-----------------------------------------------------------------
'home' => array(
    'type' => 'Zend\Mvc\Router\Http\Literal',
    'options' => array(
        'route'    => '/',
        'defaults' => array(
            'controller' => 'Application\Controller\Index',
            'action'     => 'index',
        ),
    ),
),
'application' => array(
    'type'    => 'Literal',
    'options' => array(
        'route'    => '/application',
        'defaults' => array(
            '__NAMESPACE__' => 'Application\Controller',
            'controller'    => 'Index',
            'action'        => 'index',
        ),
    ),
    'may_terminate' => true,
    'child_routes' => array(
        'default' => array(
            'type'    => 'Segment',
            'options' => array(
                'route'    => '/[:controller[/:action]]',
                'constraints' => array(
                    'controller' => '[a-zA-Z][a-zA-Z0-9_-]*',
                    'action'     => '[a-zA-Z][a-zA-Z0-9_-]*',
                ),
                'defaults' => array(
                ),
            ),
        ),
    ),
),
-----------------------------------------------------------------

After creating my Album module I have another route:

-----------------------------------------------------------------
'album' => array(
    'type'    => 'segment',
    'options' => array(
        'route'    => '/album[/:action][/:id]',
        'constraints' => array(
            'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
            'id'     => '[0-9]+',
        ),
        'defaults' => array(
            'controller' => 'Album\Controller\Album',
            'action'     => 'index',
        ),
    ),
),
-----------------------------------------------------------------

Within ZF1 I really liked the convention to have the One Route to rule
them all.

I would like to do the same for my ZF2 application but have some
problems with it. A rule should look like this

/lang/module/controller/action/id/*

First is the language, followed by the module, the controller, the
action and an id. At the end I would other optional parameters to be
addable. The route should have some reasonable defaults and alle
segments should be optional. The reason for this is, that I don't want
to think about the routing any more for each module once I set the
standard route.

I tried aroud a little and thought that this might work, but it doesn't
even match a route with only a language in it.

-----------------------------------------------------------------
'application' => array(
    'type'    => 'Segment',
    'options' => array(
        'route'    => '[/:lang][/:module][/:controller][/:action]
                       [/:id]',
        'defaults' => array(
            'lang'          => 'de',
            'module'        => 'Application',
            'controller'    => 'Index',
            'action'        => 'index',
        ),
        'constraints' => array(
            'lang'       => '[a-z]{2}',
            'module'     => '[a-zA-Z][a-zA-Z0-9_-]*',
            'controller' => '[a-zA-Z][a-zA-Z0-9_-]*',
            'action'     => '[a-zA-Z][a-zA-Z0-9_-]*',
            'id'         => '[0-9]+',
        ),
    ),
),
-----------------------------------------------------------------

Any ideas?

Best regards,

Ralf

--
List: [hidden email]
Info: http://framework.zend.com/archives
Unsubscribe: [hidden email]


Reply | Threaded
Open this post in threaded view
|

Re: ZF2: One Route to rule them all

cstrosser
This post has NOT been accepted by the mailing list yet.
Hi Ralf,

You aren't alone. I've been trying to understand how to do this for quite sometime, but haven't been able to get a concrete answer or find a proper example.  I also valued the ease with which a global routing structure could be implemented in ZF1.

Chris
Reply | Threaded
Open this post in threaded view
|

Re: ZF2: One Route to rule them all

Marco Pivetta
I think you can use a wildcard route ( https://github.com/zendframework/zf2/blob/master/library/Zend/Mvc/Router/Http/Wildcard.php ) as a child route of `/lang/module/controller/action/id/`.

Marco Pivetta

http://twitter.com/Ocramius     

http://marco-pivetta.com    



On 21 August 2012 07:01, cstrosser [via Zend Framework Community] <[hidden email]> wrote:
Hi Ralf,

You aren't alone. I've been trying to understand how to do this for quite sometime, but haven't been able to get a concrete answer or find a proper example.  I also valued the ease with which a global routing structure could be implemented in ZF1.

Chris


If you reply to this email, your message will be added to the discussion below:
http://zend-framework-community.634137.n4.nabble.com/ZF2-One-Route-to-rule-them-all-tp4656357p4656376.html
To unsubscribe from Zend Framework Community, click here.
NAML

Reply | Threaded
Open this post in threaded view
|

Re: ZF2: One Route to rule them all

Ralf Eggert
Hi Marco,

> I think you can use a wildcard route (
> https://github.com/zendframework/zf2/blob/master/library/Zend/Mvc/Router/Http/Wildcard.php)
> as a child route of `/lang/module/controller/action/id/`.

thanks for the hint, do you have an example? Or did I use it right here:

------------------------------------------------------------------------
'application' => array(
    'type'    => 'segment',
    'options' => array(
        'route'    => '/[:lang][/:module][/:controller][/:action][/:id]',
        'constraints' => array(
            'lang'       => '[a-z]{2}',
            'module'     => '[a-zA-Z][a-zA-Z0-9_-]*',
            'controller' => '[a-zA-Z][a-zA-Z0-9_-]*',
            'action'     => '[a-zA-Z][a-zA-Z0-9_-]*',
            'id'         => '[0-9]+',
        ),
        'defaults' => array(
            'module'     => 'Application',
            'controller' => 'Application\Controller\Index',
            'action'     => 'index',
            'lang'       => 'de',
        ),
    ),
    'may_terminate' => true,
    'child_routes' => array(
        'default' => array(
            'type'    => 'Wildcard',
            'options' => array(
            ),
        ),
    ),
),
------------------------------------------------------------------------

Anyway my route only works for these urls:

  /de
  /de/application

When I add the controller part

  /de/application/index

I get an exception

  The requested controller could not be mapped to an existing
  controller class.
  Controller:
      index(resolves to invalid controller class or alias: index)

I can fix this issue, when I change the controller definitions to:

    'controllers' => array(
        'invokables' => array(
            'index' => 'Application\Controller\IndexController'
        ),
    ),

But this would mean, that only my Application module can have an
IndexController. Ok, maybe I could live with it somehow.

But when it comes to the routing for a module, it fails.

  /de/user
  /de/news

It always roots to the Application module but not to the expected modules.

Does anyone have any idea for automated module detection?

Regards,

Ralf

--
List: [hidden email]
Info: http://framework.zend.com/archives
Unsubscribe: [hidden email]


Reply | Threaded
Open this post in threaded view
|

Re: ZF2: One Route to rule them all

Marco Pivetta
I think the route cannot be matched in such case, since it hasn't an ending
delimiter. Your parts should not be optional for such a route to work.

Marco Pivetta

http://twitter.com/Ocramius

http://marco-pivetta.com



On 21 August 2012 12:17, Ralf Eggert <[hidden email]> wrote:

> Hi Marco,
>
> > I think you can use a wildcard route (
> >
> https://github.com/zendframework/zf2/blob/master/library/Zend/Mvc/Router/Http/Wildcard.php
> )
> > as a child route of `/lang/module/controller/action/id/`.
>
> thanks for the hint, do you have an example? Or did I use it right here:
>
> ------------------------------------------------------------------------
> 'application' => array(
>     'type'    => 'segment',
>     'options' => array(
>         'route'    => '/[:lang][/:module][/:controller][/:action][/:id]',
>         'constraints' => array(
>             'lang'       => '[a-z]{2}',
>             'module'     => '[a-zA-Z][a-zA-Z0-9_-]*',
>             'controller' => '[a-zA-Z][a-zA-Z0-9_-]*',
>             'action'     => '[a-zA-Z][a-zA-Z0-9_-]*',
>             'id'         => '[0-9]+',
>         ),
>         'defaults' => array(
>             'module'     => 'Application',
>             'controller' => 'Application\Controller\Index',
>             'action'     => 'index',
>             'lang'       => 'de',
>         ),
>     ),
>     'may_terminate' => true,
>     'child_routes' => array(
>         'default' => array(
>             'type'    => 'Wildcard',
>             'options' => array(
>             ),
>         ),
>     ),
> ),
> ------------------------------------------------------------------------
>
> Anyway my route only works for these urls:
>
>   /de
>   /de/application
>
> When I add the controller part
>
>   /de/application/index
>
> I get an exception
>
>   The requested controller could not be mapped to an existing
>   controller class.
>   Controller:
>       index(resolves to invalid controller class or alias: index)
>
> I can fix this issue, when I change the controller definitions to:
>
>     'controllers' => array(
>         'invokables' => array(
>             'index' => 'Application\Controller\IndexController'
>         ),
>     ),
>
> But this would mean, that only my Application module can have an
> IndexController. Ok, maybe I could live with it somehow.
>
> But when it comes to the routing for a module, it fails.
>
>   /de/user
>   /de/news
>
> It always roots to the Application module but not to the expected modules.
>
> Does anyone have any idea for automated module detection?
>
> Regards,
>
> Ralf
>
> --
> List: [hidden email]
> Info: http://framework.zend.com/archives
> Unsubscribe: [hidden email]
>
>
>
Reply | Threaded
Open this post in threaded view
|

Re: ZF2: One Route to rule them all

Ralf Eggert
Hi Marco,

I got an solution now. This is my route in
/module/Application/config/module.config.php:

------------------------------------------------------------------------
'application' => array(
    'type'    => 'segment',
    'options' => array(
        'route'    => '/[:lang][/:controller][/:action][/:id]',
        'constraints' => array(
            'lang'       => '[a-z]{2}',
            'controller' => '[a-zA-Z][a-zA-Z0-9_-]*',
            'action'     => '[a-zA-Z][a-zA-Z0-9_-]*',
            'id'         => '[0-9]+',
        ),
        'defaults' => array(
            'controller' => 'Application',
            'action'     => 'index',
            'lang'       => 'de',
        ),
    ),
    'may_terminate' => true,
    'child_routes' => array(
        'default' => array(
            'type'    => 'Wildcard',
            'options' => array(
            ),
        ),
    ),
),
------------------------------------------------------------------------

Then I have a couple of controllers -> invokables definitions in my
Application module and two other modules User and News:


------------------------------------------------------------------------
'controllers' => array(
    'invokables' => array(
        'Application' => 'Application\Controller\IndexController',
    ),
),

'controllers' => array(
    'invokables' => array(
        'User' => 'User\Controller\UserController',
        'UserAdmin' => 'User\Controller\AdminController',
    ),
),

'controllers' => array(
    'invokables' => array(
        'News' => 'News\Controller\NewsController',
        'NewsAdmin' => 'News\Controller\AdminController',
    ),
),
------------------------------------------------------------------------

Now al these routes work as expected:

/
/de
/de/application
/de/application/index/7
/de/application/index/7/foo/bar
/de/news
/de/news/add
/de/news/index/7/foo/bar
/de/user
/de/user-admin/index/7/foo/bar

The only problem I have is connected to the Url() View Helper. This call

   $this->url('application', array('lang' => 'de', 'action'=>'add',
                                   'controller' => 'user'))

results in an output of

   //user/add

and not as expected

   /de/user/add

What is going wrong here?

Regards,

Ralf

--
List: [hidden email]
Info: http://framework.zend.com/archives
Unsubscribe: [hidden email]


Reply | Threaded
Open this post in threaded view
|

Re: ZF2: One Route to rule them all

Ralf Eggert
Hi again,

> The only problem I have is connected to the Url() View Helper. This call
>
>    $this->url('application', array('lang' => 'de', 'action'=>'add',
>                                    'controller' => 'user'))

the same issue occurs with the Url controller plugin. Somehow the 'lang'
segment is not filled properly:

Within an action controller this call

  $this->url()->fromRoute(
      'application',
      array('action' => 'add', 'controller' => 'user', 'lang' => 'de')
  );

returns just

  //user/add

and not

  /de/user/add

I wonder if this is a bug or a feature I don't understand.

Regards,

Ralf

--
List: [hidden email]
Info: http://framework.zend.com/archives
Unsubscribe: [hidden email]


Reply | Threaded
Open this post in threaded view
|

Re: ZF2: One Route to rule them all

weierophinney
Administrator
-- Ralf Eggert <[hidden email]> wrote
(on Tuesday, 21 August 2012, 11:33 PM +0200):

> > The only problem I have is connected to the Url() View Helper. This call
> >
> >    $this->url('application', array('lang' => 'de', 'action'=>'add',
> >                                    'controller' => 'user'))
>
> the same issue occurs with the Url controller plugin. Somehow the 'lang'
> segment is not filled properly:
>
> Within an action controller this call
>
>   $this->url()->fromRoute(
>       'application',
>       array('action' => 'add', 'controller' => 'user', 'lang' => 'de')
>   );
>
> returns just
>
>   //user/add
>
> and not
>
>   /de/user/add
>
> I wonder if this is a bug or a feature I don't understand.

This looks like a bug. Can you post an issue, and include the route
you're defining as part of it? I'll see if either myself or Ben can
track it down.

--
Matthew Weier O'Phinney
Project Lead            | [hidden email]
Zend Framework          | http://framework.zend.com/
PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc

--
List: [hidden email]
Info: http://framework.zend.com/archives
Unsubscribe: [hidden email]


Reply | Threaded
Open this post in threaded view
|

Re: ZF2: One Route to rule them all

Ralf Eggert
Hi Matthew,

> This looks like a bug. Can you post an issue, and include the route
> you're defining as part of it? I'll see if either myself or Ben can
> track it down.

Done:

http://framework.zend.com/issues/browse/ZF2-486

I found out, that deleting the default for the locale does the job.
Maybe that helps to track the error down.

Regards,

Ralf

--
List: [hidden email]
Info: http://framework.zend.com/archives
Unsubscribe: [hidden email]


MGP
Reply | Threaded
Open this post in threaded view
|

Re: ZF2: One Route to rule them all

MGP
This post has NOT been accepted by the mailing list yet.
This post was updated on .
In reply to this post by Ralf Eggert
What about if you want to access "/application/index/7" , how would you pass the default language, in this case "de"? The route breaks if the first param isn't a type [a-z]{2}..

EDIT:

One method is to constrain 'lang' => '(en|de)'; but that don't give you much freedom since you got to define all the permitted locales..
Reply | Threaded
Open this post in threaded view
|

Re: ZF2: One Route to rule them all

sinimix
This post has NOT been accepted by the mailing list yet.
In reply to this post by Ralf Eggert
Ralf,

You mentioned in the first post that the route should be lang/module/controller/action/id, but following your current configuration the route is actually lang/controller/action/id (without module).

So, what will happen if you have two or more controllers with the same name in two different modules? For example, if the User module contains News controller, that already exists in the News module, will /de/news refer to news controller in News or User module?

I think that your route should also include [:/module] part in order to avoid controllers' ambiguity. Or I've missed something?