推动 ORM 自引用外键

Posted

技术标签:

【中文标题】推动 ORM 自引用外键【英文标题】:Propel ORM self referencing foreign keys 【发布时间】:2013-03-22 10:22:53 【问题描述】:

我正在使用 Propel ORM 并且对 Propel 非常陌生。我需要一些帮助,从表中选择数据,但查询不正确。我有一张这样的表(注意:不是实际的表,而是相同的主体):

+---------------------+
| ID  | Page | Parent |
+---------------------+
| 1   |  A   |  0     |
| 2   |  B   |  0     |
| 3   |  C   |  2     |
| 4   |  D   |  3     |
| 5   |  E   |  1     |
| 6   |  F   |  0     |
| 7   |  G   |  3     |
| 8   |  H   |  4     |
| 9   |  I   |  6     |
| 10  |  J   |  5     |
+---------------------+

这个表在加载页面时给了我一个树状结构。在使用 propel 之前,我有一个带有函数“loadPages”的类,它将内页嵌套在 Pages 类中名为 $nested 的数组上,看起来像这样(注意:不是实际函数,只是一个紧密的表示):

function loadPages($parent=0, $data)
    $sql = "sql query here to select pages where parent = $parent";
    while($results)
       $pages = new Pages();
       $pages->setId(blah blah);
       $pages->setPage(blah blah);
       $pages->setParent(blah blah);

       $innerPages = new Pages();
       /* load innerpages into the nested array */
       $innerPages->loadPages($pages->getId(), $pages->nested);

       array_push($data, $pages);
       return true;
    

基本上我怎么能用 Propel 做到这一点?我可以很容易地拉出父值为 0 的页面,如下所示:

$pages = PagesQuery::create()
->filterByParent(0)
->find();

但是我需要将内页递归地嵌套到它返回的对象中,即使 Propel 网站上有所有好的文档,我的努力也没有取得多大成果。

如果我使用旧的 Pages 类打印 $data,我会得到这样的结果(这里只是使用上表的一个示例。):

Array(
   [0] => Pages Object
   (
    [id] => 2
    [page] => B
    [parent] => 0
    [nested] = Array(
       [0] => Pages Object
       (
        [id] => 3
        [page] => C
        [parent] => 2
       )
    )
)

我已经让这个工作了,但我不确定它是最好的方法。

function loadPages($parent=0, $siteId, &$arr)
        $arr = PagesQuery::create()
        ->filterBySiteId($siteId)
        ->filterByParentId($parent)
        ->find();

        foreach ($arr as $i => $v) 
            $arr[$i]->nested = '';
            loadPages($v->getId(), $siteId, $arr[$i]->nested);
        

    
    $site->pages = '';
    loadPages(0, $site->getId(), $site->pages);

我的模式没有自我关系设置,所以我刚刚像这样在同一个表中添加了一个外键,然后重新运行推进以重新创建类。我仍然不确定如何写出推进查询(我从架构中删除了几列只是为了节省空间)。抱歉,这篇文章现在越来越火了。

<table name="pages" phpName="Pages">
    <column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true"/>
    <column name="userId" type="integer" required="false"/>
    <column name="siteId" type="integer" required="false"/>
    <column name="parentId" type="integer" required="false"/>
    <foreign-key foreignTable="users" phpName="Users" refPhpName="Pages">
         <reference local="userId" foreign="id"/>
        </foreign-key>
    <foreign-key foreignTable="sites">
          <reference local="siteId" foreign="id"/>
        </foreign-key>
        <foreign-key foreignTable="pages">
         <reference local="parentId" foreign="id"/>
        </foreign-key>
</table>

【问题讨论】:

我不太明白你想要什么结果。关于您给我们的内容表示例,您可以粘贴您想要的结果内容吗? 您好,感谢您的关注。我在问题的底部添加了一个示例数组,它显示了我的旧课程会产生什么。它不是整个 talb 只是 1 个例子。 你能粘贴你的架构吗?我只是想知道你是如何定义自我关系的。有人认为像PagesParent 我希望这可能是我的问题,我认为我的架构中没有自我关系。你让我现在开始思考了。我去看看,让你知道。您可能刚刚解决了问题^^ 我已经将架构的页面部分添加到我的问题中,是的,它没有自我关系,所以我已经为自己添加了一个外键,如您所见。在上面的 scema 上方,你会看到我添加了一个我刚刚创建的函数,它似乎可以工作,但我不确定这是正确的方法。 【参考方案1】:

这不是您问题的直接答案,但它仍然是一个解决方案。

正如您在对该问题的评论中提到的那样:nested-set 就是答案。

起初这听起来像是解决问题的一种方法,但嵌套集是其他人遇到类似问题的结果。它经过充分研究,是一种表示树木的流行方式。它的主要优势正是能够管理可变深度树。

您提到您是 Propel 的新手,并且切换到嵌套集需要相当多的工作,所以我冒昧地假设您之前没有使用过 Propel 的行为。如果是这种情况,请务必阅读有关 Behaviors 的文档部分。

如果你现在喜欢 Propel,以后你会爱上

要解决您的问题:查看NestedSet Behavior。与所有其他行为一样,很容易将其添加到您的架构中。重新生成您的模型,您只需对代码进行少量重构(示例将向您展示如何)。

(注意:如果您有一个现有的数据库,则需要将该父链接迁移到嵌套集结构。但这基本上是您应该关注的唯一问题。)

Using a RecursiveIterator 部分提供了一个显示整个树的示例。

【讨论】:

感谢 smhg。是的,我肯定会考虑将其移至嵌套集模型。我只是希望我错过了 Propel 中的一个简单技巧 =)我将其标记为已接受的答案,因为我看不到任何其他合乎逻辑的方法来做到这一点。感谢您抽出宝贵时间阅读我的问题/帖子。 我假设当您使用现有数据库并从中反向生成模式时,您必须自己添加 NestedSet 行为?如果它可以在看到表中有内部关系时自动执行此操作,那就太好了。 是的,您必须自己添加行为。内部关系并不总是意味着它是嵌套的集合结构。

以上是关于推动 ORM 自引用外键的主要内容,如果未能解决你的问题,请参考以下文章

3.啥是键、候选键、主键和外键?

SQLAlchemy(三):外键连表关系

Oracle查看表索引、主键、外键、约束

sql用命令创建主键与外键。

SqlServer主键外键添加及判断表是否存在

sql中创建组合主键和组合外键