Разработка на Symfony2 » Без рубрики https://symfonydev.ru Разработка на Symfony2: Doctrine file upload , CMS, Sonata Admin Bundle Fri, 07 Feb 2014 13:33:43 +0000 ru-RU hourly 1 http://wordpress.org/?v=3.4.1 Вакансия для разработчика PHP / Symfony 2, Москва https://symfonydev.ru/programmers-wante/ https://symfonydev.ru/programmers-wante/#comments Fri, 02 Aug.2026 08:29:18 +0000 vitiko https://symfonydev.ru/?p=408 Сфера деятельности — участие в разработке новых масштабных проектов Требования: PHP >= 5.3, ООП, понимание шаблонов проектирования и умение их применять; Наличие реализованных проектов на Symfony 2 MySQL – индексы, оптимизация запросов; HTML, CSS – базовые навыки верстки; Javascript, jQuery; … Читать далее

Запись Вакансия для разработчика PHP / Symfony 2, Москва впервые появилась Разработка на Symfony2.

]]>
Сфера деятельности — участие в разработке новых масштабных проектов

Требования:
PHP >= 5.3, ООП, понимание шаблонов проектирования и умение их применять;
Наличие реализованных проектов на Symfony 2
MySQL – индексы, оптимизация запросов;
HTML, CSS – базовые навыки верстки;
Javascript, jQuery;
Опыт работы с Linux, shell/bash и администрирования nginx/Apache;
Умение работать с системами контроля версий (Git);
Умение писать модульные тесты, соблюдать стандарты кодирования, работать с чужим кодом.

Условия:
«Белая» заработная плата от 90 тыс.р./мес.
Полный рабочий день.
Работа в офисе, Москва, центр.
Оформление в соответствии с ТК РФ, ДМС;
Возможность обучения в аспирантуре;

Обращаться [email protected]

Запись Вакансия для разработчика PHP / Symfony 2, Москва впервые появилась Разработка на Symfony2.

]]>
https://symfonydev.ru/programmers-wante/feed/ 0
Symfony 2 / Doctrine ORM entity file upload https://symfonydev.ru/doctrine-orm-entity-file-upload/ https://symfonydev.ru/doctrine-orm-entity-file-upload/#comments Wed, 29 May.2026 07:37:57 +0000 vitiko https://symfonydev.ru/?p=397 The IphpFileStoreBundle is a Symfony2 bundle that automates file uploads that are attached to an entity. The bundle will automatically name and save the uploaded file according to the configuration specified on a per property basis using a mix of … Читать далее

Запись Symfony 2 / Doctrine ORM entity file upload впервые появилась Разработка на Symfony2.

]]>
The IphpFileStoreBundle is a Symfony2 bundle that automates file uploads that are attached to an entity. The bundle will automatically name and save the uploaded file according to the configuration specified on a per property basis using a mix of configuration and annotations. After the entity has been created and the file has been saved, array with data of uploaded file will be saved to according property. The bundle provide different ways to naming uploaded files and directories.

Read more on GitHub

Запись Symfony 2 / Doctrine ORM entity file upload впервые появилась Разработка на Symfony2.

]]>
https://symfonydev.ru/doctrine-orm-entity-file-upload/feed/ 0
Загрузка файлов в Symfony 2 / Doctrine ORM https://symfonydev.ru/file-upload-symfony-2/ https://symfonydev.ru/file-upload-symfony-2/#comments Thu, 20 Dec 2012 09:12:24 +0000 vitiko https://symfonydev.ru/?p=270 Symfony 2 предоставляет значительное количество типов элементов форм для работы с датами, текстами, коллекциями и т.п. Однако для загрузки файлов существуют только стандартный элемент input type=upload и всю процедуру сохранения загруженного файла требуется программировать с 0 Существуют сторонние бандлы, такие … Читать далее

Запись Загрузка файлов в Symfony 2 / Doctrine ORM впервые появилась Разработка на Symfony2.

]]>
Symfony 2 предоставляет значительное количество типов элементов форм для работы с датами, текстами, коллекциями и т.п. Однако для загрузки файлов существуют только стандартный элемент input type=upload и всю процедуру сохранения загруженного файла требуется программировать с 0

