Retrieve current user in Symfony app while respecting LoD

92 Views Asked by At

I'm having some issues understanding how the Law of Demeter should be applied in some cases with Symfony's DI system.

I have some factory that requires to access current logged in user in the application. To do that I need to require @security.token_storage to inject it as a constructor argument.

But in my factory, to access the user I will need to do : $tokenStorage->getToken()->getUser(), and worst, if I want to access some property of my user, I will need to dive one level deeper.

How would you fix this issue according to the law of demeter ?

Here is a sample of my code :

class SomeFactory
{

    /**
     * @var User
     */
    private $currentUser;

    /**
     * @param TokenStorageInterface $tokenStorage
     */
    public function __construct(TokenStorageInterface $tokenStorage)
    {
        $this->currentUser = $this->setCurrentUser($tokenStorage);
    }

    /**
     * @param TokenStorageInterface $tokenStorage
     */
    protected function setCurrentUser(TokenStorageInterface $tokenStorage)
    {
        if ($tokenStorage->getToken()
            && $tokenStorage->getToken()->getUser()
            && in_array('ADMIN_ROLE', $tokenStorage->getToken()->getUser()->getRoles())
        ) {
            $this->currentUser = $tokenStorage->getToken()->getUser();
        }
    }
}

I hope i am being clear.

Thank you very much :)

1

There are 1 best solutions below

0
On

It seems that in DI factories the session has not been initialized, which makes the current user token unavailable, at this point.

What I did to make it work:

  1. Register a new event listener for kernel.event:

    services:
      before_request_listener:
        class: App\EventListener\MyRequestListener
        tags:
            -
                name: kernel.event_listener
                event: kernel.request
                method: onKernelRequest
    
    
  2. In MyRequestListener I've added my service as dependency (which invokes the factory) and provides the service, with incomplete data. Also I require Security:

    public function __construct(\Symfony\Component\Security\Core\Security $security, MyService $service)
    {
        $this->security = $security;
        $this->service = $service;
    }
    
  3. Now, in MyRequestListener::onKernelRequest I can access the user's data and add/change the incomplete properties of my service.

    public function onKernelRequest(): void
    {
        if ($user = $this->security->getUser()) {
            $this->service->whatever = $user->whatever;
        }
    }
    

Because Symfony uses the same instance of MyService, those modification will be available in all further services.

But keep in mind, that your service, also needs to deal with the incomplete data, when no active user session is existing (e.g. because no user is logged in, or on CLI).