spacer
spacer
DOWNLOAD NOW
  • Community Events
  • A week of symfony
  • Call the expert
  • Case studies
  • Community
  • Documentation
  • Living on the edge
  • Plugins
  • Releases
  • Tutorials

Archives

  • November 2012 (3)
  • October 2012 (13)
  • September 2012 (11)
  • August 2012 (9)
  • July 2012 (14)
  • June 2012 (9)
  • May 2012 (11)
  • April 2012 (12)
  • March 2012 (11)
  • February 2012 (7)
  • January 2012 (9)
  • December 2011 (8)
  • November 2011 (9)
  • October 2011 (9)
  • September 2011 (9)
  • August 2011 (7)
  • July 2011 (11)
  • June 2011 (13)
  • May 2011 (10)
  • April 2011 (6)
  • March 2011 (11)
  • February 2011 (12)
  • January 2011 (13)
  • December 2010 (5)
  • November 2010 (4)
  • October 2010 (8)
  • September 2010 (8)
  • August 2010 (7)
  • July 2010 (7)
  • June 2010 (12)
  • May 2010 (12)
  • April 2010 (6)
  • March 2010 (7)
  • February 2010 (12)
  • January 2010 (8)
  • December 2009 (12)
  • November 2009 (16)
  • October 2009 (12)
  • September 2009 (13)
  • August 2009 (13)
  • July 2009 (13)
  • June 2009 (13)
  • May 2009 (11)
  • April 2009 (9)
  • March 2009 (10)
  • February 2009 (14)
  • January 2009 (15)
  • December 2008 (35)
  • November 2008 (21)
  • October 2008 (19)
  • September 2008 (22)
  • August 2008 (14)
  • July 2008 (7)
  • June 2008 (20)
  • May 2008 (11)
  • April 2008 (8)
  • March 2008 (10)
  • February 2008 (7)
  • January 2008 (5)
  • December 2007 (10)
  • November 2007 (9)
  • October 2007 (8)
  • September 2007 (10)
  • August 2007 (7)
  • July 2007 (8)
  • June 2007 (4)
  • May 2007 (6)
  • April 2007 (8)
  • March 2007 (4)
  • February 2007 (4)
  • January 2007 (8)
  • December 2006 (1)
  • November 2006 (2)
  • October 2006 (4)
  • September 2006 (4)
  • August 2006 (5)
  • July 2006 (5)
  • June 2006 (9)
  • May 2006 (5)
  • April 2006 (8)
  • March 2006 (10)
  • February 2006 (5)
  • January 2006 (9)
  • December 2005 (10)
  • November 2005 (11)
  • October 2005 (7)

Master Symfony2 fundamentals

Be trained by SensioLabs experts (2 to 6 day sessions -- French or English).
trainings.sensiolabs.com

Symfony hosting done right

ServerGrove, outstanding support at the right price for your Symfony hosting needs.
servergrove.com

L'audit Qualité par SensioLabs

200 points de contrôle de votre applicatif web.
audit.sensiolabs.com
spacer
Home » Blog » Call the expert: Adding subdomain requirements to routing.yml
spacer comments
spacer posts
spacer twitter

Blog

CALL THE EXPERT

spacer
Call the expert: Adding subdomain requirements to routing.yml
by Kris Wallsmith – March 02, 2009 – 16 comments - Edit

A question came across the mailing list today that provides an excellent opportunity to demonstrate the flexibility of the symfony 1.2 routing system.

Evert Harmeling posed the following:

How can I point sub1.domain.com/test to a different route than sub2.domain.com/test inside the same application?

This is not supported natively by the symfony core, but the routing system can easily be extended to meet Evert's requirement.

Extending sfRequestRoute

Each route in routing.yml includes a set of requirements that must be met in order for that route to be successfully connected. Symfony 1.2 introduced the sfRequestRoute class, which exposes a sf_method requirement, allowing you to create RESTful interfaces based on HTTP request method. We can create a similar mechanism to expose a sf_host requirement.

Implementing this new requirement option would look something like this:

# apps/*/config/routing.yml
homepage_sub1:
  url:    /
  param:  { module: main, action: homepage1 }
  class:  sfRequestHostRoute
  requirements:
    sf_host: sub1.example.com
 
homepage_sub2:
  url:    /
  param:  { module: main, action: homepage2 }
  class:  sfRequestHostRoute
  requirements:
    sf_host: sub2.example.com