Существуют сторонние бандлы, такие как VichUploaderBundle, однако в нем не хватает возможности использования без дополнительного программирования в административном интерфейсе Sonata Admin Bundle., а также встроенных возможностей по переименованию загружаемых файлов и директорий в момент размещения файлов в файловой системе сервера. Чтобы добавить эти возможности был разработан IphpFileStoreBundle, описание которого представлено тут. Он создан на основе VichUploaderBundle (Doctrine ORM File Upload), но т.к. все переписано достаточно сильно данному бандлу дано собственное название.

Отличия IphpFileStoreBundle от VichUploaderBundle

1. Добавлен виджет формы iphp_file, который позволяет загрузить новый файл, удалить загруженный файл и выводит информацию о текущем файле. Данный виджет можно использовать при создании интерфейса редактирования в Sonata Admin Bundle.

2. В БД хранится не название файла, а массив с информацией о файле — директория хранения, путь на сайте, название файла, оригинальное название файла до переименования, размер файла. Для изображений — ширина и высота изображения. Соответственно тип поля в БД — array.

3. Полностью переработан интерфейс Namer’ов для файлов и директорий. Добавлен код переименования файлов в случае совпадения имен.

Пример использования IphpFileStoreBundle

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

Инсталляция бандла

Получение кода бандла

Пример фрагмента composer.json для установки через composer

 
"require":{
   ...
   "iphp/filestore-bundle":"dev-master"
 }

Инициализация

// app/AppKernel.php
public function registerBundles()
{
    $bundles = array(
        // ...
        new Iphp\FileStoreBundle\IphpFileStoreBundle(),
    );
)

Конфигурация IphpFileStoreBundle

Для хранения фотографий нужно добавить в app/config/config.yml:

# app/config/config.yml
iphp_file_store:
    mappings:
       photo:
           upload_dir:  %kernel.root_dir%/../web/photo
           upload_path: /photo
           directory_namer:
               date:
                  params: { field : date, depth : month }
           namer: ~

Данная конфигурация нужна для того чтобы указать место хранения файлов (upload_dir), путь к этой директории на сайте (upload_path). Также, чтобы равномерно разбить загружаемые фотографии по директориям, указываем параметр directory_namer = date. Бандл поддерживает ряд встроенных именователей директорий, в которые загружаются файлы, один из них date. В данном случае атрибут date, который будет использоваться для создания директории файла, имеет тип datetime. Параметр depth определяет глубину, до которой будет создаваться поддиректории (month), доступные варианты — year, month, date. Именователь файлов (namer) не указан, по умолчанию используется именователь translit — названия файла транслитирируется в английские символы.
Чтобы имя файла оставалось без изменений — нужно указать namer: false.

Создание бандла IphpsandboxPhotoBundle

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

php app/console generate:bundle

Создание сущности Doctrine ORM

В объекте Photo хранится название фотографии и информация о файле фотографии. В начале класса нужно добавить аннотацию @FileStore\Uploadable, а к полю, которое будет использоваться для загрузки файлов, аннотацию @FileStore\UploadableField(mapping=’photo’), c указанием маппинга файлов фотографий, определенного на предыдущем шаге в файле config.yml. Также для поля photo используется валидация на тип файла — изображение размером до 20 Мб.

<?php
#src/Iphpsandbox/PhotoBundle/Entity/Photo.php
namespace Iphpsandbox\PhotoBundle\Entity;
use Iphp\FileStoreBundle\Mapping\Annotation as FileStore;
use Symfony\Component\Validator\Constraints as Assert;
 
/**
 * @FileStore\Uploadable
 */
class Photo
{
    /**
     * @var integer
     */
    private $id;
 
    /**
     * @var string
     */
    private $title;
 
 
    /**
     * @var \Datetime
     */
    private $date;
 
    /**
     * @Assert\File( maxSize="20M")
     * @FileStore\UploadableField(mapping="photo")
     **/
    private $photo;
 
    /**
     * @return integer 
     */
    public function getId()
    {
        return $this->id;
    }
 
    /**
     * @param string $title
     * @return Photo
     */
    public function setTitle($title)
    {
        $this->title = $title;
        return $this;
    }
 
    /**
     * @return string 
     */
    public function getTitle()
    {
        return $this->title;
    }
 
