Symfony2: Inject current user in Service

43,977

Solution 1

I would use a twig extension for that:

class UserDateExtension extends \Twig_Extension
{
    private $context;

    public function __construct(SecurityContext $context)
    {
        $this->context = $context;
    }

    public function getUser()
    {
        return $this->context->getToken()->getUser();
    }

    public function getFilters()
    {
        return array(
            'user_date' => new \Twig_Filter_Method($this, "formatUserDate"),
        );
    }

    public function formatUserDate($date, $format)
    {
        $user = $this->getUser();
        // do stuff
    }

Now in services.xml

    <service id="user_date_twig_extension" class="%user_date_twig_extension.class%">
        <tag name="twig.extension" />
        <argument type="service" id="security.context" />
    </service>

Then in twig you could do:

{{ date | user_date('d/m/Y') }}

Solution 2

I think that this question deserves an updated answer since 2.6.x+ since the new security component improvements.

use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;

class UserDateExtension extends \Twig_Extension
{
    /**
     * @var TokenStorage
     */
    protected $tokenStorage;


    /**
     * @param \Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage    $tokenStorage
     */
    public function __construct(TokenStorage $tokenStorage)
    {
        $this->tokenStorage = $tokenStorage;
    }

    public function getUser()
    {
        return $this->tokenStorage->getToken()->getUser();
    }

    public function getFilters()
    {
        return array(
            'user_date' => new \Twig_Filter_Method($this, "formatUserDate"),
        );
    }

    public function formatUserDate($date, $format)
    {
        $user = $this->getUser();
        // do stuff
    }
}

Services.yml

twig.date_extension:
    class: Acme\Twig\SpecialDateExtension
    tags:
        - { name: twig.extension }
    arguments:
        - "@security.token_storage"

Solution 3

services.yml

my_service:
    class: ...
    arguments:
        - "@=service('security.token_storage').getToken().getUser()"

Service.php

protected $currentUser;

public function __construct($user)
{
    $this->currentUser = $user;
}

http://symfony.com/doc/current/book/service_container.html#using-the-expression-language

Solution 4

The user is a bad candidate to be a service.

  • First it is a model not a service
  • Second there is service security.context where you can get user from.

In a twig template you can use app.user. See symfony doc global-template-variables. If you want to show something based on user permissions you can do {{ is_granted('ROLE_USER') }}.

Solution 5

From Symfony 2.6.

You need use @security.token_storage

use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;

class UserDateExtension extends \Twig_Extension
{
/**
 * @var TokenStorageInterface
 */
protected $tokenStorage;


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

public function getUser()
{
    return $this->tokenStorage->getToken()->getUser();
}

public function getFilters()
{
    return array(
        'user_date' => new \Twig_Filter_Method($this, "formatUserDate"),
    );
}

public function formatUserDate($date, $format)
{
    $user = $this->getUser();
    // do stuff
}

}

And Services.yml

twig.date_extension:
    class: Acme\Twig\SpecialDateExtension
    tags:
        - { name: twig.extension }
    arguments: ["@security.token_storage"]

reference: http://symfony.com/blog/new-in-symfony-2-6-security-component-improvements

Share:
43,977
n0xie
Author by

n0xie

Updated on March 13, 2020

Comments

  • n0xie
    n0xie about 4 years

    I am trying to inject the currently logged in user into a service. My goal is to extend some twig functionality to output it based on user preferences. In this example I want to output any date function using the user specific Timezone.

    There doesn't seem to be any way to inject the current user into a service, which seems really odd to me. When injecting the security context, it doesn't have a token even if the user is logged in

    I am using FOS user bundle.

    services:
        ...
        twigdate.listener.request:
            class: App\AppBundle\Services\TwigDateRequestListener
            arguments: [@twig, @security.context]
            tags:
                - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
    
    
    <?php
    
    namespace App\AppBundle\Services;
    
    use Symfony\Component\HttpKernel\HttpKernelInterface;
    use Symfony\Component\HttpKernel\Event\GetResponseEvent;
    
    class TwigDateRequestListener
    {
        protected $twig;
    
        function __construct(\Twig_Environment $twig, SecurityContext $context) {
    
            $this->twig = $twig;
            //$this->user = $context->get...;
    
            var_dump($context); die;
        }
    
