浏览代码

Merge pull request #3577 from andrey1s/menu

use knp voter for the active menu
Oskar Stark 9 年之前
父节点
当前提交
1b9f421e88

+ 31 - 0
Menu/Matcher/Voter/ActiveVoter.php

@@ -0,0 +1,31 @@
+<?php
+
+/*
+ * This file is part of the Sonata Project package.
+ *
+ * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Sonata\AdminBundle\Menu\Matcher\Voter;
+
+use Knp\Menu\ItemInterface;
+use Knp\Menu\Matcher\Voter\VoterInterface;
+
+/**
+ * Active menu voter bases in extra `active`.
+ *
+ * @author Samusev Andrey <andrey.simfi@ya.ru>
+ */
+class ActiveVoter implements VoterInterface
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function matchItem(ItemInterface $item)
+    {
+        return $item->getExtra('active', null);
+    }
+}

+ 59 - 0
Menu/Matcher/Voter/AdminVoter.php

@@ -0,0 +1,59 @@
+<?php
+
+/*
+ * This file is part of the Sonata Project package.
+ *
+ * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Sonata\AdminBundle\Menu\Matcher\Voter;
+
+use Knp\Menu\ItemInterface;
+use Knp\Menu\Matcher\Voter\VoterInterface;
+use Sonata\AdminBundle\Admin\AdminInterface;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Admin menu voter based on extra `admin`.
+ *
+ * @author Samusev Andrey <andrey.simfi@ya.ru>
+ */
+class AdminVoter implements VoterInterface
+{
+    /**
+     * @var Request
+     */
+    private $request = null;
+
+    /**
+     * @param Request $request
+     *
+     * @return $this
+     */
+    public function setRequest($request)
+    {
+        $this->request = $request;
+
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function matchItem(ItemInterface $item)
+    {
+        $admin = $item->getExtra('admin');
+        $match = null;
+        if ($admin instanceof AdminInterface
+            && $admin->hasRoute('list') && $admin->isGranted('LIST')
+            && $this->request && $this->request->get('_sonata_admin') == $admin->getCode()
+        ) {
+            $match = true;
+        }
+
+        return $match;
+    }
+}

+ 56 - 0
Menu/Matcher/Voter/ChildrenVoter.php

@@ -0,0 +1,56 @@
+<?php
+
+/*
+ * This file is part of the Sonata Project package.
+ *
+ * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Sonata\AdminBundle\Menu\Matcher\Voter;
+
+use Knp\Menu\ItemInterface;
+use Knp\Menu\Matcher\MatcherInterface;
+use Knp\Menu\Matcher\Voter\VoterInterface;
+
+/**
+ * Children menu voter based on children items.
+ *
+ * @author Samusev Andrey <andrey.simfi@ya.ru>
+ */
+class ChildrenVoter implements VoterInterface
+{
+    /**
+     * @var MatcherInterface
+     */
+    private $matcher;
+
+    /**
+     * ChildrenVoter constructor.
+     *
+     * @param MatcherInterface $matcher
+     */
+    public function __construct(MatcherInterface $matcher)
+    {
+        $this->matcher = $matcher;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function matchItem(ItemInterface $item)
+    {
+        $children = $item->getChildren();
+        $match = null;
+        foreach ($children as $child) {
+            if ($this->matcher->isCurrent($child)) {
+                $match = true;
+                break;
+            }
+        }
+
+        return $match;
+    }
+}

+ 12 - 0
Resources/config/menu.xml

@@ -21,5 +21,17 @@
             <argument type="service" id="sonata.admin.pool" />
             <tag name="knp_menu.provider" />
         </service>
+
+        <service id="sonata.admin.menu.matcher.voter.admin" class="Sonata\AdminBundle\Menu\Matcher\Voter\AdminVoter">
+            <tag name="knp_menu.voter" request="true"/>
+        </service>
+        <service id="sonata.admin.menu.matcher.voter.children" class="Sonata\AdminBundle\Menu\Matcher\Voter\ChildrenVoter">
+            <argument type="service" id="knp_menu.matcher"/>
+            <tag name="knp_menu.voter"/>
+        </service>
+        <service id="sonata.admin.menu.matcher.voter.active" class="Sonata\AdminBundle\Menu\Matcher\Voter\ActiveVoter">
+            <tag name="knp_menu.voter"/>
+        </service>
+
     </services>
 </container>

+ 2 - 25
Resources/views/Menu/sonata_menu.html.twig

@@ -16,31 +16,8 @@
     {%- endif %}
 
     {%- if item.displayed and display|default %}
-        {%- set active = false %}
-        {%- if item.extra('active') is not empty and item.extra('active') %}
-            {%- set active = true %}
-        {%- elseif item.extra('admin') is not empty and item.extra('admin').hasroute('list') and item.extra('admin').isGranted('LIST') and request.get('_sonata_admin') == item.extra('admin').code %}
-            {%- set active = true %}
-        {%- elseif item.route is defined and request.get('_route') == item.route %}
-            {%- set active = true %}
-        {%- else %}
-            {%- for child in item.children if not active %}
-                {%- if child.extra('admin') is not empty and child.extra('admin').hasroute('list') and child.extra('admin').isGranted('LIST') and request.get('_sonata_admin') == child.extra('admin').code %}
-                    {%- set active = true %}
-                {%-  elseif child.route is defined and request.get('_route') == child.route %}
-                    {%- set active = true %}
-                {%- endif %}
-            {%- endfor %}
-        {%- endif %}
-
-        {%- if item.hasChildren %}
-            {%- do item.setAttribute('class', (item.attribute('class')~' treeview')|trim) %}
-        {%- endif %}
-        {%- if active %}
-            {%- do item.setAttribute('class', (item.attribute('class')~' active')|trim) %}
-            {%- do item.setChildrenAttribute('class', (item.childrenAttribute('class')~' active')|trim) %}
-        {%- endif %}
-
+        {% set options = options|merge({branch_class: 'treeview', currentClass: "active"}) %}
+        {%- do item.setChildrenAttribute('class', (item.childrenAttribute('class')~' active')|trim) %}
         {%- do item.setChildrenAttribute('class', (item.childrenAttribute('class')~' treeview-menu')|trim) %}
         {{ parent() }}
     {% endif %}

+ 3 - 0
Tests/DependencyInjection/Compiler/AddDependencyCallsCompilerPassTest.php

@@ -417,6 +417,9 @@ class AddDependencyCallsCompilerPassTest extends \PHPUnit_Framework_TestCase
         $container
             ->register('knp_menu.menu_provider')
             ->setClass('Knp\Menu\Provider\MenuProviderInterface');
+        $container
+            ->register('knp_menu.matcher')
+            ->setClass('Knp\Menu\Matcher\MatcherInterface');
         $container
             ->register('event_dispatcher')
             ->setClass('Symfony\Component\EventDispatcher\EventDispatcherInterface');

+ 3 - 0
Tests/DependencyInjection/Compiler/ExtensionCompilerPassTest.php

@@ -361,6 +361,9 @@ class ExtensionCompilerPassTest extends \PHPUnit_Framework_TestCase
         $container
             ->register('knp_menu.factory')
             ->setClass('Knp\Menu\FactoryInterface');
+        $container
+            ->register('knp_menu.matcher')
+            ->setClass('Knp\Menu\Matcher\MatcherInterface');
         $container
             ->register('knp_menu.menu_provider')
             ->setClass('Knp\Menu\Provider\MenuProviderInterface');

+ 51 - 0
Tests/Menu/Matcher/Voter/AbstractVoterTest.php

@@ -0,0 +1,51 @@
+<?php
+
+/*
+ * This file is part of the Sonata Project package.
+ *
+ * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Sonata\AdminBundle\Tests\Menu\Matcher\Voter;
+
+use Knp\Menu\ItemInterface;
+use Knp\Menu\Matcher\Voter\VoterInterface;
+
+abstract class AbstractVoterTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @return array
+     */
+    abstract public function provideData();
+
+    /**
+     * @param mixed $dataVoter
+     *
+     * @return VoterInterface
+     */
+    abstract protected function createVoter($dataVoter);
+
+    /**
+     * @param mixed $data
+     *
+     * @return ItemInterface
+     */
+    abstract protected function createItem($data);
+
+    /**
+     * @param mixed     $itemData
+     * @param mixed     $voterData
+     * @param bool|null $expected
+     *
+     * @dataProvider provideData
+     */
+    public function testMatching($itemData, $voterData, $expected)
+    {
+        $item = $this->createItem($itemData);
+        $voter = $this->createVoter($voterData);
+        $this->assertSame($expected, $voter->matchItem($item));
+    }
+}

+ 55 - 0
Tests/Menu/Matcher/Voter/ActiveVoterTest.php

@@ -0,0 +1,55 @@
+<?php
+
+/*
+ * This file is part of the Sonata Project package.
+ *
+ * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Sonata\AdminBundle\Tests\Menu\Matcher\Voter;
+
+use Knp\Menu\ItemInterface;
+use Sonata\AdminBundle\Menu\Matcher\Voter\ActiveVoter;
+
+class ActiveVoterTest extends AbstractVoterTest
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function createVoter($dataVoter)
+    {
+        return new ActiveVoter();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function provideData()
+    {
+        return array(
+            'active'    => array(true, null, true),
+            'no active' => array(false, null, false),
+            'null'      => array(null, null, null),
+        );
+    }
+
+    /**
+     * @param mixed $data
+     *
+     * @return ItemInterface
+     */
+    protected function createItem($data)
+    {
+        $item = $this->getMock('Knp\Menu\ItemInterface');
+        $item->expects($this->any())
+             ->method('getExtra')
+             ->with('active')
+             ->will($this->returnValue($data))
+        ;
+
+        return $item;
+    }
+}

