Laravel递归关系 [英] Laravel Recursive Relationships

查看:580
本文介绍了Laravel递归关系的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

  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 = $这 - > 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}(顺序不重要)



再次,我的方法工作,但似乎我做的太多查询。



另外,我不知道是否可以问这个在这里,但它是相关的。如何获取包含子帐户的所有帐户的列表?所以基本上是这个方法的倒数。这是一个用户不会尝试给一个已经是孩子的父母的帐户。使用上面的图,我想(伪代码):



Account :: where(account_id not in(A-> allChildAccounts()))。所以我会得到{G,H}



感谢任何见解。

解决方案>

这是你如何使用递归关系:

  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; //收集递归加载的子元素
//每个都有相同的子元素集合:
$ account-> allChildrenAccounts-> first() - > allChildrenAccounts; // ..等等

这样你就可以节省很多查询。这将对每个嵌套级别+ 1个附加查询执行1次查询。



我不能保证您的数据效率很高,您需要测试它。 / p>




这是无子帐户:

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

然后:

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


I'm working on a project in Laravel. I have an Account model that can have a parent or can have children, so I have my model set up like so:

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

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

This works fine. What I want to do is get all children under a certain account. Currently, I'm doing this:

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;
}

This also works, but I have to worry if it's slow. This project is the re-write of an old project we use at work. We will have several thousand accounts that we migrate over to this new project. For the few test accounts I have, this method poses no performance issues.

Is there a better solution? Should I just run a raw query? Does Laravel have something to handle this?

In summary What I want to do, for any given account, is get every single child account and every child of it's children and so on in a single list/collection. A diagram:

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

If I run A->immediateChildAccounts(), I should get {B, C}
If I run A->allChildAccounts(), I should get {B, D, C, E, F} (order doesn't matter)

Again, my method works, but it seems like I'm doing way too many queries.

Also, I'm not sure if it's okay to ask this here, but it is related. How can I get a list of all accounts that don't include the child accounts? So basically the inverse of that method above. This is so a user doesn't try to give an account a parent that's already it's child. Using the diagram from above, I want (in pseudocode):

Account::where(account_id not in (A->allChildAccounts())). So I would get {G, H}

Thanks for any insight.

解决方案

This is how you can use recursive relations:

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

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

Then:

$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

This way you save a lot of queries. This will execute 1 query per each nesting level + 1 additional query.

I can't guarantee it will be efficient for your data, you need to test it definitely.


This is for childless accounts:

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

then:

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

这篇关于Laravel递归关系的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