推进单表继承问题

Posted

技术标签:

【中文标题】推进单表继承问题【英文标题】:Propel Single Table Inheritance Issue 【发布时间】:2010-01-14 20:32:32 【问题描述】:

我有一个名为“talk”的表,它在我的 schema.xml 文件中定义为抽象。

它生成 4 个对象(每个类键 1 个):评论、评级、评论、签到

它也会生成TalkPeer,但我无法让它生成其他4个对等点(CommentPeer、RatingPeer、ReviewPeer、CheckinPeer),所以我手动创建了它们,并让它们继承自TalkPeer.php,后者继承自BaseTalkPeer。然后我在每个对等点中实现了 getOMClass()。

问题是,当我使用 4 个对等点进行查询时,它们会返回所有 4 种类型的对象。也就是说,ReviewPeer 将返回访问、评分、评论和评论。

示例:

$c = new Criteria();
$c->add(RatingPeer::VALUE, 5, Criteria::GREATER_THAN);
$positive_ratings = RatingPeer::doSelect($c);

这将返回值 > 5 的所有 cmets、评级、评论和签到。

ReviewPeer 应该只返回 Review 对象,并且无法计算 了解如何做到这一点。

我是否真的必须通过并更改所有条件才能手动指定类键? 这似乎有点毫无意义,因为 Peer 名称已经不同了。 我不想自定义每个 Peer。我应该能够自定义 TalkPeer,因为它们都继承自它......我只是不知道如何。

我尝试仅在 TalkPeer 中更改 doSelectStmt,以便它自动将 CLASSKEY 限制添加到 Criteria。它几乎可以工作,但我得到一个:致命错误:无法在第 503 行的 /models/om/BaseTalkPeer.php 中实例化抽象类 Talk。第 503 行在 BaseTalkPeer::populateObjects() 中,并且是下面的第 3 行:

$cls = TalkPeer::getOMClass($row, 0); 
$cls = substr('.'.$cls, strrpos('.'.$cls, '.') + 1); 
$obj = new $cls();

docs talked about overriding BaseTalkPeer::populateObject()。 我感觉这是我的问题,但即使在阅读了源代码后,我仍然无法弄清楚如何让它工作。

这是我在 TalkPeer::doSelectStmt 中尝试过的:

    public static function doSelectStmt(Criteria $criteria, PropelPDO $con = null)
    
        $keys = array('models.Visit'=>1,'models.Comment'=>2,'models.Rating'=>3,'models.Review'=>4);

        $class_name = self::getOMClass();

        if(isset($keys[$class_name]))
           //Talk itself is not a returnable type, so we must check
            $class_key = $keys[$class_name];
            $criteria->add(TalkPeer::CLASS_KEY, $class_key);
        

        return parent::doSelectStmt($criteria, $con = null);
    

这是我的来自 ReviewPeer 的 getOMClass 方法的示例:

public static function getOMClass()

    return self::CLASSNAME_4; //aka 'talk.Review';

这是我的架构的相关位:

<table name="talk" idMethod="native" abstract="true">
   <column name="talk_pk" type="INTEGER" required="true" autoIncrement="true" primaryKey="true" />
   <column name="class_key" type="INTEGER" required="true" default="" inheritance="single">
       <inheritance key="1" class="Visit" extends="models.Talk" />
       <inheritance key="2" class="Comment" extends="models.Talk" />
       <inheritance key="3" class="Rating" extends="models.Talk" />
       <inheritance key="4" class="Review" extends="models.Rating" />
       </column>
</table>

附: - 不,我不能从 1.3 升级到 1.4。太多了 需要重新测试的代码

【问题讨论】:

【参考方案1】:

您为什么不直接删除 abstract=true 语句以便生成所有 Peers,然后再次添加摘要,再次生成以完全按照您的喜好获取数据库?

【讨论】:

无论我是否使用“抽象”,Peers 都不会自动生成。此外,数据库 is 完全符合我的喜好。 “抽象”部分就在那里,因此不能直接使用 Talk 对象和 Peer,因为 Talk 必须是特定类型(评论/评分/访问/评论)。【参考方案2】:

我从未在 Propel 中使用过继承,但您应该能够修改每个 Peer 类的 doSelectRS 方法来修改条件并为继承键指定额外条件。我面前没有文档,但在伪代码中看起来像这样:

public static function doSelectRS(Criteria $c)

   // you may want to check if the condition already exists in one of the criterion's before doing the following...
   $c->add(RatingPeer::TYPE, 3);
   return parent::doSelectRS($c);

【讨论】:

官方文档都没有提到这样做,我想尽可能避免自定义每个 Peer。在处理 Propel 时,我开始相信更少的定制 = 更少的错误。 哈哈,我同意。文档阅读的方式听起来好像您在正确地做所有事情。不过,模棱两可的部分是您问题的核心(即,他们是否应该在您遇到的情况下返回所有子类,或者他们应该只返回所使用的 Peer 的子类)。我不认为它与覆盖填充对象有任何关系 - 我认为那是因为你只想要适用于实体中给定子类集的属性。我没有设置推进项目 - 你认为你可以发布 populateObjectsdoSelect 的正文吗? 我认为 doSelectRS 是 Propel 1.2 的东西。我记得它,但在我的 1.3 代码库中找不到它:( 我正在尝试通过改变 doSelectStmt 来改变你的建议。它几乎可以工作,但我得到一个:致命错误:无法在 /models 中实例化抽象类 Talk /om/BaseTalkPeer.php 在第 503 行。第 503 行在 BaseTalkPeer::populateObjects() 中,是下面的第 3 行: $cls = TalkPeer::getOMClass($row, 0); $cls = substr('.' .$cls, strrpos('.'.$cls, '.') + 1); $obj = new $cls(); 是的,这是正确的 PDO 在 > 1.3 中使用,因此他们将方法更改为 Stmt 以反映它返回 PDOStatement ... 从现在开始我就想到了它。所以你把RatingPeer::doSelectStmt改成了重载BaseRatingPeer::doSelectStmt——还是BaseRatingPeer继承了它的doSelectStmt逻辑? RatingPeer 实际上继承自 TalkPeer,而 TalkPeer 继承自 BaseTalkPeer。我更改了 TalkPeer 中的 doSelectStmt。

以上是关于推进单表继承问题的主要内容,如果未能解决你的问题,请参考以下文章

单表继承(STI)问题

Prisma 中是不是支持单表继承?

使用单表的休眠继承

Doctrine2:任意连接和单表继承

在 Rails 的单表继承中在哪里写关联?

单表继承(休眠)的 JPA 2 标准查询