web-dev-qa-db-fra.com

Symfony2 - Comment utiliser __construct () dans un contrôleur et accéder à Securty.Context?

J'ai des problèmes avec Symfony2. Notamment sur l'utilisation de la fonction __construct (). la documentation officielle est terriblement mauvaise!

Je veux pouvoir utiliser les éléments suivants:

public function __construct()
{
    parent::__construct();
    $user = $this->get('security.context')->getToken()->getUser();
}

Comment puis-je avoir l'erreur suivante:

Erreur fatale: impossible d'appeler le constructeur dans /Sites/src/DEMO/DemoBundle/Controller/Frontend/HomeController.php à la ligne 11

La ligne 11 est "parent :: __ construct ();"

Je l'ai enlevé et j'ai eu la nouvelle erreur suivante

Erreur fatale: appel d'une fonction membre get () sur un non-objet dans /Sites/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php à la ligne 242

Je pense que je devrais peut-être configurer le DIC de ContainerInterface, mais je ne sais pas comment faire cela (j’ai essayé et j’ai échoué lamentablement)

Des idées les gens?

Mise à jour - J'ai essayé de changer d'étendre ContainerAware et j'ai obtenu cette erreur:

Erreur fatale: la classe DEMO\DemoBundle\Controller\Frontend\HomeController ne peut pas s'étendre depuis l'interface Symfony\Component\DependencyInjection\ContainerAwareInterface dans /Sites/src/DEMOBundle/Controller/Frontend/HomeController.php en ligne 43

En utilisant le code suivant dans le contrôleur:

<?php

namespace DEMO\DemoBundle\Controller\Frontend;

use Symfony\Component\DependencyInjection\ContainerAware;

class HomeController extends ContainerAwareInterface
{
     protected $container;

     public function setContainer(ContainerInterface $container = null)
     {
         $this->container = $container;
     }
18
Mr Pablo

Je suppose que vous étendez le contrôleur Symfony par défaut? Si tel est le cas, un regard sur le code révélera la réponse:

namespace Symfony\Bundle\FrameworkBundle\Controller;

use Symfony\Component\DependencyInjection\ContainerAware;

class Controller extends ContainerAware
{

Notez qu'il n'y a pas de construction Controller :: __ définie, donc utiliser la construction parent :: __ ne vous mènera nulle part. Si nous examinons ContainerAware:

namespace Symfony\Component\DependencyInjection;

class ContainerAware implements ContainerAwareInterface
{
    protected $container;
    public function setContainer(ContainerInterface $container = null)
    {
        $this->container = $container;
    }
}

Là encore, aucun constructeur et le conteneur ne sont disponibles jusqu'à l'appel de setContainer. Donc, substituez setContainer et mettez votre logique là-bas. Vous pouvez également créer un contrôleur autonome qui n’étend pas la classe de contrôleur de base et injecter vos dépendances directement dans le constructeur.

Mise à jour août 2017

Encore quelques coups sur ça. Si vous voulez vraiment exécuter quelque chose avant chaque contrôleur, utilisez un écouteur de contrôleur de noyau. Si tout ce dont vous avez besoin est l'utilisateur, utilisez bien sûr getUser (). Et s'il vous plaît ne pas remplacer setContainer (). Dans certains cas, cela fonctionnerait, mais cela convoluerait simplement votre code.

23
Cerad

Je veux aussi fréquemment une instance de l'utilisateur actuel dans la plupart de mes contrôleurs. Je trouve qu'il est plus facile de faire quelque chose comme ceci:

class SomeController extends Controller
{
    protected $user;

    public function getUser()
    {
        if ($this->user === null) {
            $this->user = $this->get('security.context')->getToken()->getUser();
        }
        return $this->user;
    }
}

Cependant, il s’agit d’un cas par trop simpliste. Si vous souhaitez effectuer davantage de travaux avant le lancement d'une action de contrôleur, je vous suggère de définir votre contrôleur en tant que service .

Jetez également un coup d'œil à cet article: S'éloignant du contrôleur de base _

4
leek

Je dois récupérer le gestionnaire de façade pour la ressource de mon api de repos. Ne pas utiliser le constructeur et utiliser une fonction privée me semble le plus simple et le plus simple.

/**
 * Class ExchangesController
 * @RouteResource("Exchange")
 */
class ExchangesController extends Controller
{
    /**
     * Get exchange manager
     * @return ExchangeManager
     */
    protected function getExchangeManager()
    {
        return $this->get('exchange_manager');
    }

    /**
     * @ApiDoc(
     *  description="Retrieve all exchanges",
     *  statusCodes={
     *    200="Successful"
     *  }
     * )
     */
    public function cgetAction()
    {
        return $this->getExchangeManager()->findAll();
    }

PS Je peux utiliser des fonctions privées/protégées dans mon contrôleur tant qu'il ne contient aucune condition

1
Tjorriemorrie

Il n'y a que deux solutions à ce problème:

  1. Utilisez une méthode privée comme indiqué par @Tjorriemorrie ici . Mais c'est une méthode sale pour les puristes. (J'utilise ceci!: D);

  2. Définissez le contrôleur en tant que service, mais vous perdrez ainsi tous les raccourcis fournis par Symfony\Bundle\FrameworkBundle\Controller\Controller. Ici est l'article qui montre comment faire cela.

Comme dit, personnellement, dans ma situation, je préfère une solution comme celle-ci:

class MyController extends Controller
{
    /** @var AwesomeDependency */
    private $dependency;

    public function anAction()
    {
         $result = $this->getDependency();
    }

    /**
     * Returns your dependency.
     */
    private function getDependency()
    {
        if (null === $this->dependency)
            $this->dependency = $this->get('your.awesome.dependency');

        return $this->dependency;
    }
}

Il s’agit généralement d’une classe que j’appelle MyManager dans laquelle je mets le code que j’utilise dans plusieurs actions du contrôleur ou qui occupe inutilement des lignes (par exemple, le code pour créer et remplir des formulaires, ou un autre code pour effectuer des tâches lourdes nécessitant beaucoup de code).

Ainsi, le code reste clair dans le but de l'action, sans ajouter de confusion.

Peut-être que l’utilisation d’une propriété pour stocker la dépendance est une suroptimisation, mais ... j’aime bien :)

0
Aerendir

Je sais que la question est très ancienne, mais je n’ai trouvé aucune réponse jusqu’à présent. Alors je vais le partager.

L'objectif ici, est d'exécuter un code chaque fois qu'une action de notre contrôleur est appelée.

La méthode __construct ne fonctionne pas, car elle est appelée avant toute autre chose. Vous ne pouvez donc pas accéder au conteneur de services.

L'astuce consiste à surcharger chaque méthode automatiquement quand elles sont appelées:

<?php
namespace AppBundle\DefaultController;

class DefaultController extends Controller {
    private function method1Action() {
        return $this->render('method1.html.twig');
    }

    private function method2Action() {
        return $this->render('method2.html.twig');
    }

    public function __call($method, $args) {
         $user = $this->get('security.tokenStorage')->getToken()->getUser();

         // Do what you want with the User object or any service. This will be executed each time before one of those controller's actions are called.

        return call_user_func_array(array($this, $method), $args);
    }
}

Attention ! Vous devez définir chaque méthode en tant que méthode privée! Ou la méthode magique __call ne sera pas appelée.

0
Thibault Henry

Vous ne pouvez pas appeler getUser () ou get () pour des services dans les constructeurs de contrôleur. Si vous vous en souvenez, vous épargnerez beaucoup de temps de débogage.

0
highroller