Доступ к контейнеру или securityContext или EntityManager из MenuBuilder через RequestVoter

Я нашел этот фрагмент кода в Gist (где-то я потерял ссылку), и мне нужно что-то подобное, поэтому я начал использовать в своем приложении, но я еще не полностью понял, и поэтому у меня есть некоторые проблемы.

Я пытаюсь создать динамическое меню с помощью KnpMenuBundle и динамических средств, в какой-то момент я должен проверить права доступа через базу данных, и было бы идеально, если бы я мог читать маршруты с контроллеров, но это еще одна задача, возможно, создавая аннотацию, я могу это сделать но я открою другую тему, когда придет время.

Прямо сейчас мне нужно получить доступ к SecurityContext, чтобы проверить, вошел ли пользователь в систему или нет, но не знаю как.

Я рендеринг меню, хотя RequestVoter (Я думаю) и это код:

namespace PlantillaBundle\Menu;

use Knp\Menu\ItemInterface;
use Knp\Menu\Matcher\Voter\VoterInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Security\Core\SecurityContextInterface; 

class RequestVoter implements VoterInterface {

    private $container;

    private $securityContext; 

    public function __construct(ContainerInterface $container, SecurityContextInterface $securityContext)
    {
        $this->container = $container;
        $this->securityContext = $securityContext;
    }

    public function matchItem(ItemInterface $item)
    {
        if ($item->getUri() === $this->container->get('request')->getRequestUri())
        {
            // URL's completely match
            return true;
        }
        else if ($item->getUri() !== $this->container->get('request')->getBaseUrl() . '/' && (substr($this->container->get('request')->getRequestUri(), 0, strlen($item->getUri())) === $item->getUri()))
        {
            // URL isn't just "/" and the first part of the URL match
            return true;
        }
        return null;
    }

}

Весь код, связанный с securityContext был добавлен мной в попытке работать с ним из menuBuilder, Теперь это код, где я делаю меню:

namespace PlantillaBundle\Menu;

use Knp\Menu\FactoryInterface;
use Symfony\Component\DependencyInjection\ContainerAware;

class MenuBuilder extends ContainerAware {

    public function mainMenu(FactoryInterface $factory, array $options)
    {
        // and here is where I need to access securityContext 
        // and in the near future EntityManger

        $user = $this->securityContext->getToken()->getUser();
        $logged_in = $this->securityContext->isGranted('IS_AUTHENTICATED_FULLY');

        $menu = $factory->createItem('root');
        $menu->setChildrenAttribute('class', 'nav');

        if ($logged_in)
        {
            $menu->addChild('Home', array('route' => 'home'))->setAttribute('icon', 'fa fa-list');
        }
        else
        {
            $menu->addChild('Some Menu');
        }

        return $menu;
    }     

}

Но это совершенно неправильно, так как я не прохожу securityContext к методу, и я не знаю, как и я получаю эту ошибку:

Исключение было выдано во время рендеринга шаблона ("Примечание: неопределенное свойство: PlantillaBundle\Menu\MenuBuilder::$securityContext в /var/www/html/src/PlantillaBundle/Menu/MenuBuilder.php строка 12") в /var/www/html/src/PlantillaBundle/Resources/views/menu.html.twig в строке 2.

Избиратель определяется в services.yml следующим образом:

plantilla.menu.voter.request:
    class: PlantillaBundle\Menu\RequestVoter
    arguments:
        - @service_container
        - @security.context
    tags:
        - { name: knp_menu.voter }

Итак, как я ввожу securityContext (Я не буду просить EntityManager, так как, как я полагаю, будет та же процедура) и доступ к нему из menuBuilder?

Обновление: рефакторинг кода

Итак, после предложения @Cerad я внес эти изменения:

services.yml

services:
    plantilla.menu_builder:
        class: PlantillaBundle\Menu\MenuBuilder
        arguments: ["@knp_menu.factory", "@security.context"]

    plantilla.frontend_menu_builder:
        class: Knp\Menu\MenuItem # the service definition requires setting the class
        factory_service: plantilla.menu_builder
        factory_method: createMainMenu
        arguments: ["@request_stack"]
        tags:
            - { name: knp_menu.menu, alias: frontend_menu } 

MenuBuilder.php

namespace PlantillaBundle\Menu;

use Knp\Menu\FactoryInterface;
use Symfony\Component\HttpFoundation\RequestStack;

class MenuBuilder {

    /**
     * @var Symfony\Component\Form\FormFactory $factory
     */
    private $factory;

    /**
     * @var Symfony\Component\Security\Core\SecurityContext $securityContext
     */
    private $securityContext;

    /**
     * @param FactoryInterface $factory
     */
    public function __construct(FactoryInterface $factory, $securityContext)
    {
        $this->factory = $factory;
        $this->securityContext = $securityContext;
    }

    public function createMainMenu(RequestStack $request)
    {
        $user = $this->securityContext->getToken()->getUser();
        $logged_in = $this->securityContext->isGranted('IS_AUTHENTICATED_FULLY');

        $menu = $this->factory->createItem('root');
        $menu->setChildrenAttribute('class', 'nav');

        if ($logged_in)
        {
            $menu->addChild('Home', array('route' => 'home'))->setAttribute('icon', 'fa fa-list');
        }
        else
        {
            $menu->addChild('Some Menu');
        }

        return $menu;
    }

}

Абд иб мой шаблон просто сделать меню {{ knp_menu_render('frontend_menu') }} но теперь я теряю часть FontAwesome и до того, как она заработает, почему?

2 ответа

Решение

Согласно вашему запросу на комментарий, вот как может выглядеть ваш построитель меню:

namespace PlantillaBundle\Menu;

use Knp\Menu\FactoryInterface;

class MenuBuilder {

    protected $securityContext;

    public function __construct($securityContext)
    {
        $this->securityContext = $securityContext;
    }
    public function mainMenu(FactoryInterface $factory, array $options)
    {
        // and here is where I need to access securityContext 
        // and in the near future EntityManger

        $user = $this->securityContext->getToken()->getUser();
        ...

// services.yml
plantilla.menu.builder:
    class: PlantillaBundle\Menu\MenuBuilder
    arguments:
        - '@security.context'

// controller
$menuBuilder = $this->container->get('plantilla.menu.builder');

Обратите внимание, что нет необходимости оповещать контейнер компоновщика, поскольку вам нужна только служба контекста безопасности. Конечно, вы также можете ввести менеджер сущностей.

================================

Что касается материала для избирателей, сейчас вы проверяете только, вошел ли пользователь в систему. Так что в действительности нет необходимости в избирателях. Но предположим, что определенные пользователи (администраторы и т. Д.) Имели доступ к дополнительным пунктам меню. Вы можете переместить всю логику проверки безопасности избирателю. Ваш код конструктора меню может выглядеть так:

if ($this->securityContext->isGranted('view','homeMenuItem')
{
    $menu->addChild('Home', array('route' ...

Другими словами, вы можете получить более точный контроллер того, кто получает какой пункт меню.

Но сначала запустите ваш MenuBuilder, а затем добавьте материал для избирателей, если это необходимо.

Ваш конструктор меню ContainerAware, так что я думаю, что в нем вы должны получить доступ к SecurityContext с помощью $this->getContainer()->get('security.context'),

И вы не предоставили никаких вариантов использования для класса избирателей, так что я предполагаю, что вы не используете matchItem метод.

Вы должны определенно попытаться реструктурировать свои услуги так, чтобы зависимости были очевидны.

Другие вопросы по тегам