Ответы пользователя по тегу Doctrine ORM
  • Как в Doctrine из Entity получить все записи?

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

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

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

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

    Aside note: почитайте на досуге PSR-1, PSR-2, PSR-12
    Ответ написан
    Комментировать
  • Как победить Undefined index в UnitOfWork при сохранении внутри postRemove?

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

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

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

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

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

    Вот здесь можно посмотреть на практически готовый пример того как должен выглядеть ваш код коммита изменений с учётом использования lock'а.
    Ответ написан
    1 комментарий
  • 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.
    Ответ написан
    7 комментариев
  • 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 и по необходимости подчищаете.
    Ответ написан
    5 комментариев
  • 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.
    Ответ написан
    1 комментарий
  • 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;
    }
    Ответ написан
    Комментировать
  • Как получить удаленные из коллекции сущности Doctrine?

    @Flying
    Ответы на ваши вопросы, очевидно, кроются в исходниках Doctrine:
    Видно что поведение метода clear() зависит от того какую из сторон ассоциации вы очищаете. Также видно что если вы работаете с owner side ассоциации - то Doctrine создаёт новый snapshot.

    Из кода методов getInsertDiff и getDeleteDiff видно что вычисление разницы происходит через сравнение snapshot'а с текущим содержимым коллекци, так что очевидно что в случае создания нового snapshot'а разница будет пустым массивом.

    Проверить, вызывает ли сама Symfony метод clear() проще всего, поставив там точку останова.
    Ответ написан
    3 комментария