How to redefine the URLs generation without changes at the HtmlHelper#link(...) calls in CakePHP?

395 Views Asked by At

I have a CakePHP website with many internal links, that are build with the HtmlHelper:

/app/View/MyController/myaction.ctp

<?php
echo $this->Html->link(
    $item['Search']['name'],
    array(
        'controller' => 'targetcontroller',
        'action' => 'targetaction',
        $profileId,
        $languageId
    )
);
?>

It works fine with the default route:

/app/Config/routes.php

Router::connect('/pages/*', array('controller' => 'pages', 'action' => 'display'));

The generated links look like that: /mycontroller/myaction/$profileId/$languageId.

Now I want to use search engine friendly URLs (with profile names and ISO-639-1 language codes instead of IDs) for a part of the website and added a new Route:

/app/Config/routes.php

Router::connect(
    '/:iso6391/:name.html',
    array('controller' => 'mycontroller', 'action' => 'myaction'),
    array(
        'iso6391' => '[a-zA-Z]+',
        'name' => '[0-9a-zA-ZäöüßÄÖÜ\-]+',
    )
);

And it also works fine and the incomming URIs like /producer/en/TestName.html are interpreted correctly.

But the HtmlHelper is still generating the old URIs like /mycontroller/myaction/1/1.

The docu says:

Reverse routing is a feature in CakePHP that is used to allow you to easily change your URL structure without having to modify all your code. By using routing arrays to define your URLs, you can later configure routes and the generated URLs will automatically update.

Well, the HtmlHelper gets a routing array as input, that means: I'm using the reverse routing.

Why does it not work? How to make the HtmlHelper generate the new URLs (without changing the HtmlHelper#link(...) calls)?

1

There are 1 best solutions below

2
On

Bit of explanation first

You are technically not using reverse routing. You see, the output link, /mycontroller/myaction/1/1 definitively doesn't match /iso/name.html. Like, in no way. So, the routing skips that rule because it doesn't apply.

Code

Try this

echo $this->Html->link(
    $item['Search']['name'],
    array(
        'controller' => 'targetcontroller',
        'action' => 'targetaction',
        'iso6391' => $someStringWithIso,
        'name' => $someName
    )
);

But for that, you have to change your routing a bit, because you are not passing the parameters (check the docs for examples)

Router::connect(
    '/:iso6391/:name.html',
    array('controller' => 'mycontroller', 'action' => 'myaction'),
    array(
        'pass' => array('iso6391', 'name'),
        'iso6391' => '[a-zA-Z]+',
        'name' => '[0-9a-zA-ZäöüßÄÖÜ\-]+',
    )
);

And you have to mind the first string match /:iso6391/:name.html. Do you want to match this route to every controller and action in your project, or just the one controller and the one view. If it is for all projects, just for precaution, use this

  /:controller/:action/:iso6391/:name.html

if is just for, say, Controller1 and action "view", use

  /controller1/view/:iso6391/:name.html

The detail you need to consider is the extension you use .html, is that really necessary in the url? If it is, add it as a parameter in the Html#link

echo $this->Html->link(
    $item['Search']['name'],
    array(
        'controller' => 'targetcontroller',
        'action' => 'targetaction',
        'iso6391' => $someStringWithIso,
        'name' => $someName

        'ext' => 'html'
    )
);

and also add parseExtensions to the routing file. Read this. Would be easier if you don't add the extension, but that's up to you.

In the end, you still have to change your calls to Html->link...