Как правильно реализовать модель в Symfony2 на базе MVC?
Я разобрался, что есть в Symfony2 View, а где Controller, но не могу понять где и как создавать свою модель логики? Знаю, что есть сущности доктрины в папке Entity, которые предоставляют интерфейс к БД с геттерами и сеттерами, но ведь это еще не модель. Модель как раз должна описывать всю логику работы и не с одной таблицей данных, а со всеми возможными связями и ограничениями. Но куда выносить эту модель?
Если отдельно в папку Models, то как в классах модели получить доступ к контейнеру служб, чтобы использовать ту же Doctrine, если класс модели не наследовать от Controller (это же модель, а не контроллер!)? Или нужно реализовывать модель «рядом» с Entity, наследуя от них классы? Мне представляется, что основная часть логики должна быть именно в отдельной модели, а не в контроллере или я, что-то не правильно понимаю?
Прошу поделиться знаниями опытных разработчиков по этому вопросу и помочь в этом вопросе.
Спасибо, значит будем копать туда, хорошо, что сейчас прояснил этот момент пока «толстых» контроллеров всего пара десятков, а не сотен, иначе пришлось бы купить новую кофе-машину.
Модель в ключе MVC Это просто собирательное от бизнес логики скорее. По сути в Symfony вся логика должна храниться в сервисах.
Entity — это просто как структура данных, сама она логикой тоже может обладать, но минимально, так как ей не доступны сервисы.
Model в контексте форм — это что-то типа Data transfer object, то бишь некий объект, который содержит данные в нужном формате. В большинстве случаев моделью для форм являются сущности.
Контроллеры это просто контроллеры, тут все раскрыто довольно хорошо в документации.
Логику же, если по хорошему, нужно выносить в сервисы. Почти все можно вынести из контроллера в ивент листенеры, отдельные служебные сервисы, хэндреры форм (для дедублицирования кода в контроллерах, правда не часто помогает). Словом, тут все очень и очень зависит от проекта. Но сущность должна только хранить данные, и никак их не изменять. Можно только дополнительные геттеры писать, которые производят небольшие манипуляции с данными.
Ну ваш пример конкретно не годится. Логика по работе с выборками, с коллекциями пользователей, с базой и т.д. должна выноситься в репозиторий для вашей сущьности.
Опять же о пользователях — хороший пример — хэширование паролей. Скажем, вы добавили в вашу сущьность поле plainPassword которое заполняется при регистрации скажем. Затем, перед сохранением данных в базу, вызывается ваш сервис, который (если конечно plainPassword не null) и сгенерит новый хэш. Таким образом вся лолгика по хэшированию будет вынесена в отдельный сервис, который вручную не будет вызываться, а будет вызываться автоматически по событию.
Выборка в примере это не то, что планируется делать, там как раз думается выполнять различные подготовительные вычисления, получение доп. данных из других сущностей и выполнение добавлений/обновлений данных в репозитории.
Ознакомился, хороший инструмент Event Dispatcher, можно воткнуть callback на разные события как в ядре, так и в Доктрине, очень подойдет для подготовки данных до и после основной «работы», но все же, если у меня есть логика работы с несколькими сущностями забивать ее в конроллер нельзя, так как подобная вещь может быть использована и в другом контроллере, что я сейчас и наблюдаю в коде моих коллег по цеху, это дело нужно выносить в отдельный сервис, я так понимаю, и обращаться к нему из всех контроллеров соответственно.
Использую доктриновские сущности, нашпигованные методами бизнес-логики. Если для бизнес-логики нужны какие-то сущности, не связанные с текущей схемой данных, то передаю их как параметры методам, предварительно вытаскивая из репозиториев в контроллерах. Сами сущности ничего не знают о хранении их в репозиториях, обычные POPO, вся логика хранения обеспечивается в контроллере.
Сервисы использую когда в методах различных сущностей появляется дублирование кода или просто непонятно к какой сущности отнести тот или иной метод — типичный пример: перевод со счёта на счёт, оба счёта в принципе равноправны, и что метод DebetTo в источнике, что метод CreditFrom в получателе будет выглядеть некрасиво, особенно если будет ещё и третий счёт, на который перечисляется комиссия за транзакцию, а создание класса транзакции в модели нецелесообразно — прямой кандидат в сервисы. Вообще сервисы рассматриваю как функции.
Если логика относится непосредственно к сущности, взятой из базы данных, я её запихиваю в Entity.
Уж не знаю, насколько этот подход правилен, т.к. сам еще новичек в Symfony, но мне это решение кажется вполне логичным.
Насколько я понимаю, тут Models скорее используются для создания абстрактных сущностей или интерфейсов, чтобы потом от них отнаследовать конкретные реализации, будь то Doctrine или что-то еще.
Вы дописываете класс сущности с учетом своей логики или создаете производный от него? Вообще встречал где-то упоминания, что сущности Doctrine не рекомендуется трогать.
Если сущность ничего не знает о другой сущности, то она ей и не должна оперировать.
Я это перелагаю на Controller, или Command (для CLI приложений) или любой другой класс.
Обновление выполнит класс, который сущностью управляет:
<?php
$entity = new MyEntity();
$entity->setAttr('some attr');
$em->persist($entity);
$em->flush();
?>
Для «соседа» так же я думаю. Хотя если она знает о соседе, то возможно будет правильней из неё свойства соседа изменять. Может кто-то подскажет точнее вам.