链在哪里可以遍历自引用模型

时间:2017-03-31 10:47:00

标签: laravel laravel-5

使用Laravel 5.4 ....

想象一下,我有以下模型:

应用\位置

这具有自引用层次结构,并具有以下数据

UK
  |---North West
  |             |----Liverpool
  |             |----Manchester
  |---North East
                |----Newcastle
                |----Sunderland

在这个模型中,我有一个自我关系

public function parent()
{
    return $this->belongsTo('App\Location', 'location_id');
}

和递归关系...

public function parentRecursive()
{
   return $this->parent()->with('parentRecursive');
}

应用\店

商店模型具有“位置”关系。

public function location()
{
    return $this->belongsTo('App\Location', 'location_id');
}

我想要做的是让一个类别中的所有商店。因此,如果我有一个名为“ACME”的商店与“利物浦”相关,我可以通过在以下情况下发送“利物浦”的ID(作为$ value)轻松获得它....

->whereHas('location', function($q) use ($value) {
        $q->where('id', $value);
})

但从技术上讲,这家店也在“西北”和“英国”。

因此,如果我将英国位置的ID发送到该查询,它将不会返回ACME商店,因为它与西北或英国ID没有直接关系。

我可以通过向此发送英国ID($ value)来实现...

$this->builder->whereHas('location', function($q) use ($value) {
    $q->where('id', $value);
})->orWhereHas('location.parent', function($q) use ($value) {
        $q->where('id', $value);
})->orWhereHas('location.parent.parent', function($q) use ($value) {
        $q->where('id', $value);
});

那么是否有更好的方法来编写上面的丑陋并且仅适用于关系树中有限数量的“跳跃”?我需要它遍历所有位置,直到它到达树顶。

1 个答案:

答案 0 :(得分:0)

我遇到了同样的问题。我的解决方案并不优雅,但无需使用嵌套集模型即可运行:

首先,(如果你还没有完成),你可能需要创建现有递归关系的反转:

public function children() {
  return $this->hasMany('App\Location', 'location_id');
}

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

接下来,您需要创建递归函数以获得最大深度

public function getDepth($location) {
  $depth = 0;
  foreach ($location->children as $child) {
    $d = $this->getDepth($child);
    if ($d > $depth) {
      $depth = $d;
    }
  }
  return 1 + $depth;
}

然后,您可以使用深度将查询与多个orWhereHas子句链接

$whereHas = 'location';
$query = Shop::whereHas($whereHas, function ($q) use ($value) {
  $q->where('id', $value);
});

for ($d = 1; $depth > $d; $d++) {
  $whereHas = $whereHas . '.parent';
  $query->orWhereHas($whereHas, function ($q) use ($value) {
    $q->where('id', $value);
  });
}