Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

KnpMenuBundle Sonata. Делаем меню из базы

Anna | 31.05.2014 | нет комментариев

Всем славного времени суток уважаемые прогровчане. Я люблю Symfony. Она мне нравится и я ее обожаю. Еще мне нравится SonataAdminBundle. Думаю многим из вас тоже. Выходит, в данной статье я хочу разглядеть процесс создания меню для сайта при участие в этом процессе KNPMenuBundle SonataAdminBundle. По сути процесс создания меню довольно примитивен и детально описан на github’e самого бандла, но что если нам нужно, что бы меню было управляемо из админки? Заинтересовались? Тогда умоляю под кат.

Сразу хочу простить за то, что будет изложенно ниже, но ясного пояснения как сделать задуманное я не обнаружил. Если кто их вас видел сходственное, поделитесь ссылкой. Может некоторым моя статья сгодится и будет пригодной. Выходит приступаем. Первоначально я полагаю, что у вас теснее установлена Sonata и она работает. Выходит, первым делом начнем с генерации бандла для нашего меню.

Открываем консоль, переходим в папку с планом и пишем:
#php app/console generate:bundle

Имя для бандла вы вольны предпочесть сами, я же назвал его легко MenuBundle.
Позже нужно сделать 2 сущности. Если у вас не оказалось в папке с бандлом папки Entity — то сделайте ее. Выходит, файл номер раз — Menu.php. Файл номер два — MenuType.php. Для чего необходим 2-й файл, я поясню позднее.

Привожу начальный код файла под номером раз:

namespace MyFolder\MenuBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
/**
 * @ORM\Entity
 * @ORM\Table(name="menu")
 * @ORM\Entity(repositoryClass="MyFolder\MenuBundle\Entity\MenuRepository")
 */
class Menu{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @ORM\Column(type="string", length=100)
     */
    protected $title;

    /**
     * @ORM\Column(type="string", length=100)
     */
    protected $route;

    /**
     * @ORM\Column(type="string", nullable=true)
     */
    protected $alias;

    /**
     * @ORM\Column(type="boolean")
     */
    protected $static;

    /**
     * @ORM\ManyToOne(targetEntity="Cafe\MenuBundle\Entity\MenuType", inversedBy="menuTypeId")
     * @ORM\JoinColumn(name="menuTypeId", referencedColumnName="id")
     */
    protected $menuTypeId;

}

Посмотрим на файл под номером два:

namespace MyFolder\MenuBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
/**
 * @ORM\Entity
 * @ORM\Table(name="menu_type")
 * @ORM\Entity(repositoryClass="MyFolder\MenuBundle\Entity\MenuTypeRepository")
 */
class MenuType {

    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @ORM\Column(type="string", length=100)
     */
    protected $title;

    /**
     * @ORM\OneToMany(targetEntity="Menu", mappedBy="menuTypeId")
     */
    private $typeId;
}

Выходит, у нас написаны 2 модели, давайте сгенерируем геттеры и сеттеры для них?!
# php app/console doctrine:generate:entities MyFolder/MenuBundle/Entity/Menu
и
# php app/console doctrine:generate:entities MyFolder/MenuBundle/Entity/MenuType

Если все прошло благополучно ваши классы обязаны преобразиться и получить в свое распоряжение свои геттеры и сеттеры.

Позже нужно сделать сами таблицы в БД.
# php app/console doctrine:schema:update –force

Выходит у нас 2 таблицы, связанные друг с ином связью ManyToOne. То есть, по сути, таблица РАЗ может иметь уйма связей с таблице ДВА.

Маленькое отхождение. Давай те проговорим связи в моделях, для тех кто не знает.

Ниже строки из файла РАЗ.

/**
  * @ORM\ManyToOne(targetEntity="MyFolder\MenuBundle\Entity\MenuType", inversedBy="menuTypeId")
  * @ORM\JoinColumn(name="menuTypeId", referencedColumnName="id")
  */
protected $menuTypeId;

Говорят нам, что много строк из файла MyFolder\MenuBundle\Entity\Menu могут относиться только к одной строке из файла MyFolder\MenuBundle\Entity\MenuType о чем нам вежливо информирует аннотация из файла ДВА

/**
   * @ORM\OneToMany(targetEntity="Menu", mappedBy="menuTypeId")
   */
private $typeId;

Таким образом, это один из методов установки связей между сущностями в Symfony.

Возвращаемся к коду. Выходит, сущности мы подготовили, БД сделали. Переходим к админ части.

Для тпредложить сделать 1-й тип меню. Для этого переходим во вкладку “Меню Тип” и жмем кнопку с плюсиком. Так все легко должно быть. Одно поле — title (Заголовок). Таким образом мы можем сделать Типы Меню к которым, позднее сумеем привязать Пункты меню. Я сделал два Типа Меню (“Главное Меню” и “Меню в подвале”). Позже переходим в само Меню и добавляем новое. Вот здесь поувлекательней.
image

Собственно Title это ясно для чего, Route — это ссылка которая пойдем в KNPMenuBundle. Alias — это лично мое предпочтение, вы можете не делать такой пункт. Static чекбокс предуготовлен для того, что бы сказать системе что странице будет кастомной и ей не необходим будет Action способ в контроллере. Menu Type Id собственно там и появятся те пункты меню что вы сотворили ваше. Это та сама привязка, которая позже поможет системе осознать, какой пункт меню вы все таки ходите предпочесть в том либо другом случае.

Один момент. В надобность создания кастомного маршрута пришлось применить такой JS код.

