1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace SimpleCrud; |
4
|
|
|
|
5
|
|
|
use SimpleCrud\Scheme\Scheme; |
6
|
|
|
|
7
|
|
|
/** |
8
|
|
|
* Stores the data of an table row. |
9
|
|
|
*/ |
10
|
|
|
class Row extends AbstractRow |
11
|
|
|
{ |
12
|
|
|
private $values = []; |
13
|
|
|
private $relations = []; |
14
|
|
|
private $changed = false; |
15
|
|
|
|
16
|
|
|
/** |
17
|
|
|
* {@inheritdoc} |
18
|
|
|
*/ |
19
|
|
|
public function __construct(Table $table) |
20
|
|
|
{ |
21
|
|
|
parent::__construct($table); |
22
|
|
|
|
23
|
|
|
foreach ($table->getScheme()['fields'] as $name => $field) { |
24
|
|
|
$this->values[$name] = null; |
25
|
|
|
} |
26
|
|
|
} |
27
|
|
|
|
28
|
|
|
/** |
29
|
|
|
* Debug info. |
30
|
|
|
* |
31
|
|
|
* @return array |
32
|
|
|
*/ |
33
|
|
|
public function __debugInfo() |
34
|
|
|
{ |
35
|
|
|
return [ |
36
|
|
|
'table' => $this->getTable()->getName(), |
37
|
|
|
'values' => $this->values, |
38
|
|
|
]; |
39
|
|
|
} |
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* Return the current cache. |
43
|
|
|
* |
44
|
|
|
* @return array |
45
|
|
|
*/ |
46
|
|
|
public function getCache() |
47
|
|
|
{ |
48
|
|
|
return $this->relations; |
49
|
|
|
} |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* Set a new cache. |
53
|
|
|
* |
54
|
|
|
* @param array $relations |
55
|
|
|
*/ |
56
|
|
|
public function setCache(array $relations) |
57
|
|
|
{ |
58
|
|
|
return $this->relations = $relations; |
59
|
|
|
} |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* Magic method to return properties or load them automatically. |
63
|
|
|
* |
64
|
|
|
* @param string $name |
65
|
|
|
*/ |
66
|
|
|
public function &__get($name) |
67
|
|
|
{ |
68
|
|
|
//It's a field |
69
|
|
|
if (array_key_exists($name, $this->values)) { |
70
|
|
|
return $this->values[$name]; |
71
|
|
|
} |
72
|
|
|
|
73
|
|
|
//It's a relation |
74
|
|
|
if (array_key_exists($name, $this->relations)) { |
75
|
|
|
$return = $this->relations[$name] ?: new NullValue(); |
76
|
|
|
return $return; |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
//It's a localizable field |
80
|
|
|
$language = $this->getDatabase()->getAttribute(SimpleCrud::ATTR_LOCALE); |
81
|
|
|
|
82
|
|
|
if (!is_null($language)) { |
83
|
|
|
$localeName = "{$name}_{$language}"; |
84
|
|
|
|
85
|
|
|
if (array_key_exists($localeName, $this->values)) { |
86
|
|
|
return $this->values[$localeName]; |
87
|
|
|
} |
88
|
|
|
} |
89
|
|
|
|
90
|
|
|
//Load the relation |
91
|
|
|
$scheme = $this->getTable()->getScheme(); |
92
|
|
|
|
93
|
|
|
if (isset($scheme['relations'][$name])) { |
94
|
|
|
$return = call_user_func([$this, $name])->run() ?: new NullValue(); |
95
|
|
|
$this->relations[$name] = $return; |
96
|
|
|
return $return; |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
//Exists as a function |
100
|
|
|
if (method_exists($this, $name)) { |
101
|
|
|
$return = $this->$name(); |
102
|
|
|
return $return; |
103
|
|
|
} |
104
|
|
|
|
105
|
|
|
throw new SimpleCrudException(sprintf('Undefined property "%s"', $name)); |
106
|
|
|
} |
107
|
|
|
|
108
|
|
|
/** |
109
|
|
|
* Magic method to store properties. |
110
|
|
|
* |
111
|
|
|
* @param string $name |
112
|
|
|
* @param mixed $value |
113
|
|
|
*/ |
114
|
|
|
public function __set($name, $value) |
115
|
|
|
{ |
116
|
|
|
//It's a field |
117
|
|
View Code Duplication |
if (array_key_exists($name, $this->values)) { |
|
|
|
|
118
|
|
|
if ($this->values[$name] !== $value) { |
119
|
|
|
$this->changed = true; |
120
|
|
|
} |
121
|
|
|
|
122
|
|
|
return $this->values[$name] = $value; |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
//It's a localizable field |
126
|
|
|
$language = $this->getDatabase()->getAttribute(SimpleCrud::ATTR_LOCALE); |
127
|
|
|
|
128
|
|
|
if (!is_null($language)) { |
129
|
|
|
$localeName = "{$name}_{$language}"; |
130
|
|
|
|
131
|
|
View Code Duplication |
if (array_key_exists($localeName, $this->values)) { |
|
|
|
|
132
|
|
|
if ($this->values[$localeName] !== $value) { |
133
|
|
|
$this->changed = true; |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
return $this->values[$localeName] = $value; |
137
|
|
|
} |
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
//It's a relation |
141
|
|
|
$table = $this->getTable(); |
142
|
|
|
$scheme = $table->getScheme(); |
143
|
|
|
|
144
|
|
|
if (!isset($scheme['relations'][$name])) { |
145
|
|
|
throw new SimpleCrudException(sprintf('Undefined property "%s"', $name)); |
146
|
|
|
} |
147
|
|
|
|
148
|
|
|
$relation = $scheme['relations'][$name][0]; |
149
|
|
|
|
150
|
|
|
//Check types |
151
|
|
|
if ($relation === Scheme::HAS_ONE) { |
152
|
|
|
if ($value !== null && !($value instanceof self)) { |
153
|
|
|
throw new SimpleCrudException(sprintf('Invalid value: %s must be a Row instance or null', $name)); |
154
|
|
|
} |
155
|
|
|
} elseif (!($value instanceof RowCollection)) { |
156
|
|
|
throw new SimpleCrudException(sprintf('Invalid value: %s must be a RowCollection', $name)); |
157
|
|
|
} |
158
|
|
|
|
159
|
|
|
$this->relations[$name] = $value; |
160
|
|
|
} |
161
|
|
|
|
162
|
|
|
/** |
163
|
|
|
* Magic method to check if a property is defined or not. |
164
|
|
|
* |
165
|
|
|
* @param string $name Property name |
166
|
|
|
* |
167
|
|
|
* @return bool |
168
|
|
|
*/ |
169
|
|
|
public function __isset($name) |
170
|
|
|
{ |
171
|
|
|
$language = $this->getDatabase()->getAttribute(SimpleCrud::ATTR_LOCALE); |
172
|
|
|
|
173
|
|
|
if (!is_null($language) && isset($this->values["{$name}_{$language}"])) { |
174
|
|
|
return true; |
175
|
|
|
} |
176
|
|
|
|
177
|
|
|
return isset($this->values[$name]) || isset($this->relations[$name]); |
178
|
|
|
} |
179
|
|
|
|
180
|
|
|
/** |
181
|
|
|
* {@inheritdoc} |
182
|
|
|
*/ |
183
|
|
|
public function toArray($recursive = true, array $bannedEntities = []) |
184
|
|
|
{ |
185
|
|
|
if (!$recursive) { |
186
|
|
|
return $this->values; |
187
|
|
|
} |
188
|
|
|
|
189
|
|
|
$table = $this->getTable(); |
190
|
|
|
|
191
|
|
|
if (!empty($bannedEntities) && in_array($table->getName(), $bannedEntities)) { |
192
|
|
|
return; |
193
|
|
|
} |
194
|
|
|
|
195
|
|
|
$bannedEntities[] = $table->getName(); |
196
|
|
|
$data = $this->values; |
197
|
|
|
|
198
|
|
|
foreach ($this->relations as $name => $value) { |
199
|
|
|
if ($value !== null) { |
200
|
|
|
$data[$name] = $value->toArray(true, $bannedEntities); |
201
|
|
|
} |
202
|
|
|
} |
203
|
|
|
|
204
|
|
|
return $data; |
205
|
|
|
} |
206
|
|
|
|
207
|
|
|
public function edit(array $values) |
208
|
|
|
{ |
209
|
|
|
foreach ($values as $name => $value) { |
210
|
|
|
$this->__set($name, $value); |
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
return $this; |
214
|
|
|
} |
215
|
|
|
|
216
|
|
|
/** |
217
|
|
|
* Saves this row in the database. |
218
|
|
|
* |
219
|
|
|
* @return $this |
220
|
|
|
*/ |
221
|
|
|
public function save() |
222
|
|
|
{ |
223
|
|
|
if ($this->changed) { |
224
|
|
|
if (empty($this->id)) { |
225
|
|
|
$this->id = $this->table->insert() |
|
|
|
|
226
|
|
|
->data($this->values, $this->values) |
227
|
|
|
->run(); |
228
|
|
|
} else { |
229
|
|
|
$this->table->update() |
|
|
|
|
230
|
|
|
->data($this->values, $this->values) |
231
|
|
|
->byId($this->id) |
232
|
|
|
->limit(1) |
233
|
|
|
->run(); |
234
|
|
|
} |
235
|
|
|
|
236
|
|
|
$this->table->cache($this); |
237
|
|
|
} |
238
|
|
|
|
239
|
|
|
return $this; |
240
|
|
|
} |
241
|
|
|
|
242
|
|
|
/** |
243
|
|
|
* Relate this row with other row and save the relation. |
244
|
|
|
* |
245
|
|
|
* @param Row $row |
246
|
|
|
* |
247
|
|
|
* @return $this |
248
|
|
|
*/ |
249
|
|
|
public function relate(Row $row) |
|
|
|
|
250
|
|
|
{ |
251
|
|
|
$table = $this->getTable(); |
252
|
|
|
$relations = $table->getScheme()['relations']; |
253
|
|
|
$rows = []; |
|
|
|
|
254
|
|
|
|
255
|
|
|
foreach (func_get_args() as $row) { |
256
|
|
|
$relationTable = $row->getTable(); |
257
|
|
|
|
258
|
|
|
if (!isset($relations[$relationTable->getName()])) { |
259
|
|
|
throw new SimpleCrudException(sprintf('Invalid relation: %s - %s', $table->getName(), $relationTable->getName())); |
260
|
|
|
} |
261
|
|
|
|
262
|
|
|
$relation = $relations[$relationTable->getName()]; |
263
|
|
|
|
264
|
|
|
if (!isset($relations[$relation[0]])) { |
265
|
|
|
$relations[$relation[0]] = []; |
266
|
|
|
} |
267
|
|
|
|
268
|
|
|
$relations[$relation[0]][] = [ |
269
|
|
|
$relation, |
270
|
|
|
$relationTable, |
271
|
|
|
$row |
272
|
|
|
]; |
273
|
|
|
} |
274
|
|
|
|
275
|
|
|
if (isset($relations[Scheme::HAS_ONE])) { |
276
|
|
|
foreach ($relations[Scheme::HAS_ONE] as $r) { |
277
|
|
|
list($relation, $relationTable, $row) = $r; |
278
|
|
|
|
279
|
|
|
if ($row->id === null) { |
280
|
|
|
$row->save(); |
281
|
|
|
} |
282
|
|
|
|
283
|
|
|
$this->{$relation[1]} = $row->id; |
284
|
|
|
$this->relations[$relationTable->getName()] = $row; |
285
|
|
|
} |
286
|
|
|
|
287
|
|
|
$this->save(); |
288
|
|
|
|
289
|
|
|
foreach ($relations[Scheme::HAS_ONE] as $r) { |
290
|
|
|
list($relation, $relationTable, $row) = $r; |
291
|
|
|
|
292
|
|
|
if ($table->getName() !== $relationTable->getName()) { |
293
|
|
|
$cache = $row->getCache(); |
294
|
|
|
|
295
|
|
|
if (isset($cache[$table->getName()])) { |
296
|
|
|
$cache[$table->getName()][] = $this; |
297
|
|
|
$row->setCache($cache); |
298
|
|
|
} |
299
|
|
|
} |
300
|
|
|
} |
301
|
|
|
} |
302
|
|
|
|
303
|
|
|
if (isset($relations[Scheme::HAS_MANY])) { |
304
|
|
|
if ($this->id === null) { |
305
|
|
|
$this->save(); |
306
|
|
|
} |
307
|
|
|
|
308
|
|
|
foreach ($relations[Scheme::HAS_MANY] as $r) { |
309
|
|
|
list($relation, $relationTable, $row) = $r; |
310
|
|
|
|
311
|
|
|
$row->{$relation[1]} = $this->id; |
312
|
|
|
$row->save(); |
313
|
|
|
|
314
|
|
|
if (isset($this->relations[$relationTable->getName()])) { |
315
|
|
|
$this->relations[$relationTable->getName()][] = $row; |
316
|
|
|
} |
317
|
|
|
|
318
|
|
View Code Duplication |
if ($table->getName() !== $relationTable->getName()) { |
|
|
|
|
319
|
|
|
$cache = $row->getCache(); |
320
|
|
|
$cache[$table->getName()] = $this; |
321
|
|
|
$row->setCache($cache); |
322
|
|
|
} |
323
|
|
|
} |
324
|
|
|
} |
325
|
|
|
|
326
|
|
|
if (isset($relations[Scheme::HAS_MANY_TO_MANY])) { |
327
|
|
|
if ($this->id === null) { |
328
|
|
|
$this->save(); |
329
|
|
|
} |
330
|
|
|
|
331
|
|
|
foreach ($relations[Scheme::HAS_MANY_TO_MANY] as $r) { |
332
|
|
|
list($relation, $relationTable, $row) = $r; |
333
|
|
|
|
334
|
|
|
$bridge = $this->getDatabase()->{$relation[1]}; |
335
|
|
|
|
336
|
|
|
if ($row->id === null) { |
337
|
|
|
$row->save(); |
338
|
|
|
} |
339
|
|
|
|
340
|
|
|
$bridge |
341
|
|
|
->insert() |
342
|
|
|
->duplications() |
343
|
|
|
->data([ |
344
|
|
|
$relation[2] => $this->id, |
345
|
|
|
$relation[3] => $row->id, |
346
|
|
|
]) |
347
|
|
|
->run(); |
348
|
|
|
|
349
|
|
|
if (isset($this->relations[$relationTable->getName()])) { |
350
|
|
|
$this->relations[$relationTable->getName()][] = $row; |
351
|
|
|
} |
352
|
|
|
} |
353
|
|
|
} |
354
|
|
|
|
355
|
|
|
return $this; |
356
|
|
|
} |
357
|
|
|
|
358
|
|
|
/** |
359
|
|
|
* Unrelate this row with other row and save it. |
360
|
|
|
* |
361
|
|
|
* @param Row $row |
362
|
|
|
* |
363
|
|
|
* @return $this |
364
|
|
|
*/ |
365
|
|
|
public function unrelate(Row $row) |
366
|
|
|
{ |
367
|
|
|
$table = $this->getTable(); |
368
|
|
|
$relationTable = $row->getTable(); |
369
|
|
|
$relations = $table->getScheme()['relations']; |
370
|
|
|
|
371
|
|
|
if (!isset($relations[$relationTable->getName()])) { |
372
|
|
|
throw new SimpleCrudException(sprintf('Invalid relation: %s - %s', $table->getName(), $relationTable->getName())); |
373
|
|
|
} |
374
|
|
|
|
375
|
|
|
$relation = $relations[$relationTable->getName()]; |
376
|
|
|
|
377
|
|
|
if ($relation[0] === Scheme::HAS_ONE) { |
378
|
|
|
$row->unrelate($this); |
379
|
|
|
|
380
|
|
|
return $this; |
381
|
|
|
} |
382
|
|
|
|
383
|
|
|
if ($relation[0] === Scheme::HAS_MANY) { |
384
|
|
|
if ($row->{$relation[1]} === $this->id) { |
385
|
|
|
$row->{$relation[1]} = null; |
386
|
|
|
$row->save(); |
387
|
|
|
} |
388
|
|
|
|
389
|
|
|
if (isset($this->relations[$relationTable->getName()])) { |
390
|
|
|
unset($this->relations[$relationTable->getName()][$row->id]); |
391
|
|
|
} |
392
|
|
|
|
393
|
|
View Code Duplication |
if ($table->getName() !== $relationTable->getName()) { |
|
|
|
|
394
|
|
|
$cache = $row->getCache(); |
395
|
|
|
$cache[$table->getName()] = new NullValue(); |
396
|
|
|
$row->setCache($cache); |
397
|
|
|
} |
398
|
|
|
|
399
|
|
|
return $this; |
400
|
|
|
} |
401
|
|
|
|
402
|
|
|
if ($relation[0] === Scheme::HAS_MANY_TO_MANY) { |
403
|
|
|
$bridge = $this->getDatabase()->{$relation[1]}; |
404
|
|
|
|
405
|
|
|
$bridge |
406
|
|
|
->delete() |
407
|
|
|
->by($relation[2], $this->id) |
408
|
|
|
->by($relation[3], $row->id) |
409
|
|
|
->run(); |
410
|
|
|
|
411
|
|
|
unset($this->relations[$relation[1]]); |
412
|
|
|
unset($this->relations[$relationTable->getName()][$row->id]); |
413
|
|
|
|
414
|
|
|
$cache = $row->getCache(); |
415
|
|
|
unset($cache[$relation[1]]); |
416
|
|
|
unset($cache[$table->getName()][$this->id]); |
417
|
|
|
$row->setCache($cache); |
418
|
|
|
} |
419
|
|
|
} |
420
|
|
|
|
421
|
|
|
/** |
422
|
|
|
* Unrelate this row with all rows of a specific table. |
423
|
|
|
* |
424
|
|
|
* @param Table $relationTable |
425
|
|
|
* |
426
|
|
|
* @return $this |
427
|
|
|
*/ |
428
|
|
|
public function unrelateAll(Table $relationTable) |
429
|
|
|
{ |
430
|
|
|
$table = $this->getTable(); |
431
|
|
|
$relations = $table->getScheme()['relations']; |
432
|
|
|
|
433
|
|
|
if (!isset($relations[$relationTable->getName()])) { |
434
|
|
|
throw new SimpleCrudException(sprintf('Invalid relation: %s - %s', $table->getName(), $relationTable->getName())); |
435
|
|
|
} |
436
|
|
|
|
437
|
|
|
$relation = $relations[$relationTable->getName()]; |
438
|
|
|
|
439
|
|
|
if ($relation[0] === Scheme::HAS_ONE) { |
440
|
|
|
$this->{$relation[1]} = null; |
441
|
|
|
$this->relations[$relationTable->getName()] = new NullValue(); |
442
|
|
|
|
443
|
|
|
return $this->save(); |
444
|
|
|
} |
445
|
|
|
|
446
|
|
|
if ($relation[0] === Scheme::HAS_MANY) { |
447
|
|
|
$relationTable->update() |
|
|
|
|
448
|
|
|
->data([ |
449
|
|
|
$relation[1] => null, |
450
|
|
|
]) |
451
|
|
|
->by($relation[1], $this->id) |
452
|
|
|
->run(); |
453
|
|
|
|
454
|
|
|
$this->relations[$relationTable->getName()] = $relationTable->createCollection(); |
455
|
|
|
|
456
|
|
|
return $this; |
457
|
|
|
} |
458
|
|
|
|
459
|
|
|
if ($relation[0] === Scheme::HAS_MANY_TO_MANY) { |
460
|
|
|
$bridge = $this->getDatabase()->{$relation[1]}; |
461
|
|
|
|
462
|
|
|
$bridge |
463
|
|
|
->delete() |
464
|
|
|
->by($relation[2], $this->id) |
465
|
|
|
->run(); |
466
|
|
|
|
467
|
|
|
$this->relations[$bridge->getName()] = $bridge->createCollection(); |
468
|
|
|
$this->relations[$relationTable->getName()] = $relationTable->createCollection(); |
469
|
|
|
} |
470
|
|
|
} |
471
|
|
|
} |
472
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.