Ассоциация oneToMany для одного объекта наследования

У меня есть следующая проблема с отображением наследования одной таблицы для Doctrine ORM, также известной как STI.

Кажется, что невозможно иметь ассоциацию oneToMany от объекта STI к другому объекту.

У меня есть 2 объекта Клиент и Поставщик с очень похожими полями. Вот почему я хочу использовать опцию наследования одной таблицы в Doctrine, чтобы получить эти 2 объекта в одной таблице с простым столбцом «тип», чтобы указать, к какому объекту принадлежит строка. Класс АннотацияБизнес будет служить классом базовый класс для этих двух объектов.

Рассмотрим это сопоставление сущностей для AbstractBusiness.

Axelvnk\CRMBundle\Entity\AbstractBusiness:
    type: entity
    table: axelvnk_crm_business

    inheritanceType: SINGLE_TABLE

    discriminatorColumn:
        name: type
        type: string

    discriminatorMap:
        customer: Axelvnk\CRMBundle\Entity\CustomerInterface
        supplier: Axelvnk\CRMBundle\Entity\SupplierInterface

    id:
        id:
            type: guid
            generator:
                strategy: UUID

    fields:
        vatNumber:
            type: string

        label:
            type: string

    oneToOne:
        billingAddress:
            targetEntity: Axelvnk\CRMBundle\Entity\AddressInterface
            joinColumn:
                name: billing_address_id
                referencedColumnName: id
            cascade: ["all"]

    oneToMany:
        addresses:
            targetEntity: Axelvnk\CRMBundle\Entity\AddressInterface
            mappedBy: business
            cascade: ["all"]

Это будет сопоставление для объекта Customer.

Axelvnk\CRMBundle\Entity\Customer:
    type: entity

Это будет сопоставление для сущности Поставщика.

Axelvnk\CRMBundle\Entity\Supplier:
    type: entity

Дополнительных полей пока нет. Но это не имело бы значения для этого вопроса.

Теперь свойство AbstractBusiness::billingAddress ссылается на экземпляр объекта Адрес. Который отображается следующим образом:

Axelvnk\CRMBundle\Entity\Address:
    type: entity
    table: axelvnk_crm_address

    id:
        id:
            type: guid
            generator:
                strategy: UUID

    fields:
        streetAndNumber:
            type: string

        city:
            type: string

    manyToOne:
        business:
            targetEntity: Axelvnk\CRMBundle\Entity\AbstractBusiness
            inversedBy: addresses
            joinColumn:
                name: business_id
                referencedColumnName: id

Как видите, свойство Адрес::бизнес ссылается на сущность STI, являющуюся AbstractBusiness. Это может быть либо Поставщик, либо Клиент в зависимости от стороны-владельца.

Я ожидаю, что доктрина решит, какой класс сущностей следует гидратировать, поскольку она знает, что AbstractBusiness является сущностью STI, и карта дискриминатора на месте. После запроса бизнес-таблицы с идентификатором business_id в адресной таблице он должен определить класс на основе столбца «тип» в адресной таблице, верно? Но, видимо, это не так. Я не могу вернуться к объекту STI, потому что доктрина не может создавать прокси-классы из абстрактных классов, что, я думаю, имеет смысл.

Но теперь у меня вопрос: как правильно сопоставить сущность STI? Или это просто невозможно в доктрине?

Спасибо за ваши ответы!

У меня есть сопоставления ассоциаций с сущностями STI в моих проектах, и они работают нормально, но в моем случае базовая сущность не определена как abstract.

Flying 21.02.2019 11:21

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

axelvnk 21.02.2019 11:42

Нет, не должно. Вот определение карты дискриминатора из реального проекта для разных типов контента. Базовая сущность называется Content, а не abstract: @ORM\DiscriminatorMap({"photo" = "Photo", "video" = "Video", "comics" = "Comics"})

Flying 21.02.2019 12:35

Странно, какая версия? Это исключение, которое я получаю, когда делаю это: «Сущность« Axelvnk \ CRMBundle \ Entity \ Business »должна быть частью карты дискриминатора« Axelvnk \ CRMBundle \ Entity \ Business », чтобы быть правильно сопоставленным в иерархии наследования. В качестве альтернативы вы можно сделать «Axelvnk\CRMBundle\Entity\Business» абстрактным классом, чтобы избежать возникновения этого исключения».

axelvnk 21.02.2019 13:48

В вашем случае я имею в виду, что AbstractBusiness не должен быть частью карты дискриминатора (и это не то, что я вижу) и не должен быть абстрактным классом. Business и Supplier должны быть инстанцируемыми классами, унаследованными от AbstractBusiness. Кстати, вы используете CustomerInterface вместо Customer в своей карте дискриминатора, возможно, это неправильно, попробуйте вместо этого использовать явные имена классов сущностей.

Flying 21.02.2019 15:27

Не уверен, на что вы указываете. Почему это не должно быть абстрактно? И вы имеете в виду, что Customer и Supplier должны быть инстанцируемыми классами, которые расширяют AbstractBusiness, да, они уже есть. И я использую интерфейсы, да, но слушатель разрешает их из доктрины в фактические конкретные классы, не беспокойтесь.

axelvnk 21.02.2019 15:37

Правильно ли эти разрешения интерфейса сохранены в ClassMetadata? Doctrine использует информацию из этого источника для своих операций.

Flying 21.02.2019 15:52
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
1
7
81
1

Ответы 1

Таким образом, очевидно, использование интерфейсов в DISCTORMAP, а затем использование распознавателя целевых объектов доктрины для замены этих интерфейсов конкретными классами отлично работает для сохранения объектов, но для их извлечения он не может понять, какой класс гидратировать...

Итак, решение: всегда используйте конкретные классы в DISCTORMAP...

На github есть открытая проблема для поддержки разрешения классов/интерфейсов в карте дискриминатора так же, как это работает для свойств targetEntity: https://github.com/doctrine/orm/issues/7622

Другие вопросы по теме