+ 89 - 0
Tests/Menu/Matcher/Voter/AdminVoterTest.php

@@ -0,0 +1,89 @@
+<?php
+
+/*
+ * This file is part of the Sonata Project package.
+ *
+ * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Sonata\AdminBundle\Tests\Menu\Matcher\Voter;
+
+use Sonata\AdminBundle\Menu\Matcher\Voter\AdminVoter;
+use Symfony\Component\HttpFoundation\Request;
+
+class AdminVoterTest extends AbstractVoterTest
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function provideData()
+    {
+        return array(
+            'no data'              => array(null, null, null),
+            'no route and granted' => array($this->getAdmin('_sonata_admin'), '_sonata_admin', null),
+            'no granted'           => array($this->getAdmin('_sonata_admin', true, false), '_sonata_admin', null),
+            'no code'              => array($this->getAdmin('_sonata_admin_code', true, true), '_sonata_admin', null),
+            'no code request'      => array($this->getAdmin('_sonata_admin', true, true), '_sonata_admin_unexpected', null),
+            'no route'             => array($this->getAdmin('_sonata_admin', false, true), '_sonata_admin', null),
+            'has admin'            => array($this->getAdmin('_sonata_admin', true, true), '_sonata_admin', true),
+        );
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function createVoter($dataVoter)
+    {
+        $voter = new AdminVoter();
+        $request = new Request();
+        $request->request->set('_sonata_admin', $dataVoter);
+        $voter->setRequest($request);
+
+        return $voter;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function createItem($data)
+    {
+        $item = $this->getMock('Knp\Menu\ItemInterface');
+        $item->expects($this->any())
+             ->method('getExtra')
+             ->with('admin')
+             ->will($this->returnValue($data))
+        ;
+
+        return $item;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    private function getAdmin($code, $list = false, $granted = false)
+    {
+        $admin = $this->getMock('Sonata\AdminBundle\Admin\AdminInterface');
+        $admin
+            ->expects($this->any())
+            ->method('hasRoute')
+            ->with('list')
+            ->will($this->returnValue($list))
+        ;
+        $admin
+            ->expects($this->any())
+            ->method('isGranted')
+            ->with('LIST')
+            ->will($this->returnValue($granted))
+        ;
+        $admin
+            ->expects($this->any())
+            ->method('getCode')
+            ->will($this->returnValue($code))
+        ;
+
+        return $admin;
+    }
+}