В базовой поставке Symfony 2 предусмотрен только минимальный функционал создания CRUD интерфейса. Для реализации административного интерфейса разработан ряд бандлов, в частности SonataAdminBundle.
Для чего это нужно?
С помощью SonataAdminBundle можно быстро создать конфигурируемый интерфейс редактирования сущностей ORM-модели (также выделены бандлы для работы с MongoDb и PHPCr, но они пока находятся на раннем этапе развития). При этом любую часть интерфейса можно доработать под себя. В конце октября 2011 оформление было переведено на фреймворк Twitter Boostrap, поэтому внешний вид административного интерфейса получается довольно современным.
Установка и базовая конфигурация
Для установки зависимостей в Symfony 2.1 используется Composer, в отличие от версии 2.0, в которой использовался файл deps. Чтобы загрузить бандл Sonata Admin (в частности Sonata Orm Admin), в файл composer.json нужно добавить
"require":{
"php":">=5.3.3",
...
"sonata-project/doctrine-orm-admin-bundle":"dev-master",И затем запустить
php composer.phar update
В отличие от загрузки зависимостей посредством deps, файл app/autoload.php править не нужно, namespace для загруженных бандлов будут прописаны автоматически. Нужно только добавить инициализацию бандлов в app/AppKernel.php
<?php // app/AppKernel.php public function registerBundles() { return array( // ... new Sonata\AdminBundle\SonataAdminBundle(), new Sonata\DoctrineORMAdminBundle\SonataDoctrineORMAdminBundle(), new Sonata\BlockBundle\SonataBlockBundle(), new Knp\Bundle\MenuBundle\KnpMenuBundle(), new Sonata\jQueryBundle\SonatajQueryBundle(), // ... ); }
В файл app/config/routing.yml нужно добавить роутинг для административного интерфейса:
# app/config/routing.yml
admin:
resource: '@SonataAdminBundle/Resources/config/routing/sonata_admin.xml'
prefix: /admin
_sonata_admin:
resource: .
type: sonata_admin
prefix: /adminИ записать в директорию web css,js и пр. от установленных бандлов
php app/console assets:install web
Чтобы добавить пароль на адмистистративный интерфейс можно воспользоваться либо штатной авторизацией Symfony 2, либо поставить дополнительный бандл FOSUserBundle
В файле app/config/config.yml нужно прописать блоки, используемые SonataAdmig, а также можно задать заголовок и логотип , а также переопределить шаблоны административного интерфейса. Для начала добавим заголовок административного интерфейса:
sonata_admin:
title: Сайт.Ру
sonata_block:
default_contexts: [cms]
blocks:
sonata.admin.block.admin_list:
contexts: [admin]
sonata.block.service.text:
sonata.block.service.rss:Для того чтобы включить сервис translator нужно модифицировать app/config/config.yml:
framework:
translator: { fallback: %locale% }После установки, при обращении по адресу http://localhost/admin/dashboard (предполагаем что Symfony 2 установлена на сайт с именем http://localhost) выводится пустой административный интерфейс, для которого пока не прописаны сервисы администрирования сущностей.
Замечание: Translator и IE
Чтобы компонент translator определял русский accept-language нужно в настройках IE добавить в разделе Свойства обозревателя/ Общие / Языки русский язык с кодом ru-Ru
Пример использования
В качеcтве примера сделаем административный интерфейс для редактирования новостей, сущности которых описаны в статье Создание CRUD приложения на Symfony 2. Исходники используемых сущностей можно посмотреть на Github.
SonataAdminBundle использует архитектуру, в которой описание административного интерфейса производится посредством специального класса Admin, в котором производится конфигурация формы редактирования, списка записей, формы поиска записей, страницы отображения записи. Этот принцип был заимствован из проекта Django.
Классы {Имя сущности}Admin
Для редактирования новостей, ссылок к новостям и категорий новостей нужно создать 3 класса в директории Test/NewsBundle/Admin: NewsAdmin, NewsLinkAdmin и NewsCategoryAdmin:
<?php namespace Test\NewsBundle\Admin; use Sonata\AdminBundle\Admin\Admin; use Sonata\AdminBundle\Form\FormMapper; use Sonata\AdminBundle\Datagrid\DatagridMapper; use Sonata\AdminBundle\Datagrid\ListMapper; use Sonata\AdminBundle\Show\ShowMapper; use Knp\Menu\ItemInterface as MenuItemInterface; class NewsAdmin extends Admin { /** * Конфигурация отображения записи * * @param \Sonata\AdminBundle\Show\ShowMapper $showMapper * @return void */ protected function configureShowField(ShowMapper $showMapper) { $showMapper ->add('id', null, array('label' => 'Идентификатор')) ->add('title', null, array('label' => 'Заголовок')) ->add('announce', null, array('label' => 'Анонс')) ->add('text', null, array('label' => 'Текст')) ->add('pubDate', null, array('label' => 'Дата публикации')) ->add('newsLinks', null, array('label' => 'Ссылки к новости')) ->add('newsCategory', null, array('label' => 'Идентификатор')); } /** * Конфигурация формы редактирования записи * @param \Sonata\AdminBundle\Form\FormMapper $formMapper * @return void */ protected function configureFormFields(FormMapper $formMapper) { $formMapper ->add('title', null, array('label' => 'Заголовок')) ->add('announce', null, array('label' => 'Анонс')) ->add('text', null, array('label' => 'Текст')) ->add('pubDate', null, array('label' => 'Дата публикации')) //by_reference используется для того чтобы при трансформации данных запроса в объект сущности //которую выполняет Symfony Form Framework, использовался setter сущности News::setNewsLinks ->add('newsLinks', 'sonata_type_collection', array('label' => 'Ссылки', 'by_reference' => false), array( 'edit' => 'inline', //В сущности NewsLink есть поле pos, отражающее положение ссылки в списке //указание опции sortable позволяет менять положение ссылок в списке перетаскиваением 'sortable' => 'pos', 'inline' => 'table', )) ->add('newsCategory', null, array('label' => 'Категория')) ->setHelps(array( 'title' => 'Подсказка по заголовку', 'pubDate' => 'Дата публикации новости на сайте' )); } /** * Конфигурация списка записей * * @param \Sonata\AdminBundle\Datagrid\ListMapper $listMapper * @return void */ protected function configureListFields(ListMapper $listMapper) { $listMapper ->addIdentifier('id') ->addIdentifier('title', null, array('label' => 'Заголовок')) ->add('pubDate', null, array('label' => 'Дата публикации')) ->add('newsCategory', null, array('label' => 'Категория')); } /** * Поля, по которым производится поиск в списке записей * * @param \Sonata\AdminBundle\Datagrid\DatagridMapper $datagridMapper * @return void */ protected function configureDatagridFilters(DatagridMapper $datagridMapper) { $datagridMapper ->add('title', null, array('label' => 'Заголовок')); } /** * Конфигурация левого меню при отображении и редатировании записи * * @param \Knp\Menu\ItemInterface $menu * @param $action * @param null|\Sonata\AdminBundle\Admin\Admin $childAdmin * * @return void */ protected function configureSideMenu(MenuItemInterface $menu, $action, Admin $childAdmin = null) { $menu->addChild( $action == 'edit' ? 'Просмотр новости' : 'Редактирование новости', array('uri' => $this->generateUrl( $action == 'edit' ? 'show' : 'edit', array('id' => $this->getRequest()->get('id')))) ); } }
Административный класс для ссылок новостей содержит только метод configureFormFields, т.к. ссылки новостей редактируются вместе с новостью:
<?php namespace Test\NewsBundle\Admin; use Sonata\AdminBundle\Admin\Admin; use Sonata\AdminBundle\Form\FormMapper; class NewsLinkAdmin extends Admin { /** * @param \Sonata\AdminBundle\Form\FormMapper $formMapper * @return void */ protected function configureFormFields(FormMapper $formMapper) { $formMapper ->add('url', null, array('label' => 'URL', 'required' => true)) ->add('text', null, array('label' => 'Описание')) ->add ('pos','hidden'); } }
NewsCategoryAdmin создается по аналогии с NewsAdmin. Исходники административных классов можно посмотреть на Github.
Регистрация админстративных сервисов
Административный класс нужно зарегистировать как сервис, для чего его нужно прописать в Test/NewsBundle/Resources/config/services.xml. Для сервисов административного интерфейса указывается тэг «sonata.admin», позволяющий отличать их от других сервисов. Также указывается название группы пунктов меню (атрибут «group») и название пункта меню (атрибут «label») — эти данные используются для построения меню административного интерфейса. В нашем случае пункт меню для редактирования ссылок к новости в главном меню показывать не нужно, т.к. они заносятся на странице редактирования новости. Поэтому для сервиса c id=»test.news.admin.newsLink» ставим атрибут show_in_dashboard=»false».
В приведенном примере сервисы используют стандартный контроллер SonataAdminBundle:CRUD, однако при необходимости можно создавать свои контроллеры.
<?xml version="1.0" ?> <container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> <services> <service id="test.news.admin.news" class="Test\NewsBundle\Admin\NewsAdmin"> <tag name="sonata.admin" manager_type="orm" group="Новости" label="Новости"/> <argument/> <argument>Test\NewsBundle\Entity\News</argument> <argument>SonataAdminBundle:CRUD</argument> </service> <service id="test.news.admin.newsLink" class="Test\NewsBundle\Admin\NewsLinkAdmin"> <tag name="sonata.admin" manager_type="orm" show_in_dashboard="false" /> <argument/> <argument>Test\NewsBundle\Entity\NewsLink</argument> <argument>SonataAdminBundle:CRUD</argument> </service> <service id="test.news.admin.newsCategory" class="Test\NewsBundle\Admin\NewsCategoryAdmin"> <tag name="sonata.admin" manager_type="orm" group="Новости" label="Категории новостей"/> <argument/> <argument>Test\NewsBundle\Entity\NewsCategory</argument> <argument>SonataAdminBundle:CRUD</argument> </service> </services> </container>
Что получилось
После перезагрузки административный интерфейс выглядит так:
При нажатии на ссылку «Новости / Список» выводится список новостей с возможностью фильтрации записей:
Страница редактирования новоcти выглядит так:
Изменение позиции привязанных сущностей
Привязанные к новости ссылки добавляются без перезагрузки страницы. В сущность «NewsLink» добавлено поле pos, по которому ведется сортировка при запрашивании ссылок к новости. Указании опции ‘sortable’ => ‘pos’ для типа поля sonata_type_collection добавляет в интерфейс возможность изменения порядка новостей, путем перетаскивания строк таблицы:
Навигация
В базовой поставке SonataAdminBundle есть русская локализация стандартных названий кнопок, заголовков и и т.п. Чтобы локализация была полной, для созданных разделов административного интерфейса нужно создать переводы заголовков, которые автоматически создаются на основе названий сущностей, например, News List, News Create. Для этого в директории Test/NewsBundle/Resources/translations требуется создать файл messages.ru.xliff (про сервис трансляции можно почитать здесь)
<?xml version="1.0"?> <xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2"> <file source-language="ru" datatype="plaintext" original="" > <body> <trans-unit id="News List"> <source>News List</sourсe> <target>Список новостей</target> </trans-unit> <trans-unit id="News Create"> <source>News Create</sourсe> <target>Создание новости</target> </trans-unit> <trans-unit id="News Edit"> <source>News Edit</sourсe> <target>Редактирование новости</target> </trans-unit> <trans-unit id="News Category List"> <source>News Category List< sourсe> <target>Список категорий новостей</target> </trans-unit> <trans-unit id="News Category Create"> <source>News Category Create</sourсe> <target>Создание категории новости</target> </trans-unit> <trans-unit id="News Category Edit"> <source>News Category Edit</sourсe> <target>Редактирование категории новостей</target> </trans-unit> </body> </file> </xliff>
Заключение
В итоге получился функциональный, расширяемый интерфейс редактирования записей. Все шаблоны и контроллеры, используемые SonataAdmin можно переопределить в конфигурации приложения. Разработчики на базе SonataAdmin сделали несколько полезных для разработки веб-приложений бандлов, реализующих ряд базовых функций: SonataUserBundle (управление пользователями) , SonataNewsBundle (блог) , SonataMediaBundle (управление медиа-ресурсами) и SonataPageBundle (прототип CMS). Большой проблемой является плохая документированность, особенно SonataPageBundle, хотя на первый взляд интересный продукт.
Спасибо за отличную статью — очень помогла! Прошу помочь в одном вопросе — на картинке видно что вверху в панели «истории» стоит «0/1/2″ вместо необходимых ссылок. Почему так происходит и как это поправить.
Спасибо.
http://i49.tinypic.com/wclh78.png
Судя по картинке, похоже что так занесли переводы стандартных сообщений Sonata Admin
У меня возникла подобная проблема, правда приложение падало с эксепшеном:
An exception has been thrown during the rendering of a template («Notice: Array to string conversion in /var/www/wersoo/app/cache/dev/twig/10/0d/5ff8f68f99b5858b50f810959ee8.php line 206″) in SonataAdminBundle::standard_layout.html.twig at line 132.
Пофиксилось следующим образом: https://gist.github.com/3455114
Однако, как выяснилось позже, проблема в версиях бандлов KnpMenu и KnpMenuBundle, они должны быть v1.1.1 и v1.1.0 соответственно.
Чуть больше информации в пулреквестах таких же велосипдов как и мой способ:
https://github.com/sonata-project/SonataAdminBundle/pull/787
https://github.com/sonata-project/SonataAdminBundle/pull/848
А здесь описана точно такая же проблема с хлебными крошками: https://github.com/sonata-project/SonataAdminBundle/issues/776
в функции
public function registerBundles()вы забыли
new Sonata\BlockBundle\SonataBlockBundle()Спасибо, поправил
Hi,
Here is the answer for this issue:
https://github.com/sonata-project/SonataAdminBundle/issues/776
and this
https://groups.google.com/forum/?fromgroups=#!topic/sonata-users/ogsRx_urdaM
А как все это завернуть в отдельную авторизацию? вот я прикрутил sonata user bundle. Создал сущность admin_user , которые хранятся в отдельной таблице admi_user. через фикстуры залил туда тестового админа. при логине, если ввести правильные данные, он не ругается, но и не авторизирует, а обратно редиректит. Но если данные левые вводить. то пишет что bad credentials, т.е. он лезет в эту таблицу нормально, но сама авторизация в сессию не устанавливается. Не подскажете, как побороть такое?
Нужно настроить права доступа к разделу /admin/, например так https://github.com/vitiko/IphpCoreBundle/blob/master/Resources/config/security.yml
Реализовал теги так как на скриншоте http://uploads.ru/UexJ6.png
Использовал связь много ко многим.
Можно ли как-то реализовать добавление тегов, вводя их через запятую в строку. Потому что большое количество тегов очень сильно затрудняет их поиск в общем списке
Достаточно удобный вариант https://github.com/genemu/GenemuFormBundle/blob/master/Resources/doc/jquery/select2/index.md