    /**
     * @param array $photo
     * @return Photo
     */
    public function setPhoto($photo)
    {
        $this->photo = $photo;
        return $this;
    }
 
    /**
     * @return array 
     */
    public function getPhoto()
    {
        return $this->photo;
    }
 
    /**
     * @param \Datetime $date
     */
    public function setDate($date)
    {
        $this->date = $date;
        return $this;
    }
 
    /**
     * @return \Datetime
     */
    public function getDate()
    {
        return $this->date;
    }
}

В xml-описании содержится описание связи PHP-класса и таблицы БД:

<?xml version="1.0" encoding="utf-8"?>
<!-- src/Iphpsandbox/PhotoBundle/Ressources/config/doctrine/Photo.orm.xml -->
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
                  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                  xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
    <entity name="Iphpsandbox\PhotoBundle\Entity\Photo">
        <id name="id" type="integer" column="id">
            <generator strategy="AUTO"/>
        </id>
        <field name="title" type="string" column="title" length="255"/>
         <field name="date" type="datetime" column="date"/>
        <field name="photo" type="array" column="photo"/>
   </entity>
</doctrine-mapping>

После создания PHP-класса сущности и XML-описания, нужно сгенерировать таблицу в БД командой:

php app/console doctrine:schema:update --force

Административный интерфейс

Подробно процесс создания административного интерфейса с помощью SonataAdminBundle описан тут. Для редактирования фотографий в административном интерфейс создаем класс

<?php
#src/Iphpsandbox/PhotoBundle/Admin/PhotoAdmin.php
namespace Iphpsandbox\PhotoBundle\Admin;
 
use Sonata\AdminBundle\Admin\Admin;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\AdminBundle\Datagrid\ListMapper;
 
class PhotoAdmin extends Admin
{
    protected function configureListFields(ListMapper $listMapper)
    {
        return $listMapper->addIdentifier('title')->add ('date');
    }
 
    protected function configureFormFields(FormMapper $formMapper)
    {
        return $formMapper->add('title')->add ('date')->add('photo', 'iphp_file');
    }
}

И создаем сервис:

<?xml version="1.0" ?>
<!-- src/Iphpsandbox/PhotoBundle/Ressources/config/services.xml -->
<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="iphpsanbox.photo.admin.photo" class="Iphpsandbox\PhotoBundle\Admin\PhotoAdmin">
            <tag name="sonata.admin" manager_type="orm" group="Фотографии" label="Фотографии"/>
            <argument/>
            <argument>Iphpsandbox\PhotoBundle\Entity\Photo</argument>
            <argument>SonataAdminBundle:CRUD</argument>
        </service>
    </services>
</container>

После этого в административном интерфейсе появляется группа меню «Фотографии». Для добавления фотографии нужно выбрать пункт меню «Фотографии» и затем кнопку «Добавить новый». Затем вводится название фотографии, дата и файл с фотографией. После сохранения фотографии форма редактирования выглядит следующим образом:

Тип поля iphp_file отображает данные о загруженной фотографии — исходное название файла, текущее название файла, размер изображения и файла, предварительный просмотр изображения. Фотография загружена в директорию, согласно конфигурации, содержащую год и месяц даты фотографии — http://localhost/photo/2012/12/desert.jpg.

Пользовательский интерфейс

Теперь добавим в пользовательский интерфейс возможность загружать свои фотографии и просматривать загруженные фотографии. Для реализации можно использовать как стандартный подход Symfony 2, так и предлагаемый в Iphp Core Bundle подход с созданию пользовательского интерфейса на основе модулей, размещаемых в древовидной структуре сайта. В данной статье используем стандартный подход, описанный тут.

Шаг 1. Создание маршрута (Route)

В файл app/config/routing.yml нужно добавить

# app/config/routing.yml
iphpsandbox_photo:
    pattern:  /photo/
    defaults: { _controller: IphpsandboxPhotoBundle:Photo:index }

Шаг 2. Контроллер

В контроллере создается форма для редактирования сущности Photo, аналогичная форме в административном интерфейсе, за исключением того, что в пользовательском интерфейсе нужны возможности, предоставляемые типом iphp_file (вывод информации о загруженном файле), поэтому используется стандартный тип формы File.

