2

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 :)

  • It's doubtful that even Demeter expected his self-proclaimed law to be taken seriously. You could probably define a factory service to inject the current user with a type hint. But you need to be careful. The user is not available until part way through the request processing cycle. If your SomeFactory is instantiated before the user is available then you would suffer the consequences of premature injection. – Cerad Nov 25 '18 at 16:53
  • Yes actually that's another issue I just encountered. Since I'm trying to inject the user too early, the conditions are never met in my setCurrentUser method. It seems like what ritter is suggesting on this post is true : https://stackoverflow.com/questions/32590621/tokenstorage-sometimes-returns-null-in-service The service should keep a reference to the tokenStorage so that when the method needing the current user is called, it can access the right value of the user logged in at that particular moment. Otherwise the value is null. So I guess Demeter is not something to be applied here. – simsComputing Nov 25 '18 at 17:10
  • I've got the exact same issue. I'm able to access the HTTP request, but without session. My token storage also has no token. Same goes for Security. I think those factories are executed very early in request bootstrap. Apparently, before the session has been initialized. – Armin Mar 17 '22 at 12:45

1 Answers1

0

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).

Armin
  • 15,582
  • 10
  • 47
  • 64