Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
53 / 53
100.00% covered (success)
100.00%
11 / 11
CRAP
100.00% covered (success)
100.00%
1 / 1
BelongsToTree
100.00% covered (success)
100.00%
53 / 53
100.00% covered (success)
100.00%
11 / 11
16
100.00% covered (success)
100.00%
1 / 1
 getTree
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
2
 getTreeDepth
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 scopeWhereIsRoot
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 scopeOnlyRoots
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 scopeWithoutRoots
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 isRoot
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 deleteNode
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
2
 deleteTree
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
3
 graft
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 graftOnto
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 cut
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace Baril\Bonsai\Concerns;
4
5use Baril\Bonsai\Relations\BelongsToManyThroughClosures;
6use Illuminate\Database\Eloquent\Builder;
7
8trait BelongsToTree
9{
10    use HasAncestors;
11    use HasClosures;
12    use HasDescendants;
13    use ManagesClosures;
14
15    /**
16     * @deprecated
17     *
18     * Shortcut method that returns a collection of the tree roots, with their
19     * eager-loaded descendants.
20     *
21     * @param  int|null  $depth
22     * @return \Illuminate\Database\Eloquent\Collection
23     */
24    public static function getTree($depth = null)
25    {
26        return static::query()->onlyRoots()->with([
27            'descendants' => function (BelongsToManyThroughClosures $relation) use ($depth) {
28                if (null !== $depth) {
29                    $relation->maxDepth($depth);
30                }
31            },
32        ])->get();
33    }
34
35    /**
36     * @deprecated
37     *
38     * Return the depth of the tree (0 if the tree is flat).
39     *
40     * @return int
41     */
42    public static function getTreeDepth()
43    {
44        $instance = new static();
45
46        return $instance->newClosureQuery()->max('depth');
47    }
48
49    // =========================================================================
50    // QUERY SCOPES
51    // =========================================================================
52
53    /**
54     * @deprecated Use ->onlyRoots() instead
55     *
56     * @param  \Illuminate\Database\Eloquent\Builder  $query
57     * @param  bool  $bool
58     * @return void
59     */
60    public function scopeWhereIsRoot(Builder $query, $bool = true)
61    {
62        if ($bool) {
63            $this->scopeOnlyRoots($query);
64        } else {
65            $this->scopeWithoutRoots($query);
66        }
67    }
68
69    /**
70     * @param  \Illuminate\Database\Eloquent\Builder  $query
71     * @return void
72     */
73    public function scopeOnlyRoots(Builder $query)
74    {
75        $query->where(
76            $this->getParentForeignKeyName(),
77            '=',
78            null
79        );
80    }
81
82    /**
83     * @param  \Illuminate\Database\Eloquent\Builder  $query
84     * @return void
85     */
86    public function scopeWithoutRoots(Builder $query)
87    {
88        $query->where(
89            $this->getParentForeignKeyName(),
90            '!=',
91            null
92        );
93    }
94
95    // =========================================================================
96    // MODEL METHODS
97    // =========================================================================
98
99    /**
100     * @return bool
101     */
102    public function isRoot()
103    {
104        return $this->getParentKey() === null;
105    }
106
107    /**
108     * @deprecated
109     *
110     * Deletes the model after having attached its children to its parent.
111     *
112     * @return bool|null
113     *
114     * @throws \Exception
115     */
116    public function deleteNode()
117    {
118        if ($this->parent) {
119            $this->parent->children()->saveMany($this->children);
120        } else {
121            $this->children()->get([
122                $this->getKeyName(),
123                $this->getParentForeignKeyName(),
124            ])->each(function ($child) {
125                $child->parent()->dissociate();
126                $child->save();
127            });
128        }
129
130        return $this->delete();
131    }
132
133    /**
134     * @return int
135     */
136    public function deleteTree()
137    {
138        return $this
139            ->descendants()
140            ->withSelf()
141            ->orderByDepth('desc')
142            ->select(array_filter([
143                $this->getKeyName(),
144                $this->usesTimestamps() ? $this->getUpdatedAtColumn() : null,
145                // @todo replace with static::isSoftDeletable()
146                method_exists($this, 'getDeletedAtColumn') ? $this->getDeletedAtColumn() : null,
147            ]))
148            ->cursor()
149            ->map
150            ->delete()
151            ->sum();
152    }
153
154    /**
155     * Attach $newChild to $this.
156     *
157     * @param  static  $newChild
158     * @return $this
159     */
160    public function graft($newChild)
161    {
162        $this->children()->save($newChild);
163
164        return $this;
165    }
166
167    /**
168     * Attach $this to $newParent.
169     *
170     * @param  static  $newParent
171     * @return $this
172     */
173    public function graftOnto($newParent)
174    {
175        $this->parent()->associate($newParent);
176        $this->save();
177
178        return $this;
179    }
180
181    /**
182     * Detach $this from its current parent and make it a new root.
183     *
184     * @return $this
185     */
186    public function cut()
187    {
188        $this->parent()->dissociate();
189        $this->save();
190
191        return $this;
192    }
193}