Manejando multi subdominios en Symfony

Hace poco que me he tenido que enfrentar al manejo de subdominios dinámicos dentro de un proyecto Symfony. Básicamente la idea se trata solucionar la necesidad de poder responder desde la misma aplicación con subdominios distintos. Esto significa que podemos responder con distintos controladores a peticiones del tipo subdominio1.midominio.com/ruta y subdominio2.midominio.com/ruta. Esta flexibilidad en las ruta nos abre distintas aplicaciones que podemos implementar en nuestro proyecto.

Aplicaciones de multi subdominios

La necesidad principal que teníamos era proporcionar un subdominio personalizado para cada cliente. Imagina un producto software que podemos ofrecer a múltiples clientes, de tal manera que la funcionalidad es común para todos ellos, sin embargo se requiere que el cliente pueda acceder a su aplicación desde nombredeliente.midominio.com.

Manos a la Obra

A continuación vamos a ver la parte práctica de este asunto, ofreciendo los pasos que debemos de realizar para poder activar esta opción multi subdominio en Symfony.

1. Configurando el routing

Desde Symfony 2.2 se incluyó la posibilidad de poder definir un patrón de url dentro del hostname. Aquí un ejemplo para definir este host:

company:
resource: "routing/routing.yml"
host: "{subdomain}.%base_domain%"
defaults:
subdomain: %default_media%
enviroment: dev

Definiendo el VirtualHost

En este caso voy a poner un ejemplo de configuración para Apache, pero no hay problema si usais otro servidor web como nginx. Lo único que necesitamos es definir en la configuración como va a tratar el servidor web las peticiones que vengan desde este dominio, de tal manera que sepa responder a llamadas del tipo *.midominio.com

<VirtualHost *:80>  ServerName dominio.com
ServerAlias *.dominio.com
DocumentRoot "/home/user/sites/site/web"
<Directory "/home/user/sites/site/web">
AllowOverride All
Allow from All
</Directory>
</VirtualHost>

Configurando el routing

Simulamos en nuestra máquina el dns forzando el fichero /etc/hosts

#/etc/hosts127.0.0.1      dominio.com subdominio1.midominio.com

Definiendo el EventListener

Una vez tenemos configurado a nivel de infraestructura nuestra gestión del dominio, vamos a incluir un Listener en nuestra aplicación que escuche cada petición para poder identificar a través de que subdominio se está atacando la aplicación, así podemos aplicar distinto comportamiento dependiendo del propio subdominio. En nuestro caso particular, buscamos en base de datos si existe un elemento Media en nuestra base de datos, para poder inyectarlo a nivel global, y que en nuestros controladores y servicios podamos saber en que Media estamos y que configuración tiene activada.

<?php
namespace AppBundle\Event;
use AppBundle\Entity\Media;
use AppBundle\Entity\MediaManager;
use Doctrine\ORM\EntityManager;
use Symfony\Bundle\FrameworkBundle\Routing\Router;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\KernelEvents;
class RequestListener implements EventSubscriberInterface
{
/**
* @var MediaManager
*/
private $mediaManager;
/**
* @var string
*/
private $enviroment;
/**
* @var Router
*/
private $router;
/**
* @var EntityManager
*/
private $em;
public function __construct(MediaManager $mediaManager, $enviroment, Router $router, EntityManager $em)
{
$this->mediaManager = $mediaManager;
$this->enviroment = $enviroment;
$this->router = $router;
$this->em = $em;
}
public function onKernelRequest(GetResponseEvent $event)
{
$request = $event->getRequest();
$currentHost = $request->getHttpHost();
$baseHost = $this->container->getParameter("base_domain");
$explodeElements = explode('.', $currentHost);
$subdomain = $explodeElements[0];
$site = $this->em->getRepository(Media::class)->findOneBy(array(
'subdomain' => $subdomain
));
if (!$site) {
throw new NotFoundHttpException(sprintf('No site for host "%s", subdomain "%s"', $baseHost, $subdomain));
}
if (!$site->isActive()) {
throw new LogicException("Media is not active");
}
$this->mediaManager->setCurrentSite($site);
$this->router->getContext()->setParameter('subdomain', $subdomain);
$this->router->getContext()->setParameter("enviroment", $enviroment);
}
public static function getSubscribedEvents()
{
return array(
KernelEvents::REQUEST => array(
array(
'onKernelRequest',
33
)
)
);
}
}

Conclusión

Como has podido leer, es bastante sencillo configurar multi subdominios en nuestra aplicación Symfony. El ejemplo mostrado es muy simple, pero partiendo de este ejemplo puedes aplicarlo para las necesidades concretas de tu proyecto. Espero que el post haya sido de utilidad 🙂

Full Stack Web Developer — adrianalonso.es

Full Stack Web Developer — adrianalonso.es