These routing rules reference a sfRequestHostRoute class, which we will write now:

// lib/routing/sfRequestHostRoute.class.php
class sfRequestHostRoute extends sfRequestRoute
{
}

Enforcing the sf_host requirement

The sf_host requirement must be enforced when the routing system inspects an incoming URL. This is done in the matchesUrl() method:

class sfRequestHostRoute extends sfRequestRoute
{
  public function matchesUrl($url, $context = array())
  {
    if (
      isset($this->requirements['sf_host'])
      &&
      $this->requirements['sf_host'] != $context['host']
    )
    {
      return false;
    }
 
    return parent::matchesUrl($url, $context);
  }
}

This code inspects the current request's host and compares it to the route's sf_host requirement. If they don't match, the method returns false; if they do match, sfRequestHostRoute passes the URL to the overloaded method.

Generating URLs that respect sf_host

Enforcing the sf_host requirement is only part of the solution. In addition to parsing incoming URLs, the routing system also generates URLs for the view layer. We add consideration for the generated URL's host to the generate() method:

class sfRequestHostRoute extends sfRequestRoute
{
  // ...
 
  public function generate($params, $context = array(), $absolute = false)
  {
    $url = parent::generate($params, $context, $absolute);
 
    if (
      isset($this->requirements['sf_host'])
      &&
      $this->requirements['sf_host'] != $context['host']
    )
    {
      // apply the required host
      $protocol = $context['is_secure'] ? 'https' : 'http';
      $url = $protocol.'://'.$this->requirements['sf_host'].$url;
    }
 
    return $url;
  }
}

This code compares the current request's host with the sf_host requirement of the route and forces generation of an absolute URL if they don't match.

The $context parameter passed to these route methods is not the familiar sfContext object, but rather an array describing the current request. For more information take a look at sfWebRequest::getRequestContext().

Using the new sfRequestHostRoute class

Once you define a routing rule with the new sfRequestHostRoute class you may use this route in your templates in the same way you would any other. The tables below illustrate how the routes we defined above will behave when called from each host.

Called from sub1.example.com Result
url_for('@homepage_sub1') /
url_for('@homepage_sub1', true) sub1.example.com/
url_for('@homepage_sub2') sub2.example.com/
url_for('@homepage_sub2', true) sub2.example.com/
Called from sub2.example.com Result
url_for('@homepage_sub1') sub1.example.com/
url_for('@homepage_sub1', true) sub1.example.com/
url_for('@homepage_sub2') /
url_for('@homepage_sub2', true) sub2.example.com/

Vary Subdomain by Environment

One of the more useful features of symfony is its support for multiple environments. The production environment may be running on the example.com domain, but the development environment most certainly is not.

We can vary the host requirements by environment by defining them in app.yml:

# apps/*/config/app.yml
prod:
  sub1_host: sub1.example.com
  sub2_host: sub2.example.com
 
dev:
  sub1_host: sub1.example.local
  sub2_host: sub2.example.local

These values can then be placed in routing.yml using PHP:

# apps/*/config/routing.yml
homepage_sub1:
  url:    /
  param:  { module: main, action: homepage1 }
  class:  sfRequestHostRoute
  requirements:
    sf_host: <?php echo sfConfig::get('app_sub1_host')."\n" ?>
 
homepage_sub2:
  url:    /
  param:  { module: main, action: homepage2 }
  class:  sfRequestHostRoute
  requirements:
    sf_host: <?php echo sfConfig::get('app_sub2_host')."\n" ?>

Conclusion

By overloading two methods and editing two configuration files we've extended the routing system to consider subdomains. Happy coding!

Add a Comment

You must be connected to post a comment.

