Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
59 / 59
100.00% covered (success)
100.00%
15 / 15
CRAP
100.00% covered (success)
100.00%
1 / 1
Closure
100.00% covered (success)
100.00%
59 / 59
100.00% covered (success)
100.00%
15 / 15
24
100.00% covered (success)
100.00%
1 / 1
 getAlias
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 newInstance
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 setParentModel
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setRelatedModel
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setRawAttributes
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 hydrateRelatedKey
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
3
 related
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 scopeAs
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 scopeSelfCrossJoin
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 scopeSelfJoin
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
3
 scopeWhereAncestor
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 scopeWhereDescendant
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 scopeWhereDepth
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 scopeWithoutSelf
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 parseIds
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
4
1<?php
2
3namespace Baril\Bonsai;
4
5use Illuminate\Database\Eloquent\Builder;
6use Illuminate\Database\Eloquent\Model;
7use Illuminate\Database\Eloquent\Relations\Pivot;
8
9class Closure extends Pivot
10{
11    /**
12     * Added for compatibility with older Laravel versions.
13     *
14     * @todo remove in v4
15     *
16     * @var \Illuminate\Database\Eloquent\Model
17     */
18    public $pivotRelated;
19
20    /**
21     * Alias for the query builder instance that references
22     * this model.
23     *
24     * @var string
25     */
26    protected $as;
27
28    /**
29     * @return string
30     */
31    protected function getAlias()
32    {
33        return $this->as ?? $this->getTable();
34    }
35
36    /** @inheritDoc */
37    public function newInstance($attributes = [], $exists = false)
38    {
39        return parent::newInstance($attributes, $exists)
40            ->setPivotKeys($this->foreignKey, $this->relatedKey)
41            ->setParentModel($this->pivotParent)
42            ->setRelatedModel($this->pivotRelated)
43            ->hydrateRelatedKey($attributes);
44    }
45
46    /**
47     * Set the parent model of the relationship.
48     *
49     * @param  \Illuminate\Database\Eloquent\Model  $parent
50     * @return $this
51     */
52    public function setParentModel(Model $parent)
53    {
54        $this->pivotParent = $parent;
55
56        return $this;
57    }
58
59    /**
60     * Set the related model of the relationship.
61     * Added for compatibility with older Laravel versions.
62     *
63     * @todo remove in v4
64     *
65     * @param  \Illuminate\Database\Eloquent\Model|null  $related
66     * @return $this
67     */
68    public function setRelatedModel(?Model $related = null)
69    {
70        $this->pivotRelated = $related;
71
72        return $this;
73    }
74
75    /** @inheritDoc */
76    public function setRawAttributes(array $attributes, $sync = false)
77    {
78        return parent::setRawAttributes($attributes, $sync)
79            ->hydrateRelatedKey($attributes);
80    }
81
82    /**
83     * Hydrate the related model id when the closure is hydrated.
84     *
85     * @param  array  $attributes
86     * @return $this
87     */
88    protected function hydrateRelatedKey($attributes)
89    {
90        if (
91            array_key_exists($this->relatedKey, $attributes)
92            && $this->pivotRelated->getKey() != $attributes[$this->relatedKey]
93        ) {
94            $this->pivotRelated = $this->pivotRelated
95                ->newInstance([], true)
96                ->setRawAttributes([
97                    $this->pivotRelated->getKeyName() => $attributes[$this->relatedKey]
98                ], true);
99        }
100
101        return $this;
102    }
103
104    /**
105     * Belongs-to relation to the related model.
106     *
107     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
108     */
109    public function related()
110    {
111        return $this->belongsTo(
112            get_class($this->pivotRelated),
113            $this->getRelatedKey()
114        );
115    }
116
117    /**
118     * @param  \Illuminate\Database\Eloquent\Builder  $query
119     * @param  string  $as
120     * @return  void
121     */
122    public function scopeAs(Builder $query, $as)
123    {
124        $this->as = $as;
125
126        $from = $query->getQuery()->from;
127        $table = preg_split('/\s+as\s+/i', $from)[0];
128
129        $query->getQuery()->from = "$table as $as";
130    }
131
132    /**
133     * @param  \Illuminate\Database\Eloquent\Builder  $query
134     * @param  string  $as
135     * @return  void
136     */
137    public function scopeSelfCrossJoin(Builder $query, $as)
138    {
139        $table = $this->getTable();
140
141        $query->crossJoin("$table as $as");
142    }
143
144    /**
145     * @param  \Illuminate\Database\Eloquent\Builder  $query
146     * @param  string  $as
147     * @param  string  $first
148     * @param  string  $second
149     * @return  void
150     */
151    public function scopeSelfJoin(Builder $query, $as, $first = null, $second = null)
152    {
153        $table = $this->getTable();
154
155        $first = $first ?? $this->getRelatedKey();
156        $second = $second ?? $first;
157
158        if (false === strstr($first, '.')) {
159            $first = $this->getAlias() . ".$first";
160        }
161        if (false === strstr($second, '.')) {
162            $second = "$as.$second";
163        }
164
165        $query->join(
166            "$table as $as",
167            $first,
168            '=',
169            $second
170        );
171    }
172
173    /**
174     * @param  \Illuminate\Database\Eloquent\Builder  $query
175     * @param  mixed|\Illuminate\Database\Eloquent\Model  $node
176     * @return  void
177     */
178    public function scopeWhereAncestor(Builder $query, $node)
179    {
180        $value = $this->parseIds($node);
181
182        $method = is_array($value) ? 'whereIn' : 'where';
183
184        $query->$method('ancestor_id', $value);
185    }
186
187    /**
188     * @param  \Illuminate\Database\Eloquent\Builder  $query
189     * @param  mixed|\Illuminate\Database\Eloquent\Model  $node
190     * @return  void
191     */
192    public function scopeWhereDescendant(Builder $query, $node)
193    {
194        $value = $this->parseIds($node);
195
196        $method = is_array($value) ? 'whereIn' : 'where';
197
198        $query->$method('descendant_id', $value);
199    }
200
201    /**
202     * @param  \Illuminate\Database\Eloquent\Builder  $query
203     * @param  string  $operator
204     * @param  int  $value
205     * @return  void
206     */
207    public function scopeWhereDepth(Builder $query, $operator, $value)
208    {
209        $query->where('depth', $operator, $value);
210    }
211
212    /**
213     * @param  \Illuminate\Database\Eloquent\Builder  $query
214     * @return  void
215     */
216    public function scopeWithoutSelf(Builder $query)
217    {
218        $query->where('depth', '>', 0);
219    }
220
221    /**
222     * Get the ID or IDs from the given mixed value.
223     *
224     * @param  mixed  $value
225     * @return mixed
226     */
227    protected function parseIds($value)
228    {
229        if ($value instanceof Model) {
230            return $value->getKey();
231        }
232
233        if (!is_iterable($value)) {
234            return $value;
235        }
236
237        $parsedIds = [];
238        foreach ($value as $id) {
239            $parsedIds[] = $this->parseIds($id);
240        }
241
242        return $parsedIds;
243    }
244}