Ответы пользователя по тегу Doctrine ORM
  • Как в аннотациях Doctrine добавить статическое условие выборки?

    @tukreb
    1. Чтобы разбить ManyToMany, на ManyToOneи OneToMany, вам нужно вручную выделить сущность FileTarget и вручную там расписать связи. Соответственно в сущности FileTarget
    Вам нужно сделать что-то такое.
    /**
     * @ORM\Entity
     * @ORM\Table(name="file_target")
     */
    class FileTarget
    {
        //другие свойства.
         /**
         * @ManyToOne(targetEntity="File" , inversedBy="fileTarget")
         * @JoinColumn(name="file_id", referencedColumnName="id")
         */
        private File|null $file= null;
    
    }

    В классе File создайте коллекцию OneToMany https://www.doctrine-project.org/projects/doctrine...

    Далее тоже самое делаем ManyToOne в сущности File, но уже на User, а в самом User уже коллекцию OneToMany на File.
    В итоге в сущности User вы сможете получить коллекцию на ваши file.
    class User
    {
        //другие свойства и функции.
    
        /** @return File[] */
        public function getFiles(): array {
            return $this->files->toArray();
        }
    }


    Далее через foreach достаём нужные объекты из getFiles() и обращаемся по свойствам объекта куда нужно.

    2. Это делается через отельную функцию в сущности или лучше всего в сервисе.
    Создаёте функцию в классе, где у вас есть доступ к User, по коду это получается класс File
    public function getUserWithTargetType(int $targetType): User {
            //через foreach перебираете User и по условию $targetType возвращаете User 
        }

    Хотя я бы это вынес в отдельный сервис скорее всего.

    П.С Для меня странной выглядит ваша сущность FileTarget, у вас один файл может быть разных типов? Почему типы файлы не перенести в сущность файл? Как и всё остальное. Но в общем меня это волновать не должно :)
    Ответ написан
  • Как создать автозаполняемое int-поле?

    @tukreb
    Заходим в документацию https://www.doctrine-project.org/projects/doctrine...
    Читаем описание
    Specifies which strategy is used for identifier generation for an instance variable which is annotated by @Id. This annotation is optional and only has meaning when used in conjunction with @Id.

    Поэтому либо используйте с параметров @id, либо не используйте вообще.

    И чисто из логики я не могу представить, когда нужно, чтобы отдельно зачем то последовательно генерировалось число. Ведь это и так делается, если вы сделаете уникальную айдишку, зачем дублировать, мне не понятно.
    Ответ написан
    7 комментариев
  • Как привести Entity в соответствие с таблицей в в Symfony 6?

    @tukreb
    Доктрина очень капризна, даже порядок объявлений атрибутов в таблице важен (в частности для ManyToMany).
    Я обычно делаю так.
    Автоматически генерируемые имена индексов использую только для отношений ManyToMany (потому что доктрина не поддерживает другие :))
    Всё остальное пишу руками.
    Далее делаю doctrine:mapping:import (эта функция кстати депрекейтет и скоро удалят, если никто не вызовется поддерживать) и сверяю с тем, что написано у меня, т.к Доктрина очень капризна, у вас может быть всё правильно написано, но не в том порядке или не в том месте.
    Я не знаю как там с MySQL, но в PostgreSQL все индексы и комментарии (за исключением внешних ключей) объявляю только через отдельные: $this->addSql();
    Например:
    $this->addSql('COMMENT ON COLUMN table.field IS \'(DC2Type:datetime_immutable)\'');

    Потому что доктрина не всегда видит или не любит, если всё это делать сразу при объявление таблиц. Это конечно звучит как бред, но работая с доктриной и набив шишки некоторые моменты уже обходишь за километр проверенными решениями.
    Ответ написан
    Комментировать
  • Сделать Entity по имеющейся уже таблице?

    @tukreb
    Это делает депрекейтет команда:
    php bin/console doctrine:mapping:import "App\EntityFromDB" annotation --path=src/EntityFromDB

    https://symfony.com/doc/current/doctrine/reverse_e...

    Почему депрекейтет? Да, потому что она не может покрыть все случае, а поддерживать никто её не хочет.
    Даже если вы создадите по ней сущности, вам придётся всё равно перепахать их все, особенно если у вас всё на атрибутах, т.к она поддерживает только аннотации.

    Запускать миграции нужно всегда. Все изменения БД в современном мире идут ТОЛЬКО через миграции.

    П.С Почему люди предпочитаю сначала код, потом бд, можете почитать здесь :) https://elisdn.ru/blog/104/domain-entities-modelling
    Ответ написан
    4 комментария
  • Как составить запрос на выборку в Symfony с объединением таблиц?

    @tukreb
    1. Вам нужно объявить связи в сущностях - https://www.doctrine-project.org/projects/doctrine...
    2. Написать метод в вашем репозитории, через createQueryBuilder
    Например что- такое используя leftJoinc Join::WITH
    public function getAllProductsWithCategories(): array
        {
            /** @var Product[] $entities */
            $entities = $this->repo->createQueryBuilder('t')
                ->select('t')
                ->leftJoin(Category::class, 't2', Join::WITH,'t.id = t2.product') //здесь джойним по полям, которые объявлены в наших сущностях (пункт 1)
                ->getQuery()
                ->getArrayResult();
    
            if (count($entities) === 0) {
                throw new EntityNotFoundException('No product.');
            }
            return $entities;
        }


    Но 2 пункт очень специфичен и возможно не ваш случай, обычно, если вы правильно сделали 1 пункт, то есть, если Entity имеет правильные ассоциации вы сможете через них достать другие сущности.
    Ответ написан
    Комментировать
  • Почему при создании записи обновляются все связанные сущности?

    @tukreb
    Доктрина обновляет всегда сущность если фиксирует любое её изменение.
    Самый простой пример, например в БД дата записана в одном тип дате, а при доставание вы её конвертируете в самом объекте в другой тип даты.
    Другой пример, в базе данных у вас данные в BIGINT, а доктрина при доставание конвертирует их в строку.
    Чтобы избежать этого, вам нужно сделать так, чтобы в сущность попадали уже готовый формат данных полностью идентичный сохранённым в БД.
    Возможно вам нужно написать свой тип данных.
    https://www.doctrine-project.org/projects/doctrine...

    Пример фикса для bigint
    class BigIntType extends Type
    {
        public function convertToPHPValue($value, AbstractPlatform $platform): ?int
        {
            return $value === null ? null : (int) $value; //до того как данные попали в сущность, мы насильно конвертируем в int (иначе будет строка)
        }
    
        public function getSQLDeclaration(array $column, AbstractPlatform $platform): string
        {
            return $platform->getBigIntTypeDeclarationSQL($column);
        }
    
        public function getName(): string
        {
            return Types::BIGINT;
        }
    }
    Ответ написан
  • Средствами доктрины можно сделать аннотациями при 2 ссылках таблицы на себя, именно на одно поле id?

    @tukreb
    Нельзя ссылаться в одной таблице на тоже самое поле.
    То что вы попытались описать называется Adjacency List (но нужно использовать дополнительное поле parent_id)
    А то что вы изобразили как 2 таблицы, называется Subsets
    Рекомендую ознакомится с этими понятиями и выбрать что вам нужно.
    Ответ написан
    1 комментарий
  • Ошибка The doctrine/orm package is required when the doctrine.orm config is set как вылечить?

    @tukreb
    Установите symfony/orm-pack в него уже входит всё что нужно в том числе и doctrine/doctrine-bundle
    Ответ написан
    1 комментарий
  • Как задать значение по умолчанию для сущности DOCTRINE ORM?

    @tukreb
    Можно так, через options={"default"="now()"}
    /**
         * @ORM\Column(name="date", type="datetime_immutable", nullable=false, options={"default"="now()"})
         */
        private \DateTimeImmutable $date;

    Точно работает с now(), более сложного не проверял, но я не вижу причин почему не должно сработать. Правда не факт, кто доктрина сможет правильно сгенерировать миграцию, возможно руками придётся править в миграции.
    Ответ написан
    Комментировать
  • Как реализовать Форму создания товара с генератором атрибутов?

    @tukreb
    Дальше отображаете это всё в twig.
    Например можете в twig отправить готовую html код CollectionType формы в html атрибут data-prototype
    <div data-prototype="
    {% apply escape %}
         {{ include('app/template/prototype/collection_form.html.twig', { 'form': form.attributes.vars.prototype }) }}
    {% endapply %}"></div>

    в файле collection_form.html.twig что-то такое:
    {{ form_widget(form.name) }}
    {{ form_widget(form.product) }}
    {{ form_widget(form.attribute) }}

    Далее через javascript вставляете html форму из data-prototype, когда пользователь нажмёт кнопку добавить новый атрибут. При вставке вам нужно будет вручную выставлять индексы новым полям, по дефолту symfony засунет туда что-то вида form[__name__]
    Ну при сохранение, всё будет в виде массива в attributes[]

    Если у вас уже готовые атрибуты в attributes[], то просто через foreach создаёте форму.
    {% for row in form.attributes %}
        {{ form_widget(row.name) }}
        {{ form_widget(row.attribute) }}
    {% endfor %}
    Ответ написан
    Комментировать
  • Как мне создать отношение ManyToOne при джойне к 2 колонкам?

    @tukreb
    Нельзя в Доктрине джойнить сразу по 2 полям одну сущность, если это не ManyToMany (которая в свою очередь не может содержать ничего кроме 2 ID полей, иначе придётся вручную разбивать связь на ManyToOne и выделять отдельные ID).
    У вас уже имеется специальный ID по ним и описывайте связь ManyToOne
    Например
    class Inventory {
        /**
         * @ORM\ManyToOne(targetEntity=Price::class, inversedBy="prices")
         * @ORM\JoinColumns({
         *   @ORM\JoinColumn(name="id_price", referencedColumnName="id")
         * })
         */
        protected $prices;
    }
    Ответ написан
    Комментировать
  • Насколько вредно джойнить таблицы где отсутствует информация?

    @tukreb Автор вопроса
    В общем, пришлось выкинуть пока наследование в доктрине, ну или альтернатива - писать везде через ручные SQL запросы, но тогда доктрину вообще можно выкинуть.
    Потестил как получилось:
    Количество вызываемых полей, через SELECT очень сильно влияет на скорость запроса, даже сильнее чем JOIN и не важно, что там 90% полей пустые.
    Когда нужно вызвать допустим всего 5 полей определённой таблицы, доктрина вызывает 20 и более полей (сотнями может исчисляться), т.к автоматом JOIN всех детей, и увеличивая тем самым задержку с 30мс до 100мс (Docker под Windows) и это только при 3-5 наследоваемых таблицах, а планировалось десятки.
    Если выбрать только 5 полей, но сделать JOIN на все таблицы, как делает Доктрина, то задержка будет всего 50мс, против эталонного 30мс.

    П.С Возможно влияние JOIN будет заметно, при большем количестве таблиц, но при мини тесте поля сильнее повлияли.
    Ответ написан
    Комментировать
  • Как отключить наследование в Doctrine?

    @tukreb Автор вопроса
    В классе Parent
    protected $id;
    переименовал в
    protected $id_parent;

    Забавная ошибка. И как я её должен был диагностировать без метода тыка непонятно, ну да ладно..
    Ответ написан
    Комментировать