Skip to content

Commit 774a648

Browse files
committed
Refactored ancestors and descendants relations
1 parent 8d60600 commit 774a648

9 files changed

+300
-327
lines changed

CHANGELOG.markdown

+6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
### 4.2.5
2+
3+
* #208: dirty parent and bounds when making a root
4+
* #206: fixed where has on descendants
5+
* refactored ancestors and descendants relations
6+
17
### 4.2.4
28

39
* Fixed issues related to rebuilding tree when using `SoftDeletes` and scoping

phpunit.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
include __DIR__.'/vendor/autoload.php';
44

55
$capsule = new \Illuminate\Database\Capsule\Manager;
6-
$capsule->addConnection([ 'driver' => 'sqlite', 'database' => ':memory:', 'prefix' => 'prefix' ]);
6+
$capsule->addConnection([ 'driver' => 'sqlite', 'database' => ':memory:', 'prefix' => 'prfx_' ]);
77
$capsule->setEventDispatcher(new \Illuminate\Events\Dispatcher);
88
$capsule->bootEloquent();
99
$capsule->setAsGlobal();

src/AncestorsRelation.php

+19-166
Original file line numberDiff line numberDiff line change
@@ -2,101 +2,10 @@
22

33
namespace Kalnoy\Nestedset;
44

5-
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
6-
use Illuminate\Database\Eloquent\Collection as EloquentCollection;
75
use Illuminate\Database\Eloquent\Model;
8-
use Illuminate\Database\Eloquent\Relations\Relation;
9-
use Illuminate\Database\Query\Builder;
10-
use InvalidArgumentException;
11-
use RuntimeException;
126

