Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Load Symfony's parameter with compiler pass from custom service

According to this question How to load Symfony's config parameters from database (Doctrine) I have a similar problem. I need to set the parameter dynamically and I want to provide data from another custom service.

So, I have Event Listener which setting current account entity (by sub-domain or currently logged user)

namespace AppBundle\EventListener;

use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
use Doctrine\ORM\EntityManager;
use AppBundle\Manager\AccountManager;

use Palma\UserBundle\Entity\User;

/**
 * Class CurrentAccountListener
 *
 * @package AppBundle\EventListener
 */
class CurrentAccountListener {

    /**
     * @var TokenStorage
     */
    private $tokenStorage;

    /**
     * @var EntityManager
     */
    private $em;

    /**
     * @var AccountManager
     */
    private $accountManager;

    private $baseHost;

    public function __construct(TokenStorage $tokenStorage, EntityManager $em, AccountManager $accountManager, $baseHost) {
        $this->tokenStorage = $tokenStorage;
        $this->em = $em;
        $this->accountManager = $accountManager;
        $this->baseHost = $baseHost;
    }

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

        $accountManager = $this->accountManager;
        $accountManager->setCurrentAccount( $this->getCurrentAccount($request) );
    }

    private function getCurrentAccount($request){
        if($this->getCurrentAccountByLoggedUser()) {
            return $this->getCurrentAccountByLoggedUser();
        }
        if($this->getCurrentAccountBySubDomain($request) ) {
            return $this->getCurrentAccountBySubDomain($request);
        }
        return;
    }

    private function getCurrentAccountBySubDomain($request) {
        $host = $request->getHost();
        $baseHost = $this->baseHost;

        $subdomain = str_replace('.'.$baseHost, '', $host);

        $account = $this->em->getRepository('AppBundle:Account')
                ->findOneBy([ 'urlName' => $subdomain ]);

        if(!$account) return;

        return $account;
    }

    private function getCurrentAccountByLoggedUser() {
        if( is_null($token = $this->tokenStorage->getToken()) ) return;

        $user = $token->getUser();
        return ($user instanceof User) ? $user->getAccount() : null;
    }

}

services.yml

app.eventlistener.current_account_listener:
    class: AppBundle\EventListener\CurrentAccountListener
    arguments:
        - "@security.token_storage"
        - "@doctrine.orm.default_entity_manager"
        - "@app.manager.account_manager"
        - "%base_host%"
    tags:
        - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }

And very simply account manager with setter and getter only. If I need access to current account I call

$this->get('app.manager.account_manager')->getCurrentAccount();

Everything works fine.

Now I am trying set some parameter from my service with compiler pass

namespace AppBundle\DependencyInjection\Compiler;

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;

class ParametersCompilerPass implements CompilerPassInterface {

    const ACCOUNT_MANAGER_SERVICE_ID = 'app.manager.account_manager';

    public function process(ContainerBuilder $container) {

        if(!$container->has(self::ACCOUNT_MANAGER_SERVICE_ID)) {
            return;
        }

        $currentAccount = $container->get(self::ACCOUNT_MANAGER_SERVICE_ID)
            ->getCurrentAccount();

        $container->setParameter(
            'current_account', $currentAccount
        );
    }

}

AppBundle.php

    namespace AppBundle;

    use AppBundle\DependencyInjection\Compiler\ParametersCompilerPass;
    use Symfony\Component\DependencyInjection\Compiler\PassConfig;
    use Symfony\Component\DependencyInjection\ContainerBuilder;
    use Symfony\Component\HttpKernel\Bundle\Bundle;

    class AppBundle extends Bundle
    {
        public function build(ContainerBuilder $container)
        {
            parent::build($container);

            $container->addCompilerPass(new ParametersCompilerPass(), PassConfig::TYPE_AFTER_REMOVING);
        }
}

I got current_account as null every time, no matter what PassConfig I use. Any ideas?

Thank you for your attention.

like image 651
Mike Base Avatar asked Nov 15 '17 17:11

Mike Base


1 Answers

Compilation pass are executed when you run Symfony for the first time (CLI command or first http request). Once the cache is build (compiled) this code it never gets executed again.

Solution with parameters [I do not recommend this]

If your parameter can change from one to another HTTP Request you should not use a parameter as some services may be initialized before your parameter is ready and others after. Although if this is the way you want to go, you can add an event that listens to the kernel request and modify/set the parameter there. Have a look at https://symfony.com/doc/current/components/http_kernel.html#component-http-kernel-event-table

Current account in the user / session

If currentAccount depends on the user logged, why you do not store that info in the user or session and access to it from your services?

like image 100
albert Avatar answered Oct 24 '22 00:10

albert