【问题标题】:Laravel Get ancestors (URL)Laravel 获取祖先(URL)
【发布时间】:2019-03-11 13:49:05
【问题描述】:

在 Laravel 中,我有一个包含 id、parent_id、slug(自引用)的表,

当我有一个 ID 时,我需要以这样的格式获取它的所有祖先(用“/”分隔)。

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('/') 将其变为字符串
  • 你在数据库中有一个名为 level 的列吗?如果你这样做了,你可以动态构建一个查询来获取所有父母,然后使用GROUP_CONCAT 之类的东西,让数据库做腿部工作 - PS 我不使用 Laravel,但我已经用嵌套集、简单的父子关系完成了这个有点困难,因为您需要为每个级别加入。
  • 有什么方法可以使用 eloquent "with()" - Eager loading?
  • @Flame 你的意思是像return Collection::find(33)->parents->implode('/');
  • 抱歉parent 是一个对象,你应该像->implode('yourproperty', '/') 一样调用它,其中yourproperty 是父对象的一个​​属性。仅当您的集合只有简单的标量值时,单参数调用才有效

标签: php laravel laravel-5 eloquent


【解决方案1】:

在 cmets 中进行了一些交谈后,我认为这是一个很好的解决方案:

// 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;
}

然后您可以使用以下方法找回您的父母:

正如 Nikolai Kiselev 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.
  • 我想你可以将$this 推送到getParentsAttribute() 内的$collection,你可能还想把reverse() 方法也放在那里。
【解决方案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,没错,但至少查询次数更少
  • @NikolaiKiselev 为什么你认为这个解决方案执行的查询更少?为了在邻接列表模型中检索完整的树,我们需要根据树的深度加入。 (在本例中为 3)
  • 我不确定这是否真的解决了 1 个查询,即使解决了,速度的提高也是有争议的,因为您正在交易一个 PHP 循环来从这个嵌套属性创建一个父数组,VS执行 3 个小查询并将它们推送到 PHP 数组中的原始解决方案
  • @hoseinz3,每次在whilewhile 循环中调用$parent->parent 都会触发一个新的数据库查询,除非之前预加载了那些嵌套关系
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-03-21
  • 2011-07-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-23
相关资源
最近更新 更多