UnionOfRAD/lithium — pull request #416 by jails (2012-04-06 13:22:42+00:00)
Title: Absolute/Relative URL managment strategy close #363

In each web projects we deal URLs. But URL "managment" is generaly left to the coder in many framework. This lack is the source of many headaches (in my case at least).

** For example: **

- I wan't to use plugins w/o the need to modify each times the views
- I wan't to use a CDN for my assets (one or two), but only in production
- I have two plugins which implements a same route name (ex: /user/login)
- I wan't to deal with subdomain like ".mydomain.com"
- I wan't to deal URLs according the environment
- I wan't all my admin area in the admin directory, tests in the tests directory and fully accessible link
from my app to the admin and tests (for example) without setting absolute URLs directly in views
- etc.

Using relative URL solves some issues, but when projects manage many subdomains, when we need plugins with templates (like a forum plugin), working with relative URLs makes things harder to maintain.

This PR provide a way to deal with absolute URL (from route to assets)

** How it works ? **

So when you previously have :

Router::connect('/', 'Pages::view');
Router::connect('/pages/{:args}', 'Pages::view');

This PR introduce the concept of location. So you can set routes into a specific location :

Router::beginLocation('app');
Router::connect('/ambiguous/{:action}');
Router::connect('/{:controller}/{:action}/{:args}');
Router::endLocation();

Router::beginLocation('test');
Router::connect('/ambiguous/{:action}');
Router::connect('/test/{:args}', array('controller' => 'lithium\test\Controller'));
Router::connect('/test', array('controller' => 'lithium\test\Controller'));
Router::endLocation();

** What does it change ? **

Well nothing yet. Everything works like before, but now Router::process() can identify the location you are on.
So if you are in a /controller/action url, Router::getLocation() will return `'app'`
So if you are in a /test/action url, Router::getLocation() will return `'test'`
If you are in /ambiguous/index will return `'app'` (since the app location is not defined right now, Router::process can't resolve the ambiguity and works like before)

** So what can i do with that ? **

Now you can set locations and put them in different "places". The following example will produce absolute urls for links in the /controller/action context :

Router::location('app', array('absolute' => true));

Note : by default the absolute url it's based on the Request's host and scheme

To use your own you can set your location as following :

Router::location('app', array(
'absolute' => true,
'host' => 'www.mysite.com',
'scheme' => 'http://',
'base' => '/web'
));

You can also set your locations with varialbes. This way with you can manage subdomains like : `.mysite.com`

Location::url('app', array(
'absolute' => true,
'host' => '{:subdomain:[a-z]+}.mysite.com',
'scheme' => 'http://',
'base' => '/' //if base has no leading '/' the Request's base will be preprended the the location base.
));

** How can i write a link form a location to a different location ? **

Until now, your templates works in "insulation" since Router::process detect your location so the links in your templates will follow your Router::location() configuration.

Suppose you are in a /controller/action url (so the app location) you need a link to the test location. To change "context" you can use the following syntax in your view :

$this->html->link('test', '/test', array('location' =>' 'test'));

Another way is to change the Router location like the following (for example to build a newsletter template):

Router::beginLocation('newsletter');
// building the view
Router::endLocation();

You can also definitly change the location with :

Router::setLocation('custom');

** Ok but how does the variables in location works ? **

Suppose you wan't to manage subdomains like : `.mysite.com` with the following location :

Location::url('app', array(
'absolute' => true,
'host' => '{:subdomain:[a-z]+}.mysite.com',
'scheme' => 'http://',
'base' => '/'
));

if you are on http://bob.mysite.com/controller/action, all your links will start with http://bob.mysite.com/ and $this->request->params looks like :

array(
'controller' => 'controller',
'action' => 'action',
'subdomain' => 'bob'
)

** So how if a wan't a link form max.mysite.com to bob.mysite.com ? **

You can override the default location params with the following syntax :

$this->html->link('Max home', '/home/index', array(
'location' => array(
'app' => array(
'subdomain' => 'max'
)
)
));

But since max you are in the app location too you could simply it to :

$this->html->link('Max home', '/home/index', array(
'location' => array(
'subdomain' => 'max'
)
));

This will produce : http://max.mysite.com/home/index

With this PR you can also set location for your Media like the following :

Media::location('cdncss', array(
'absolute' => true,
'host' => 'www.cdn.com',
'scheme' => 'http://',
'base' => '/web/assets/',
'path' => $webroot
));

Media::location('cdnimage', array(
'absolute' => true,
'host' => 'www.cdn.com',
'scheme' => 'http://',
'base' => '/web/assets/',
'path' => $webroot
));

Notice media's location can't be detected automagically by Router::process so you need to use the location variable for using your locations like the following :

$this->html->style('style.css', array('location' => 'cdncss'));
$this->html->image('test.gif', array('location' => 'cdnimage'));

See tests for complete features