<?php
// src/Iphpsandbox/PhotoBundle/Controller/PhotoController.php
namespace Iphpsandbox\PhotoBundle\Controller;
 
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Iphpsandbox\PhotoBundle\Entity\Photo;
use Symfony\Component\HttpFoundation\Request;
 
class PhotoController extends Controller
{
    public function indexAction(Request $request)
    {
        $em = $this->getDoctrine()->getManager();
 
        $photo = new Photo();
        $uploadForm = $this->createFormBuilder($photo)
            ->add('title')
            ->add('date','date')
 
            //Using standart field type
            ->add ('photo','file')
            ->getForm();
 
 
        if ($request->isMethod('POST')) {
            $uploadForm->bind($request);
 
            if ($uploadForm->isValid()) {
                $em->persist($photo);
                $em->flush();
                return $this->redirect($this->generateUrl('iphpsandbox_photo'));
            }
        }
 
        return $this->render('IphpsandboxPhotoBundle:Photo:index.html.twig', array(
            'uploadForm' => $uploadForm->createView(),
            'photos' => $em->getRepository ('IphpsandboxPhotoBundle:Photo')->findAll()
        ));
    }
}

Шаг 3. Шаблон

В шаблоне выводится список загруженных фотографий и отображается форма загрузки новой фотографии. В списке фотографий, в случае если высота фотографии больше 150 пикселей (информация о размере фотографии хранится БД, данные в БД заносятся автоматически при загрузке фотографии) — размер фотографии ограничивается высотой 150px. Это решение показано только для примера, для создания превью-картинок можно использовать LiipImagineBundle.

{# src/Iphpsandbox/PhotoBundle/Resources/views/Photo/index.html.twig #}
<h1>Фотографии</h1>
 
<h2>Загруженные фотографии</h2>
 
{% for photo in photos %}
<div>
<h3>{{ photo.title }}</h3>
<div>{{ photo.date | date ('Y.m.d') }}</div>
<a href="{{ photo.photo.path }}" target="_blank"><img src="{{ photo.photo.path }}" {{ photo.photo.height > 150 ? 'height="150"':''}}/></a>
</div>
{% endfor%}
 
<h2>Загрузить фото</h2>
 
<form action="./" enctype="multipart/form-data" method="post">
 
    {{ form_widget(uploadForm) }}
    <input type="submit" value="Загрузить фото"/>
</form>

В итоге пользовательский интерфейс выглядит вот так.

С минимальными затратами сделан административный и пользовательский интерфейс для работы с записями, к которым можно присоединять файлы. Бандл IphpFilestoreBundle в своем составе имеет несколько именователей для файлов и директорий, с помощью который можно создавать названия файлов на основе атрибутов записи.

Код бандла с данным примером можно увидеть здесь https://github.com/vitiko/IphpSandbox/tree/master/src/Iphpsandbox/PhotoBundle

Запись Загрузка файлов в Symfony 2 / Doctrine ORM впервые появилась Разработка на Symfony2.

]]>
https://symfonydev.ru/file-upload-symfony-2/feed/ 4
Создание интерфейсов к БД на Symfony 2 https://symfonydev.ru/symfony-back-and-front-interfaces/ https://symfonydev.ru/symfony-back-and-front-interfaces/#comments Mon, 10 Sep 2012 12:33:29 +0000 vitiko https://symfonydev.ru/?p=94 Часто встречающейся задачей при разработке веб-приложений является создание интерфейсов, административного и пользовательского, для редактирования и отображения некоторых записей БД. Соответственно в административном интерфейсе должна быть возможность заносить, искать и редактировать записи, а в пользовательском интерфейсе — искать и просматривать записи. … Читать далее

Запись Создание интерфейсов к БД на Symfony 2 впервые появилась Разработка на Symfony2.

]]>
Часто встречающейся задачей при разработке веб-приложений является создание интерфейсов, административного и пользовательского, для редактирования и отображения некоторых записей БД. Соответственно в административном интерфейсе должна быть возможность заносить, искать и редактировать записи, а в пользовательском интерфейсе — искать и просматривать записи.

При разработке проекта на основе фреймворка Symfony 2.1 для создания такого типового решения будем использовать:

1. Разработка модели данных — Doctrine ORM, интегрированная с фреймворком Symfony 2
2. Разработка административного интерфейса — Sonata Admin Bundle
2. Разработка пользовательского интерфейса — стандартные возможности Symfony 2 + IphpCoreBundle

Таблица БД

Допустим у нас уже есть некоторые данные (таблица БД), которую нужно перенести в проект на Symfony 2. В примере используем следующую структуру таблицы БД:

CREATE TABLE `link` (
  `id` INT(10) NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(200) NOT NULL DEFAULT '',
  `url` VARCHAR(100) NOT NULL DEFAULT '',
  `annotation` text NOT NULL,
  `created_at` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `updated_at` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  PRIMARY KEY  (`link_id`)
) ;

В таблице должно быть поле id, которое по умолчанию используется как уникальный идентификатор записи (первичный ключ). Если поле называется иначе — переименуйте его. Если в таблице есть поля с датой создания и обновления записи — из имена должны быть created_at и updated_at соответственно.

Создание доменной модели Doctrine ORM

Предполагаем что в проекте уже есть бандл, который называется MyDbBundle. Для того чтобы на основе таблицы создать xml-описание сущности и php-класс сущности выполним команды:

#php app/console doctrine:mapping:import MyDbBundle xml --filter=Link
#php app/console doctrine:generate:entities MyDbBundle:Link

Первая команда создаст файл src/My/DbBundle/Resources/config/doctrine/Link.orm.xml вида

<?xml version="1.0" encoding="utf-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
  <entity name="My\DbBundle\Entity\Link" TABLE="link">
    <id name="id" TYPE="integer" COLUMN="id">
      <generator strategy="IDENTITY"/>
    </id>
    <field name="title" TYPE="string" COLUMN="title" LENGTH="200"/>
    <field name="url" TYPE="string" COLUMN="url" LENGTH="100"/>
    <field name="annotation" TYPE="text" COLUMN="annotation"/>
    <field name="createdAt" TYPE="datetime" COLUMN="created_at"/>
    <field name="updatedAt" TYPE="datetime" COLUMN="updated_at"/>
  </entity>
</doctrine-mapping>

Добавление сущности symfony

Вторая на основе xml-описания сущности создает PHP класс сущности src/My/DbBundle/Entity/Link

<?php
namespace My\DbBundle\Entity;
 
class Link
{
    /**
     * @var string $title
     */
    private $title;
 
    /**
     * @var string $url
     */
    private $url;
 
    /**
     * @var string $annotation
     */
    private $annotation;
 
    /**
     * @var \DateTime $createdAt
     */
    private $createdAt;
 
    /**
     * @var \DateTime $updatedAt
     */
    private $updatedAt;
 
    /**
     * @var integer $id
     */
    private $id;
 
 
    /**
     * Set title
     *
     * @param string $title
     * @return Link
     */
    public function setTitle($title)
    {
        $this->title = $title;
 
        return $this;
    }
 
    /* 
     ...
     Геттеры и сеттеры для cущности
     ...
    */
}

Xml-описание , PHP-класс и таблицу в БД также можно создать при помощи команды

doctrine:generate:entity

подробнее про эту команду можно посмотреть здесь

Админинистративный интерфейс с Sonata Admin Bundle

Установка и начало работы с Sonata Admin Bundle подробно описана в статье. В нашем случае нужно создать один административный класс и один сервис.

Административный класс

<?php
# src/My/DbBundle/Admin/LinkAdmin.php
namespace My\DbBundle\Admin;
 
use Sonata\AdminBundle\Admin\Admin;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Datagrid\ListMapper;
 
use My\DbBundle\Entity\Link;
 
class LinkAdmin extends Admin
{
    protected function configureDatagridFilters(DatagridMapper $datagridMapper)
    {
        $datagridMapper->add('title')
                      ->add('url');
    }
 
    protected function configureListFields(ListMapper $listMapper)
    {
        $listMapper->addIdentifier('title')
                   ->add('url')
                   ->add('updatedAt');
    }
 
    protected function configureFormFields(FormMapper $formMapper)
    {
        $formMapper->add('title')
                   ->add('url')
                   ->add('annotation');
    }
}

Описание сервиса

