Update:
I'm using a shared CakePHP lib across several apps (with all of them living in subdomains). They're not all nested the same; so, for example, on the filesystem I might have:
/foo
.htaccess
/1.0 (app)
/1.1 (app)
/1.2 (app)
...
/bar
.htaccess
/1.0 (app)
/1.1 (app)
/1.2 (app)
...
Where the .htaccess in each just defines which app is the default that requests are routed to within that subfolder, while all of them are still available by a direct url (eg /foo/1.0/...).
It seems that if I'm using the direct URL (eg not the .htaccess-rewritten one) it recognizes the routes appropriately, but if the URL is rewritten by .htaccess, it doesn't; it will invoke AppError::error404() with the ouput at the end of the question. This doesn't seem right to me... if anyone has any insight, that would be awesome.
Hey guys,
I'm using CakePHP 1.3 and having a routing issue. The gist of the concept is this:
I want to use 'en_us' style i18n/i10n, not simply 'en'
I'm not using built-in i18n because of requirements to have some languages display pages with localized views (views/:i18n/...) and others to common views with calls to __()
So, I have routes set up like so:
$sI18nFormat = '/[a-z]{2}_[a-z]{2}/';
Router::connect('/:i18n/:controller/:action',
array('controller' => ':controller', 'action' => ':action', 'i18n' => ':i18n'),
array('i18n' => $sI18nFormat));
Router::connect('/:i18n/:controller',
array('controller' => ':controller', 'action' => 'index', 'i18n' => ':i18n'),
array('i18n' => $sI18nFormat));
Router::connect('/:controller/:action',
array('controller' => ':controller', 'action' => ':action', 'i18n' => 'en_us'),
array('i18n' => $sI18nFormat));
Router::connect('/:controller',
array('controller' => ':controller', 'action' => 'index', 'i18n' => 'en_us'),
array('i18n' => $sI18nFormat));
where obviously the routes are mirrored except that one has a dynamic i18n parameter passed, and the other has a static one.
The problem comes with using certain values for :i18n - for example, en_us is okay, but fr_fr seems to have Cake looking for FrController (not FrFrController) - seemingly because it's trying to work some magic with a built-in i18n fr prefix.
As an example, here's what AppError::error404 is given:
Array
(
[className] => FrController
[webroot] => /path/to/webroot
[url] => _fr
[base] => /path/to/webroot
)
Is it possible to a) make Cake stop this so my routes work as expected, or b) tell Cake what format I want my i18n/i10n in so it doesn't try to do it its own way?
Any thoughts would be appreciated.
You should not (need to) delimit the Regex, and there should also be no need to populate the default parameter. Try this:
$sI18nFormat = '[a-z]{2}_[a-z]{2}';
Router::connect('/:i18n/:controller/:action', array(), array('i18n' => $sI18nFormat));
It's probably also failing because the URL does not match the full defined route. I.e., /fr_fr/foo will not match the above route, since it contains no :action. Try adding shorter variants as well:
Router::connect('/:i18n/:controller', array('action' => 'index'), array('i18n' => $sI18nFormat));
Router::connect('/:i18n', array('controller' => 'foo', 'action' => 'index'), array('i18n' => $sI18nFormat));
Have you looked at p28n? http://bakery.cakephp.org/articles/p0windah/2007/09/12/p28n-the-top-to-bottom-persistent-internationalization-tutorial
I found that it made the whole thing much less of a handful.
Related
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)?
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...
I have such a link:
<?=$this->Html->link(__('Kontakt'), array('controller' => 'contacts', 'action' => 'show', 'language' => $this->Session->read('Config.language')));?>
My route looks like this:
Router::connect('/kontakt', array('controller' => 'contacts', 'action' => 'show', 'language' => 'deu'));
This way I get such a link in browser:
domain.com/kontakt (german is my base language)
Now I want the other languages as well, therefore I transport the language flag with the url in order to get this:
domain.com/eng/contact (all languages except german need this language shortcut)
I have this route
Router::connect('/:language/kontakt', array('controller' => 'contacts', 'action' => 'show'),
array(
'language' => 'eng|spa|fre|rus'
));
This creates my nicely urls like:
/spa/kontakt or /eng/kontakt or /fre/kontakt
I wonder now, how I can set the the translated words into the routes, without having a single line for each translation? Is there a way to make it somehow dynamic?
Thanks!
Use the translation methods, just like in any other place:
Router::connect('/' . __d('routes', 'kontakt'), array('controller' => 'contacts', 'action' => 'show', 'language' => 'deu'));
The issue with that is that you don't set the language dynamically which will cause that the translation works but what ever "kontakt" will become is going to be routed to /deu/.
You must set the language dynamically to the route, not static as you do or you'll still have to add each language manually to the routes. If you prefer to do that I would check conditionally what language is active and load a separate route file depending on the language.
I'd like reroute all domainname.com/affiliateXYZ visits to an AffController reroute action. But how?
The route element substring of 'affiliate' will be consistent and substring 'XYZ' will vary.
I would've preferred project requirements that had allowed domainname.com/aff/reroute/XYX (or domainname.com/aff/reroute/CigarKing) in the controller/action/parameter format, but alas.. tis not the case.
Initially I tried the following unsuccessfully:
Router::connect('/affiliate*', array('controller' => 'aff', 'action' => 'reroute'));
Router::connect('/affiliate**', array('controller' => 'aff', 'action' => 'reroute'));
Router::connect('/:controller*', array('controller' => 'aff', 'action' => 'reroute'), array('controller'=>'(affiliate)');
Once in the controller reroute action I will explode the affiliateXYZ into just 'XYZ' and then proceed with things I know how to do. My issue is routing to the controller with affiliateXYZ as the first routing element and at the same time not messing up current functionality.
I'd used Router::connect('/:affiliate', array('controller' => 'aff', 'action' => 'reroute'),array('pass'=>array('affiliate'))); to get to the info into the controller, until I saw the negative impact on preexisting controllers (I assume because I didn't explicitly list all other controllers, which I don't think I should have to do.).
How to route w/prefix ( more accurately a substring of 'affiliate' )?
I know prefix is the wrong terminology.
Thx, in advance.
You are looking for regular expression matching, with the help of this functionality you can define how specific route elements are matched.
This option is available for the third parameter of Router::connect().
You define it in element => regex format:
Router::connect(
'/:affilate',
array(
'controller' => 'aff',
'action' => 'reroute'
),
array(
'pass' => array('affilate'),
'affilate' => 'affilate[^/]+'
)
);
This would match any URL that starts with affilate, followd by 1 or more characters that are not a /.
See also http://book.cakephp.org/2.0/en/development/routing.html#route-elements
I'm trying to get to grips with Cake's routing, but I'm having no luck finding a solution to my particular problem.
I want to map www.example.com/slug to www.example.com/venues/view/slug, where slug is the URL friendly name of a record for a particular venue.
I also want to then map www.example.com/slug/events to www.example.com/events/index/venue:slug.
After reading the CakePHP documentation on Routing, a few times over, I'm none the wiser. I understand how I would create these routes for each individual venue, but I'm not sure how to get Cake to generate the routes on the fly.
You want to be careful mapping something to the first path after the domain name. This means that you would be breaking the controller/action/param/param paradigm. You can do this, but it may mean that you need to define every url for your site in your routes file rather than using Cake's routing magic.
An alternative may be to use /v/ for venues and /e/ for events to keep your url short but break up the url for the normal paradigm.
If you still want to do what you requested here, you could do something like the following. It limits the slug to letters, numbers, dashes, and underscores.
Router::connect(
'/:slug',
array(
'controller' => 'venues',
'action' => 'view'
),
array(
'slug' => '[a-zA-Z0-9_-]+'
)
);
Router::connect(
'/:slug/:events',
array(
'controller' => 'events',
'action' => 'index'
),
array(
'slug' => '[a-zA-Z0-9_-]+'
)
);
In your controller, you would then access the slug with the following (using the first route as an example).
function view(){
if(isset($this->params['slug'])){
//Do something with your code here.
}
}
First off, you're not connecting www.example.com/slug to www.example.com/venues/view/slug, you're connecting /slug to a controller action. Like this:
Router::connect('/:slug',
array('controller' => 'venues', 'action' => 'view'),
array('pass' => array('slug'));
To generate the appropriate link, you'd do the same in reverse:
$this->Html->link('Foo',
array('controller' => 'venues', 'action' => 'view', 'slug' => 'bar'))
That should result in the link /bar.
The problem with routing a /:slug URL is that it's a catch-all route. You need to carefully define all other routes you may want to use before this one.
I need to launch a web app in spanish for the moment and I need to translate the app...
I already modified the default.po and added configure::write('Config.language', 'es') to the core.php...
what now? I don't want to add routing rigth now. Any suggestions?
PD: did everything as it is in the manual and ##$%^&%$## I cant get it to work
i18n is a tricky one to get your head around. If you're producing a website that will just be in Spanish, there is no need to use it, but I do use po messages as a matter of course, just in case.
There is a component that will help you a lot: http://bakery.cakephp.org/articles/p0windah/2007/09/12/p28n-the-top-to-bottom-persistent-internationalization-tutorial
There also used to be a script that would allow translation of slugs so that SEO would direct you to the right language. Last time I looked, it had vanished, but I'll try to piece it together for you.
For the moment, this is what I used in router.php
//route to switch locale
Router::connect('/lang/*', array('controller' => 'p28n', 'action' => 'change'));
//forgiving routes that allow users to change the lang of any page
Router::connect('/eng?/*', array(
'controller' => "p28n",
'action' => "shuntRequest",
'lang' => 'en-gb'
));
Router::connect('/ca?/*', array(
'controller' => "p28n",
'action' => "shuntRequest",
'lang' => 'cat'
));
Router::connect('/es?/*', array(
'controller' => "p28n",
'action' => "shuntRequest",
'lang' => 'es_es'
));
I'll dig around for the url translation, but it may take a while....