Comments spacer

  • spacer
    #1 Lambert said on the 2009/03/02 at 23:10
    Hi Kris,
    Great example! Especially the environment-dependend-routing is excellent!

    Regards, Lambert

  • spacer
    #2 Ryan Weaver said on the 2009/03/03 at 00:07
    Great - I haven't touched the new routing stuff yet - this is very very enlightening.
  • spacer
    #3 Kris said on the 2009/03/03 at 00:56
    I discovered a minor bug in the routing system having to do with absolute URLs which has been fixed in r15940 and will be included in symfony 1.2.5.
  • spacer
    #4 Ice_j7 said on the 2009/03/03 at 02:46
    I still don't have the time to check for the new Routing system in symfony 1.2, but this article will make me search the moment to do it, thx.
  • spacer
    #5 zero0x said on the 2009/03/03 at 07:10
    It looks awesome!
  • spacer
    #6 Frank Stelzer said on the 2009/03/03 at 08:34
    Great! This mechanism will save me a lot of time in my next task, where i am forced with those requirements. Thanks for this article.
  • spacer
    #7 lloyd27 said on the 2009/03/03 at 09:39
    Just a question..

    This code:
    $url = parent::generate($params, $context, $absolute);

    If $absolute is true, doesn't sfRequestRoute already add the "%%host%%/" section of the url? So it would result in something like
    "%%host%%/%%host%%/my_url"

    Or maybe I just don't understand really well how it works..
    This is how I think it works...
  • spacer
    #8 pacogliss said on the 2009/03/03 at 09:53
    Maybe I am stating the obvious, but isn't setting up virtual hosts for every sub-domain a necessary step to set this tuto working ?
    [On local with windows, I have moreover to enter a new line for every vh in my hosts file.]
  • spacer
    #9 Evert Harmeling said on the 2009/03/03 at 11:48
    Great tutorial Kris!

    But, now we still have a problem with dynamic subdomains as in www.domain.com, members.domain.com and *.domain.com.

    We thought to set all requirements to www and members subs, but we want to set the opposite in case these aren't the subdomains. We looked at filters, and using regex in the requirements... but that wasn't the solution...

    Could you point me in the right direction?
  • spacer
    #10 Lawrence said on the 2009/03/04 at 05:07
    I am a bit confused. What is the calling code for sfRequestHostRoute? Where is this called? How does the calling code know which method to call?
  • spacer
    #11 Kai said on the 2009/03/04 at 07:49
    Hi. Great Tutorial. But I'm looking for the same solution as Evert Harmeling is looking for.
    *.domain.com
    Would be great if I could do something like:

    @dynamic_vhost:
    url: :slug.domain.com
    param{ module: vhost, action: show }

    For the moment I have a rewrite in lighttpd to do this things:

    ^(.*).domain.com => index.php/dynamic/$1

    And the route in symfony is:

    @dynamic_vhost:
    url: /dynamic/:slug
    param{ module: vhost, action: show }

    My problem here ist, that I cant use the linkhelpers to link back to the regular domain because the host is allways *.domain.com and not domain.com or www.domain.com.
    And there are a lot of other helpers that dount function...

    Any further Idears would be great
  • spacer
    #12 Dominik said on the 2009/03/04 at 12:05
    I'm looking for an solution for the problem of Evert Harmeling and Kai.

    I want to do something like usernameA.site.com
    usernameB.site.com

    any ideas?
  • spacer
    #13 Evert Harmeling said on the 2009/03/05 at 10:05
    Hopefully this is a solution:

    What the problem was is that we couldn't give a requirement for *.domain.com, and we had more than 1 subdomain. So we have to look from a different angle, if we know what the static subdomains are, we could check on those...

    What we've done is we made another HostRouteClass which extends the one from this article, and there check if it's one of the static subs.

    class sfDynamicHostRoute extends sfHostRoute
    {
    public function matchesUrl($url, $context = array())
    {
    if (sfConfig::has('app_static_host') && sfConfig::has('app_member_host'))
    {
    if ($context['host'] == sfConfig::get('app_static_host') || $context['host'] == sfConfig::get('app_member_host'))
    {
    return false;
    }
    }

    return parent::matchesUrl($url, $context);
    }
    }

    Another thing we discovered is that sfRequestRoute overrules the sf_method property and sets it to (from the core sfRequestRoute.class.php):
    if (!isset($this->requirements['sf_method']))
    {
    $this->requirements['sf_method'] = array('get', 'head');
    }

    So you loose the post method in case of posting forms... From that point of view we decided to not extend the HostRoute class in this example to sfRequestRoute but to just to sfRoute, and that works.

    In your routing.yml you give the requirements of the static subs the requirement sfHostRoute and the dynamic subs the requirement sfDynamicHostRoute...

    Hopefully this is a solution for more people!
  • spacer
    #14 Pauli2 said on the 2009/03/08 at 10:39
    off the subject :
    What is the mailing list Kris is talking about ?
gipoco.com is neither affiliated with the authors of this page nor responsible for its contents. This is a safe-cache copy of the original web site.