Рекурсивные отношения 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 = $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- > instantChildAccounts(), я должен получить {B, C}
Если я запустил A- > allChildAccounts(), я должен получить {B, D, C, E, F} (порядок не имеет значения)
Опять же, мой метод работает, но похоже, что я делаю слишком много запросов.
Кроме того, я не уверен, можно ли здесь спросить об этом, но это связано. Как я могу получить список всех учетных записей, в которых не включать дочерние аккаунты? Таким образом, в основном обратный этому методу выше. Это значит, что пользователь не пытается предоставить учетную запись родительскому, который уже является дочерним. Используя диаграмму сверху, я хочу (в псевдокоде):
Учетная запись:: где (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
Я делаю что-то подобное. Я думаю, что ответ заключается в кэшировании вывода и очистке кеша при каждом обновлении базы данных (если ваши учетные записи сами не сильно меняются)