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!

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;
    }