Laravel Получить предков (URL)

В Laravel у меня есть таблица, которая содержит id, parent_id, slug (самообращение),

Когда у меня есть идентификатор, мне нужно получить всех его предков в таком формате (разделенные знаком «/»).

level1/level2/level3

Но эффективным способом без пакета типа "laravel-nestedset ".

Я реализовал это так.

public function parent()
{
    return $this->belongsTo('Collection', 'parent_id');
}

public function getParentsAttribute()
{
    $parents = collect([]);

    $parent = $this->parent;

    while(!is_null($parent)) {
        $parents->push($parent);
        $parent = $parent->parent;
    }

    return $parents;
}

Любой другой способ сделать это эффективно и разделенный «/»?

Я думаю, что это уже хорошее решение. Кроме того, атрибут parents возвращает коллекцию (хотя это может быть и простой массив). Затем просто вызовите $collection->implode('/'), чтобы превратить его в строку

Flame 11.03.2019 14:53

у вас есть столбец с именем уровня в БД с уровнем? если бы вы это сделали, вы могли бы динамически построить запрос, чтобы получить всех родителей, затем использовать что-то вроде GROUP_CONCAT и позволить БД делать всю работу. PS Я не использую Laravel, но я сделал это с вложенными наборами, простые отношения родитель-потомок - это немного сложнее, потому что вам нужно соединение для каждого уровня.

ArtisticPhoenix 11.03.2019 14:56

Любой способ сделать это с помощью красноречивого "with ()" - Нетерпеливая загрузка?

stackminu 11.03.2019 14:56

@Flame Ты имеешь в виду как return Collection::find(33)->parents->implode('/'); ?

stackminu 11.03.2019 15:00

да, я думаю, что это работает, так как ->parents-> будет относиться к вашему получателю атрибутов. Возможно, вы сможете добавить protected $appends = ['parents'] к своей модели, чтобы это свойство автоматически заполнялось.

Flame 11.03.2019 15:01

@Flame Я тоже так думал, но у меня пустой белый экран. :(

stackminu 11.03.2019 15:04

@stackminu попробуйте отладить саму родительскую коллекцию, чтобы проверить, пуста ли она, или поставьте точки останова в геттере.

Flame 11.03.2019 15:05

Коллекция @Flame Parent возвращает все поля с «родителем».

stackminu 11.03.2019 15:08

о, извините, конечно parent — это объект, вы должны называть его как ->implode('yourproperty', '/'), где yourproperty — свойство родительского объекта. Вызов с одним параметром работает только в том случае, если ваша коллекция содержит только простые скалярные значения.

Flame 11.03.2019 15:09

@Flame Круто, это сработало, но результат изменился. :/

stackminu 11.03.2019 15:15

в этой ссылке вы можете увидеть два разных подхода к работе с иерархическими данными в реляционной базе данных mikehillyer.com/articles/managing-hierarchical-data-in-mysql

hoseinz3 11.03.2019 15:18
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
4
11
1 208
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Если вы знаете, сколько максимум уровней может быть вложено, вы можете использовать Eager Loading. Скажем, если максимальная глубина составляет 3 уровня, вы можете сделать:

$model->with('parent.parent.parent');

Вы также можете использовать рекурсию вместо цикла.

public function getParentsAttribute()
{
    if (!$this->parent) {
        return collect([]);
    }

    return collect($this->parent->parents)->push($this->parent);
}

Если вы хотите добавить и первый объект (self), полный вызов будет таким:

$model->parents->push($model)->reverse->implode('attr_name', '/');

Который вы также можете обернуть в атрибут

public function getPathAttribute() {
    return $model->parents->push($model)->reverse->implode('attr_name', '/');
}

И ставь лайк $model->path;

Это делает его довольно неудобным, поскольку ваши родители также будут вложены в ваш объект. Вам нужно будет получить к ним доступ, используя $mainObject->parent->parent->parent, поэтому это неудобно, если вам нужен массив родителей.

Flame 11.03.2019 15:03

@Flame, правда, но по крайней мере меньше запросов

Nikolai Kiselev 11.03.2019 15:11

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

hoseinz3 11.03.2019 15:13

Я не уверен, что это действительно разрешает 1 запрос, даже если это так, увеличение скорости спорно, поскольку вы торгуете циклом PHP для создания массива родителей из этого вложенного свойства, VS оригинальное решение, которое выполняет 3 небольших запроса и помещает их в массив PHP

Flame 11.03.2019 15:17

@hoseinz3, каждый раз, когда $parent->parent вызывается в while цикле getParentsAttribute(), будет запускаться новый запрос к БД, если только эти вложенные отношения не были предварительно загружены ранее

Nikolai Kiselev 11.03.2019 15:17

@Flame кажется, ваше решение хорошее, но его можно комбинировать с загрузкой Eager, без вреда :)

Nikolai Kiselev 11.03.2019 15:20

@stackminu, вы можете использовать рекурсию, я обновил ответ.

Nikolai Kiselev 11.03.2019 16:10
Ответ принят как подходящий

После небольшого разговора в комментариях я думаю, что это хорошее решение:

// YourModel.php

// Add this line of you want the "parents" property to be populated all the time.
protected $appends = ['parents'];

public function getParentsAttribute()
{
    $collection = collect([]);
    $parent = $this->parent;
    while($parent) {
        $collection->push($parent);
        $parent = $parent->parent;
    }

    return $collection;
}

Затем вы можете получить своих родителей, используя:

Как заметил Николай Киселев https://stackoverflow.com/a/55103589/1346367, вы также можете комбинировать это с этим, чтобы сэкономить несколько запросов:

protected $with = ['parent.parent.parent'];
// or inline:
YourModel::find(123)->with(['parent.parent.parent']);

Это предварительно загружает родителя при загрузке объекта. Если вы решите не использовать это, родитель (лениво) загружается, как только вы вызываете $yourModel->parent.

Извините, у меня есть еще один вопрос, связанный с этим. Есть ли способ получить annsestor и self ? level1/level2/level3/WITH-THIS.

stackminu 11.03.2019 15:24

Я думаю, вы могли бы просто поместить $this в коллекцию $ внутри getParentsAttribute(), вы также можете поместить туда метод reverse().

Flame 11.03.2019 15:25

Другие вопросы по теме