Архитектура форм в Symfony2

Original author: Bernhard
  • Translation
Картинка для привлечения внимания

В Symfony 2 появился совершенно новый компонент для работы с формами, который, насколько я знаю, легко заменит большинство подобных библиотек для PHP и по функционалу, и по возможности в расширении оного (конечно, если не брать в расчет небольшие недостатки при работе с JavaScript). Разработка этого компонента заняла более двух лет, хотя думать над ним я начал еще где-то в 2009-ом году или даже раньше. С каждой новой версией этот компонент становится все более и более стабильным, а полностью стабильная версия ожидается с выходом Symfony 2.2.

Данный пост приурочен к выходу Zend Framework 2 Form RFC, так как мне кажется, что его разработчики, по сути, сделали много того, что уже было сделано нами. Конечно же всем ясно, что Zend Framework 2 должен обладать прослойкой для работы с формами, который полностью учитывает особенности компонентов, поставляемых с фреймворком. Целью данного поста является попытка показать, что Symfony2 Forms прекрасно подходит под эти требования. Функционал, присущий Symfony2, может быть легко убран: код для обработки форм и все уровни абстракций полностью независимы. Привязать же поддержку особенностей компонентов Zend-а так же не составит труда.

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


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

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


Влияние


На компонент форм повлияло множество фреймворков, написанных на разных языках, включая symfony 1, Zend Framework 1, Django, Ruby on Rails, Struts и JSF. Кроме того, в нем можно найти сходства с Formlets, WUI и iData, библиотеками для работы с формами, написанными для функциональных языков программирования, таких как Links, Curry и Clean.

Ключевые аспекты


Ключевыми аспектами при проектировании компонента для работы с формами являются:
  • Повышенный уровень абстракции
  • Расширяемость
  • Композиционность
  • Разделение задач
  • Привязка моделей
  • Динамические поведения


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

Повышенный уровень абстракции


Повышение уровня абстракции позволяет взять любую часть формы — даже форму целиком — и поместить ее в структуру данных, которую затем вы сможете использовать вновь. Рассмотрим форму для выбора даты, в которой имеются три выпадающих списка для выбора по дню, месяцу и году. Для начала вам нужен код, который сформирует HTML со всеми опциями выпадающих списков. Затем, вам понадобится код, который преобразует данные из формата вашего приложения (к примеру объект DateTime, если мы говорим о PHP) в формат, подходящий для нашего представления (для того что бы отметить выбранное значение у списков). Конечно понадобится и обратная процедура. А теперь представим ситуацию, когда вам придется добавить еще один выпадающий список для каких-то дополнительных данных. В этом случае вам придется продублировать весь код для этого списка и адаптировать его под новые требования.

Введение абстракции решает эту проблему, предоставляя подходящие структуры для описания и повторного использования вашего кода.

Расширяемость


Расширяемость основывается на двух концепциях, относящихся к абстракции:
  1. Специализация, это логическая последовательность абстракций. Если есть возможность абстрагировать функционал в обобщенные структуры данных, то их можно так же и расширить в более специализированные структуры. Например, мы можем расширить приведенный выше пример с выбором даты, добавив поля для выбора времени. Если бы возможности расширить функционал существующего поля для выбора даты не было, то нам пришлось бы переписывать существенную часть функционала.
  2. Примеси являются противоположностью специализации. К примеру вы захотели изменить все существующие поле так, что бы в их описаниях была звездочка ("*"), которая бы показывала то, что эти поля обязательны к заполнению. Использовать для этого подход со специализацией несколько непрактично, так как вам придется расширить все имеющиеся поля, реализуя тот же функционал. Примеси же позволяют подключать функционал к существующим объектам без необходимости переопределять их. К тому же, добавленный таким способом функционал будет наследоваться для всех дочерних объектов в древе наследования.


Расширяемость так же относится к более углубленному расширению функционала, при помощи событий. Это мы рассмотрим позже.

Компоновщик


Если мы проанализируем последний пример, мы можем заметить, что особой разницы между полями (составными полями, такими как в рассмотренном примере, или примитивными, такими как текстовое поле ввода) и формами нету. И поля и формы
  1. Принимают значения по умолчанию из моделей (из массивов, из дат, из строк...)
  2. Преобразуют значения, что бы его можно было использовать в представлении
  3. Формируют HTML
  4. Принимают значения, введенные пользователем
  5. Преобразуют значение обратно в формат модели
  6. Могут проводить валидацию данных


Мы можем реализовать поля и формы используя одни и те же фундаментальные структуры данных. Добавив компоновщик — инструмент, позволяющий добавлять вложенные структуры (смотри шаблон проектирования «Компоновщик») — мы сможем создать форму любой сложности. Вместо того что бы разграничивать поля и формы, поговорим о формах и их составных частях. Как только у формы появляется потомок, он так же должен
  1. передавать значения по умолчанию (массив или объект) его потомкам
  2. передавать введенные значения каждого дочернего элемента обратно в массив или объект


Разделение задач

Мы можем разделить задачи, описанные выше, на несколько определенных групп:
  • Преобразование данных
  • Формирование HTML (представление)
  • Валидация
  • Привязка данных


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

Привязка моделей


В большинстве случаев, формы напрямую привязаны к структурам, так или иначе, описанным в модели области определения. Рассмотрим на примере отправки информации профиля пользователя. Предположим, что вся информация о пользователе хранится в отдельной таблице в вашей базе данных. Таблица хранит информацию о свойствах, которые содержит профиль, о том, какой тип данных используется для каждого из свойств, их значения по умолчанию и ограничения по вводу данных. В идеале, ваше приложение так же должно содержать класс Profile со всеми необходимыми свойствами, с привязкой к таблице в базе данных, например, посредством Doctrine2 ORM. Этот класс может содержать больше информации о профиле, нежели хранится в таблице, например если в профиле указывается какой-то набор предметов, интересующих пользователя. Список этих предметов, для простоты, будем брать из файла конфигураций.

