2012-04-03 14 views
37

Estoy intentando inyectar el usuario actualmente conectado a un servicio. Mi objetivo es ampliar algunas funcionalidades de ramas para que se muestren en función de las preferencias del usuario. En este ejemplo, quiero mostrar cualquier función de fecha usando la zona horaria específica del usuario.Symfony2: Inyectar usuario actual en el Servicio

No parece haber ninguna forma de inyectar al usuario actual en un servicio, lo cual me parece realmente extraño. Al inyectar el contexto de seguridad, no tiene un token aunque el usuario haya iniciado sesión en

Estoy utilizando el paquete de usuario de FOS.

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 

¿Falta algo?

Respuesta

41

me gustaría utilizar una extensión de rama para que:

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 
    } 

Ahora en 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> 

Luego, en rama que podría hacer:

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

Gracias, funcionó a la perfección. Todavía no entiendo por qué mi primer ejemplo no devolvió el objeto del usuario, pero lo hice funcionar, y creo que una extensión de Twig es en realidad un enfoque más limpio. Gracias por la respuesta rápida – n0xie

+2

Bueno, estabas escuchando el evento 'kernel.request'. La configuración del token de contexto mediante la autenticación o lectura del token de la sesión se realiza después de que se envíen todos los oyentes 'kernel.request'. Si has escuchado el evento 'kernel.response', entonces tendrías el token deseado. También siento haber leído mal tu pregunta. Es un mal día para mí :). –

+0

Eso tiene sentido. Gracias por la explicación. Aprendes algo cada día. – n0xie

-9

Puede intentar inyectar @service_container y do $this->container->get('security.context')->getToken()->getUser();.

+26

Inyectar todo el contenedor es una mala idea en la mayoría casos. En su lugar, debe inyectar cada servicio que necesita de forma explícita. –

+0

@elnur, dígaselo a [FriendsOfSymfony] (https://github.com/FriendsOfSymfony/FOSFacebookBundle/blob/master/Twig/Extension/FacebookExtension.php#L26) :) –

+2

Las extensiones de Twig son una de las excepciones a esta regla . –

4

El usuario es un mal candidato ser un servicio.

  • En primer lugar, no es un modelo de un servicio
  • En segundo lugar hay servicio security.context donde se puede obtener a partir de usuario.

En una plantilla de rama se puede usar app.user. Ver symfony doc global-template-variables. Si desea mostrar algo en función de los permisos del usuario, puede hacer {{is_granted ('ROLE_USER')}}.

56

Creo que esta pregunta merece una respuesta actualizada desde 2.6.x + desde las nuevas mejoras del componente de seguridad.

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" 
1

Yo recomendaría unirse a un evento diferente, si se utiliza el evento kernel.controller, tendrá una ficha y no tienen ningún problema. La ficha no está disponible en kernel.request desde Symfony 2.3

escribí una guía sobre cómo implementar zonas horarias de usuario para Symfony 2.3+ y 2.6+ en la ramita en mi blog llamado Symfony 2.6+ User Timezones.

Esto es muy superior al uso de Twig Extension porque puede usar las funciones de formato de fecha estándar en Twig, así como proporcionar fecha UTC independiente del backend, zonas horarias de fecha Twig predeterminadas y zonas horarias de fecha Twig definidas por el usuario.

Aquí está el extracto más importante:

src/AppBundle/EventListener/TwigSubscriber.php

<?php 

namespace AppBundle\EventListener; 

use AppBundle\Entity\User; 
use Symfony\Component\EventDispatcher\EventSubscriberInterface; 
use Symfony\Component\HttpKernel\Event\FilterControllerEvent; 
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; 

class TwigSubscriber implements EventSubscriberInterface 
{ 
    protected $twig; 
    protected $tokenStorage; 

    function __construct(\Twig_Environment $twig, TokenStorageInterface $tokenStorage) 
    { 
     $this->twig = $twig; 
     $this->tokenStorage = $tokenStorage; 
    } 

    public static function getSubscribedEvents() 
    { 
     return [ 
      'kernel.controller' => 'onKernelController' 
     ]; 
    } 

    public function onKernelController(FilterControllerEvent $event) 
    { 
     $token = $this->tokenStorage->getToken(); 

     if ($token !== null) { 
      $user = $token->getUser(); 

      if ($user instanceof User) { 
       $timezone = $user->getTimezone(); 
       if ($timezone !== null) { 
        $this->twig->getExtension('core')->setTimezone($timezone); 
       } 
      } 
     } 
    } 
} 

ya se puede utilizar ramita de forma normal y utiliza sus preferencias de usuario, si está disponible.

29

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

+2

Esta es la mejor respuesta en 2016 para Symfony 2.6 y superior – 10us

+10

¿Funciona fuera de un firewall de seguridad? Es decir, ¿devuelve "nulo" o arroja una excepción porque getToken() devuelve nulo y -> getUser() no se puede aplicar en un objeto nulo? –

+1

Funciona bien para Symfony 3.2 – Londeren

2

Desde Symfony 2.6.

usted necesita el uso @ 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 
} 

}

Y Services.yml

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

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

Cuestiones relacionadas