Skip to content

Commit df8736a

Browse files
committed
eagerLoadingAncestorsWithScope | fix applying scope in whereAncestorOf() and whereDescendantOf()
1 parent bf599ab commit df8736a

File tree

4 files changed

+58
-22
lines changed

4 files changed

+58
-22
lines changed

src/BaseRelation.php

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -141,11 +141,6 @@ public function getResults()
141141
*/
142142
public function addEagerConstraints(array $models)
143143
{
144-
// The first model in the array is always the parent, so add the scope constraints based on that model.
145-
// @link https://github.com/laravel/framework/pull/25240
146-
// @link https://github.com/lazychaser/laravel-nestedset/issues/351
147-
optional($models[0])->applyNestedSetScope($this->query);
148-
149144
$this->query->whereNested(function (Builder $inner) use ($models) {
150145
// We will use this query in order to apply constraints to the
151146
// base query builder

src/NodeTrait.php

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1005,7 +1005,8 @@ public function getPrevSibling(array $columns = [ '*' ])
10051005
public function isDescendantOf(self $other)
10061006
{
10071007
return $this->getLft() > $other->getLft() &&
1008-
$this->getLft() < $other->getRgt();
1008+
$this->getLft() < $other->getRgt() &&
1009+
$this->isSameScope($other);
10091010
}
10101011

10111012
/**
@@ -1201,6 +1202,24 @@ protected function assertSameScope(self $node)
12011202
}
12021203
}
12031204

1205+
/**
1206+
* @param self $node
1207+
*/
1208+
protected function isSameScope(self $node): bool
1209+
{
1210+
if ( ! $scoped = $this->getScopeAttributes()) {
1211+
return true;
1212+
}
1213+
1214+
foreach ($scoped as $attr) {
1215+
if ($this->getAttribute($attr) != $node->getAttribute($attr)) {
1216+
return false;
1217+
}
1218+
}
1219+
1220+
return true;
1221+
}
1222+
12041223
/**
12051224
* @param array|null $except
12061225
*

src/QueryBuilder.php

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,10 @@ public function whereIsRoot()
8787
public function whereAncestorOf($id, $andSelf = false, $boolean = 'and')
8888
{
8989
$keyName = $this->model->getTable() . '.' . $this->model->getKeyName();
90+
$model = null;
9091

9192
if (NestedSet::isNode($id)) {
93+
$model = $id;
9294
$value = '?';
9395

9496
$this->query->addBinding($id->getRgt());
@@ -108,7 +110,7 @@ public function whereAncestorOf($id, $andSelf = false, $boolean = 'and')
108110
$value = '('.$valueQuery->toSql().')';
109111
}
110112

111-
$this->query->whereNested(function ($inner) use ($value, $andSelf, $id, $keyName) {
113+
$this->query->whereNested(function ($inner) use ($model, $value, $andSelf, $id, $keyName) {
112114
list($lft, $rgt) = $this->wrappedColumns();
113115
$wrappedTable = $this->query->getGrammar()->wrapTable($this->model->getTable());
114116

@@ -117,9 +119,13 @@ public function whereAncestorOf($id, $andSelf = false, $boolean = 'and')
117119
if ( ! $andSelf) {
118120
$inner->where($keyName, '<>', $id);
119121
}
122+
if ($model !== null) {
123+
// we apply scope only when Node was passed as $id.
124+
// In other cases, according to docs, query should be scoped() before calling this method
125+
$model->applyNestedSetScope($inner);
126+
}
120127
}, $boolean);
121128

122-
123129
return $this;
124130
}
125131

@@ -178,12 +184,13 @@ public function ancestorsAndSelf($id, array $columns = [ '*' ])
178184
* @param array $values
179185
* @param string $boolean
180186
* @param bool $not
187+
* @param Query $query
181188
*
182189
* @return $this
183190
*/
184-
public function whereNodeBetween($values, $boolean = 'and', $not = false)
191+
public function whereNodeBetween($values, $boolean = 'and', $not = false, $query = null)
185192
{
186-
$this->query->whereBetween($this->model->getTable() . '.' . $this->model->getLftName(), $values, $boolean, $not);
193+
($query ?? $this->query)->whereBetween($this->model->getTable() . '.' . $this->model->getLftName(), $values, $boolean, $not);
187194

188195
return $this;
189196
}
@@ -217,19 +224,26 @@ public function orWhereNodeBetween($values)
217224
public function whereDescendantOf($id, $boolean = 'and', $not = false,
218225
$andSelf = false
219226
) {
220-
if (NestedSet::isNode($id)) {
221-
$data = $id->getBounds();
222-
} else {
223-
$data = $this->model->newNestedSetQuery()
224-
->getPlainNodeData($id, true);
225-
}
227+
$this->query->whereNested(function (Query $inner) use ($id, $andSelf, $not) {
228+
if (NestedSet::isNode($id)) {
229+
$id->applyNestedSetScope($inner);
230+
$data = $id->getBounds();
231+
} else {
232+
// we apply scope only when Node was passed as $id.
233+
// In other cases, according to docs, query should be scoped() before calling this method
234+
$data = $this->model->newNestedSetQuery()
235+
->getPlainNodeData($id, true);
236+
}
226237

227-
// Don't include the node
228-
if ( ! $andSelf) {
229-
++$data[0];
230-
}
238+
// Don't include the node
239+
if (!$andSelf) {
240+
++$data[0];
241+
}
231242

232-
return $this->whereNodeBetween($data, $boolean, $not);
243+
return $this->whereNodeBetween($data, 'and', $not, $inner);
244+
}, $boolean);
245+
246+
return $this;
233247
}
234248

235249
/**

tests/ScopedNodeTest.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,12 +219,20 @@ public function testInsertingBeforeAnotherScopeFails()
219219

220220
$a->insertAfterNode($b);
221221
}
222-
222+
223223
public function testEagerLoadingAncestorsWithScope()
224224
{
225225
$filteredNodes = MenuItem::where('title', 'menu item 3')->with(['ancestors'])->get();
226226

227227
$this->assertEquals(2, $filteredNodes->find(5)->ancestors[0]->id);
228228
$this->assertEquals(4, $filteredNodes->find(6)->ancestors[0]->id);
229229
}
230+
231+
public function testEagerLoadingDescendantsWithScope()
232+
{
233+
$filteredNodes = MenuItem::where('title', 'menu item 2')->with(['descendants'])->get();
234+
235+
$this->assertEquals(5, $filteredNodes->find(2)->descendants[0]->id);
236+
$this->assertEquals(6, $filteredNodes->find(4)->descendants[0]->id);
237+
}
230238
}

0 commit comments

Comments
 (0)