Отображение наследования таблиц классов Doctrine: почему внешний ключ к родительской таблице? - PullRequest
0 голосов
/ 15 мая 2019

Короткий вопрос: можно ли избежать генерации внешних ключей для унаследованных классов к родительским классам?Можно ли установить столбец дискриминатора для сопоставления наследования в владельце отношений вместо родительского класса?

Объяснение:

Я разрабатываю модель для счетов-фактур, в которой субъект счета-фактурыможет быть одного из 3 типов:

  • Контракт
  • Клиент
  • Поставщик

После прочтения Отображение наследования доктрины Я думаю Наследование таблиц классов - это то, что лучше всего соответствует моим потребностям.

Отображенный суперкласс мог бы лучше соответствовать моим потребностям, если бы я мог рисоватьотношение от Invoice до InvoiceSubject, но я думаю, что не могу:

Сопоставленный суперкласс не может быть сущностью, он не поддерживает запросы и постоянные отношения, определенные сопоставленным суперклассомдолжен быть однонаправленным (только со стороны владельца).Это означает, что связи «один ко многим» вообще не возможны для сопоставленного суперкласса.Более того, ассоциации «многие ко многим» возможны только в том случае, если сопоставленный суперкласс в настоящий момент используется только в одном объекте.

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

Итак, это моя модель:

/**
 * @ORM\Entity
 */
class Invoice
{
    /**
     * @ORM\ManyToOne(targetEntity="InvoiceSubject")
     * @var InvoiceSubject
     */
    protected $subject;
}

/**
 * @ORM\Entity
 * @ORM\InheritanceType("JOINED")
 * @ORM\DiscriminatorColumn(name="invoicesubject_type", type="string")
 * @ORM\DiscriminatorMap({"invoice-subject" = "InvoiceSubject", "contract" = "Contract", "provider" = "Provider", "client" = "Client"})
 */
class InvoiceSubject
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     */
    protected $id;
}

/**
 * @ORM\Entity
 */
class Contract extends InvoiceSubject{}

/**
 * @ORM\Entity
 */
class Provider extends InvoiceSubject implements ProviderInterface{}

/**
 * @ORM\Entity
 */
class Client extends InvoiceSubject{}

Но, когда я пытаюсь сгенерироватьмодель (bin/console doctrine:schema:update --dump-sql) Я вижу, как она пытается создать внешние ключи от дочерних классов к родительским (таблицы уже существуют и имеют данные):

ALTER TABLE contract ADD CONSTRAINT FK_E9CCE71ABF396750 FOREIGN KEY (id) REFERENCES invoice_subject (id) ON DELETE CASCADE;
ALTER TABLE provider ADD CONSTRAINT FK_B2F1AF1BBF396750 FOREIGN KEY (id) REFERENCES invoice_subject (id) ON DELETE CASCADE;
ALTER TABLE client ADD CONSTRAINT FK_B2F1AF1BBF396750 FOREIGN KEY (id) REFERENCES invoice_subject (id) ON DELETE CASCADE;

Это означает, что между идентификаторами * будет коллизий1049 * исходя из разных таблиц, или мне нужно будет использовать разные значения в каждой таблице, ни одно из решений не является хорошим.Итак, вопрос:

  • Есть ли способ избежать этого внешнего ключа и установить дискриминатор в классе владельцев отношений Invoice?В моем случае InvoiceSubject на самом деле не нужно существовать как таблица, я вынужден создать ее как Наследование таблиц классов заставляет меня сделать это.
  • Или естьэто моделирование совершенно неправильно, и я должен использовать другой подход?

1 Ответ

2 голосов
/ 15 мая 2019

хорошо, я, вероятно, неправильно прочитал часть вашего вопроса:

да, у трех дочерних объектов есть внешний ключ к тому же полю id в родительской сущности (таблице), и он предназначен.Идея состоит в том, что субъект core является субъектом счета.Эта сущность имеет идентификатор.дочерние объекты наследуют этот идентификатор и получают больше атрибутов (в дочерней таблице), расширяя родительскую сущность.Вот что значит наследование.По сути, у вас есть основная сущность, которая имеет разные подтипы с дополнительными атрибутами.

(примечание: вы также можете сделать это вручную, добавив сопоставление ассоциации к потенциальным «дополнительным данным разнообразия контракт / клиент / поставщик»).для некоторой сущности субъекта-фактуры)

Это также означает, что вам на самом деле не нужно заботиться о коллизиях, поскольку запись родительской таблицы всегда сначала создается доктриной.Когда вы создаете новый InvoiceSubject любого подтипа, вы фактически создаете InvoiceSubject (имеет идентификатор) и расширяете его.Таким образом, сущности вашего контракта / клиента / провайдера просто не будут иметь одинаковый идентификатор (если вы не установите его вручную с помощью SQL).

старый ответ

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

Вопросы: у вас есть ровно один форма для их ввода?Есть ли у вас (уже) отдельные поля, которые принимают какие-либо из этих объектов?Предоставляют ли сущности одинаковую семантику?разве лень заставляет вас хотеть иметь дело только с одним «типом» сущности?Есть ли так много мест, которые хотят быть чрезвычайно агностичными к тому, что это за предмет, и не могут ли они быть решены с помощью четко определенного интерфейса?Когда к ним действительно относятся одинаково?

Лично, глядя на ваш вариант использования, я бы, вероятно, остановился на трех объектах и ​​счете-фактуре с тремя полями, по одному для каждого объекта.Это просто, это быстро, колонки дискриминатора отстой (ИМХО, семантически, а не технически).

Добавить функцию в свой Счет, например

function getSubject() {
    return $this->contract ?? $this->provider ?? $this->client;
}

, на чуть сложнее ... но если вам не нужны три разных сеттера (честно говоря,сомневаетесь, что вы создаете клиента и, устанавливая тему, вы забываете, что это клиент, и хотите рассматривать его как InvoiceSubject)

function setSubject(InvoiceSubject $subject) {
    if($subject instanceof Client) {
         $this->client = $subject;
    } elseif (...) {} elseif (...) {}
    //... technically you should unset the others, if it can only ever be one
}

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

Отображение наследования ИМХО - больше проблем, чем оно того стоит.Если у вас нет действительно веских причин действительно нуждаться в этом: не делайте этого.Работать с уникально разными сущностями гораздо проще, чем иметь дело с какой-то абстрактной сущностью, где вы всегда должны проверять, какой это тип, и обращать внимание ... это действительно раздражает.С другой стороны, когда вы относитесь к трем сущностям одинаково ?Бьюсь об заклад, у каждого из них есть что-то уникальное, и в любом случае всегда есть распределительные коробки.если нет: интерфейс.

не усложняйте.

...