Разбираемся с Doctrine в Symfony 3.4

При работе с Doctrine возникает очень много вопросов, иногда даже кажется, что она ведёт себя непредсказуемо. Поэтому лучше прояснить некоторые моменты перед началом работы с ней.

Чем отличается refresh от persist в Doctrine 2?

Какая разница будет между

$em = $this->getDoctrine()->getManager();
$em->persist($entity);
$em->flush();

и

$em = $this->getDoctrine()->getManager();
$em->refresh($entity);

Разница очень большая.

Метод persist() выполняет сохранение объекта в базе данных (точнее не сохранение а его подготовку, физически запись в базу происходит при вызове метода flush() )

Метод refresh() перечитывает данные сущности из базы. Если вы изменили какие-либо данные у объекта и не выполнили persist(), при вызове refresh() они будут сброшены.

Общие правила работы с Doctrine

Как правильно при первом знакомстве с Doctrine все сущности, которые мы создаем являются по сути обычными POCO объектами , которые на каждое свойство имеют по get\set методу и не имеют конкретного поведения.

«Сущности должны работать без ORM», означает то, что вы должны абстрагировать ваши сущности от логики ORM, т.е. если вы вдруг захотите сменить Doctrine на Propel, вам не пришлось бы менять код в бизнес-логике. Однако тут возникают ограничения, накладываемые ORM, например коллекции в Doctrine — «ArrayCollection» которые используются внутри сущностей, для работы со связями.

«Сущности должны работать без БД» — пункт, который в принципе и так исполняем, так как вся логика работы с БД у нас инкапсулируется в самой ORM. Исключение составляет разве, что Active Record сущности с sql внутри.

«Сущности являются отображением доменной логики вашего приложения» — доменная логика — это часть подхода в DDD методологии, сформулированной Эвансом. Соответственно, ваши сущности не должны быть пустыми болванками, с двумя методами на свойство. Ваши сущности, в первую очередь, должны обладать поведением.

При проектировании вашего приложения, рекомендуется пользоваться code-first подходом,  когда вы сперва проектируете ваши сущности и взаимодействие между ними, а после уже занимаетесь проектированием хранилища и тем, каким образом помещать в него данные. В ходе работы может появиться необходимость в нескольких базах данных, или в разделении хранилищ с хранением различных кусков данных единой сущности. В итоге ваши сущности не должно волновать, каким образом вы организуете персистентный слой, ваши сущности должны быть заняты бизнес логикой приложения.

Сущности должны находится в валидном состоянии

Как касательно самих типов данных, чтобы в поле для целого значения не прилетела строка, например, так и касательно самих бизнес-правил, чтобы email был валидным, а пароль не меньше 3 и т.д. Вы должны всегда доверять сущности и быть в ней уверены. Однако не следует помещать в методы сущности комплексные валидационные правила, которые работают с данными пользователя. У вас всегда должны быть слои взаимодействия. Валидацию самой пользовательской формы отдавайте валидаторам — это может быть сервис, или input-фильтры, как в ZF2, или формы, как в SF2, или любые другие инструменты.

Используйте конструктор

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

Не используйте классы из уровня выше, в классах, уровня ниже

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

Что можно почитать на эту тему?

  • https://doctrine-orm.readthedocs.io/en/latest/reference/batch-processing.html
  • https://doctrine-orm.readthedocs.io/en/latest/reference/working-with-objects.html
  • https://ocramius.github.io/doctrine-best-practices/#/