拉拉维尔递归关系

2022-08-30 09:48:33

我正在拉拉维尔做一个项目。我有一个可以有父母或可以有孩子的帐户模型,所以我的模型设置如下:

public function immediateChildAccounts()
{
    return $this->hasMany('Account', 'act_parent', 'act_id');
}

public function parentAccount()
{
    return $this->belongsTo('Account', 'act_parent', 'act_id');
}

这工作正常。我想做的是让所有孩子都使用某个帐户。目前,我正在这样做:

public function allChildAccounts()
{
    $childAccounts = $this->immediateChildAccounts;
    if (empty($childAccounts))
        return $childAccounts;

    foreach ($childAccounts as $child)
    {
        $child->load('immediateChildAccounts');
        $childAccounts = $childAccounts->merge($child->allChildAccounts());
    }

    return $childAccounts;
}

这也有效,但是如果它很慢,我不得不担心。这个项目是我们在工作中使用的旧项目的重写。我们将有几千个帐户迁移到这个新项目中。对于我拥有的少数测试帐户,此方法不会造成性能问题。

有没有更好的解决方案?我应该只运行原始查询吗?Laravel有什么东西可以处理吗?

总结对于任何给定的帐户,我想做的是将每个孩子帐户及其子帐户的每个孩子等放在一个列表/集合中。图示:

A -> B -> D
|--> C -> E
     |--> F 
G -> H

如果我运行A->immediateChildAccounts(),我应该得到{B,C}
如果我运行A->allChildAccounts(),我应该得到{B,D,C,E,F}(顺序无关紧要)

同样,我的方法有效,但似乎我正在做太多的查询。

另外,我不确定是否可以在这里问这个问题,但它是相关的。如何获取不包含儿童帐户的所有帐户的列表?所以基本上与上面的那个方法相反。这样,用户就不会尝试为帐户提供已经是其子级的父级。使用上面的图表,我想要(在伪代码中):

帐户::where(account_id不在(A->allChildAccounts())中)。所以我会得到{G,H}

感谢您的任何见解。


答案 1

以下是使用递归关系的方法:

public function childrenAccounts()
{
    return $this->hasMany('Account', 'act_parent', 'act_id');
}

public function allChildrenAccounts()
{
    return $this->childrenAccounts()->with('allChildrenAccounts');
}

然后:

$account = Account::with('allChildrenAccounts')->first();

$account->allChildrenAccounts; // collection of recursively loaded children
// each of them having the same collection of children:
$account->allChildrenAccounts->first()->allChildrenAccounts; // .. and so on

这样可以节省大量查询。这将为每个嵌套级别执行 1 个查询 + 1 个附加查询。

我不能保证它对您的数据是有效的,你需要绝对测试它。


这适用于无子女帐户:

public function scopeChildless($q)
{
   $q->has('childrenAccounts', '=', 0);
}

然后:

$childlessAccounts = Account::childless()->get();

答案 2
public function childrenAccounts()
{
    return $this->hasMany('Account', 'act_parent', 'act_id')->with('childrenAccounts');
}

此代码返回所有子帐户(定期)


推荐