1: <?php
2:
3: namespace Baril\Bonsai\Concerns;
4:
5: use Baril\Bonsai\Relations\BelongsToManyThroughClosures;
6: use Illuminate\Database\Eloquent\Builder;
7:
8: trait 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: }
194: