Рекурсивные отношения 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

Я делаю что-то подобное. Я думаю, что ответ заключается в кэшировании вывода и очистке кеша при каждом обновлении базы данных (если ваши учетные записи сами не сильно меняются)