Обычно, информация описывающая объект (назовем ее метаданными) должна быть продублирована в прослойке форм. Разработчику приходится следить за тем, не изменились ли поля, какие из них можно редактировать, следить за тем, что бы форма отображала соответствующие полям HTML виджеты, следить за тем, что бы некоторые из полей не могли оставаться пустыми ну и т.д. Именно из-за этих вещей создание форм становится довольно неблагодарным делом.

Привязка моделей призвана изменить ситуацию. Она основывается на двух идеях:
  1. Использование существующих метаданных во время создании формы, дабы уменьшить количество дублируемого кода и/или настроек.
Значения по умолчанию должны получаться из модели области определения (в нашем случае это будет экземпляр класса Profile) и запись введенных данных так же должна осуществляться в нашу модель.


Динамические поведения

И последнее, но не наименьшее, это динамические поведения для форм. Прошли те времена, когда код для формы полностью реализовался на сервере, где проверка на безопасность введенных пользователем данных велась под жестко заданные структуры. Сегодня модно использовать JavaScript для изменения форм на стороне клиента для повышения удобства работы с системой.

Представим себе форму в виде таблицы. Каждая колонка имеет поля одного и того же типа, каждый ряд представляет собой объект на сервере. У формы имеются кнопочки, позволяющие добавлять или удалять ряды. При всем этом, должна сохраниться возможность валидации форм, они должны успешно обрабатываться на сервере.

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

Высокоуровневая архитектура



Высокоуровневая архитектура


Позвольте представить высокоуровневое описание архитектуры прослойки в Symfony2, предназначенной для работы с формами. Краеугольным камнем ее служит компонент Form. Этот компонент реализует основу для составления и обработки форм, а так же использует диспетчер событий Symfony2 для обработки внутренних событий. Поверх этого компонента лежит слой подключаемых расширений:

  • Расширения уровня ядра предоставляют описания структуры полей (назовем это типами форм (FormType)), реализованных во фреймворке.
  • Расширение валидации необходимо для проверки введенных пользователем данных внутри форм.
  • Расширение DI позволяет использовать контейнер зависимостей.
  • Расширение CSRF, как можно понять из названия, добавляет CSRF защиту для формы.
  • Расширения Doctrine2 (поставляемые с Doctrine Bridge) добавляют специфичные для Doctrine выпадающие списки и компоненты, позволяющие получить метаданные объекта для использования их в формах.


Верхний уровень содержит компоненты, реализующие формирование HTML-я. По умолчанию в Symfony2 есть два таких компонента: один для рендринга форм через Twig (поставляется в Twig bridge) и другой для рендринга через старый добрый PHP (поставляется с FrameworkBundle).

Наверное самое интересной особенностью данной архитектуры является то, что любой из компонентов можно заменить. Можно написать расширение для того, что бы формы использовали компонент валидации из Zend-а. Так же вы можете написать компонент представления реализующий рендринг через Smarty или любой другой шаблонизатор. Вы даже можете удалить расширение уровня ядра и написать свои основные типы форм для простых полей. Даже лежащий в основе диспетчек событий может быть заменен на ваш компонент, реализующий интерфейс EventDispatcherInterface. Это дает неплохое преимущество в гибкости, несмотря на некоторые неудобства.

Низкоуровневая архитектура



В данном разделе речь пойдет о внутренней архитектуре компонента форм. Как уже было упомянуто выше, форма и все ее части представлены в виде одной и той же структуры данных, которая реализует шаблон проектирования «Компоновщик». В компоненте Form данная структура описана интерфейсом FormInterface. Основным классом, реализующим этот интерфейс, является класс Form, который использует три компонента, при помощи которых и работает все это дело:
  • Мэппер данных распределяет данные формы по ее дочерним элементам. Кроме того, в обязанности этого компонента входит сбор данных от дочерних элементов обратно к данным формы.
  • Две цепочки преобразователей данных, как понятно из названия, преобразуют значения для разных их представлений. Преобразователи данных гарантируют то, что ваше приложение получит значение в строго определенном формате, вне зависимости от формата, который используется для его представления.
  • Диспетчер событий позволяет запускать специфичный код на заранее определенных этапах обработки формы. Такой подход позволяет адаптировать структуру формы под входящие данные, или же отфильтровать эти самые входящие данные, произвести валидацию или изменить их.


Все эти компоненты передаются в конструктор объекта Form и не могут быть изменены после создания формы, так как это может привести к нежелательным изменениям состояния формы. Так как интерфейс конструктора объекта Form достаточно сложный, был реализован конструктор форм (FormBuilder), который упрощает создание экземпляров этих объектов.

Представление формы это структура данных, описывающая представление. То-есть вместо того, что бы в шаблонах вызывать непосредственно класс Form, вы будете работать с экземпляром класса FormView. Этот объект хранит в себе дополнительную, специфичную только для представления информацию, такую как атрибуты name для элементов HTML форм, их ID и т.д.

Приведенная ниже UML диаграмма отображает суть архитектуры.

UML диаграмма архитектуры

Как видно из этой диаграммы, жизненный цикл формы состоит из трех разных представлений:

  • При создании, форма представлена в виде иерархии объектов FormBuilder
  • В контроллере, форма представлена иерархией объектов Form
  • На уровне представления, она представлена иерархией объектов FormView


