Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
84.85% |
56 / 66 |
|
28.57% |
2 / 7 |
CRAP | |
0.00% |
0 / 1 |
DeleteMapper | |
84.85% |
56 / 66 |
|
28.57% |
2 / 7 |
34.34 | |
0.00% |
0 / 1 |
delete | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
execute | |
66.67% |
2 / 3 |
|
0.00% |
0 / 1 |
3.33 | |||
executeDelete | |
88.89% |
8 / 9 |
|
0.00% |
0 / 1 |
2.01 | |||
deleteModel | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
2 | |||
deleteSingleRelation | |
83.33% |
10 / 12 |
|
0.00% |
0 / 1 |
5.12 | |||
deleteHasMany | |
75.00% |
15 / 20 |
|
0.00% |
0 / 1 |
12.89 | |||
deleteRelationTable | |
92.31% |
12 / 13 |
|
0.00% |
0 / 1 |
7.02 |
1 | <?php |
2 | /** |
3 | * Jingga |
4 | * |
5 | * PHP Version 8.1 |
6 | * |
7 | * @package phpOMS\DataStorage\Database\Mapper |
8 | * @copyright Dennis Eichhorn |
9 | * @license OMS License 2.0 |
10 | * @version 1.0.0 |
11 | * @link https://jingga.app |
12 | */ |
13 | declare(strict_types=1); |
14 | |
15 | namespace phpOMS\DataStorage\Database\Mapper; |
16 | |
17 | use phpOMS\DataStorage\Database\Query\Builder; |
18 | |
19 | /** |
20 | * Delete mapper (DELETE). |
21 | * |
22 | * @package phpOMS\DataStorage\Database\Mapper |
23 | * @license OMS License 2.0 |
24 | * @link https://jingga.app |
25 | * @since 1.0.0 |
26 | */ |
27 | final class DeleteMapper extends DataMapperAbstract |
28 | { |
29 | /** |
30 | * Get delete mapper |
31 | * |
32 | * @return self |
33 | * |
34 | * @since 1.0.0 |
35 | */ |
36 | public function delete() : self |
37 | { |
38 | $this->type = MapperType::DELETE; |
39 | |
40 | return $this; |
41 | } |
42 | |
43 | /** |
44 | * Execute mapper |
45 | * |
46 | * @param mixed ...$options Options to pass to the selete mapper |
47 | * |
48 | * @return mixed |
49 | * |
50 | * @since 1.0.0 |
51 | */ |
52 | public function execute(mixed ...$options) : mixed |
53 | { |
54 | switch($this->type) { |
55 | case MapperType::DELETE: |
56 | /** @var object[] ...$options */ |
57 | return $this->executeDelete(...$options); |
58 | default: |
59 | return null; |
60 | } |
61 | } |
62 | |
63 | /** |
64 | * Execute mapper |
65 | * |
66 | * @param object $obj Object to delete |
67 | * |
68 | * @return mixed |
69 | * |
70 | * @since 1.0.0 |
71 | */ |
72 | public function executeDelete(object $obj) : mixed |
73 | { |
74 | $refClass = new \ReflectionClass($obj); |
75 | $objId = $this->mapper::getObjectId($obj); |
76 | |
77 | if (empty($objId)) { |
78 | return null; |
79 | } |
80 | |
81 | $this->deleteSingleRelation($obj, $refClass, $this->mapper::BELONGS_TO); |
82 | $this->deleteHasMany($refClass, $obj, $objId); |
83 | $this->deleteModel($objId); |
84 | $this->deleteSingleRelation($obj, $refClass, $this->mapper::OWNS_ONE); |
85 | |
86 | return $objId; |
87 | } |
88 | |
89 | /** |
90 | * Delete model |
91 | * |
92 | * @param mixed $objId Object id to delete |
93 | * |
94 | * @return void |
95 | * |
96 | * @since 1.0.0 |
97 | */ |
98 | private function deleteModel(mixed $objId) : void |
99 | { |
100 | $query = new Builder($this->db); |
101 | $query->delete() |
102 | ->from($this->mapper::TABLE) |
103 | ->where($this->mapper::TABLE . '.' . $this->mapper::PRIMARYFIELD, '=', $objId); |
104 | |
105 | $sth = $this->db->con->prepare($query->toSql()); |
106 | if ($sth !== false) { |
107 | $sth->execute(); |
108 | } |
109 | } |
110 | |
111 | /** |
112 | * Delete ownsOne, belongsTo relations |
113 | * |
114 | * @param object $obj Object to delete |
115 | * @param \ReflectionClass $refClass Reflection of object to delete |
116 | * @param array $relation Relation data (e.g. ::BELONGS_TO, ::OWNS_ONE) |
117 | * |
118 | * @return void |
119 | * |
120 | * @since 1.0.0 |
121 | */ |
122 | private function deleteSingleRelation(object $obj, \ReflectionClass $refClass, array $relation) : void |
123 | { |
124 | if (empty($relation)) { |
125 | return; |
126 | } |
127 | |
128 | foreach ($relation as $member => $relData) { |
129 | if (!isset($this->with[$member])) { |
130 | continue; |
131 | } |
132 | |
133 | /** @var class-string<DataMapperFactory> $mapper */ |
134 | $mapper = $relData['mapper']; |
135 | |
136 | /** @var self $relMapper */ |
137 | $relMapper = $this->createRelationMapper($mapper::delete(db: $this->db), $member); |
138 | $relMapper->depth = $this->depth + 1; |
139 | |
140 | $refProp = $refClass->getProperty($member); |
141 | if (!$refProp->isPublic()) { |
142 | $relMapper->execute($refProp->getValue($obj)); |
143 | } else { |
144 | $relMapper->execute($obj->{$member}); |
145 | } |
146 | } |
147 | } |
148 | |
149 | /** |
150 | * Delete hasMany |
151 | * |
152 | * @param \ReflectionClass $refClass Reflection of object to delete |
153 | * @param object $obj Object to delete |
154 | * @param mixed $objId Object id to delete |
155 | * |
156 | * @return void |
157 | * |
158 | * @since 1.0.0 |
159 | */ |
160 | private function deleteHasMany(\ReflectionClass $refClass, object $obj, mixed $objId) : void |
161 | { |
162 | if (empty($this->mapper::HAS_MANY)) { |
163 | return; |
164 | } |
165 | |
166 | foreach ($this->mapper::HAS_MANY as $member => $rel) { |
167 | // always |
168 | if (!isset($this->with[$member]) && !isset($rel['external'])) { |
169 | continue; |
170 | } |
171 | |
172 | $objIds = []; |
173 | $refProp = $refClass->getProperty($member); |
174 | $values = $refProp->isPublic() ? $obj->{$member} : $refProp->getValue($obj); |
175 | |
176 | if (!\is_array($values)) { |
177 | // conditionals |
178 | continue; |
179 | } |
180 | |
181 | /** @var class-string<DataMapperFactory> $mapper */ |
182 | $mapper = $this->mapper::HAS_MANY[$member]['mapper']; |
183 | |
184 | foreach ($values as $key => $value) { |
185 | if (!\is_object($value)) { |
186 | // Is scalar => already in database |
187 | $objIds[$key] = $value; |
188 | |
189 | continue; |
190 | } |
191 | |
192 | $objIds[$key] = $mapper::getObjectId($value); |
193 | } |
194 | |
195 | // delete relation tables |
196 | if (isset($rel['external'])) { |
197 | $this->deleteRelationTable($member, $objIds, $objId); |
198 | } else { |
199 | // only delete related obj if it is NOT in a relation table |
200 | // if it is not in a relation table it must be directly related |
201 | // this means it CAN ONLY be related to this object and not others |
202 | foreach ($objIds as $id) { |
203 | $mapper::delete(db: $this->db)->execute($id); |
204 | } |
205 | } |
206 | } |
207 | } |
208 | |
209 | /** |
210 | * Delete has many relations if the relation is handled in a relation table |
211 | * |
212 | * @param string $member Property which contains the has many models |
213 | * @param array $objIds Objects which are related to the parent object |
214 | * @param mixed $objId Parent object id |
215 | * |
216 | * @return void |
217 | * |
218 | * @since 1.0.0 |
219 | */ |
220 | public function deleteRelationTable(string $member, array $objIds = null, mixed $objId) : void |
221 | { |
222 | if ((empty($objIds) && $objIds !== null) |
223 | || $this->mapper::HAS_MANY[$member]['table'] === $this->mapper::TABLE |
224 | || $this->mapper::HAS_MANY[$member]['table'] === $this->mapper::HAS_MANY[$member]['mapper']::TABLE |
225 | ) { |
226 | return; |
227 | } |
228 | |
229 | $relQuery = new Builder($this->db); |
230 | $relQuery->delete() |
231 | ->from($this->mapper::HAS_MANY[$member]['table']) |
232 | ->where($this->mapper::HAS_MANY[$member]['table'] . '.' . $this->mapper::HAS_MANY[$member]['self'], '=', $objId); |
233 | |
234 | if ($objIds !== null) { |
235 | $relQuery->where($this->mapper::HAS_MANY[$member]['table'] . '.' . $this->mapper::HAS_MANY[$member]['external'], 'in', $objIds); |
236 | } |
237 | |
238 | $sth = $this->db->con->prepare($relQuery->toSql()); |
239 | if ($sth !== false) { |
240 | $sth->execute(); |
241 | } |
242 | } |
243 | } |