open-orchestra/open-orchestra-front-bundle

View on GitHub
FrontBundle/EventSubscriber/KernelExceptionSubscriber.php

Summary

Maintainability
A
3 hrs
Test Coverage
<?php

namespace OpenOrchestra\FrontBundle\EventSubscriber;

use OpenOrchestra\DisplayBundle\Manager\ContextInterface;
use OpenOrchestra\FrontBundle\Exception\DisplayBlockException;
use OpenOrchestra\FrontBundle\Manager\TemplateManager;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use OpenOrchestra\ModelInterface\Repository\ReadSiteRepositoryInterface;
use OpenOrchestra\ModelInterface\Repository\ReadNodeRepositoryInterface;
use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use OpenOrchestra\ModelInterface\Model\ReadSiteInterface;
use OpenOrchestra\ModelInterface\Model\ReadNodeInterface;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
use OpenOrchestra\FrontBundle\Exception\NonExistingSiteException;

/**
 * Class KernelExceptionSubscriber
 */
class KernelExceptionSubscriber implements EventSubscriberInterface
{
    protected $siteRepository;
    protected $nodeRepository;
    protected $templating;
    protected $request;
    protected $currentSiteManager;
    protected $templateManager;

    /**
     * @param ReadSiteRepositoryInterface $siteRepository
     * @param ReadNodeRepositoryInterface $nodeRepository
     * @param EngineInterface             $templating
     * @param RequestStack                $requestStack
     * @param ContextInterface            $currentSiteManager
     * @param TemplateManager             $templateManager
     */
    public function __construct(
        ReadSiteRepositoryInterface $siteRepository,
        ReadNodeRepositoryInterface $nodeRepository,
        EngineInterface $templating,
        RequestStack $requestStack,
        ContextInterface $currentSiteManager,
        TemplateManager $templateManager
    ) {
        $this->siteRepository = $siteRepository;
        $this->nodeRepository = $nodeRepository;
        $this->templating = $templating;
        $this->request = $requestStack->getMasterRequest();
        $this->currentSiteManager = $currentSiteManager;
        $this->templateManager = $templateManager;
    }

    /**
     * If exception type is 404, display the Orchestra 404 node instead of Symfony exception
     * 
     * @param GetResponseForExceptionEvent $event
     */
    public function onKernelException(GetResponseForExceptionEvent $event)
    {
        if ($event->getException() instanceof DisplayBlockException) {
            $event->getRequest()->setRequestFormat('fragment.'. $event->getRequest()->getRequestFormat());
            $event->setException($event->getException()->getPrevious());
        } elseif ($event->getException() instanceof HttpExceptionInterface && '404' == $event->getException()->getStatusCode()) {
            $this->setCurrentSiteInfo(trim($this->request->getHost(), '/'), trim($this->request->getPathInfo(), '/'));

            if ($html = $this->getCustom404Html()) {
                $event->setResponse(new Response($html, 404));
            }
        }
    }

    /**
     * Find and set the current site id, language, and alias id
     * 
     * @param string $host
     * @param string $path
     * 
     * @throws NonExistingSiteException
     */
    protected function setCurrentSiteInfo($host, $path)
    {
        $path = $this->formatPath($path);
        $currentSiteId = null;
        $currentLanguage = null;
        $currentAliasId = null;
        $matchingLength = -1;

        $matchingSites = $this->siteRepository->findByAliasDomain($host);

        /** @var ReadSiteInterface $site */
        foreach ($matchingSites as $site) {
            foreach ($site->getAliases() as $aliasId => $alias) {
                $aliasPrefix = $this->formatPath($alias->getPrefix());
                if ($host == $alias->getDomain() && strpos($path, $aliasPrefix) === 0) {
                    $splitLength = count(explode('/', $aliasPrefix));
                    if ($splitLength > $matchingLength) {
                        $currentSiteId = $site->getSiteId();
                        $currentLanguage = $alias->getLanguage();
                        $currentAliasId = $aliasId;
                        $matchingLength = $splitLength;
                    }
                }
            }
        }

        if (is_null($currentSiteId)) {
            throw new NonExistingSiteException();
        }

        $this->currentSiteManager->setSiteId($currentSiteId);
        $this->currentSiteManager->setLanguage($currentLanguage);

        $this->request->attributes->set('siteId', $currentSiteId);
        $this->request->attributes->set('_locale', $currentLanguage);
        $this->request->attributes->set('aliasId', $currentAliasId);
    }

    /**
     * Format a path to be compared with another formatted path
     * Ouptut a path formatted like this : /lvl1/lvl2/lvl3/ (/ if no path)
     * 
     * @param string $path
     * 
     * @return string
     */
    protected function formatPath($path)
    {
        $path = trim($path, '/') . '/';
        if (strlen($path) > 1) {
            $path = '/' . $path;
        }

        return $path;
    }

    /**
     * Get the 404 custom page for the current site / language if it has been contributed
     * 
     * @return string | null
     */
    protected function getCustom404Html()
    {
        if (!$this->currentSiteManager->getSiteId() || !$this->currentSiteManager->getSiteLanguage()) {
            return null;
        }

        $nodeId = ReadNodeInterface::ERROR_404_NODE_ID;
        $node = $this->nodeRepository->findOnePublished(
            $nodeId,
            $this->currentSiteManager->getSiteLanguage(),
            $this->currentSiteManager->getSiteId()
        );

        if ($node) {
            $site = $this->siteRepository->findOneBySiteId($node->getSiteId());

            return $this->templating->render(
                $this->getTemplate($node),
                array(
                    'node' => $node,
                    'site' => $site,
                    'parameters' => array('siteId' => $node->getSiteId(), '_locale' => $node->getLanguage())
                )
            );
        }

        return null;
    }

    /**
     * @param ReadNodeInterface $node
     *
     * @return string
     * @throws \OpenOrchestra\FrontBundle\Exception\NonExistingTemplateException
     */
    protected function getTemplate(ReadNodeInterface $node) {
        $site = $this->siteRepository->findOneBySiteId($node->getSiteId());

        $template = $node->getTemplate();
        $templateSet = $site->getTemplateSet();

        return $this->templateManager->getTemplate($template, $templateSet);
    }

    /**
     * @return array The event names to listen to
     */
    public static function getSubscribedEvents()
    {
        return array(
            KernelEvents::EXCEPTION => array('onKernelException', 50)
        );
    }
}