Ответы пользователя по тегу Doctrine ORM
  • Комбинированные oneToMany связи User->RelationCollection[ApiTokenCollection, ResetPassCollection]?

    @Flying
    Если я правильно понимаю ваш вопрос, то вы можете сделать AbstractToken базовой entity для inheritance mapping, а остальные виды токенов наследовать от него, попутно определяя их в @DiscriminatorMap.

    Тогда, если вам где-то будет необходимо получать список только определённого вида токенов - достаточно будет в опредении, к примеру, @OneToMany, указать в targetEntity нужный вам класс entity, а если нужны будут все токены - то можно будет указывать AbstractToken
    Ответ написан
  • Как в Doctrine из Entity получить все записи?

    @Flying
    У вас некорректные mapping'и.

    У меня не очень хорошо получилось понять что вы хотели изобразить здесь... Из ваших классов следует, что интервью может быть многих типов одновременно, а не наоборот?

    Но, вне зависимости от того как это должно быть - у вас по факту всё равно должна быть связь 1:N, а не 1:1, именно здесь основная ошибка.

    Смените тип ассоциации, тогда со стороны OneToMany у вас будет коллекция entities, откуда вы их сможете получить.

    Aside note: почитайте на досуге PSR-1, PSR-2, PSR-12
    Ответ написан
  • Валидация ValueObject поля?

    @Flying
    Если, судя по тегам, речь идёт от Symfony - то зачем вам вообще базовый абстрактный класс для ValueObject, которые по-идее должны представлять значения, а не логику их валидации. Для валидации в Symfony, как вы наверняка знаете, есть соответствующий компонент, поэтому вам достаточно просто описать правила валидации через соответствующие аннотации.

    Для первой валидации стоит использовать LessThan с датой. Для второй придётся писать свой валидатор, но там нет ничего сложного.

    Если данные приходят в запросе - то можно воспользоваться подходом, который предложил BoShurik в своём ответе на похожий вопрос (смотрите комментарии, там больше информации и примеры), в этом случае до контроллера у вас гарантированно будет долетать только заполненный и валидный объект.
    Ответ написан
  • Как победить Undefined index в UnitOfWork при сохранении внутри postRemove?

    @Flying
    В документации к событию postRemove прямо указано что это событие вызывается внутри метода flush(). Таким образом вызывая $this->entityManager->flush() внутри postRemove вы, фактически порождаете потенциально бесконечный цикл, поэтому "Undefined index" на самом деле - наименьшая из ваших проблем :)

    Более корректно будет организовать работу примерно следующим образом:
    1. Вынести логику сохранения изменений в отдельный сервис
    2. Обернуть процесс сохранения изменений в транзакцию
    3. В lifecycle методах не пытаться писать данные сразу, а вместо этого собирать информацию для записи в некую коллекцию, в простейшем случае - массив
    4. После основного flush'а проверять содержимое коллекции и если там что-то есть - формировать отдельный набор изменений в entities и делать новый flush.
    5. Если всё прошло хорошо - в конце делать commit транзакции
    Ответ написан
  • Как будет выглядеть запрос с QueryBuilder?

    @Flying
    Напрямую - никак, поскольку в DQL нет функции RAND(). Если хотите - можете добавить свою.

    Также, хотя подзапросы и можно сделать через QueryBuilder, но получившаяся конструкция будет выглядеть весьма странно. Кроме того важно помнить что QueryBuilder - это инструмент формирования запроса, к примеру если вам надо добавлять какие-то части запроса в зависимости от логики приложения. Если сам запрос статический - то вполне можно напрямую использовать DQL т.к. QueryBuilder компилируется именно в него.
    Ответ написан
  • Doctrine как разрешить состояние гонки?

    @Flying
    По сути в вашем случае запись в базу данных является критической секцией. Соответственно вам необходима реализация любого из способов синхронизации потоков для избегания состояния гонки. Вариантов множество, но конкретно в Symfony именно для этого (и для других подобных сценариев) существует компонент Lock, он предоставляет реализации готовых примитивов синхронизации.

    Вот здесь можно посмотреть на практически готовый пример того как должен выглядеть ваш код коммита изменений с учётом использования lock'а.
    Ответ написан
  • Symfony 3 - 5, Doctrine 2. Возможно ли используя Criteria выбрать данные из связанных таблиц одним запросом?

    @Flying
    Criteria - это просто возможность сформировать какое-то относительно сложное условие для QueryBuilder, не более того.

    Вам необходимо определиться с тем что именно вы хотите сделать:
    • Если вы хотите выбрать данные из связанных таблиц - то это можно сделать через QueryBuilder с гидрацией, к примеру, в массив. В этом случае запрос будет ровно один - тот что вы запускаете
    • Если вы хотите выбрать entities - то логика этих выборок определяется UnitOfWork и в общем может как приводить к 10 запросам так и не приводить в зависимости от содержимого identity map. Т.е., условно говоря, если бы связанные entities уже были бы в identity map - повторной выборки бы не происходило. Также выборки бы не происходило если бы вы обращались к identity полю связанной entity. Однако вы запрашиваете данные из связанной entity, причём делаете это раздельно для каждого entity, почему же вы ожидаете что Doctrine не полезет в базу за этими данными?
    Ответ написан
  • Есть ли аналог laravel's GlobalScope у doctrine или symfony?

    @Flying
    Я на Laravel не писал, но судя по документации это какой-то аналог дополнительного фильтра который можно навесить на запрос. Если я прав, то ближайший аналог - фильтры.
    Ответ написан
  • Doctrine: как указать, что в БД тип поля не тот, что в сущности?

    @Flying
    Лезть в исходники mapping'ов для entities в Doctrine явно не стоит т.к. Doctrine поддерживает несколько вариантов задания mapping'а и никто заранее не скажет вам какой источник (или источники) используются - это определяется конфигурацией Doctrine.

    Стандартным способом работы с mapping'ами в runtime является ClassMetadata API. Получить доступ к class metadata factory можно через EntityManager::getMetadataFactory() либо, если вас интересует конкретный entity - через EntityManager::getClassMetadata().

    Объект ClassMetadataInfo содержит информацию обо всех деталях mapping'а конкретной entity. Большинство его properties объявлено как public для более быстрого доступа, хотя писать туда, разумеется, запрещено.

    Класс весьма большой, так что проще всего посмотреть на его содержимое либо в отладчике либо через какой-нибудь dump. Конкретно mapping'и полей находятся в переменной $fieldMappings.
    Ответ написан
  • Doctrine does not have a property named "mappedBy"?

    @Flying
    Всё очень просто: В коде Product в строке 123 вы используете
    @ORM\ManyToOne(targetEntity="Category", mappedBy="product")

    тогда как для ManyToOne необходимо использовать inversedBy, это прямо описано в документации и видно в коде этой аннотации.
    Ответ написан
  • Есть ли бандл для Symfony на подобие DoctrineAuditBundle, но с возможностью формирования более расширенного человекочитаемого текста изменений?

    @Flying
    Это довольно просто делается через Doctrine lifecycle events. Вешаете обработчики на preUpdate, pretPersist, prePremove, собираете из переданных вам событий информацию для формирования нужного вам лога, затем в postFlush из собранной информации формируете запись в лог и записываете.
    Ответ написан
  • Как правильно реализовать теги в Symfony?

    @Flying
    п.п.3 и 4 реализуются довольно просто через lifecycle events в Doctrine. Просто на preRemove вешаете обработчик, в нём проверяете тип entity и, если это что-то подходящее (статья или тег) то проверяете связи удаляемой entity и по необходимости подчищаете.
    Ответ написан
  • Doctrine, как задать значение даты по умолчанию?

    @Flying
    Просто проинициализируйте это поле в конструкторе или в обработчике @PrePersist lifecycle event.
    Ответ написан
  • Почему всем так нужен Doctrine, если он много не умеет?

    @Flying
    Doctrine реализует концептуально другой подход к работе с данными, именно в этом её большое преимущество и именно из этого следуют её ограничения.

    Если вы когда-нибудь сталкивались с паттернами проектирования то, возможно, слышали о таком человеке как Мартин Фаулер и о его книге "Patterns of Enterprise Application Architecture". В ней описываются паттерны проектирования enterprise level приложений. В этой книге Фаулер предложил набор паттернов, организующих работу с источниками данных через представление этих данных в виде связанных между собой объектов. В этот набор входят Data Mapper, Identity Map, Unit of Work и множество других паттернов.

    Если идти чуть глубже то суть Doctrine - это возможность работать с данными в базе данных как с обычными объектами. Если задуматься - это открывает невиданные перспективы в обеспечении предсказуемости разработки и поддержки кода проекта т.к. обеспечивает разработчика возможностью абстрагироваться от деталей хранения информации и сосредоточиться на важной части его работы - реализации бизнес-логики проекта. Doctrine же берёт на себя заботу о том чтобы приложение получило нужные данные когда ему это будет нужно, чтобы эти данные были корректно сохранены, чтобы не возникло конфликтов и т.д. и т.п.

    Попробуйте представить себе большой бизнес-проект над которым работают множество людей, в котором есть сотни видов данных, взаимодействующих друг с другом и определены сложные процессы вовлекающие множество видов данных. Разумеется это все можно написать и поддерживать вручную, таких примеров много, вопрос в стоимости подобной работы. Представьте себе необходимость вручную описывать последовательность запросов для сохранения данных в 20 таблиц и необходимость поддерживать корректность этого кода при всех следующих изменений бизнес-требований проекта. Уверен, если после полугода подобной работы вам предложат заменить всё это на одну строку $em->flush() - вы с радостью согласитесь и, возможно, тогда поймёте что даёт Doctrine разработчику.

    Именно из идеи перевода фокуса разработчика с деталей реализации хранилища данных на детали реализации бизнес-логики проекта рождаются ограничения Doctrine. Они могут восприниматься негативно если пытаться воспринимать Doctrine и DQL как урезанный SQL, почему-то возвращающий объекты, а не массивы. Да, разумеется какие-то сложные аналитические запросы вы на DQL не построите, но это только потому что у Doctrine другая цель. Если посмотреть на DQL чуть пристальнее (к примеру на то как в нём описываются join'ы) то можно заметить что Doctrine отталкивается не от того как данные разложены по таблицам, а от того как данные представлены в entities. Это не самое заметное, но очень важное отличие т.к. оно определяет пространство операций над данными. Грубо говоря приведённый вами ifnull() в DQL становится довольно бессмысленной конструкцией т.к. эта функция довольно слабо применима к объектам.

    Разумеется в реальных проектах зачастую бывают задачи которые требуют работы с данными в базе данных за пределами Doctrine, это нормально, ни один инструмент не является всеобъемлющим. Однако описываемые вами "недостатки" Doctrine проистекают скорее от непонимания того что это за инструмент и зачем он нужен, какие задачи он призван решить. Это непонимание устраняется через изучение того с чем вы работаете на более глубоком уровне. Если вы решите устранить его - вы получите в свои руки один из лучших инструментов для работы с данными в бизнес-проектах который только есть в мире PHP и тогда, надеюсь, сможете оценить его по достоинству.
    Ответ написан
  • Как сделать связь одного поля таблицы с несколькими сущьностями (entity)?

    @Flying
    Вы уже используете DiscriminatorMap который является правильным решением в данной задаче. Вам ничто не мешает теперь иметь в наследуемых классах отдельные свойства содержащие связи с конкретными entities, специфичными для каждого конкретного типа пункта меню. Вы также вполне можете объявить getEntity() / setEntity() в классе MenuItem абстрактными, а в дочерних классах реализовывать их. Конечно типы на уровне языка вам будет не задать, но никто не будет вам мешать использовать PHPDoc для создания type hints.
    Ответ написан
  • Doctrine ORM не рекомендует частичную выборку, каковы альтернативы?

    @Flying
    А почему не работает с join? По-идее никаких ограничений в этом аспекте быть не должно.

    Если вам нужны не все поля или хочется оптимизировать запрос так чтобы он обязательно вытаскивал не всё - то лучше строить отдельный запрос и вытаскивать только нужные данные как данные, а не как entities. В конечном итоге вам ничто не может помешать в своём custom repository сделать запрос на данные и насоздавать entities, заполненных этими данными. В большинстве случаев всё-таки содержимое одной строки базы данных не настолько увеличивает размер выборки как количество строк самой выборки. Т.е. выбрать, условно, 20 строк с текстами постов скорее всего будет быстрее чем 10к постов без их текстов.

    Если же вам настолько важно выбирать сущности, но не выбирать значения полей (к примеру у вас каждый Post - это книга на несколько мегабайт текста, кто знает?) - можно вынести текст Post'а в отдельную сущность и использовать `OneToOne` отношение, тогда вы сможете использовать преимущества lazy загрузки связей в Doctrine и не выбирать тексты если они не нужны.
    Ответ написан
  • Как сделать вложенный запрос в Doctrine DQL?

    @Flying
    Возможно вам стоит обратить внимание на готовую реализацию деревьев для Doctrine? Есть также интеграция для этого пакета в Symfony. Это позволит вам не изобретать велосипед, а использовать готовое, проверенное и весьма популярное решение освободив время от написания и поддержки своей реализации для других задач.
    Ответ написан
  • Как лучше организовать сущности в Doctrine?

    @Flying
    Если список типов у вас статический - замените EntryType на наследуемые entities через discriminator map, количество запосов это не снизит, но снизит количество join'ов и таблиц. Вот здесь можно подробнее почитать про него на русском.

    А вообще судя по задаче у вас основная работа идёт не на уровне строк таблиц, а на уровне их анализа, Doctrine в этом случае - не лучшее решение т.к. задача больше про построение запросов, а не про удобную работу с сильно связанными данными. Тот же Excel с этим справится гораздо лучше :)
    Ответ написан
  • Как в Doctrine сделать one-to-many с коллекцией?

    @Flying
    У вас потенциально две ошибки в приведённом коде:

    1. В качестве возвращаемоног типа вы указываете \Collection, тогда как коллекции Doctrine - это \Doctrine\Common\Collections\Collection
    2. Ваш код не создаёт эту коллекцию, соответственно если это не entity созданная самой Doctrine при загрузке данных из базы - значение $meta - null. Корректный подход - это либо инициализировать коллекции в конструкторе:
    public function __construct() {
      $this->meta = new ArrayCollection(); // где ArrayCollection это \Doctrine\Common\Collections\ArrayCollection
    }

    либо непосредственно в методе:
    public function getMeta() : Collection
    {
        if (!$this->meta) {
            $this->meta = new ArrayCollection();
        }
        return $this->meta;
    }
    Ответ написан