        public function onKernelRequest(GetResponseEvent $event) {
           // $this->twig->getExtension('core')->setDateFormat($user->getProfile()->getFormat());
           // $this->twig->getExtension('core')->setTimeZone($user->getProfile()->getTimezone());
        }
    }
    
    output:
    
    object(Symfony\Component\Security\Core\SecurityContext)[325]
      private 'token' => null
      private 'accessDecisionManager' => 
        object(Symfony\Component\Security\Core\Authorization\AccessDecisionManager)[150]
          private 'voters' => 
            array
              0 => 
                object(Symfony\Component\Security\Core\Authorization\Voter\RoleHierarchyVoter)[151]
                  ...
              1 => 
                object(Symfony\Component\Security\Core\Authorization\Voter\AuthenticatedVoter)[153]
                  ...
              2 => 
                object(Symfony\Component\Security\Acl\Voter\AclVoter)[155]
                  ...
          private 'strategy' => string 'decideAffirmative' (length=17)
          private 'allowIfAllAbstainDecisions' => boolean false
          private 'allowIfEqualGrantedDeniedDecisions' => boolean true
      private 'authenticationManager' => 
        object(Symfony\Component\Security\Core\Authentication\AuthenticationProviderManager)[324]
          private 'providers' => 
            array
              0 => 
                object(Symfony\Component\Security\Core\Authentication\Provider\DaoAuthenticationProvider)[323]
                  ...
              1 => 
                object(Symfony\Component\Security\Core\Authentication\Provider\AnonymousAuthenticationProvider)[149]
                  ...
          private 'eraseCredentials' => boolean true
      private 'alwaysAuthenticate' => boolean false
    

    Am I missing something?

  • Elnur Abdurrakhimov
    Elnur Abdurrakhimov about 12 years
    Injecting the whole container is a bad idea in most cases. Instead you should inject each service you need explicitly.
  • Mun Mun Das
    Mun Mun Das about 12 years
    @elnur, Tell that to FriendsOfSymfony :)
  • Elnur Abdurrakhimov
    Elnur Abdurrakhimov about 12 years
    Twig extensions is one of the exceptions to this rule.
  • n0xie
    n0xie about 12 years
    Thanks this worked perfectly. I still don't understand why my first example didn't return the user object, but I got it working, and I think a Twig Extension is actually a cleaner approach. Thanks for the quick reply
  • Mun Mun Das
    Mun Mun Das about 12 years
    @elnur, Sorry I was wrong. It seems that the helper service FacebookExtension uses have templating.helper tag. Which may conflict with the extension. You are right injecting @service_container is a bad Idea.
  • Mun Mun Das
    Mun Mun Das about 12 years
    Well you were listening for kernel.request event. Setting context token by authentication or reading token from session are done after all kernel.request listeners are dispatched. If you have listened for kernel.response event then you would have get desired token. Also sorry to misread your question. It is a bad day for me :).
  • n0xie
    n0xie about 12 years
    That makes sense. Thanks for the explenation. You learn something every day.
  • Waiting for Dev...
    Waiting for Dev... over 11 years
    You can inject just security.context. You do not need to inject all the service container.
  • skonsoft
    skonsoft over 10 years
    Don't inject the whole container !
  • Mun Mun Das
    Mun Mun Das over 10 years
    @skonsoft, yes you are right. At the time I answered the question I had confusion about service locator anti-pattern. But still I did not update the answer because I wanted to leave trace of my own stupidity at that time :)
  • nutrija
    nutrija over 8 years
    SecurityContext (security.context) is deprecated since 2.6. Use security.token_storage instead
  • pawel.kalisz
    pawel.kalisz about 8 years
    Never inject the whole container!!
  • 10us
    10us almost 8 years
    This is the best answer in 2016 for symfony 2.6 and up
  • Flo Schild
    Flo Schild almost 8 years
    Does it work outside of a security firewall? I mean, does it return "null" or does it throw an exception because getToken() returns null and ->getUser() cannot be applied on a null object?
  • Thomas Kekeisen
    Thomas Kekeisen over 7 years
    In addition to the comment from @nutrija, see: symfony.com/blog/…
  • Londeren
    Londeren about 7 years
    Works fine for Symfony 3.2
  • userfuser
    userfuser over 6 years
    I believe this was throwing errors when there's no user.