Как определить Doctrine mappedSuperclass с помощью XML или YAML вместо сопоставления аннотаций

Следующий сценарий взят из https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/reference/inheritance-mapping.html#mapped-superclasses и был изменен только для включения второго подкласса . Насколько я понимаю, MappedSuperclassBase не может существовать сам по себе, но должен быть расширен одним и только одним подклассом (т.е. либо EntitySubClassOne, либо EntitySubClassTwo), и это та же концепция, что и супертип / подтип для SQL. Согласен?

Как определяется супер / подтип с использованием YAML или XML вместо сопоставления аннотаций?

<?php
/** @MappedSuperclass */
class MappedSuperclassBase
{
    /** @Column(type="integer") */
    protected $mapped1;
    /** @Column(type="string") */
    protected $mapped2;
    /**
     * @OneToOne(targetEntity="MappedSuperclassRelated1")
     * @JoinColumn(name="related1_id", referencedColumnName="id")
     */
    protected $mappedRelated1;

    // ... more fields and methods
}

/** @Entity */
class EntitySubClassOne extends MappedSuperclassBase
{
    /** @Id @Column(type="integer") */
    private $id;
    /** @Column(type="string") */
    private $name;

    // ... more fields and methods
}

/** @Entity */
class EntitySubClassTwo extends MappedSuperclassBase
{
    /** @Id @Column(type="integer") */
    private $id;
    /** @Column(type="string") */
    private $name;

    // ... more fields and methods
}

person user1032531    schedule 05.02.2019    source источник
comment
Прокрутите ссылку вниз до этой страницы: doctrine-project.org/projects/doctrine-orm/en/2.6/reference/ ;-)   -  person rkeet    schedule 05.02.2019
comment
@rkeet Если я не ошибаюсь, этот раздел описывает только переопределения ассоциаций, а рассматриваемая часть опущена и описана как //other fields mapping.   -  person user1032531    schedule 05.02.2019
comment
Ваш вопрос спрашивает, как сделать, используя Yaml / XML. Если вы перейдете по этой ссылке, в примере кода вверху есть вкладки, позволяющие переключать язык. Если вы прочитаете этот код, вы увидите, как отобразить там запрошенный MappedSuperClass. (Ямл: type: mappedSuperclass, XML: <mapped-superclass name="MyProject\Model\User">)   -  person rkeet    schedule 05.02.2019
comment
@rkeet Но он не показывает эквиваленты @OneToOne(targetEntity="MappedSuperclassRelated1") и @JoinColumn(name="related1_id", referencedColumnName="id"), конечно, это не так уж сложно преобразовать. Что еще более важно, это не объясняет, как EntitySubClass знает, что это связано с MappedSuperclassBase, и это та часть, над которой я борюсь. Спасибо   -  person user1032531    schedule 05.02.2019
comment
Если Worker расширяет Person (суперкласс), то у вас есть 1 Entity: Worker. Создание отношения к Worker (т.е. от Paycheck), ссылки только на Worker. Суперкласс Person просто создает базовый набор свойств / значений по умолчанию, которые используются в Worker.   -  person rkeet    schedule 06.02.2019
comment
Я думаю, вы можете путать MappedSuperClass с классом Discriminator. Первый предоставляет свойства / значения по умолчанию для дочернего класса, но никогда не является сущностью сам по себе. Последняя, ​​выделенная сущность, представляет собой отдельный класс.   -  person rkeet    schedule 06.02.2019


Ответы (1)


Судя по нашим комментариям, я вижу ваше недоумение. Поскольку документы обрабатывают и MappedSuperclass, и Discriminator на одной странице, я думаю, вы перепутали их использование в своей голове. Надеюсь, это поможет вам:

  • MappedSuperclass предоставляет свойства / значения по умолчанию для многократного использования, но он никогда не может быть сущностью сам по себе. Это сопоставимо с классами PHP abstract (которые не могут быть созданы сами по себе)
  • Дискриминатор дает возможность расширить сущность, сделав ее другой сущностью. Например, наличие Person Entity дает вам 1 Entity. Этот объект может быть расширен, например, на Worker и Manager.

Хорошим вариантом использования MappedSuperclass будет AbstractEntity. Каждой сущности нужен идентификатор, уникальный идентификатор. Это также дает вам что-то общее, что можно проверить в Listeners и тому подобном. Итак, вперед и создайте:

/**
 * @ORM\MappedSuperclass
 */
abstract class AbstractEntity
{
    /**
     * @var int
     * @ORM\Id
     * @ORM\Column(name="id", type="integer", options={"unsigned":true})
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    protected $id;
  
    // getter / setter
}

Видите, как это объявлено как abstract, так и MappedSuperclass?

Это связано с тем, что ни один из (abstract class и MappedSuperclass) не может быть создан сам по себе. Вы не можете делать $entity = new AbstractEntity(), потому что это abstract класс PHP. Doctrine также не будет создавать отдельную таблицу для AbstractEntity.

Затем создайте Person:

/**
 * @ORM\Entity
 * @ORM\Table(name="persons")
 *
 * @InheritanceType("JOINED")
 * @DiscriminatorColumn(name="discr", type="string")
 */
class Person extends AbstractEntity
{
    /**
     * @var string
     * @ORM\Column(name="name", type="string", length=255, nullable=false)
     */
    protected $name;

    // getter / setter
}

Вышеупомянутый Person, Entity настроен для Наследование таблицы классов через JOINED тип наследования. Это означает, что на уровне базы данных таблица persons будет отделена от любых столбцов, добавленных другими сущностями, расширяя Person.

Обратите внимание, как я не объявил DiscriminatorMap. Ниже из документов, выделенных мной жирным шрифтом:

На заметку:

