DoppyNL
DoppyNL

Reputation: 1470

Symfony2; domain/host based info in controller and base template

I'm building an symfony2 app that is configurable up to some point based on what domain is used to access the site. For ease of this question, lets say there is an "Domain" entity in the database containing the hostname and further configuration. Think about minor template differences, some differences in header/footer. A difference in products being offered. The routes available would not be different.

There are 2 places where I need this Domain object. * in a Controller::action * in a base template (even if the controller didn't need it) I would not need it somewhere else, if I did, I could simply pass it from the controller.

What would be the best way to get this object without creating too much overhead and not fetching it when we don't actually need it.

Some thoughts I got so far: * I could override the ControllerResolver and determine the Domain object based on the Request object. Although I don't seem to have access to the ServiceContainer there. * I could add some method to a BaseController that can retrieve the domain for me when I'm in a Controller:Action. * For usage in the template I could create a TwigExtension that adds a global variable. But it would need access to the Request object or RequestStack. Also, this would only help me in the template, I might be doing the same thing twice.

Any suggestions what might be a good approach here?

Upvotes: 0

Views: 1603

Answers (1)

giosh94mhz
giosh94mhz

Reputation: 3066

Don't know if this is the best solution, but worked well for me so far.

Since the domain information depends on the request it is NOT a service, so don't try to inject it in services or you'll get a bad headache. The most natural place to set information about the domain is in the request, and allow the controllers to read this information to interact with the services.

So, you can setup a Kernel event listeners which read the information from the database and set a domain Request attribute, like this:

<?php
namespace Acme\SiteBundle\EventListener;

use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Doctrine\ORM\EntityRepository;

class DomainSubscriber implements EventSubscriberInterface
{
    protected $domainRepository;

    public static function getSubscribedEvents()
    {
        return array(
            KernelEvents::REQUEST => 'onKernelRequest'
        );
    }

    public function __construct(EntityRepository $domainRepository)
    {
        $this->domainRepository = $domainRepository;
    }

    public function onKernelRequest(GetResponseEvent $event)
    {
        $request = $event->getRequest();

        // Console/CLI commands don't have Domain info
        if ($request === null)
            return;

        $domain = $this->domainRepository->find($request->getHost());

        if ($domain === null)
            throw new \RuntimeException(sprintf("Cannot find domain %s", $request->getHost()));

        $request->attributes->set('domain', $domain);
    }
}

Which must be registered in services.yml (or XML) with:

acme_site.manager:
    class: Doctrine\ORM\EntityManager
    factory_service: doctrine
    factory_method: getManager

acme_site.domain_repository:
    class: Doctrine\ORM\EntityRepository
    factory_service: acme_site.manager
    factory_method: getRepository
    arguments:
        - 'AcmeSiteBundle:Domain'

acme_site.domain_subscriber:
    class: Acme\SiteBundle\EventListener\DomainSubscriber
    arguments:
        - "@acme_site.domain_repository"
    tags:
        - { name: kernel.event_subscriber }

In your Controller you can now access the data by simply doing this:

public function someAction(Request $request) {
    $domain = $request->attributes->get('domain');
    $domain->getWhatever();
}

And in Twig you can always access the request with this:

{% set domain = app.request.attributes.get('domain') %}
whatever: {{ domain.whatever }}

Hope this help!

DISCLAIMER: the code is copy-pasted and then edited, so it may contain some minor error.

NOTE: If you really need to inject the request in services, then I suggest you to read the docs about the RequestStack (Symfony 2.4+), or use a setRequest method and take care of container scopes.

Upvotes: 2

Related Questions