Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Absolute/Relative URL managment strategy close #363 #416

Closed
wants to merge 8 commits into from

3 participants

@jails
Collaborator

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 ?

Previously you have :

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

Now 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();

Note : the above routes should be in different routes.php files, but it' ok for examples.

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',
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 configure them. 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
Note2 : use full relative strategy or full absolute strategy otherwise the routing looks like really "chaotic". Since rules are matched in order, the first who match is the winner and a relative routes can match in an unwanted place.

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 : <username>.mysite.com

Router::location('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 : <username>.mysite.com with the following location :

Router::location('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 I wan't a link from bob.mysite.com to max.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 is in the app location 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

@gwoo
Owner

Could you achieve the same things by modifying the Request and Response classes?

@jails
Collaborator

Not really. I guess you can achieve the same thing as Media::location() using a static Request function and/or attributes. But in my opinion it is not possible to work with something else for the Router class. As to the Response class, it step to late to be usable imo.

You thought of something specific ?

@hans-d

Concept looks interesting, perhaps a shorthand notation can be used (beside the more official longhand you used). Eg using : for location:relative-uri

  • $this->html->style('cdncss:style.css'); instead of $this->html->style('style.css', array('location' => 'cdncss'));

  • Router::connect('app:/{:controller}/{:action}/{:args}'); instead of enclosing them with beginLocation/endLocation

  • $this->html->link('Max home', '['subdomain' => 'max']:/home/index')); instead of $this->html->link('Max home', '/home/index', array('location' => array('subdomain' => 'max')));

  • $this->html->link('do something on our members site', 'members:SomeController::action'));

@jails
Collaborator

Yeah right, a shorthand notation shouldn't be that hard to introduce.

@jails jails referenced this pull request
Merged

New Router Feature #878

@jails jails closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.