Однозначное наследование (STI) с отношением ManyToOne

Я довольно новичок в Доктрине, поэтому любые общие советы приветствуются. Я пытаюсь добиться следующего:

На странице могут быть как видео, так и фотографии. Оба являются средами и общими свойствами, поэтому я думал, что однозначное наследование имеет смысл. Вот некоторые составляющие моей установки:

страница

/** * @ORM\Entity */ class Page { /** * @ORM\Id * @ORM\GeneratedValue * @ORM\Column(type="integer") */ protected $id; /** * @ORM\OneToMany(targetEntity="Media", mappedBy="page", cascade={"persist"}) * @var ArrayCollection|Media[] */ protected $media; /** * @return Media[]|ArrayCollection */ public function getMedia() { return $this->media; } } 

СМИ

 /** * @ORM\Entity * @ORM\Table(name="media") * @ORM\InheritanceType("SINGLE_TABLE") * @ORM\DiscriminatorColumn(name="media_type", type="string") * @ORM\DiscriminatorMap({"video" = "Video", "photo" = "Photo"}) */ abstract class Media { /** * @ORM\Id * @ORM\GeneratedValue * @ORM\Column(type="integer") */ protected $id; /** * @ORM\Column(type="string") */ protected $url; /** * @ORM\ManyToOne(targetEntity="Page", inversedBy="media") */ protected $page; } 

Фото

 /** * @ORM\Entity */ class Photo extends Media { /** * @ORM\Column(type="string") */ protected $exif; } 

видео

 /** * @ORM\Entity */ class Video extends Media { /** * @ORM\Column(type="integer") */ protected $length; } 

Это работает отлично, но – и это мой вопрос – как мне получить все Photo или Video с страницы. Я пробовал добавлять такие вещи, как

 /** * @ORM\OneToMany(targetEntity="Photo", mappedBy="page", cascade={"persist"}) * @var ArrayCollection|Photo[] */ protected $photos; 

на Page , но это приводит к ошибке схемы, так как она не имеет обратного, определенного на Фото. Любая помощь очень ценится!

Solutions Collecting From Web of "Однозначное наследование (STI) с отношением ManyToOne"

Вы можете применить фильтр к медиа-коллекции

 class Page { ..... public function getPhotos() { return $this->getMedia()->filter( function($media) { return $media instanceof Photo; } ); } public function getVideos() { return $this->getMedia()->filter( function($media) { return $media instanceof Video; } ); } } 

Имейте в виду, что он будет извлекать все медийные средства в одном запросе на select , создавать экземпляры как «Фотографии», так и «Видео», а затем фильтровать результат. Это может повлиять на производительность, но вы можете использовать кеш доктрины в зависимости от вашего сценария.

Если производительность является ключом, вам может потребоваться реализовать собственные методы в PageRepository чтобы на самом деле извлечь только подмножество медийных средств, используя instance of dql, как описано в Query the Type .

Я думаю, вы можете решить это, используя join в конкретном подклассе:

 $queryBuilder->select('m') ->from('Media', 'm') ->join('Photo', 'p', 'WITH', 'm.id = p.id') ->where('m.page = :page_id') ->setParameter('page_id', $pageId); 

Вы не сможете иметь одно свойство $media в своем классе Page которое сопоставляется с суперклассом. У вас должно быть свойство для каждого класса. См. Примечание в документах Doctrine :

Отображаемый суперкласс не может быть сущностью, он не зависит от запросов, а постоянные отношения, определяемые сопоставленным суперклассом, должны быть однонаправленными (только с собственной стороной). Это означает, что ассоциации «один-ко-многим» вообще невозможны для сопоставленного суперкласса. Кроме того, ассоциации Many-to-Many возможны только в том случае, если сопоставленный суперкласс используется только в одном объекте на данный момент. Для дальнейшей поддержки наследования необходимо использовать одиночные или объединенные функции наследования таблиц.