Skip optional Route-Params

Hi,

I want to set up different languages for my application. Therefore I configured my routes like this:

[
    'type'          => 'segment',
    'options'       => [
        'route'       => '[/:lang]/',
        'defaults'    => [],
        'constraints' => [
             'lang' => '(de|en|fr)'
        ]
    ],
    'may_terminate' => true,
    'child_routes'  => [ ... ]
]

All other routes are child-routes.

My expectations are like this:

default language (de)
/foo
/bar

english
/en/foo
/en/bar

french
/fr/foo
/fr/bar

Unfortunately the current configuration throws an exception: InvalidArgumentException: Missing parameter “lang” on generating the urls. If I set a default [‘lang’ => ‘de’] all generated links always have the default-language as part of the url

/de/foo
/de/bar

Is there a way around that?

I assume you are using laminas-view Url Helper? One way could be to override the url generation method.

Redirects have the same issue. It is more a routing-problem, I guess.

Customizing the routers assemble()-method would be my idea, but I don’t see how the router could be overwritten. Is there a way?

I found the same issue was mentioned before:

But the provided solution doesen’t work anymore. Is there really no out-of-the-box-solution for that? As it is a pretty common SEO-requirement.

Omitting the url-segment, if it matches the default-value would be in my opinion the expected behaviour. Because currently, it is not really optional - it is required (at least for the assambly).

The reason given for that behaviour (Segment route always renders optional params if route has childs · Issue #6697 · zendframework/zendframework · GitHub) is maybe better solved by adding constraints or redesigning the url concept.

You convinced me, overriding \Laminas\View\Helper\Url and \Laminas\Mvc\Controller\Plugin\Url seems to be the way to go. Thanks!

1 Like

Please check the following library:

It works as you need if you use optional constraint for lang.
The following is adapted from my route config that I use for multilingual web applications.

'routes' => [
    'home' => [
        'type'    => Segment::class,
        'options' => [
            'route'    => '[/:lang]/',
            'constraints' => [
                'lang' => '[de|en|fr]*'
            ],
            'defaults' => [
                'lang' => 'de',
                'controller' => Controller\IndexController::class,
                'action'     => 'index',
            ],
        ],
        'may_terminate' => true,
        'child_routes' => [
            'test' => [
                'type' => Literal::class,
                'options' => [
                    'route' => 'test/',
                    'defaults' => [
                        'controller' => Controller\IndexController::class,
                        'action' => 'test',
                    ],
                ],
            ],
            'test-segment' => [
                'type' => Segment::class,
                'options' => [
                    'route' => ':action/[:id/]',
                    'constraints' => [
                        'id' => '[0-9]+',
                    ],
                    'defaults' => [
                        'controller' => Controller\IndexController::class,
                        'action' => 'test-segment',
                    ],
                ],
            ],
        ]
    ],
],

And controller actions:

    public function indexAction()
    {
        $lang = $this->params()->fromRoute('lang');
        print $lang;
        exit;
    }

    public function testAction()
    {
        $lang = $this->params()->fromRoute('lang');
        print $lang;
        exit;
    }

    public function testSegmentAction()
    {
        $lang = $this->params()->fromRoute('lang');
        $id = $this->params()->fromRoute('id', 0);
        print $lang;
        print '<br>';
        print $id;
        exit;
    }