$(document).ready(function () {
    $('input[id$="_static"]').click(function(){
        var $_thisRoute = $('input[id$="_route"]'),
            defaultValues = $_thisRoute.val().split('/');
        if($(this).is(':checked')) {
            $_thisRoute.val('/custom/' defaultValues[defaultValues.length -1]);
        } else {
            $_thisRoute.val('/' defaultValues[defaultValues.length -1]);
        }
    })
});

То есть при клике на пункт Static маршрут превращается, если вы написали скажем about-us то он становится /custom/about-us.

Как добавить свой js в админ часть сонаты не буду расписывать, это выходит за пределы рассматриваемой области, если есть надобность, подскажу, только спросите :)

Я сотворил 7 пунктов меню

image

Как видете Route примерно у всех идентичен помимо последнего пункта. Привязано все это только к Основному Меню. Собственно с этой частью мы завершили. Переходит к KNPMenuBundle.

У меня установлена версия бандла 1.1 не смотря на то, что теснее есть 2.2 тем не менее подружить 2.2 с Сонатой у меня не получилось, да и в Сонате в requirements стоит версия KNPMenuBundle 1.1 следственно мы ничего не нарушаем.

Продолжаем. В папке с нашим бандлом создаем папку Menu в ней файл Builder.php. Вот его код:

namespace MyFolder\MenuBundle\Menu;

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

class Builder extends ContainerAware
{

    public function mainMenu(FactoryInterface $factory, array $options)
    {
        $menuItems = $this->container->get('menu')->getMainMenu();
        $menu = $factory->createItem('root');

        $this->setCurrentItem($menu);

        $menu->setChildrenAttribute('class', 'nav');
        $menu->setExtra('currentElement', 'active');

        foreach($menuItems as $item) {
            $menu->addChild($item->getTitle(), array('uri' => $item->getRoute()));
        }

        return $menu;
    }

    protected function setCurrentItem(ItemInterface $menu)
    {
        $menu->setCurrentUri($this->container->get('request')->getPathInfo());
    }
}

Тут пару моментов. Так как сам Builder наследует ContainerAware то у нас очевидно есть вероятность применять $this->container->get(), а если так, то мы можем стремительно написать сервис на выборку нужных пунктов меню. Сказано — сделано.

В папке бандла создаем папку Service а в ней один файл MenuService.php. Перед тем начать писать в него код, давай те сделаем сервис доступным, то есть, отредактируем файл MyFolder/MenuBundle/Resources/config/services.yml таким образом, что бы у нас получилось нижеследующее:

parameters:

services:
  menu:
        class: MyFolder\MenuBundle\Service\MenuService
        arguments: [@service_container]

  admin.menu:
        class: MyFolder\MenuBundle\Admin\MenuAdmin
        tags:
        - { name:  sonata.admin, manager_type: orm, group: Меню, label: Меню}
        arguments: [null, MyFolder\MenuBundle\Entity\Menu, SonataAdminBundle:CRUD]

  admin.menu_type:
          class: MyFolder\MenuBundle\Admin\MenuTypeAdmin
          tags:
          - { name:  sonata.admin, manager_type: orm, group: Меню Тип, label: Меню Тип}
          arguments: [null, MyFolder\MenuBundle\Entity\MenuType, SonataAdminBundle:CRUD]

Собственно сейчас код файла MyFolder/MenuBundle/Service/MenuService

namespace MyFolder\MenuBundle\Service;

use Symfony\Component\DependencyInjection\Container;

class MenuService
{
    private $doctrine;
    private $container;
    private $menuRepository;

    public function __construct(Container $container)
    {
        $this->container = $container;
        $this->doctrine = $this->container->get('doctrine');
        $this->menuRepository = $this->doctrine->getRepository('MenuBundle:Menu');
    }

    public function getMainMenu()
    {
        return $this->menuRepository->getMainMenu();
    }
}

Напомню одну строчку из Menu.php сущности
ORM\Entity(repositoryClass=“MyFolder\MenuBundle\Entity\MenuRepository”)
Это обозначает, что в папке с сущностью сделайте файл MenuRepository.php и код в нем выглядит вот так:

namespace MyFolder\MenuBundle\Entity;

use Doctrine\ORM\EntityRepository;

use Doctrine\ORM\Query\ResultSetMapping;

class MenuRepository extends EntityRepository
{
   public function getMainMenu()
    {
        return $this->findBy(array('menuTypeId' => 1));
    }
}

собственно это и есть та самая выборка, которая вернет нам все пункты меню, которые относятся только к “Главному Меню” типу.

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

{{ knp_menu_render('MenuBundle:Builder:mainMenu', { 'currentClass': 'active'}) }}

CSS класс active будет у значения которое в данный момент энергично. Если вы делали меню как я, то вы можете сделать в одном из ваших контролеров такой способ

/**
     * @Template()
     * @Route("/custom/{link}", name="_custom_page",  defaults={"link" = "/"})
     */
    public function customAction($link)
    {
        return $this->render('CommonBundle:Default:commonPage.html.twig', array('page' => $link));
    }

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

Если вам показалось, что статья все только усложняет, и есть метод как написать это проще либо стремительней, умоляю поделиться со мною, потому как вначале постижения всякого фреймверка, мы любим все усложнять.

P.S. Если что не верно пояснил либо что не пояснил, пишите на почту, с охотой каждому отвечу. Спасибо за внимание. До встречи.

Источник: programmingmaster.ru

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB