Laravel красноречивые рекурсивные дети - PullRequest
0 голосов
/ 31 января 2019

Структура, которую я получил, выглядит следующим образом

Таблица пользователей

id       PK
name
email
username
password

Таблица пользовательских иерархий

user_parent_id FK of User
user_child_id  FK of User 
(Composite primary key)

Я написал эти 2 отношения, чтобы выяснить, кто является отцом пользователя, а кто - ребенком пользователя

public function parent()
{
    return $this->hasManyThrough(\App\Models\User::class, \App\Models\UserHierarchy::class, 'user_child_id', 'id', 'id', 'user_parent_id');
}

public function children()
{
    return $this->hasManyThrough(\App\Models\User::class, \App\Models\UserHierarchy::class, 'user_parent_id', 'id', 'id', 'user_child_id');
}

Чтобы получить всех детей, внуков и т. Д.Я разработал эти дополнительные отношения, в которых используется энергичная загрузка

public function childrenRecursive()
{
        return $this->children()->with('childrenRecursive.children');
}

Пока все хорошо, когда я нахожу пользователя с идентификатором, я могу получить все нисходящее дерево с помощью childrenRecursive.Сейчас я пытаюсь добиться повторного использования этих отношений для фильтрации определенного набора результатов, и под этим я подразумеваю: когда это определенный пользователь (например, идентификатор 1), я хочу, чтобы коллекция пользователей принадлежалаего нисходящее дерево (дети рекурсивные) и его первые прямые родители.

$model->where(function ($advancedWhere) use ($id) {
    $advancedWhere->whereHas('parent', function ($advancedWhereHas) use ($filterValue) {
        $advancedWhereHas->orWhere('user_child_id', $id);
        //I want all users that are recorded as his parents
 })->whereHas('childrenRecursive', function ($advancedWhereHas) use ($id) {
        // Missing code, I want all users that are recorded as his children and downwards
 })->get();

enter image description here

Это полное дерево, которое я проверяю, ирезультат, полученный выше (если я добавлю аналогичный orWhere для childrenRecursive), это то, что он возвращает каждого пользователя, у которого есть отношение Parent-Child.Например, пользователь 2 должен возвращать все числа, кроме 11 и 12, и он возвращает все числа, кроме 11 (потому что 11 никому не принадлежит)

1 Ответ

0 голосов
/ 31 января 2019

Сначала я собираюсь ответить на ваш вопрос, но во второй половине ответа я предложил альтернативу, которую я настоятельно рекомендую принять.

MySQL (в отличие, кстати, отMicrosoft SQL) не имеет возможности писать рекурсивные запросы.Соответственно, нет хороших отношений Laravel для моделирования этого.

Таким образом, у Laravel нет никакого способа сделать это, кроме как наивно, что, если у вас сложное дерево, приведет к множеству запросов.,

По сути, когда вы загружаете своего родителя, у вас будет доступ только к его дочерним элементам (как к коллекции отношений).Затем вы бы foreach через его потомков (а затем и их потомков и т. Д., Рекурсивно) сгенерировали все дерево.Каждый раз, когда вы делаете это, он выполняет новые запросы для дочернего элемента и его дочерних элементов.По сути, это то, чем вы сейчас занимаетесь, и вы обнаружите, что по мере роста вашего набора данных он станет очень медленным.В конце концов, это предоставляет вам структуру данных, к которой вы можете применять свои фильтры и условия в коде. Вы не сможете достичь этого за один запрос.

Если вы много пишете в БД, то есть добавляете много новых детей, но редко читаете результаты, тогда это можетбыть вашим лучшим решением.

(Редактировать: приведенный ниже комментарий abr связал меня с примечаниями к выпуску MySQL 8, которые имеет , имеют эту функциональность. Мой первоначальный ответ был основан на MySQL 5.7.Я не знаю, что у Laravel / Eloquent есть решение для канонических отношений, использующее это. Более того, я ранее использовал эту функциональность в MSSQL, а вложенные множества являются лучшим решением IMO.

Кроме того, Laravel не обязательно связан сMySQL - это просто часто выбираемая база данных. Поэтому он, вероятно, никогда не будет использовать такое конкретное решение, чтобы избежать такой тесной связи.)


Однако большинство иерархических структур читают больше, чем пишут, и в этом случаеэто приведет к значительной нагрузке на ваш сервер.

Если это так, я бытиски:

https://en.wikipedia.org/wiki/Nested_set_model

Мы используем https://github.com/lazychaser/laravel-nestedset, который является реализацией вышеупомянутого, и он очень хорошо работает для нас.

ЭтоСтоит отметить, что это может быть медленным и занимать много памяти, когда мы переопределяем все дерево (у нас около 20 000 родительско-дочерних отношений), но это должно произойти только тогда, когда мы допустили ошибку в иерархии, которая не может быть выбранавручную, и это редко (мы не делали это в течение 6 месяцев).Опять же, если вы считаете, что вам, возможно, придется делать это регулярно, это может оказаться не лучшим вариантом для вас.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...