  • @InheritanceType, @DiscriminatorColumn и @DiscriminatorMap должны быть указаны в самом верхнем классе, который является частью иерархии сопоставленных сущностей.
  • @DiscriminatorMap указывает, какие значения столбца дискриминатора идентифицируют строку как принадлежащую к какому типу. В приведенном выше случае значение person идентифицирует строку как имеющую тип Person, а employee определяет строку как имеющую тип Employee.
  • Имена классов в карте дискриминатора не должны быть полностью квалифицированы, если классы содержатся в том же пространстве имен, что и класс сущности, к которому применяется карта дискриминатора.
  • Если карта дискриминатора не указана, она создается автоматически. Автоматически созданная карта дискриминатора содержит краткое имя каждого класса в нижнем регистре в качестве ключа.

Теперь давайте создадим Worker:

/**
 * @ORM\Entity
 * @ORM\Table(name="workers")
 */
class Worker extends Person
{
    /**
     * @var int
     * @ORM\Column(name="worker_id", type="integer", length=11, nullable=false)
     */
    protected $workerId;

    // getter / setter
}

Итак, теперь у нас есть:

  • MappedSuperclass: AbstractEntity - не автономный объект
  • Дискриминация: Person - является отдельной организацией
  • нормальный: Worker - расширяется Person

На заметку:

  • Невозможно создать экземпляр MappedSuperclass. Таким образом: вы не можете создавать ссылки / отношения с ним. Сравним с PHP abstract class
  • Дискриминируемая сущность - это такая сущность, которая также стоит особняком и может использоваться как обычная сущность. Вы можете создавать отношения с ним и от него без проблем
  • Сущность, расширяющая Дискриминируемую Сущность, является экземпляром обоих. В приведенном выше коде истинны оба значения: $worker instanceof Worker и $worker instanceof Person, потому что Worker расширяет Person. Однако $person instanceof Worker будет false!

$workerId = $person->getWorkerId() // generates "method does not exist" fatal

$workerId = $worker->getWorkerId() // generates integer value


Надеюсь, вам удалось прояснить ситуацию. Если нет, не стесняйтесь спрашивать.

person rkeet    schedule 06.02.2019
comment
Спасибо, rkeet. В вашем примере вы явно определили class Person extends AbstractEntity. Но если бы он был реализован с использованием XML или YAML, разве нельзя было бы начать с класса PHP, поэтому нельзя было бы определить эту связь таким образом? - person user1032531; 06.02.2019
comment
Вы начали бы таким образом, см. Более раннюю ссылку здесь: doctrine-project.org/projects/doctrine-orm/en/2.6/reference/ - случай, когда User - это MappedSuperclass, который расширяется на Admin. Если вы последуете этому, то сможете заставить его работать. - person rkeet; 06.02.2019
comment
Ах, я вижу источник моего замешательства. Я ошибочно думал, что MappedSuperclass представляет супертип / подтип SQL (Learndatamodeling.com/blog/supertype-and -подтип). Я понимал, что нельзя создать экземпляр MappedSuperclass, но я не понимал, что с ним не связана таблица! - person user1032531; 07.02.2019
comment
Ах, хорошо, один бит, который я не объяснил в ответе ;-) Но опять же, это похоже на abstract class vs class ;-) Поскольку первый не может быть создан, как Entity у него нет таблицы. Последний может быть создан и должен иметь связанное хранилище данных (таблицу). Затем есть размеченные классы, где наследование одной таблицы имеет корневую таблицу самого родительского класса, который расширяется, и наследование таблицы классов, где каждая сущность имеет свою собственную таблицу, но присоединяется к своей родительской (и, таким образом, конкретная таблица только добавляет это добавленные свойства). - person rkeet; 07.02.2019
comment
В очередной раз благодарим за помощь. Впервые каждый раз экспериментирую с ORM, и это было путешествие, но оно начинает складываться. Различаемые классы - это то, что я хочу. - person user1032531; 07.02.2019
comment
Что касается discriminator column, я еще не понял эту часть. Если у меня есть абстрактная Person и person таблица с первичным ключом id, и конкретные Teacher и Student с таблицами teacher и student также с первичными ключами id, где Person может быть присоединен только к одному Teacher или одному Student с использованием взаимно-однозначного соответствия одна связь, я бы поместил @InheritanceType("JOINED"), @DiscriminatorColumn(name="id", type="integer") и @DiscriminatorMap({"teacher" = "Teacher", "student" = "Student"} в Person? - person user1032531; 07.02.2019
comment
Ах, я считаю, что ошибаюсь насчет discriminator column. В настоящее время у меня нет столбца type в person, но я полагаюсь на соединение id. Может, стоит добавить ... - person user1032531; 07.02.2019
comment
JOINED вызывает таблицу для каждой сущности, однако первая сущность (например, Person) содержит только name столбец (и первичный ключ идентификатора), вторая таблица (например, Worker) содержит только workerId столбец (и идентификатор внешний ключ). - person rkeet; 07.02.2019
comment
SINGLE_TABLE заставляет все дочерние объекты расширять исходную таблицу. Таким образом, и для Person, и для Worker (а также для Boss и Relative) вы получите 1 (одну) таблицу! - ›id, name, workerId, bossId, relativeId - где все дополнительные поля имеют значение« null ». - person rkeet; 07.02.2019
comment
Что касается @DiscriminiatorMap, просто удалите эту строку (см. Жирную цитату в ответе). Оставьте это Доктрине. Только делайте @DiscriminatorColumn(name="discr", type="string") и DiscriminatorType. (последнее залейте в себя, советую JOINED) - person rkeet; 07.02.2019