Привет читатель, сегодня я расскажу тебе о фомах в Symfony2.
Картинка с Лукасом на затравочку.
Дисклеймер
Из-за того, что Symfony2 довольно verbose фреймворк, я не буду приводить в статье полные исходники чего либо. Потому что, такие статьи превращиются в wall-of-code и потом набигают рубияны и начинается holy war. Вместо этого я приготовил рабочее Symfony2 приложение, ссылка на которое будет в конце статьи. Однако несколько строк кода будут присутствовать в статье.
По запросу трудящих сегодня поговорим о «сложных» формах.
То, что просили описать:
Расскажите о динамических формах, когда могут на клиенте добавляться-убираться поля. Как это все грамотно обрабатывать на клиенте и т.п.
И что бы поля у моделей были виртуальными. Т.е. у модели этих полей нет, а они предположим хранились в другой модели FieldEntity которая ссылалась бы на нашу Entity
Модель
В качестве хранилища буду использовать MongoDB, со всеми вытекающими последствиями.
Модель взята из жизни и сильно упрощена, для улучшения понимания.
Есть Предмет (Item), который может продаваться. Цена (Price) отделена от Предмета, так как один и тот же Предмет может продаваться в одно и тоже время по разным ценам, в зависимости от каких-либо условий.
Назовем Продукт с установленной Ценой — Пакет (Bundle). Пакет сам по себе не может продаваться. Пакет может продаваться в рамках Набора (BundleSet). Набор — это по сути коллекция Пакетов, а значит может содержать ноль и более Пакетов.
Предмет
/**
* @MongoDBDocument(
* collection="item",
* repositoryClass="AcmeDemoBundleDocumentItemRepository"
* )
*/
class Item
{
/**
* @AssertNotBlank (message="Did you forget the title?")
* @MongoDBString
*/
protected $title;
Цена
/**
* @MongoDBEmbeddedDocument
*/
class Price
{
/**
* @MongoDBFloat
*/
protected $real;
/**
* @MongoDBString
*/
protected $display;
Пакет
/**
* @MongoDBEmbeddedDocument
*/
class Bundle
{
/**
* @MongoDBReferenceOne(targetDocument="AcmeDemoBundleDocumentItem")
*/
protected $item;
/**
* @MongoDBEmbedOne(targetDocument="AcmeDemoBundleDocumentPrice")
*/
protected $price;
Набор
/**
* @MongoDBDocument(
* collection="bundle_set",
* repositoryClass="AcmeDemoBundleDocumentBundleSetRepository"
* )
*/
class BundleSet
{
/**
* @AssertNotBlank (message="Did you forget the title?")
* @MongoDBString
*/
protected $title;
/**
* @MongoDBEmbedMany(targetDocument="AcmeDemoBundleDocumentBundle")
*/
protected $bundles;
Все модели можно посмотреть в директории Document
Формы
Формы простроены через стандартный Симфонийский FormTypes.
Для всех ТиповФорм я всегда явно указываю опцию data_class
для того, что бы точно знать какой объект получится в результате $form->getData()
PriceFormType.php — Ничем не примечателен.
BundleFormType.php — содержит поле item
с типом document
->add('item', 'document', array(
'class' => 'AcmeDemoBundleDocumentItem',
'query_builder' => function(ItemRepository $repository) {
return $repository->createQueryBuilder();
}
))
Опция class
содержит полный неймспейс целевого документа.
Опция query_builder
содержит кложур замыкание которое вернет QueryBuilder
для получения всех требуемых документов.
В данном случае поле item
отрендерится как выпадающий список со всеми требуемыми документами.
И самое интересное это BundleSetFormType.php — содержит поле bundles
которое есть коллекция/набор BundleFormType
->add('bundles', 'collection', array(
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'type' => new BundleFormType(),
))
Тип поля коллекция, разрешаем добавлять/удалять элементы, тип BundleFormType.
Все FormType`ы можно посмотреть в директории Form
Как всем этим управлять.
К сожалению чуда не произошло и все добавления/удаления Input`ов в браузере придется делать руками на client-side`е — в темплейтах и JavaScript`ом.
1) При помощи Form Theming (дока) добавим кнопки Add и Remove в форму. Кнопка Add будет одна на всю форму и будет добавлять новый элемент в набор. Кнопка Remove будет возле каждого элемента набора и соответственно будет это удалять.
2) Вешаем JavaScript обработчики событий нажатия на эти кнопки. Исходники
Смысл всех client-side плясок с бубном в том, что бы при сабмите формы на сервер приходили структурированные данные и Симфа могла их взять из глобального $_POST.
После связывания (bind) данных формы с объектом, получим готовенький объект,
$form->bindRequest($request);
$bundleSet = $form->getData();
который можно использовать в своих целях далее. Писать в базу или что-то ещё.
Ссылка на рабочее демо-приложение HabrAcmeDemo
PS
Мопед JavaScript код не мой.
PSS
Все орфо замечания прошу присылать в личку.
Автор: cystbear
Искал простой туториал по созданию форм в симфони, но в итоге запутался окончательно!