Поскольку настройка конструкторов форм их представление дело довольно нудное, в Symfony2 реализованы основные типы форм, которые уже реализуют основные настройки. Типы форм поддерживают динамическое наследование. Это значит, что вы можете расширять различные базовые типы, основываясь на опциях, передаваемых в конструктор формы. Следующая диаграмма отображает все типы, поставляемые с Symfony2 (типы, отмеченные зеленым цветом, поставляются с ядром, когда как желтые — с дополнительными бандлами):

Иерархия типов

Примеси, о которых мы раньше упоминали, реализуются в SYmfony2 как, так называемые, расширения типов. Эти расширения могут быть подключены к существующим типам форм и добавляют к ним какое-либо поведение. В Symfony2, например, существуют расширения для добавления CSRF защиты, применительно к типу «form» (и как следствие, всем типам, которые наследуют этот тип).

Фабрика форм получает иерархию типов из загруженных расширений, и использует ее для настройки объектов FormBuilder и FormView. Важно отметить, что этот этап можно контролировать. Например, тип "choice" позволяет указывать в настройках атрибут "choices", который содержит в себе все значения для опций, которые должны быть отображены.

Есть еще одна достаточно важная концепция компонента Form — предсказатели типов. Эти «предсказатели» пытаются предугадать тип и опции поля формы, основываясь на метаданных, взятых из объекта области определения, за которой закреплена форма (если, конечно, она закреплена). К примеру, если какое-либо из свойств нашего объекта содержит связь «один ко многим» между объектом Tag, предсказатели типов автоматически настроят это поле формы для этого свойства так, что бы оно было полем выбора, с возможностью выбрать несколько значений, а так же будет использовать все экземпляры объектов Tag в качестве опций для этого поля. Похожая концепция используется в ModelForms во фреймворке Django. Правда есть одно достаточно большое отличие: предсказатели типов используют метаданные предоставляемые не только ORM-ом, но и все метаданные объекта. Symfony2 поставляется с тремя предсказателями типов: один использует метаданные, предоставляемые Doctrine2, другой — предоставляемые Propel и еще один использует правила валидации.

Описанные выше концепции можно описать следующей UML диаграммой.

image

Заключение


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

Эта архитектура решает проблемы недостаточного уровня абстракций, специализации и примесей, предоставляя динамическое древо наследования типов форм и их расширений. Она решает проблему композиционности, распределяя задачи по обработке формы на все ее элементы. Так же она обеспечивает четкое разделение задач между компонентами, благодаря чему вы можете заменить их на свои. Используя метаданные объектов области определения при создании формы, описанная архитектура реализует привязку данных к этим моделям, что позволяет производить чтение и запись данных напрямую. Так же мы не обошли вниманием и динамические поведения, предоставляя возможность отслеживать определенные события, возникающие в процессе обработки формы, используя свои обработчики, например для валидации или фильтрации данных.

Заинтересовались? Изучайте код. Экспериментируйте с ним. И помогите нам интегрировать его в ваш любимый фреймворк.

От переводчика: переводы данной статьи существуют, но мне они показались не слишком качественными. Да и думаю не помешает. В переводе возможны неточности, посему буду благодарен, если вы укажите на них в ЛС. Спасибо за внимание.

Similar posts

Ads
AdBlock has stolen the banner, but banners are not teeth — they will be back

More