<!-- src/My/DbBundle/Resources/config/services.xml -->
<?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="sonata.admin.my.db.link" class="My\DbBundle\Admin\LinkAdmin">
            <tag name="sonata.admin" manager_type="orm" group="Базы данных" label="Ссылки"/>
            <argument/>
            <argument>My\DbBundle\Entity\Link</argument>
            <argument>SonataAdminBundle:CRUD</argument>
        </service>
    </services>
 
</container>

После этого в административном меню Sonata Admin должен появится пункт «Базы данных» / «Ссылки» (если список пунктов административного меню формируются вручную — нужно добавить идентификатор сервиса sonata.admin.my.db.link в список отображаемых пунктов меню)

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

<!-- src/My/DbBundle/Resources/tranlations/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="Link List">
                <source>Link List</source>
                <target>Список ссылок</target>
            </trans-unit>
           <trans-unit id="Link Edit">
                <source>Link Edit</source>
                <target>Редактирование ссылки</target>
            </trans-unit>
            <trans-unit id="Link Create">
                <source>Link Create</source>
                <target>Занесение информации о ссылке</target>
            </trans-unit>
            <trans-unit id="Link Delete">
                <source>Link Delete</source>
                <target>Удаление ссылки</target>
            </trans-unit>
        </body>
    </file>
</xliff>

Автоматическое сохранение даты создания и обновления записи

Добавим в модель данных автоматическое заполнение даты создания и обновления записи. Для этого нужно модифицировать xml-описание сущности Doctrine ORM

#src/My/DbBundle/Resources/config/doctrine/Link.orm.xml

..
  <field name="createdAt" type="datetime" column="created_at">
    <gedmo:timestampable on="create"/>
 </field>
 <field name="updatedAt" type="datetime" column="updated_at">
    <gedmo:timestampable on="update"/>
 </field>
...

В данном случае используются директивы библиотеки Doctrine Extensions, которые интегрируются в проект на Symfony 2 с помощью StofDoctrineExtensionsBundle. Для обновления даты применяется поведение Timestampable.

Для того, чтобы в административном интерфейсе отображались дата и время создание и обновления записи, изменим административный класс, унаследовав его от класса Iphp\CoreBundle\Admin\Admin:

<?php
# src/My/DbBundle/Admin/LinkAdmin.php
use Iphp\CoreBundle\Admin\Admin as IphpAdmin;
..
class LinkAdmin extends IphpAdmin
{
...

Класс Iphp\CoreBundle\Admin\Admin дополняет стандартный метод формирования меню кодом, который в случае наличия у редактируемой сущности методов getUpdatedAt и/или getCreatedAt добавляет в левое меню соответствующие записи. В результате на странице с формой редактирования записи в левом столбце отображаются дата и время создания и редактирования информации о ссылке.

Пользовательский интерфейс

При создании пользовательского интерфейса в данном примере используем возможности IphpCoreBundle: Модули и базовый EntityController.

Сначала нужно создать класс-модуль, который будет размещен в рубрике. В конструкторе класса указывается название модуля («Ссылки») и название сущности, которая будет отображаться на сайте (MyDbBundle:Link). Класс EntityModule по умолчанию заносит два маршрута — index и view для отображения списка записей и вывода одной записи.

<?php
#src/My/DbBundle/Module/LinkModule.php
namespace My\DbBundle\Module;
use Iphp\CoreBundle\Module\EntityModule;
 
class LinkModule extends EntityModule
{
    function __construct()
    {
        $this->setName('Ссылки');
        $this->setEntityName ('MyDbBundle:Link');
    }
}

Затем создаем контроллер, который будет выводить список ссылок и информацию о ссылке.
В качестве базового класса используем Iphp\CoreBundle\Controller\EntityController, в котором базовый контроллер Symfony дополнен методами, с помощью которых базовые операции (список, отображение) уже заложены в код класса.

<?php
#src/My/DbBundle/Controller/LinkController.php
namespace My\DbBundle\Controller;
use Iphp\CoreBundle\Controller\EntityController;
class LinkController extends EntityController
{
    function __construct()
    {
        $this->entityName = 'MyDbBundle:Link';
    }
}

Шаблоны для пользовательского интерфейса

Далее нужно создать шаблоны для вывода списка записей и отображения информации о записи. В базовом классе EntityModule для методов indexAction и viewAction используется аннотация /* @template */ из бандла SensioFrameworkExtraBundle которая автоматически назначает имя шаблона для действия (action). В нашем случае нужно создать шаблоны

