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();
public function childrenAccounts() { return $this->hasMany('Account', 'act_parent', 'act_id')->with('childrenAccounts'); }
This code returns all children accounts (recurring)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With