13-
class AncestorsRelation extends Relation
7+
class AncestorsRelation extends BaseRelation
148
{
15-
/**
16-
* @var QueryBuilder
17-
*/
18-
protected $query;
19-
20-
/**
21-
* @var NodeTrait|Model
22-
*/
23-
protected $parent;
24-
25-
/**
26-
* AncestorsRelation constructor.
27-
*
28-
* @param QueryBuilder $builder
29-
* @param Model $model
30-
*/
31-
public function __construct(QueryBuilder $builder, Model $model)
32-
{
33-
if ( ! NestedSet::isNode($model)) {
34-
throw new InvalidArgumentException('Model must be node.');
35-
}
36-
37-
parent::__construct($builder, $model);
38-
}
39-
40-
/**
41-
* @param EloquentBuilder $query
42-
* @param EloquentBuilder $parentQuery
43-
*
44-
* @return null
45-
*/
46-
public function getRelationExistenceCountQuery(EloquentBuilder $query, EloquentBuilder $parentQuery)
47-
{
48-
throw new RuntimeException('Cannot count ancestors, use depth functionality instead');
49-
}
50-
51-
/**
52-
* @param EloquentBuilder $query
53-
* @param EloquentBuilder $parent
54-
* @param array $columns
55-
*
56-
* @return mixed
57-
*/
58-
public function getRelationExistenceQuery(EloquentBuilder $query, EloquentBuilder $parent,
59-
$columns = [ '*' ]
60-
) {
61-
$query->select($columns);
62-
63-
$table = $query->getModel()->getTable();
64-
65-
$query->from($table.' as '.$hash = $this->getRelationSubSelectHash());
66-
67-
$grammar = $query->getQuery()->getGrammar();
68-
69-
$table = $grammar->wrapTable($table);
70-
$hash = $grammar->wrapTable($hash);
71-
$parentIdName = $grammar->wrap($this->parent->getParentIdName());
72-
73-
return $query->whereRaw("{$hash}.{$parentIdName} = {$table}.{$parentIdName}");
74-
}
75-
76-
/**
77-
* @param EloquentBuilder $query
78-
* @param EloquentBuilder $parent
79-
* @param array $columns
80-
*
81-
* @return mixed
82-
*/
83-
public function getRelationQuery(
84-
EloquentBuilder $query, EloquentBuilder $parent,
85-
$columns = [ '*' ]
86-
) {
87-
return $this->getRelationExistenceQuery($query, $parent, $columns);
88-
}
89-
90-
/**
91-
* Get a relationship join table hash.
92-
*
93-
* @return string
94-
*/
95-
public function getRelationSubSelectHash()
96-
{
97-
return 'self_'.md5(microtime(true));
98-
}
99-
1009
/**
10110
* Set the base constraints on the relation query.
10211
*
@@ -110,95 +19,39 @@ public function addConstraints()
11019
}
11120

11221
/**
113-
* Set the constraints for an eager load of the relation.
114-
*
115-
* @param array $models
116-
*
117-
* @return void
118-
*/
119-
public function addEagerConstraints(array $models)
120-
{
121-
$model = $this->query->getModel();
122-
$table = $model->getTable();
123-
$key = $model->getKeyName();
124-
125-
$grammar = $this->query->getQuery()->getGrammar();
126-
127-
$table = $grammar->wrapTable($table);
128-
$hash = $grammar->wrap($this->getRelationSubSelectHash());
129-
$key = $grammar->wrap($key);
130-
$lft = $grammar->wrap($this->parent->getLftName());
131-
$rgt = $grammar->wrap($this->parent->getRgtName());
132-
133-
$sql = "$key IN (SELECT DISTINCT($key) FROM {$table} INNER JOIN "
134-
. "(SELECT {$lft}, {$rgt} FROM {$table} WHERE {$key} IN (" . implode(',', $this->getKeys($models))
135-
. ")) AS $hash ON {$table}.{$lft} <= {$hash}.{$lft} AND {$table}.{$rgt} >= {$hash}.{$rgt})";
136-
137-
$this->query->whereNested(function (Builder $inner) use ($sql) {
138-
$inner->whereRaw($sql);
139-
});
140-
$this->query->orderBy($this->parent->getLftName(), 'ASC');
141-
}
142-
143-
/**
144-
* Initialize the relation on a set of models.
145-
*
146-
* @param array $models
147-
* @param string $relation
148-
*
149-
* @return array
150-
*/
151-
public function initRelation(array $models, $relation)
152-
{
153-
return $models;
154-
}
155-
156-
/**
157-
* Match the eagerly loaded results to their parents.
158-
*
159-
* @param array $models
160-
* @param EloquentCollection $results
161-
* @param string $relation
22+
* @param Model $model
23+
* @param $related
16224
*
163-
* @return array
25+
* @return bool
16426
*/
165-
public function match(array $models, EloquentCollection $results, $relation)
27+
protected function matches(Model $model, $related)
16628
{
167-
foreach ($models as $model) {
168-
$ancestors = $this->getAncestorsForModel($model, $results);
169-
170-
$model->setRelation($relation, $ancestors);
171-
}
172-
173-
return $models;
29+
return $related->isAncestorOf($model);
17430
}
17531

17632
/**
177-
* Get the results of the relationship.
33+
* @param QueryBuilder $query
34+
* @param Model $model
17835
*
179-
* @return mixed
36+
* @return void
18037
*/
181-
public function getResults()
38+
protected function addEagerConstraint($query, $model)
18239
{
183-
return $this->query->get();
40+
$query->orWhereAncestorOf($model);
18441
}
18542

18643
/**
187-
* @param Model $model
188-
* @param EloquentCollection $results
44+
* @param $hash
45+
* @param $table
46+
* @param $lft
47+
* @param $rgt
18948
*
190-
* @return Collection
49+
* @return string
19150
*/
192-
protected function getAncestorsForModel(Model $model, EloquentCollection $results)
51+
protected function relationExistenceCondition($hash, $table, $lft, $rgt)
19352
{
194-
$result = $this->related->newCollection();
195-
196-
foreach ($results as $ancestor) {
197-
if ($ancestor->isAncestorOf($model)) {
198-
$result->push($ancestor);
199-
}
200-
}
53+
$key = $this->getBaseQuery()->getGrammar()->wrap($this->parent->getKeyName());
20154

202-
return $result;
55+
return "{$table}.{$rgt} between {$hash}.{$lft} and {$hash}.{$rgt} and $table.$key <> $hash.$key";
20356
}
20457
}

0 commit comments

Comments
 (0)