Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
39 / 39
100.00% covered (success)
100.00%
8 / 8
CRAP
100.00% covered (success)
100.00%
1 / 1
InteractsWithClosureTable
100.00% covered (success)
100.00%
39 / 39
100.00% covered (success)
100.00%
8 / 8
10
100.00% covered (success)
100.00%
1 / 1
 closes
100.00% covered (success)
100.00%
23 / 23
100.00% covered (success)
100.00%
1 / 1
3
 addConstraints
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 getRelationExistenceQuery
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 excludingSelf
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 includingSelf
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 upToDepth
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 maxDepth
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 orderByDepth
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace Baril\Bonsai\Relations\Concerns;
4
5use Baril\Bonsai\Closure;
6use Illuminate\Database\Eloquent\Builder;
7use Illuminate\Database\Eloquent\Collection as EloquentCollection;
8
9/**
10 * @mixin \Illuminate\Database\Eloquent\Relations\BelongsToMany
11 */
12trait InteractsWithClosureTable
13{
14    use AutoloadsOtherRelations;
15    use IsReadOnly;
16
17    /**
18     * @var int|null
19     */
20    protected $depth = null;
21
22    /**
23     * @param  string  $relation
24     * @return $this
25     */
26    public function closes($relation, $callback = null)
27    {
28        $defaultCallback = function (array $models, EloquentCollection $results) {
29            // When the relation has been queried with a max depth,
30            // we don't want the closed relation to be set to null
31            // or empty collection on models that belong to the
32            // last level before the limit.
33            if (null !== $this->depth) {
34                $models = array_filter(
35                    $models,
36                    function ($model) {
37                        $depth = $model->closure->depth ?? 0;
38                        return $depth < $this->depth;
39                    }
40                );
41            }
42
43            return [
44                $models,
45                $results->unique()
46            ];
47        };
48
49        return $this->autoloads(
50            $relation,
51            $callback
52                ? function ($models, $results) use ($callback, $defaultCallback) {
53                    list($models, $results) = $defaultCallback($models, $results);
54                    return $callback($models, $results);
55                }
56                : $defaultCallback
57        );
58    }
59
60    /**
61     * Set the base constraints on the relation query.
62     *
63     * @return void
64     */
65    public function addConstraints()
66    {
67        parent::addConstraints();
68
69        $this
70            ->as('closure')
71            ->using(Closure::class)
72            ->withPivot('depth');
73    }
74
75    /**
76     * Add the constraints for an internal relationship existence query.
77     *
78     * Essentially, these queries compare on column names like whereColumn.
79     *
80     * @param  \Illuminate\Database\Eloquent\Builder<TRelatedModel>  $query
81     * @param  \Illuminate\Database\Eloquent\Builder<TDeclaringModel>  $parentQuery
82     * @param  mixed  $columns
83     * @return \Illuminate\Database\Eloquent\Builder<TRelatedModel>
84     */
85    public function getRelationExistenceQuery(Builder $query, Builder $parentQuery, $columns = ['*'])
86    {
87        $query = parent::getRelationExistenceQuery($query, $parentQuery, $columns);
88
89        $query->macro('maxDepth', function ($query, $depth) {
90            $query->where($this->qualifyPivotColumn('depth'), '<=', $depth);
91        });
92
93        return $query;
94    }
95
96    /**
97     * @deprecated
98     *
99     * @return $this
100     */
101    public function excludingSelf()
102    {
103        return $this->withoutSelf();
104    }
105
106    /**
107     * @deprecated
108     *
109     * @return $this
110     */
111    public function includingSelf()
112    {
113        return $this->withSelf();
114    }
115
116    /**
117     * @deprecated
118     *
119     * @param  int  $depth
120     * @return $this
121     */
122    public function upToDepth($depth)
123    {
124        return $this->maxDepth($depth);
125    }
126
127    /**
128     * @see \Illuminate\Database\Eloquent\Relations\BelongsToMany::wherePivot()
129     *
130     * @param  int  $depth
131     * @return $this
132     */
133    public function maxDepth($depth)
134    {
135        // We'll need the depth again when we match the eager-loaded models:
136        $this->depth = $depth;
137        return $this->wherePivot('depth', '<=', $depth);
138    }
139
140    /**
141     * @param  string  $direction
142     * @return $this
143     */
144    public function orderByDepth($direction = 'asc')
145    {
146        return $this->orderBy($this->table . '.depth', $direction);
147    }
148}