具有ManyToOne关系的单表继承(STI)


Single Table Inheritance (STI) with a ManyToOne relationship

我对Doctrine很陌生,所以欢迎任何一般建议。我正在努力实现以下目标:

一个页面可以同时包含视频和照片。两者都是媒体和共享属性,所以我认为单表继承是有意义的。以下是我设置的相关部分:

页面

/**
 * @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;
}

这非常好,但是——这是我的问题——我如何获取页面的所有PhotoVideo。我试着添加之类的东西

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

Page,但这会导致模式错误,因为它在Photo上没有定义反向。非常感谢您的帮助!

您可以对媒体集合应用筛选器

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实际仅获取媒体的子集。

我认为您可以通过在特定子类上使用join来解决这个问题:

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

您的Page类上不能有一个映射到超类的$media属性。每个类都必须有一个属性。参见条令文件中的注释:

映射的超类不能是实体,它不可查询,并且映射超类定义的持久关系必须是单向(仅与拥有方)。这意味着一对多在映射的超类上根本不可能有关联。此外,只有当映射超类目前只在一个实体中使用。对于进一步支持继承、单表或联接表继承必须使用功能。