1 | <?php |
||
40 | class ParentChildTrashHandlers |
||
41 | { |
||
42 | |||
43 | /** |
||
44 | * Register event handlers for parent of parent-child relation. |
||
45 | * |
||
46 | * @param AnnotatedInterface|string $parent |
||
47 | * @param string $childClass |
||
48 | */ |
||
49 | 1 | public function registerParent($parent, $childClass) |
|
50 | { |
||
51 | 1 | if (!ClassChecker::exists($childClass)) |
|
52 | { |
||
53 | throw new UnexpectedValueException(sprintf('Class `%s` not found', $childClass)); |
||
54 | } |
||
55 | // Delete all of this child items after removing from trash |
||
56 | 1 | $beforeDelete = function(ModelEvent $event) use($parent, $childClass) |
|
57 | { |
||
58 | 1 | $model = $event->sender; |
|
59 | 1 | $event->isValid = true; |
|
60 | |||
61 | 1 | if (is_a($model, $parent)) |
|
62 | { |
||
63 | $child = new $childClass; |
||
64 | // Ensure owner |
||
65 | if($child instanceof OwneredInterface) |
||
66 | { |
||
67 | $child->setOwner($model); |
||
68 | } |
||
69 | $criteria = new Criteria(null, $child); |
||
70 | $criteria->parentId = $this->getPk($model); |
||
71 | |||
72 | $event->isValid = $child->deleteAll($criteria); |
||
73 | } |
||
74 | 1 | return $event->isValid; |
|
75 | 1 | }; |
|
76 | 1 | $beforeDelete->bindTo($this); |
|
77 | 1 | Event::on($parent, EntityManagerInterface::EventBeforeDelete, $beforeDelete); |
|
78 | |||
79 | // Trash all child items from parent item |
||
80 | 1 | $afterTrash = function(ModelEvent $event)use($parent, $childClass) |
|
81 | { |
||
82 | 1 | $model = $event->sender; |
|
83 | 1 | $event->isValid = true; |
|
84 | 1 | if (is_a($model, $parent)) |
|
85 | { |
||
86 | 1 | $child = new $childClass; |
|
87 | // Ensure owner |
||
88 | 1 | if($child instanceof OwneredInterface) |
|
89 | { |
||
90 | 1 | $child->setOwner($model); |
|
91 | } |
||
92 | 1 | $criteria = new Criteria(null, $child); |
|
93 | 1 | $criteria->parentId = $this->getPk($model); |
|
94 | |||
95 | 1 | $items = $child->findAll($criteria); |
|
96 | |||
97 | // No items found, so skip |
||
98 | 1 | if (empty($items)) |
|
99 | { |
||
100 | $event->isValid = true; |
||
101 | return $event->isValid; |
||
102 | } |
||
103 | |||
104 | // Trash in loop all items |
||
105 | 1 | foreach ($items as $item) |
|
106 | { |
||
107 | // Ensure owner |
||
108 | 1 | if($item instanceof OwneredInterface) |
|
109 | { |
||
110 | 1 | $item->setOwner($model); |
|
111 | } |
||
112 | 1 | if (!$item->trash()) |
|
113 | { |
||
114 | $event->isValid = false; |
||
115 | 1 | return $event->isValid; |
|
116 | } |
||
117 | } |
||
118 | } |
||
119 | 1 | return $event->isValid; |
|
120 | 1 | }; |
|
121 | 1 | $afterTrash->bindTo($this); |
|
122 | 1 | Event::on($parent, TrashInterface::EventAfterTrash, $afterTrash); |
|
123 | |||
124 | // Restore all child items from parent, but only those after it was trashed. |
||
125 | // This will keep previously trashed items in trash |
||
126 | 1 | $afterRestore = function(RestoreEvent $event)use($parent, $childClass) |
|
127 | { |
||
128 | 1 | $model = $event->sender; |
|
129 | 1 | if (is_a($model, $parent)) |
|
130 | { |
||
131 | 1 | $child = new $childClass; |
|
132 | // Ensure owner |
||
133 | 1 | if($child instanceof OwneredInterface) |
|
134 | { |
||
135 | 1 | $child->setOwner($model); |
|
136 | } |
||
137 | 1 | $trash = $event->getTrash(); |
|
138 | 1 | $criteria = new Criteria(null, $trash); |
|
139 | |||
140 | // Conditions decorator do not work with dots so sanitize manually. |
||
141 | 1 | $s = new Sanitizer($child); |
|
142 | |||
143 | 1 | $id = $s->write('parentId', $this->getPk($model)); |
|
144 | 1 | $criteria->addCond('data.parentId', '==', $id); |
|
145 | |||
146 | // Restore only child items trashed when parent was trashed. |
||
147 | // Skip earlier items |
||
148 | 1 | assert(isset($trash->createDate), sprintf('When implementing `%s`, `createDate` field is required and must be set to date of deletion', TrashInterface::class)); |
|
149 | 1 | $criteria->addCond('createDate', 'gte', $trash->createDate); |
|
150 | |||
151 | 1 | $trashedItems = $trash->findAll($criteria); |
|
152 | 1 | if (empty($trashedItems)) |
|
153 | { |
||
154 | $event->isValid = true; |
||
155 | return $event->isValid; |
||
156 | } |
||
157 | |||
158 | // Restore all items |
||
159 | 1 | $restored = []; |
|
160 | 1 | foreach ($trashedItems as $trashedItem) |
|
161 | { |
||
162 | // Ensure owner |
||
163 | 1 | if($trashedItem instanceof OwneredInterface) |
|
164 | { |
||
165 | 1 | $trashedItem->setOwner($model); |
|
166 | } |
||
167 | 1 | $restored[] = (int) $trashedItem->restore(); |
|
168 | } |
||
169 | 1 | if(array_sum($restored) !== count($restored)) |
|
170 | { |
||
171 | $event->isValid = false; |
||
172 | return $event->isValid; |
||
173 | } |
||
174 | } |
||
175 | 1 | $event->isValid = true; |
|
176 | 1 | return $event->isValid; |
|
177 | 1 | }; |
|
178 | 1 | $afterRestore->bindTo($this); |
|
179 | 1 | Event::on($parent, TrashInterface::EventAfterRestore, $afterRestore); |
|
180 | 1 | } |
|
181 | |||
182 | /** |
||
183 | * Register event handlers for child item of parent-child relation. |
||
184 | * |
||
185 | * @param AnnotatedInterface|string $child |
||
186 | * @param string $parentClass |
||
187 | * @throws UnexpectedValueException |
||
188 | */ |
||
189 | 1 | public function registerChild($child, $parentClass) |
|
190 | { |
||
191 | 1 | assert(ClassChecker::exists($parentClass), new UnexpectedValueException(sprintf('Class `%s` not found', $parentClass))); |
|
192 | |||
193 | // Prevent restoring item if parent does not exists |
||
194 | 1 | $beforeRestore = function(ModelEvent $event)use($child, $parentClass) |
|
195 | { |
||
196 | 1 | $model = $event->sender; |
|
197 | |||
198 | 1 | if (is_a($model, $child)) |
|
199 | { |
||
200 | 1 | $parent = new $parentClass; |
|
201 | 1 | $criteria = new Criteria(null, $parent); |
|
202 | 1 | assert(isset($model->parentId)); |
|
203 | 1 | $criteria->_id = $model->parentId; |
|
204 | 1 | if (!$parent->exists($criteria)) |
|
205 | { |
||
206 | 1 | $event->isValid = false; |
|
207 | 1 | return $event->isValid; |
|
208 | } |
||
209 | } |
||
210 | 1 | $event->isValid = true; |
|
211 | 1 | return $event->isValid; |
|
212 | 1 | }; |
|
213 | 1 | Event::on($child, TrashInterface::EventBeforeRestore, $beforeRestore); |
|
214 | 1 | } |
|
215 | |||
216 | 1 | private function getPk(AnnotatedInterface $model) |
|
222 | |||
223 | } |
||
224 |