Laravel / Eloquent:限制嵌套的预先加载,以便不包含空父项

时间:2017-02-12 14:50:01

标签: laravel eloquent

我有一个相对简单的数据库结构,包括国家,地区和仓库。每个库都分配给操作员和区域:

运营商

+----+------------+
| ID |    name    |
+----+------------+
|  1 | Operator 1 |
|  2 | Operator 2 |
+----+------------+

国家

+----+----------------+------+
| ID |   country_id   | code |
+----+----------------+------+
|  1 | United Kingdom | gb   |
|  2 | France         | fr   |
+----+----------------+------+

区域

+----+-----------------+-------+
| ID | country_id (FK) | name  |
+----+-----------------+-------+
|  1 |               1 | North |
|  2 |               1 | South |
|  3 |               1 | East  |
|  4 |               1 | West  |
|  5 |               2 | North |
|  6 |               2 | South |
|  7 |               2 | East  |
|  8 |               2 | West  |
+----+-----------------+-------+

+----+----------------+------------------+-----------+
| ID | region_id (FK) | operator_id (FK) |   name    |
+----+----------------+------------------+-----------+
|  1 |              1 |                1 | Newcastle |
|  2 |              8 |                2 | Nantes    |
+----+----------------+------------------+-----------+

我已经在各自的模型中成功地建立了他们雄辩的关系。

我想将每个软件仓库加载到各自的区域和国家/地区,并由特定的运营商进行过滤。

$depots = Country::with('regions.depots')->whereHas('regions.depots', function($query) use ($opID) {
    $query->where('operator_id',$opID);
})->get();

这样可以解决问题,但是,除了急需加载软件仓库外,它还急于加载所有区域,包括那些没有分配给它们的软件仓库的区域。例如。如果在$opID = 1时执行上述操作,则会得到以下结果:

name: United Kingdom,
regions: [
    {
        name: North,
        depots: [{
            name: Newcastle
        }]
    }, {
        name: South,
        depots: []
    }, {
        name: East,
        depots: []
    }, {
        name: West,
        depots: []
    }
]

我想要的是上面返回的内容,但是没有没有软件仓库的区域。

我使用withwhereHas的限制玩了很多但是无法获得所需的数据结构。为什么以下代码没有达到预期的效果?

$depots = Country::with(['regions.depots' => function($query) use ($opID) {
    $query->where('depots.operator_id',$opID);
}])->get();

如果孩子不存在,有没有什么方法可以不急切地加载父母?或者是我执行上述查询然后手动循环结果的情况?

修改

所以经过几个小时后,我终于找到了一种方法来达到预期的效果。但它看起来真的很脏。这真的是最好的方式吗?

$depots = Country::whereHas('regions.depots', function($q) use ($opID) {
    $q->where('operator_id',$opID);
})->with(['regions' => function($q) use ($opID) {
    $q->with('depots')->whereHas('depots', function($q2) use ($opID) {
        $q2->where('operator_id',$opID);
    });
}])->get();

编辑2

事实证明,第一次编辑实际上是查询除operator_id表之外的所有内容的depots,这意味着只要我添加了另一个运营商在同一地区拥有的另一个仓库,当我不想要的时候。下面看起来更加混乱,但确实有效。与自己交谈很有趣;)希望有一天能帮助别人......

$depots = Country::has('regions.depots')
          ->with(['regions' => function($q) use ($opID) {
              $q->with(['depots' => function($q2) use ($opID) {
                  $q2->where('operator_id',$opID);
              }])->has('depots');
          }])->get();

1 个答案:

答案 0 :(得分:1)

您可以随时使用延迟加载:

$depots = Country::whereHas('regions.depots', function($q) use ($opID) {
    $q->where('operator_id',$opID);
})->get()->load('regions.depots');