2012-04-03 1 views
37

Sto tentando di inserire l'utente attualmente connesso in un servizio. Il mio obiettivo è estendere alcune funzionalità di ramoscello per produrlo in base alle preferenze dell'utente. In questo esempio voglio stampare qualsiasi funzione di data usando il fuso orario specifico dell'utente.Symfony2: Inject current user in Service

Non sembra esserci alcun modo per iniettare l'utente corrente in un servizio, il che mi sembra davvero strano. Quando si inserisce il contesto di sicurezza, non ha un token anche se l'utente ha effettuato il login

Sto usando il pacchetto utente 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 

Mi manca qualcosa?

risposta

41

vorrei utilizzare un'estensione ramoscello per questo:

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 
    } 

Ora 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> 

Poi nel ramoscello si potrebbe fare:

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

Grazie a questo ha funzionato perfettamente. Ancora non capisco perché il mio primo esempio non abbia restituito l'oggetto utente, ma l'ho fatto funzionare e penso che Twig Extension sia in realtà un approccio più pulito. Grazie per la rapida risposta – n0xie

+2

Beh, stavi ascoltando l'evento 'kernel.request'. L'impostazione del token di contesto mediante autenticazione o lettura del token dalla sessione viene eseguita dopo che tutti gli ascoltatori 'kernel.request' sono stati inviati. Se hai ascoltato l'evento 'kernel.response', avresti ottenuto il token desiderato. Mi dispiace anche di aver letto male la tua domanda. È una brutta giornata per me :) –

+0

Questo ha senso. Grazie per l'esplorazione. Impari qualcosa ogni giorno – n0xie

-9

È possibile provare a iniettare @service_container e fare $this->container->get('security.context')->getToken()->getUser();.

+26

Iniettare l'intero contenitore è una cattiva idea in più casi. Invece dovresti iniettare ogni servizio che hai bisogno esplicitamente. –

+0

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

+2

Le estensioni di Twig sono una delle eccezioni a questa regola . –

4

L'utente è un cattivo candidato essere un servizio.

  • In primo luogo si tratta di un modello di un servizio non
  • In secondo luogo v'è il servizio security.context dove è possibile ottenere utente.

In un modello di ramoscello è possibile utilizzare app.user. Vedi symfony doc global-template-variables. Se vuoi mostrare qualcosa in base alle autorizzazioni utente puoi fare {{is_granted ('ROLE_USER')}}.

56

Penso che questa domanda meriti una risposta aggiornata dal 2.6.x + dal momento che i nuovi miglioramenti del componente di sicurezza.

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

Suggerirei vincolante un evento diverso, se si utilizza l'evento kernel.controller, si avrà un token e non hanno alcun problema. Il token non è disponibile in kernel.request dal Symfony 2.3

ho scritto una guida su come implementare Timezones utente per Symfony 2.3+ e 2.6+ in Ramoscello sul mio blog chiamato Symfony 2.6+ User Timezones.

Questo è di gran lunga superiore all'utilizzo di un'estensione Twig perché è possibile utilizzare le funzioni di formattazione della data standard in Twig, oltre a fornire data UTC back-end separato, fusi orari di Twig date e fusi orari di Twig date definiti dall'utente.

Ecco l'estratto più 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); 
       } 
      } 
     } 
    } 
} 

ora che è possibile utilizzare ramoscello come normale e utilizza le preferenze dell'utente, se disponibile.

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

Questa è la migliore risposta in 2016 per symfony 2.6 e versioni successive – 10us

+10

Funziona al di fuori di un firewall di sicurezza? Voglio dire, restituisce "null" o genera un'eccezione perché getToken() restituisce null e -> getUser() non può essere applicato su un oggetto nullo? –

+1

Funziona bene per Symfony 3.2 – Londeren

2

Da Symfony 2.6.

È necessario utilizzare @ 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 
} 

}

E Services.yml

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

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