  • src/My/DbBundle/Resources/views/Link/index.html.twig
  • src/My/DbBundle/Resources/views/Link/view.html.twig

Шаблон списка записей

{# src/My/DbBundle/Resources/views/Link/index.html.twig #}
{% extends '::base.html.twig' %}
 
{% block body %}
{% for link in entities %}
<div>
 
    <a target="blank" href="{{ link.url }}"><strong>{{ link.title }}</strong></a>
    {{ link.annotation|raw }}
    <a href="{{ entitypath(link)}}">Информация о ссылке </a>
</div>
{% endfor %}
{% endblock body %}

В шаблоне списка записей используется добавленная бандлом IphpCoreBundle функция шаблонизатора Twig entitypath, которая выводит значение, возращаемое методом getSitePath объекта, передаваемого в качестве аргумента. Соответственно, чтобы в нашем шаблоне работала ссылка «Информация о ссылке», нужно добавить в класс Link метод getSitePath. Этот метод может возвращать путь на сайте, жестко определенный в коде класса, например:

 ...
 function getSitePath()
 {
   return '/links/'.$this->getId();
 }

Однако, в нашем случае модуль LinkModule, выдающих список ссылок и информацию о ссылках, может быть размещен в любой рубрике сайта с любым адресом, и мы не можем жестко зашивать в коде класса путь к странице с информацией о ссылке. Поэтому мы будем использовать следующий код для метода Link::getSitePath:

<?php
namespace My\DbBundle\Entity;
 
class Link
{
 ...
    public function getSitePath(\Iphp\CoreBundle\Routing\EntityRouter $entityRouter)
    {
        return $entityRouter->generateEntityActionPath ($this,'view');
    }
 ...
}

Объект EntityRouter на основании данных объекта сущности ($this) и названия действия («view») автоматически сгенерирует путь на сайте к текущему объекту. Данный подход работает только если при создании модуля используется класс EntityModule — в данном классе маршруты автоматически именуются так, чтобы их мог найти EntityRouter по объекту и названию действия. Путь на сайте генерируется на основе рубрики, в котором размещен модуль и идентификатора объекта.

Шаблон вывода информации о записи

{# src/My/DbBundle/Resources/views/Link/view.html.twig #}
{% extends '::base.html.twig' %}
 
{% block body %}
<h1>{{ entity.title }}</h1>
<div><a href="{{ entity.url }}">{{ entity.url }}</a></div>
<div>{{ entity.annotation | raw }}</div>
{% endblock body %}

Наличие метода получения ссылки на объект и страницы с выводом информации об объекте позволяет при поиске объектов различного типа, например через ElasticSearch, в списке результатов давать ссылки на объекты, используя функцию twig entitypath.

Запись Создание интерфейсов к БД на Symfony 2 впервые появилась Разработка на Symfony2.

]]>
https://symfonydev.ru/symfony-back-and-front-interfaces/feed/ 1
Административный интерфейс на Symfony 2 и SonataAdminBundle https://symfonydev.ru/sonata-admin-bundle-getting-started/ https://symfonydev.ru/sonata-admin-bundle-getting-started/#comments Fri, 20 Jul 2012 10:51:41 +0000 vitiko http://localhost:84/?p=1 В базовой поставке Symfony 2 предусмотрен только минимальный функционал создания CRUD интерфейса. Для реализации административного интерфейса разработан ряд бандлов, в частности SonataAdminBundle. Для чего это нужно? С помощью SonataAdminBundle можно быстро создать конфигурируемый интерфейс редактирования сущностей ORM-модели (также выделены бандлы … Читать далее

Запись Административный интерфейс на Symfony 2 и SonataAdminBundle впервые появилась Разработка на Symfony2.

]]>
В базовой поставке 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, хотя на первый взляд интересный продукт.

Запись Административный интерфейс на Symfony 2 и SonataAdminBundle впервые появилась Разработка на Symfony2.

]]>
https://symfonydev.ru/sonata-admin-bundle-getting-started/feed/ 10