Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Subdomain Linking in a Zend Framework 2 Web Application

DZone's Guide to

Subdomain Linking in a Zend Framework 2 Web Application

· Web Dev Zone
Free Resource

Discover how to focus on operators for Reactive Programming and how they are essential to react to data in your application.  Brought to you in partnership with Wakanda

Recently, at work, I had a requirement for serving part of the ZF2-powered website from a different, non-www sub-domain. Problem was in fact that such requirement carries need of making all the other routes absolute, because when some link to a page on the default subdomain appears on a sub-domain page, it should be directed to the default (www) sub-domain, and not the current one. So here is the overview of a solution that I came up with in order to resolve this situation.

Zend Framework 2 introduces powerful routing mechanism, providing various strategies for defining application end-points. Among others, there's a Hostname route type, which allows matching of the request hostname.

In order to setup subdomain linking for the purpose of that separate site area/module, be it a Blog, its top route should be defined as a route of a hostname type:

'router' => array(
    'routes' => array(
        'blog' => array(
            'type' => 'hostname',
            'options' => array(
                'route' => 'blog.example.com',
                'defaults' => array(
                    'controller' => 'Application\Controller\BlogController',
                    'action' => 'index',
                ),
            ),
            'may_terminate' => true,
            'child_routes' => array(
                'post' => array(
                    'type' => 'segment',
                    'options' => array(
                        'route' => '/[:slug]',
                        'constraints' => array(
                            'slug' => '[a-zA-Z0-9_-]+'
                        ),
                        'defaults' => array(
                            'action' => 'view'
                        )
                    )
                )
            )
        )
    )
)

As a result, blog and it's posts will be accessible at the blog.example.com base URL. But some other links, like for example login/register links, should still point to the default domain, for example www.example.com/login, when rendered on some blog page. In order to achieve this, my idea was to dynamically override every non-hostname routes in the system in a way that they are prefixed with the default hostname route. In order to accomplish this, first, I defined abstract route, and named it "defaultDomain":

//...
'defaultDomain' => array(
    'type' => 'hostname',
    'options' => array(
        'route' => 'www.example.com',
    ),
),
//...

Then for the override part itself, I created a delegator, a wrapper around a factory for creating Router service, which intercepts routes configuration and inflects every non-hostname route into a chain route, consisting of a default domain route and the actual route:

public function getServiceConfig()
{
    return array(
        //...
        'delegators' => array(
            'Router' => array(
                function(ServiceLocatorInterface $sm, $name, $requestedName, $callback) {
                    if (!Console::isConsole()) {
                        $config = $sm->get('Config');
                        $routes = $config['router']['routes'];

                        $defaultDomain = $routes['defaultDomain'];
                        foreach ($routes as $name => $route) {
                            if ($route['type'] != 'hostname') {
                                $routes[$name] = array(
                                    'type' => 'chain',
                                    'options' => array(
                                        'routes' => array($defaultDomain, $route),
                                        'route_plugins' => $sm->get('RoutePluginManager')
                                    )
                                );
                            }
                        }
                        unset($routes['defaultDomain']);

                        //Save changes
                        $config['router']['routes'] = $routes;
                        $sm->setAllowOverride(true);
                        $sm->setService('Config', $config);
                        $sm->setAllowOverride(false);
                    }

                    return $callback();
                }
            ),
        ),
        //...
    );
}

In practice, I coded this a little bit differently, in a separate delegator factory class, but the point should be clear. By doing this, when, for example, user/login route is requested, it will always be assembled as an absolute URL: www.example.org/user/login on every page where it is requested, because it is now defined as a chain of the default domain route and the original route.

What I liked about this approach is that it is unobtrusive, meaning that if for ever reason that Blog module should be returned to the default subdomain, there will be no need for altering any other route except one for blog pages, because name/definition of that user/login route remained the same, and was not affected by the introduction of that root, base URL route.


Learn how divergent branches can appear in your repository and how to better understand why they are called “branches".  Brought to you in partnership with Wakanda

Topics:

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}