Comments 110

    +3
    Мне показалось что вот с формами они перегнули немного. Сложновато как-то въехать даже после многочасового изучения кода
      0
      Ну по сути да, хотя мне кажется это много более удобным чем все что есть сейчас. Во всяком случае в мире PHP ничего подобного нету. Особенно плюшки проявляются при работа с очень большими формами, с динамическими формами. Можно очень много плюшек реализовать как формтайпы, и их можно легко использовать в других проектах.
        +2
        Очень странно это услышать от меня: но в данном случае я с вами согласен — имхо, с формами перемудрили, попытавшись запихнуть в них сразу всю функциональность. (Зачем мешать логику мапинга данных и их отображения я до сих пор не пойму)
          0
          А она разве смешана?
            +1
            Да, например, с точки зрения данных choice, radio, checkbox, etc — это все одно и тоже, это коллекция/массив значений. И типы различают лишь внешнее представление. Тоже самое со всеми текстовыми полями.
              0
              Но логика мэппинга данных и отображения же не связана. Во View лишь передаются опции, а то как отображать решает уже слой View. Либо я вас не понял.
                +1
                Она разделена в реализации, но логически оно связано: меня «заставляют» делать выбор как будет отображаться форма в том месте, где мне на это наплевать. Вот именно, что во вью должно решаться отобразить select, radio или checkbox; textarea или input: «я не хочу ничего решать, я хочу string»!
                  0
                  И я еще не говорю про лейблы, html атрибуты и т.п.
                    0
                    Эм… напишите 'choice' как тип формы и на основе того, что хранится у вас в поле, вам выдадут нужное представление. Лабелы генерятся из названия поля, HTML атрибуты так же. При желании можно просто бахнуть свой шаблон с виджетами или перегрузить только часть из них. Как по мне довольно гибко и мало возни. Вот в Yii с этим грустно.
                      0
                      Как пример посмотрите формы во фреймворке onPHP (https://github.com/onPHP/onphp-framework)

                      тут (https://github.com/onPHP/onphp-framework/wiki/Ru%3Aformarticle)
                      и тут (https://github.com/onPHP/onphp-framework/wiki/Ru%3AForm)
                        0
                        Ну идея похожа, но в более примитивном исполнении. Тоже есть примитивные типы (email, пароль и т.д.), есть что-то вроде FormBuilder-а. В чем различие то? то что там названо примитивами — в Symfony Forms назвали FormType. Чисто идеологически все достаточно похоже. Если вы о том что явной связи с представлением нету, то тут да. Хотя по сути вы и в Symfony примерно так же. Может я что проглядел?

                        А вообще занятная реализация. Спасибо. Неплохой микрофреймворк.
      • UFO just landed and posted this here
          +3
          Я сильно извиняюсь за оффтоп и разжигание межплатформенной розни, но вот почему при работе с Rails я даже никогда не задумывался о том, как там работают формы? Они просто работают и всё.

          В symfony 1.x формы тоже не подарок были, но тут что-то они совсем нагородили.
            +2
            По сути при работе с формами в Symfony2 вам тоже особо париться «как оно работает» не нужно. Тут просто приводятся доводы аля «зачем делать что-то еще? может одно что-нибудь доделаем вместе?».

            На самом деле сложно только первое время. Мне вот после полугода активного использования формочек они дико нравятся. Проще покрывать тестами, повышается уровень абстракции, много клевых плюшек. Допустим, эллемент формы генерит JSON массив (какой-нибудь навороченный редактор чего-нибудь) и датаконвертер (через JMSSerializeBundle к примеру) делает из этого JSON объекта готовый к использованию внутри приложения объект (коллекцию объектов, какое-то дерево или мало ли что еще). Поработав немного на реальных проектах, я лично считаю что формы были реализованы довольно неплохо. И смею заметить, что это еще далеко не финал. Как и говорится в статье — что-то близкое к финальной версии появится только в Symfony 2.2.

            Возможно это просто дело вкуса, но я вспоминаю как сам поначалу плевался. Сейчас же ничего проще (покрайнемере в PHP) я найти не могу.
              +1
              Потому как в RCT ребята довольно умные, и понимают, что лучше один раз написать удобную вещь. Понастоящему удобную вещь. Не еще один хренов велосипед, потому что им так захотелось. А инструмент, пользуясь которым, мы, во первых сэкономим свое время, а во вторых нервые клетки, потому что мы не будем думать о том, какой он кривой, не будетм пытатся себя перебороть, заставляя пользоваться вот этим-вот инструментом, и у нас не возникнет желание в следующий раз выбрать что-то другое.

              Когда софт пишут умные люди, для других, умных (и чуть менее умных) людей, то он получается именно таким — простым и удобным настолько, что ты забываешь о том, что это инструмент написаный другими людьми а просто его используешь для решения задач.
                +1
                В Rails формы, как отдельного объекта, нет и все, что должно быть в форме, находится в модели. Это и плюс и минус. Очень просто нарисовать, но сложнее менять и расширять. Симфони, насколько я понял, претендует на звание Enterprise Framework, со всем вытекающим отсюда ООП головного мозга. Так что сравнивать не очень корректно.
                +2
                На мой взгляд, авторы симфони2 перехитрили сами себя. Все настолько абстрактно, независимо, подключаемо, заменяемо, что цельный фреймворк (которым я считаю, например, симфони1), рассыпался в горсть болтов, гаек, гаечных ключей и проволоки.
                  0
                  Так в этом же и суть, вы можете запросто использовать в своем проекте какие-то части фреймворка, или заменить какой-то слой этих гаек на свой.
                    0
                    Если людям приходиться при работе с фреймворком заменять какие-то его части… то это какой-то хреновый фреймворк
                      0
                      Под заменой я подразумевал расширение функционала, а не замена реализации.
                        0
                        Я сейчас не могу найти ни одного случая, когда бы мне пришлось бы расширять возможности фреймворка, без осознания того, что такой фреймворк мне уже не нужен. Когда после пары недель с момента «ну сейчас, тут допилим… и вот тут» — фреймворк выкидывался и брался другой — знаю с десяток. Два из которых ну просто клинические, к сожалению, в одном из них я был ключевой фигурой (увы, на тот момент я не мог повлиять на ситуацию): и напихав десяток кослытей все таки смог соединить две вещи, реализаця которых бы, даже самописная была гораздо быстрее и проще.

                        Так что то, что они там накидали пару лишних слоев абстрации для того, что бы кому то пришло в голову что-то там дополнить, допилить и заменить говорит только о двух вещах: первое они все еще не уверены в своих силах и не могут написать действительно удобную вещь. Второе — они создали себе лишние проблемы как с кодом, так и с пользователями фреймворка
                        • UFO just landed and posted this here
                            0
                            Мне кажется я знаю о чем говорю потому как у меня есть с чем сравнивать: с симфонией работал, пришлось (потому как остальные варианты были еще хуже). Хотя хочу сказать, что если бы была возможность переписать все на спринг — сделал бы не задумываясь, но увы, пришлось возиться с этой жалкой копией (и плюс такой же жалкой копией хибернейта).

                            Жалкой потому что возможности языка позволяют многие вещи сделать красиво, а разработчики просто скопипастили с топорной явы. Жалкий потому что скорость написания одних и тех же действий на яве быстрее в разы, потому как иде не путается в комментах и выдает правильный ассист. Жалкий потому что ServiceLocator выдают за DI.
                            • UFO just landed and posted this here
                      0
                      Если так, как Вы говорите, то это уже не фреймворк, а просто набор классов. Библиотека.
                        +2
                        Symfony2 Forms, Doctrine2, Propel, Twig, Symfony2 Routing и много чего еще — это отдельные компоненты. Symfony2 Framework лишь объединяет их, содержит в себе какие-то общие вещи (конфигурация, взаимодействие и прочее), готовые типы форм, плюшки. Такой подход лучше, чем мессиво тесно связанных между собой компонентов, которые сложно допилить под себя (допустим этим болеет Yii. Просто — но иногда не удобно, сложнее расширять).
                          0
                          Ну не знаю. Приведите пример, когда Yii было сложно расширить и допилить? И посчитайте сколько времени у вас заняло писать классы для каждой формы, для каждого чиха? В Yii есть недостатки, но по скорости разработки он переплюнет симфони.
                          PS: я опираюсь на опыт с zend, у них ведь схожая идеология?
                            +1
                            В Yii форм билдер вообще убогое зрелищие, он подходит только для однотипных формочек. Как минимум не хватает контроля за данными, предсказателей типов, все завязано на класс CModel и т.д.

                            Допустим, мне нужно реализовать формтайп для заполнения адреса (то бишь ввод адреса, вывод на гуглокарте, определение координат, geolocation API и все такое. Что бы пользователь зашел на страничку, а на форме уже забито откуда он и что, ему нужно будет только удостовериться что все правильно определило).

                            Если не брать в расчет реализацию JS (тут в любом случае время константно) а брать только время реализации виджета, классов и прочего… Минут за 30 я сделаю и конвертеры данных, и формтайп, и вьюшку (делал, цифры не из головы).

                            Причем я могу взять этот формтайп, перенести в другой проект, или же использовать в другой форме в рамках этого же проекта, просто прописав в формбилдере:
                                    ...
                                    ->add('address', 'location')
                            


                            А теперь предложите мне сносный вариант. что бы не пришлось затрагивать CActiveForm например, или CForm, реализовать это в Yii? Знаю что можно, но выходит не так красиво.
                              0
                              Что мешает написать виджет для визуальной составляющей и behavior для поля в модели? И то и то можно повторно использовать.
                                0
                                То, что расширять это уже будет не удобно.
                                  0
                                  куда расширять то? Вот данный, конкретный случай.
                          0
                          Практически все Известные мне популярные фреймворки можно разделить на две части — собственно фреймворк (каркас, «микрофреймворк») и библиотека. В symfony2 жесткие связи между каркасом приложения и библиотекой сведены к минимуму, каркас по сути лишь ещё одна библиотека, в которой связи со стандартными библиотеками могут быть легко заменены на сторонние или самописные, лишь бы интерфейс совпадал.
                            0
                            Да я понимаю, что вся эта модульность очень крута, но она порождает усложнение архитектуры, причем иногда излишнее. Я сейчас перехожу на rails после зенда и не могу не нарадоваться, что не нужно писать такое огромное количество своих костылей И что все сделано просто и очевидно, а наличие гемов позволяет сократить велосипедостроение до минимума. Так что зависит от целей. Тут как linux VS windows, когда хочется поразбираться, поковыряться, то выбираешь линукс. а когда нужно, чтобы просто работало — windows. И если зенд это уже не первый фреймворк и азарт пропал, то нет никакого желания принимать его выверты архитектуры(хотя я прекрасно понимаю и что, и как, и зачем) и отсутствия самых необходимых вещей.
                              +1
                              Топик всё-таки про symfony, а не Zend. А я вот пишу PoC-прототипы на rails, а потом реализую их на symfony. Конечно хотелось бы использовать один инструмент и для того, и для другого с одинаковым удобством, но, увы, не получается, а если выбирать что-то одно, то это будет symfony даже по независящим от меня причинам.
                      –2
                      А какую нишу занимает(планирует занимать) Symfony? На PHP не годится делать энтерпрайз решения, для этого же есть Java/.NET. Ну а быстро развернуть приложение, благодаря динамической типизации и прочих плюшек скриптовых языков, не получится, потому что все сделано по самым лучшим практикам из джава-ооп мира…
                        +1
                        Пустую нишу. На данный момент это единственный более-мение годный фрейворк на данном языке. ZF сильно хуже, остальные либо достаточно слабые, либо никакие полностью. А для быстрого разворачивания — рельсы, как ни крути.
                          0
                          Почему единственный? Как же Yii?
                            +1
                            К сожалению с yii знаком давно и был не лучшего мнения о нем, поэтому даже не смотрел на него, но если вы сейчас кинете сюда кусок кода работы с формами и бд буду благодарен (хотя бы увижу во что он превратился). Собственно к нам прилетает форма, мы ее валидируем и если все хорошо — пишем все поля из нее в базу, если нет — говорим в каком поле ошибка и возвращаем обратно
                              –1
                              if(isset($_POST['User'])){
                                  $model->attributes=$_POST['User'];
                                  if($model->save()){
                                       $this->redirect(...);
                                   }
                              }
                              

                              Вот так можно
                                +1
                                Ну вообще форма как предметная область, должна содержать значения полей в себе, а не в суперглобальном массиве:-) И где здесь вообще объект формы?
                                  0
                                  Модель содержит в себе правила валидации. Объекта формы нет как такового. Active Record.
                                    0
                                    Такой подход имеет право на существование если он реализован как в релсьах. Если так, как написали вы — это ужасно.
                                      0
                                      Не находите, что в рельсах точно так же? Yii сделан под большим впечатлением от них
                                        0
                                        Только вот Руби и PHP это совершенно разные вещи. Допустим, Post::model()->findAll() — это грустно. Это равносильно $post->findAll(). Мол каждый экземпляр имеет доступ ко всему репозиторию. В руби же все чуть сложнее, посему концепция AR там подходит идеально.
                                          0
                                          С точки зрения обрабоки данных из формы там то же самое. Все проверяется в модели
                                      0
                                      А вы в курсе что в Yii таки есть FormBuilder? Я тоже в Symfony2 могу нафигачить все в шаблоне и работать напрямую с POST данными, валидацию всеравно можно дергать для модели. Но зачем?
                                        0
                                        В курсе, только зачем они? Когда в модели можно провести всю валидацию?
                                          0
                                          Затем, что без использования формбилдеров реюз кода становится крайне сомтительным занятием. Реюз контроллеров вообще довольно гнилая тема, а если у вас вся логика по обработке форм там — то это грустно. Формбилдер нужен для того, что бы ваши темплейты небыли забиты вызовами методов у объекта CActiveForm. За вас это билдер сделает. И запрос обработает, и данные по полям в модели разнесет, и валидацию проведет.

                                          Поработайте на проектах с полями, которые от объекта к объекту имеют общие части. Если каждый раз просто дублировать код, поддерживать его станет просто адом.
                                        +1
                                        А зачем Вы тогда про формы пишете, если их нету:-)
                                        Обрабатываете Вы запрос, а не форму в данном случае:-)
                                      0
                                      Это ужасно. Во первых это ужасно потому, что не факт что все что у меня есть в форме у меня есть в модели, к примеру у меня некоторые поля в модели могут быть разделены на 2-3 поля в форме. Да и это не главное, как мне юзеру выдать то, что у него во втором поле ошибка а не просто «форма неправильная, давай заново».
                                        0
                                        Каждое поле модели содержит ошибку, если она есть. Потом во вью они выводятся. Так что для каждого поля своя ошибка. Если разделены, то обработайте перед отправкой в модель.
                                          0
                                          В том то и суть что контроллер это не то место, куда надо выносить обработку данных. Конечно же можно сделать через экземпляр CFormModel и там уже поставить нужные поля как unsafe, прогнать через сеттеры прописанные в модели… но это все-равно убогою
                                            0
                                            Форма чаще всего используется один раз. Какой смысл писать для нее целый класс?
                                              +1
                                              Инкапсулировать логику по работе с данной конкретной формой в один конкретный класс, к примеру.
                                                0
                                                Зачем?
                                                  +1
                                                  Reusable code — не? В честь чего один раз?

                                                  У меня 100500 проектов и одна форма авторизации, которая имеет разные представления, разную модель поведения и тд
                                                    0
                                                    Мы же про формы говорим? это кусок Html и валидация? Что мешает в модель из рельсов написать has_secure_password? И вью скопировать для отображение. Представление меняем с CSS. Я сомневаюсь, что вам не придется менять форму из Zend_Form, если вы заходите изменить разметку и «модель поведения ». Так что не надо преувеличивать реюз.
                                                      +1
                                                      Суть в том что абсолютно вся логика вынесена в отдельный компонент, который легко и просто переносится между проектами, легко поддерживается и все тому подобное.

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

                                                      Или же одна из этих сильно специфичных форм должна быть включена в родительскую, и динамически добавляться. Ну мол по нажатию на плюсик.
                                                        0
                                                        Про partials слышали?
                                                        И еще раз, хоть эта логика и вынесена, вряд ли удастся ее использовать в том же самом виде.
                                                          0
                                                          Ну мне как-то удается, вынося все в FormType и используя DataConverter в Symfony2. Кода выходит, как не странно, но меньше.
                                                            0
                                                            Ладно, наверное нет смысла продолжать насчет форм, потому что я работал с Zend_Form и там все довольно печально. Может в Симфони с этим делом лучше.
                                                          0
                                                          И еще один момент, допустим у вас три формы использующихся для одной модели. Некоторые поля пересекаются, как вы решите проблему дублирования? Zend_Form.
                                                          А если вся валидация в модели, то она и применится только тогда, когда это поле вообще присутствует в форме. И будет применяться всегда.
                                                            0
                                                            Вот проблемы дробления форм тоже небыло. Если у меня есть 3 формы — то и валидация, или логика работы разная.

                                                            Допустим для авторизации пользователей правила валидации одни, для регистрации другие, для восстановления пароля вообще третьи. А если есть пересечение — воспользовавшись наследованием это все решается, валидацию так же можно определить в одном месте, а те правила что не пересекаются — выделить в отдельные группы валидации.
                                                      +1
                                                      Чтобы абстрагироваться от неё, скрыть от разработчиков контроллера, модели и вьюшек особенности реализации данной конкретной формы, с одной стороны, но предоставить возможность кастомизации с другой.
                                                0
                                                Реальный пример — дата и время, на клиенте поле в котором можно написать 150 или 2:20 или 2:20:34 или это несколько полей: календарь + две крутилки времени. В модели — таймстемп, в реальности надо сказать человеку где он ошибся. Как тут быть?
                                                  0
                                                  Выделить всю комбинацию полей отвечающих за дату?
                                                    0
                                                    Как показала практика (да и требования ui дизайнеров) — люди не всегда быстро замечают где ошибка, если допустим мы подсвечиваем три поля, а не именно то в котором ошибка.
                                                      –1
                                                      где вы видели, чтобы дата была простым инпутом? Обычно понавешано куча джваскрипта, который позволит написать невалидные данные только если захотеть.
                                                        0
                                                        Не всегда можно навесить датапикер. бывают очень специфичные требования. Допустим у вас есть форма, в которой вы должны выбрать диапазон годов, а в базу пишется коллекция каких-то данных у каждого из которых указан год из этого диапазона.
                                                          0
                                                          Можно же в сообщении об ошибке указать — «Год окончания не может быть позже года начала». Ну или как-нибудь грамотнее =)
                                                            0
                                                            Нет, вы сути не улавливаете. У вас в форме 2 поля, в модели одно, которое к тому же хранит коллекцию объектов. Валидацию вам придется делать по двум полям, тобиш использовать для этого модель уже не выйдет.
                                                              0
                                                              Вам выше написали, что если речь про Yii, то можно сделать свой сеттер, можно написать свой метод валидации и так далее. Да, это все будет в модели, ну и что?
                                                              Ну а выделяем уже то самое одно поле из модели.
                                                                0
                                                                Читаю тред, вопрос хороший. Fesor, я так понял вы тут главный спец по Symfony 2, так дайте чёткий ответ: можно ли сделать валидацию по двум полям формы раздельно не смотря на то, что внутри модели это одно поле? Если да, укладывается ли это в архитектуру форм-моделей-валидаций в Symfony 2 или придётся извращаться?
                                                                • UFO just landed and posted this here
                                                              –1
                                                              И опять же джава скриптом это можно легко поправить и проверить.
                                                                +1
                                                                Джаваскрипт это хорошо, но без валидации на серверной стороне никак увы.
                                                                  0
                                                                  Да, но сократит критичность сообщения об ошибке уже после отправки формы.
                                                              +1
                                                              Вы когда-нибудь работали с большим количеством форм, в котором куча полей. Вот к примеру наши геймдизайнеры работали. И многие поля связанные со времеем у нас были либо в секундах по дефолту, либо в форматах: h m s | m s | s либо в 10w 3h 1d 6s — потому что это быстрее. быстрее написать 600 для 10 минут, быстрее написать 12 01 44 для 12 часов 1 минуты и 44 секунд. быстрее написать +1w для одной недели. нежели выбирать две даты из календаря.
                                                                0
                                                                что мешает указать ошибку в сообщении? Допустим у вас три поля отвечают за дату. Если выделить их красным и русским языком написать в чем ошибка, то будет непонятно?
                                                                  0
                                                                  Будет но как вы в моделе определите в каком поле ошибка, если по сути это все перегоняется в два инта и потом уже сравнивается?
                                                                    0
                                                                    вы же при проверке будете знать в чем ошибка? А значит и сообщение напишете соответственное. Ну а выделите все три =)
                                                    +1
                                                    $em = $this->getDoctrine()->getManager();
                                                    $form = $this->createForm(new MyFormType(), new Entity());
                                                    if ('POST' == $this->getRequest()->getMethod()) {
                                                          if ($form->bind($this->getRequest)->isValid()) {
                                                               $entity = $form->getData();
                                                               $em->persist($entity);
                                                               $em->flush();
                                                               // редиректы, flash сообщение и все что нужно
                                                          }
                                                    }
                                                    


                                                    Причем какой бы не была сложной логика работы с формами, контроллер обычно не меняет своего вида. Все выносится в DataConverter-ы, или же (например для хэширования паролей, загрузки файлов на сервер) используются либо хэндлеры, либо листенеры событий. Можно конечно и в контроллере, но мне лично так делать не нравится.
                                                0
                                                C чего Вы решили, что Zend_Form хуже?
                                                  +2
                                                  С того, что я с ними долгое время работал. С того, что мне приходилось доделывать некоторые кастомные компоненты. Как думаете, сколько строк кода и времени у вас займет создать в зендовых формах форму которая отличается всего лишь одним полем от модели. А теперь ошибки перевести на 2 языка, да понятные пользователям системы.

                                                  Да взгляните на одно только описание формы в ini от которого блевать тянет (а они же до сих пор, в отличие от всех остальных, кто давно уже на ямле) все либо в xml либо в ini делают
                                                    0
                                                    Я поэтому и спросил, что сам с ними очень долго работал и ничего лучше еще не встречал.

                                                    Там есть проблемы с предметной областью, например валидацией и фильтрацией не «там, где надо», потому что форма у них != view для модели, но в остальном компонент шикарен!

                                                    Zend_Form с моделями вообще никак не связана, потому что моделей в ZF нету:-)
                                                    Ошибки валидаторов переводятся в одном файле с передовом для конкретного языка, подключается к приложению одной строчкой:-)
                                                    Вы не поверите, но конфигурация тоже построена на Zend_Config, который умеет понимать все форматы:-)

                                                    А доделывать Ваши = кастомные компоненты действительно приходится, потому что они Ваши!
                                                    Плюс Zend_Form в том, что делаются они тривиально.

                                                    Так Вы с ними правда работали?)

                                                      0
                                                      Повторяю вопрос: сколько строк кода мне придется написать для того, что бы перекинуть один объект моей модели в объект ZF? 50? 150? 250 для большей модели? В остальных фреймворках порядка 4-5.

                                                      Потому что они понимают, что если написано int — то надо хотя бы проверить на то, что это интовое значение, а не «Привет я Вася».

                                                      Да это понятно что там Zend_Config везде, так что можно хоть в бинарном формате ему скормить описание (я и такое делал), но по дефолту они везде в описаниях пихают ini. Что собственно распростанило это более чем прекрасный формат для простых конфигов (простых, а не иерархичных!) на множество проектов.

                                                      Вы когда-нибудь пробовали изменить рендеринг форм? У нас вот форма должна была рендериться в представление extjs (почему было именно так — долгая история), а вот красивняшка ZF жестко завязана на htmlююю
                                                        0
                                                        $form->populate($model->toArray()) в простейшем случае — 1 строчка

                                                        Кастомная логика — you're welcome: $form->fromModel(Model $model) — писать надо будет самому.
                                                        Если есть «телепатический» фреймворк, который сам решит, что я хочу, расскажите о нем:-)
                                                        В остальных фреймворках, всегда костыли, поверьте пробовал много.

                                                        Отлично меняется рендеринг, если использовать паттерн декоратор(Zend_Form_Decorator) — хоть в brainfuck, если нужно: $el->addDecorator('brainfuck');
                                                0
                                                Имхо, Symfony 2 лучше других (правда ZF2 ещё не оценивал) подходит для создания расширяемых пользователем коробочных продуктов класса CMS. Вернее даже серии таких продуктов. Другими словами, Symfony 2 платформа для создания специализированных CMF на PHP, которые как работают из коробки и управляются «мышкой», так и предполагают возможность практически неограниченной кастомизации. Ещё можно сказать, что Symfony 2 лучше других подходит на роль инструмента для создания инструментов для веб-мастеров и веб-студий — продукты на базе Symfony 2 будут (если получится преодолеть консерватизм рынка) конкурировать с традиционными CMS/F типа Wordpress, Drupal, Bitrix.
                                                  0
                                                  Вот и получается — фреймворк для создания фреймворков, а не веб-приложений…
                                                    0
                                                    Подменяете понятия full stack framework и content managment framework. Для создания первых есть Symfony Components, для вторых — Symfony Framework.

                                                    Ну и я назвал лишь одну из потенциальных ниш, а не единственно возможную. То что Symfony 2 лучше остальных подходит для сложных и гибких приложений вовсе не значит, что он хуже подходит для простых.

                                                    0
                                                    8й drupal на symfony 2 пишется, с чего им конкурировать?
                                                      0
                                                      Drupal сохранит большую часть ядра, там просто будут использовать некоторые компоненты Symfony и Symfony CMF.
                                                  0
                                                  Надеюсь он проще в использовании, чем Zend_Form. Последний внушал мне мысли о суциде
                                                    0
                                                    Ну не уверен что сильно проще, с непривычки все же сложновато, но приятнее как минимум.
                                                    0
                                                    Простые надо делать предметные области в библиотеках, а тут наоборот слишком сложные и компоненты зачем-то связали.

                                                    P.S. ЗАЧЕМ переходить на неймспейсы, если все классы по прежнему содержат приставку-неймспейс (почему .../Form/FormErrors, а не .../Form/Errors)?

                                                      +1
                                                      Потому что Errors — не самое лучшее название для класса. В нормальных языках работа с неймспейсами прозрачна. В яве вообще пишут com.companyname.productname.someshitname.forms.BlablaForm, но кого волнует в каком пакете это все лежит если нормальная иде номально все импортит
                                                        0
                                                        А в для PHP разве есть автоимпорт?
                                                        На джаве то понятно. Там хоть и все ООП`шно и сложно, но там IDE за тебя пишет половину кода.
                                                          0
                                                          Если в IDE в которой вы пишите нет автоимпорта… то я бы вам советовал выбрать другую IDE. Скорость написания кода на PHP и так гораздо меньше нежели на другом нормальном языке (из за многих факторов), а если меня еще и самого заставят импорты прописывать…
                                                            0
                                                            Просто не писал на PHP на фреймворках с неймспейсами, поэтому не в курсе таких фишек.
                                                          +1
                                                          Если они реально думали, что «давайте назовем классы так, потому что блин хреново импортят IDE и автокомплит у пацанов адский», то я считаю надо было либо ждать пока IDE научатся обрабатывать use, либо оставаться на PEAR Conventions.

                                                          Считаю, что в таком переходе на неймспейсы нет никакого смысла.

                                                          Вообще видится бредом, ибо всем не нравились длинные имена классов, как в PEAR, а теперь всем не нравятся короткие, потому что слишком все одинаково, не коммьюнити, а хипстеры какие-то:-)
                                                            0
                                                            Так IDE работают с use (PHPStorm, например)
                                                              0
                                                              Да уже пару лет как
                                                          +1
                                                          А чтобы не было 100500 классов с одинаковыми короткими названиями. Короткие имена должны быть информативными. Я искренне проклинаю разработчиков, когда мне на InvalidArgumentException в автокомплите вываливается десяток классов из разных неймспейсов.
                                                          Но это очередная тема для холивара…
                                                            0
                                                            Что это за болезнь, для каждого пакета делать свои исключения. Люди, которые так делают, разве ниразу не работали с исключениями что бы понять, что в 97.22% случаев в кэтче будет либо Throwable, либо Exception, еще в 1.93% там будет попытка обработать какое-нибудь не самое приятное исключение, а в остальном — какой-нибудь треш жуниора
                                                              +2
                                                              ну например:
                                                              try {
                                                                  $service->foo();
                                                              }catch(\My\AcmeBundle\Exception\InvalidParameterExteption $e) {
                                                                // обрабатываем ислючение, выкинутое нам каким-то определенным сервисом в ответ на заданные параметры
                                                              }
                                                              catch(\My\FooBundle\Exception\InvalidParameterExteption $e) {
                                                                // обрабатываем ислючение, выкинутое нам каким-то определенным сервисом в ответ на заданные параметры
                                                              }
                                                              


                                                              Это позволяет разграничивать исключения. Я сам как бы не в восторге, и приведенный пример абстрактен и реально я нигде такого не видел.
                                                                0
                                                                Да понятно почему это делается. Но кому это нужно. Кто этоим занимается. Кому такое может придти в голову?
                                                            0
                                                            Ну, в Form может быть ещё и, например, FieldErrors.
                                                          • UFO just landed and posted this here
                                                            • UFO just landed and posted this here
                                                                0
                                                                Наткнулся на ваш перевод уже тогда, когда оставался один абзац. Жалко было бросать.

                                                            Only users with full accounts can post comments. Log in, please.