1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Starlit Db. |
4
|
|
|
* |
5
|
|
|
* @copyright Copyright (c) 2016 Starweb AB |
6
|
|
|
* @license BSD 3-Clause |
7
|
|
|
*/ |
8
|
|
|
|
9
|
|
|
namespace Starlit\Db; |
10
|
|
|
|
11
|
|
|
use Starlit\Utils\Str; |
12
|
|
|
use Starlit\Utils\Arr; |
13
|
|
|
|
14
|
|
|
/** |
15
|
|
|
* Abstract class to model a single database row into an object. |
16
|
|
|
* |
17
|
|
|
* @author Andreas Nilsson <http://github.com/jandreasn> |
18
|
|
|
*/ |
19
|
|
|
abstract class AbstractDbEntity implements \Serializable |
20
|
|
|
{ |
21
|
|
|
/** |
22
|
|
|
* The database table name (meant to be overridden). |
23
|
|
|
* |
24
|
|
|
* @var string |
25
|
|
|
*/ |
26
|
|
|
protected static $dbTableName; |
27
|
|
|
|
28
|
|
|
/** |
29
|
|
|
* Entity's database properties and their attributes (meant to be overridden). |
30
|
|
|
* Example format: |
31
|
|
|
* |
32
|
|
|
* $dbProperties = [ |
33
|
|
|
* 'productId' => ['type' => 'int'], |
34
|
|
|
* 'otherId' => ['type' => 'int', 'required' => true,], |
35
|
|
|
* 'name' => ['type' => 'string', 'maxLength' => 10, 'required' => true, 'default' => 'Some name'], |
36
|
|
|
* ]; |
37
|
|
|
* |
38
|
|
|
* 'type' => 'int' Corresponding PHP type (required). |
39
|
|
|
* 'required' => true The value have to be set (not '', null, false) |
40
|
|
|
* 'nonEmpty' => true The value should not be empty ('', 0, null) |
41
|
|
|
* |
42
|
|
|
* Properties correspond to database table's columns but words are |
43
|
|
|
* camel cased instead of separated with underscore (_) as in the database. |
44
|
|
|
* |
45
|
|
|
* @var array |
46
|
|
|
*/ |
47
|
|
|
protected static $dbProperties = []; |
48
|
|
|
|
49
|
|
|
/** |
50
|
|
|
* Object database field name that is used for primary key (meant to be overridden). |
51
|
|
|
* Should be camel cased as it maps to the dbFields array. |
52
|
|
|
* |
53
|
|
|
* @var string|array |
54
|
|
|
*/ |
55
|
|
|
protected static $primaryDbPropertyKey; |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* @var array |
59
|
|
|
*/ |
60
|
|
|
private static $cachedDefaultDbData = []; |
61
|
|
|
|
62
|
|
|
/** |
63
|
|
|
* @var array |
64
|
|
|
*/ |
65
|
|
|
private static $cachedDbPropertyNames; |
66
|
|
|
|
67
|
|
|
/** |
68
|
|
|
* @var array |
69
|
|
|
*/ |
70
|
|
|
private static $cachedDbFieldNames; |
71
|
|
|
|
72
|
|
|
/** |
73
|
|
|
* @var array |
74
|
|
|
*/ |
75
|
|
|
private static $typeDefaults = [ |
76
|
|
|
'string' => '', |
77
|
|
|
'int' => 0, |
78
|
|
|
'float' => 0.0, |
79
|
|
|
'bool' => false, |
80
|
|
|
'dateTime' => null, |
81
|
|
|
]; |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* Database row data with field names and their values. |
85
|
|
|
* |
86
|
|
|
* @var array |
87
|
|
|
*/ |
88
|
|
|
private $dbData = []; |
89
|
|
|
|
90
|
|
|
/** |
91
|
|
|
* Database fields that has had their value modified since init/load. |
92
|
|
|
* |
93
|
|
|
* @var array |
94
|
|
|
*/ |
95
|
|
|
private $modifiedDbProperties = []; |
96
|
|
|
|
97
|
|
|
/** |
98
|
|
|
* @var bool |
99
|
|
|
*/ |
100
|
|
|
private $deleteFromDbOnSave = false; |
101
|
|
|
|
102
|
|
|
/** |
103
|
|
|
* @var bool |
104
|
|
|
*/ |
105
|
|
|
private $deleted = false; |
106
|
|
|
|
107
|
|
|
/** |
108
|
|
|
* @var bool |
109
|
|
|
*/ |
110
|
|
|
private $forceDbInsertOnSave = false; |
111
|
|
|
|
112
|
|
|
/** |
113
|
|
|
* Constructor. |
114
|
|
|
* |
115
|
|
|
* @param mixed $primaryDbValueOrRowData |
116
|
|
|
*/ |
117
|
67 |
|
public function __construct($primaryDbValueOrRowData = null) |
118
|
|
|
{ |
119
|
67 |
|
self::checkStaticProperties(); |
120
|
|
|
|
121
|
|
|
// Set default values |
122
|
66 |
|
$this->dbData = $this->getDefaultDbData(); |
123
|
|
|
|
124
|
|
|
// Override default values with provided values |
125
|
66 |
|
if ($primaryDbValueOrRowData !== null) { |
126
|
13 |
|
$this->setPrimaryDbValueOrRowData($primaryDbValueOrRowData); |
127
|
13 |
|
} |
128
|
66 |
|
} |
129
|
|
|
|
130
|
|
|
/** |
131
|
|
|
* Make sure that class has all necessary static properties set. |
132
|
|
|
*/ |
133
|
67 |
|
private static function checkStaticProperties() |
134
|
|
|
{ |
135
|
67 |
|
static $checkedClasses = []; |
136
|
67 |
|
if (!in_array(static::class, $checkedClasses)) { |
137
|
8 |
|
if (empty(static::$dbTableName) |
138
|
7 |
|
|| empty(static::$dbProperties) |
139
|
7 |
|
|| empty(static::$primaryDbPropertyKey) |
140
|
7 |
|
|| (is_scalar(static::$primaryDbPropertyKey) |
141
|
7 |
|
&& !isset(static::$dbProperties[static::$primaryDbPropertyKey]['type'])) |
142
|
7 |
|
|| (is_array(static::$primaryDbPropertyKey) |
143
|
7 |
|
&& !Arr::allIn(static::$primaryDbPropertyKey, array_keys(static::$dbProperties))) |
144
|
8 |
|
) { |
145
|
1 |
|
throw new \LogicException("All db entity's static properties not set"); |
146
|
|
|
} |
147
|
7 |
|
$checkedClasses[] = static::class; |
148
|
7 |
|
} |
149
|
66 |
|
} |
150
|
|
|
|
151
|
|
|
/** |
152
|
|
|
* @param mixed $primaryDbValueOrRowData |
153
|
|
|
*/ |
154
|
13 |
|
public function setPrimaryDbValueOrRowData($primaryDbValueOrRowData = null) |
155
|
|
|
{ |
156
|
|
|
// Row data would ba an associative array (not sequential, that would indicate a multi column primary key) |
157
|
13 |
|
if (is_array($primaryDbValueOrRowData) && !isset($primaryDbValueOrRowData[0])) { |
158
|
1 |
|
$this->setDbDataFromRow($primaryDbValueOrRowData); |
159
|
1 |
|
} else { |
160
|
12 |
|
$this->setPrimaryDbValue($primaryDbValueOrRowData); |
161
|
|
|
} |
162
|
13 |
|
} |
163
|
|
|
|
164
|
|
|
/** |
165
|
|
|
* Get all default database values. |
166
|
|
|
* |
167
|
|
|
* @return array |
168
|
|
|
*/ |
169
|
66 |
|
public function getDefaultDbData() |
170
|
|
|
{ |
171
|
66 |
|
$class = get_called_class(); |
172
|
66 |
|
if (!isset(self::$cachedDefaultDbData[$class])) { |
173
|
7 |
|
self::$cachedDefaultDbData[$class] = []; |
174
|
7 |
|
foreach (array_keys(static::$dbProperties) as $propertyName) { |
175
|
7 |
|
self::$cachedDefaultDbData[$class][$propertyName] = $this->getDefaultDbPropertyValue($propertyName); |
176
|
7 |
|
} |
177
|
7 |
|
} |
178
|
|
|
|
179
|
66 |
|
return self::$cachedDefaultDbData[$class]; |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
/** |
183
|
|
|
* Get default db value (can be overridden if non static default values need to be used). |
184
|
|
|
* |
185
|
|
|
* @param string $propertyName |
186
|
|
|
* @return mixed |
187
|
|
|
*/ |
188
|
7 |
|
public function getDefaultDbPropertyValue($propertyName) |
189
|
|
|
{ |
190
|
|
|
// A default value is set |
191
|
7 |
|
if (array_key_exists('default', static::$dbProperties[$propertyName])) { |
192
|
5 |
|
$defaultValue = static::$dbProperties[$propertyName]['default']; |
193
|
|
|
// No default value set, use default for type |
194
|
5 |
|
} else { |
195
|
7 |
|
$defaultValue = self::$typeDefaults[static::$dbProperties[$propertyName]['type']]; |
196
|
|
|
} |
197
|
|
|
|
198
|
7 |
|
return $defaultValue; |
199
|
|
|
} |
200
|
|
|
|
201
|
|
|
/** |
202
|
|
|
* @return mixed |
203
|
|
|
*/ |
204
|
22 |
|
public function getPrimaryDbValue() |
205
|
|
|
{ |
206
|
22 |
|
if (is_array(static::$primaryDbPropertyKey)) { |
207
|
3 |
|
$primaryValues = []; |
208
|
3 |
|
foreach (static::$primaryDbPropertyKey as $keyPart) { |
209
|
3 |
|
$primaryValues[] = $this->dbData[$keyPart]; |
210
|
3 |
|
} |
211
|
|
|
|
212
|
3 |
|
return $primaryValues; |
213
|
|
|
} |
214
|
|
|
|
215
|
19 |
|
return $this->dbData[static::$primaryDbPropertyKey]; |
216
|
|
|
} |
217
|
|
|
|
218
|
|
|
/** |
219
|
|
|
* @param mixed $primaryDbValue |
220
|
|
|
*/ |
221
|
17 |
|
public function setPrimaryDbValue($primaryDbValue) |
222
|
|
|
{ |
223
|
17 |
|
if (is_array(static::$primaryDbPropertyKey)) { |
224
|
3 |
|
if (!is_array($primaryDbValue)) { |
225
|
1 |
|
throw new \InvalidArgumentException("Primary db value should be an array"); |
226
|
|
|
} |
227
|
|
|
|
228
|
2 |
|
reset($primaryDbValue); |
229
|
2 |
|
foreach (static::$primaryDbPropertyKey as $keyPart) { |
230
|
2 |
|
$this->dbData[$keyPart] = current($primaryDbValue); |
231
|
2 |
|
next($primaryDbValue); |
232
|
2 |
|
} |
233
|
2 |
|
} else { |
234
|
14 |
|
$this->dbData[static::$primaryDbPropertyKey] = $primaryDbValue; |
235
|
|
|
} |
236
|
16 |
|
} |
237
|
|
|
|
238
|
|
|
/** |
239
|
|
|
* @return bool |
240
|
|
|
*/ |
241
|
10 |
|
public function isNewDbEntity() |
242
|
|
|
{ |
243
|
10 |
|
if (is_array(static::$primaryDbPropertyKey)) { |
244
|
|
|
// Multiple column keys have to use explicit force insert because we have no way |
245
|
|
|
// to detect if it's a new entity (can't leave more than one primary field empty on insert because |
246
|
|
|
// db can't have two auto increment columns) |
247
|
1 |
|
throw new \LogicException("Can't detect if multi column primary key is a new entity"); |
248
|
|
|
} |
249
|
|
|
|
250
|
9 |
|
return !$this->getPrimaryDbValue(); |
251
|
|
|
} |
252
|
|
|
|
253
|
|
|
/** |
254
|
|
|
* @return bool |
255
|
|
|
*/ |
256
|
9 |
|
public function shouldInsertOnDbSave() |
257
|
|
|
{ |
258
|
9 |
|
return (!is_array(static::$primaryDbPropertyKey) && $this->isNewDbEntity()) |
259
|
9 |
|
|| $this->shouldForceDbInsertOnSave(); |
260
|
|
|
} |
261
|
|
|
|
262
|
|
|
/** |
263
|
|
|
* Set a row field value. |
264
|
|
|
* |
265
|
|
|
* @param string $property |
266
|
|
|
* @param mixed $value |
267
|
|
|
* @param bool $setAsModified |
268
|
|
|
* @param bool $force |
269
|
|
|
*/ |
270
|
26 |
|
protected function setDbValue($property, $value, $setAsModified = true, $force = false) |
271
|
|
|
{ |
272
|
26 |
|
if (!isset(static::$dbProperties[$property])) { |
273
|
1 |
|
throw new \InvalidArgumentException("No database entity property[{$property}] exists"); |
274
|
|
|
} |
275
|
|
|
|
276
|
|
|
// Don't set type if value is null and allowed (allowed currently indicated by default => null) |
277
|
25 |
|
$nullIsAllowed = (array_key_exists('default', static::$dbProperties[$property]) |
278
|
25 |
|
&& static::$dbProperties[$property]['default'] === null); |
279
|
25 |
|
if (!($value === null && $nullIsAllowed)) { |
280
|
24 |
|
$type = static::$dbProperties[$property]['type']; |
281
|
|
|
// Set null when empty and default is null |
282
|
24 |
|
if ($value === '' && $nullIsAllowed) { |
283
|
1 |
|
$value = null; |
284
|
24 |
|
} elseif ($type === 'dateTime') { |
285
|
1 |
|
if (!($value instanceof \DateTimeInterface)) { |
286
|
1 |
|
$value = $this->createDateTimeDbValue($value); |
287
|
1 |
|
} |
288
|
1 |
|
} else { |
289
|
23 |
|
settype($value, $type); |
290
|
|
|
} |
291
|
24 |
|
} |
292
|
|
|
|
293
|
25 |
|
if ($this->dbData[$property] !== $value || $force) { |
294
|
22 |
|
$this->dbData[$property] = $value; |
295
|
|
|
|
296
|
22 |
|
if ($setAsModified && !$this->isDbPropertyModified($property)) { |
297
|
15 |
|
$this->modifiedDbProperties[] = $property; |
298
|
15 |
|
} |
299
|
22 |
|
} |
300
|
25 |
|
} |
301
|
|
|
|
302
|
|
|
/** |
303
|
|
|
* @param string $value |
304
|
|
|
* @return \DateTime|\Carbon\Carbon|null |
305
|
|
|
*/ |
306
|
1 |
|
protected function createDateTimeDbValue($value) |
307
|
|
|
{ |
308
|
1 |
|
static $carbonExists = null; |
309
|
|
|
if ($carbonExists === true |
310
|
1 |
|
|| ($carbonExists === null && ($carbonExists = class_exists(\Carbon\Carbon::class))) |
311
|
1 |
|
) { |
312
|
1 |
|
return new \Carbon\Carbon($value); |
313
|
|
|
} |
314
|
|
|
|
315
|
|
|
return new \DateTime($value); |
316
|
|
|
} |
317
|
|
|
|
318
|
|
|
/** |
319
|
|
|
* Get a database field value. |
320
|
|
|
* |
321
|
|
|
* @param string $property |
322
|
|
|
* @return mixed |
323
|
|
|
*/ |
324
|
7 |
|
protected function getDbValue($property) |
325
|
|
|
{ |
326
|
7 |
|
return $this->dbData[$property]; |
327
|
|
|
} |
328
|
|
|
|
329
|
|
|
/** |
330
|
|
|
* Get raw (with underscore as word separator as it is formatted in database) |
331
|
|
|
* field name from a object field property name (camelcased). |
332
|
|
|
* |
333
|
|
|
* @param string $propertyName |
334
|
|
|
* @return string |
335
|
|
|
*/ |
336
|
18 |
|
public static function getDbFieldName($propertyName) |
337
|
|
|
{ |
338
|
18 |
|
if (!isset(self::$cachedDbFieldNames[$propertyName])) { |
339
|
6 |
|
self::$cachedDbFieldNames[$propertyName] = Str::camelToSeparator($propertyName); |
340
|
6 |
|
} |
341
|
|
|
|
342
|
18 |
|
return self::$cachedDbFieldNames[$propertyName]; |
343
|
|
|
} |
344
|
|
|
|
345
|
|
|
/** |
346
|
|
|
* Get object field property name (camelCased) from database field name (underscore separated). |
347
|
|
|
* |
348
|
|
|
* @param string $dbFieldName |
349
|
|
|
* @return string |
350
|
|
|
*/ |
351
|
7 |
|
public static function getDbPropertyName($dbFieldName) |
352
|
|
|
{ |
353
|
7 |
|
if (!isset(self::$cachedDbPropertyNames[$dbFieldName])) { |
354
|
2 |
|
self::$cachedDbPropertyNames[$dbFieldName] = Str::separatorToCamel($dbFieldName); |
355
|
2 |
|
} |
356
|
|
|
|
357
|
7 |
|
return self::$cachedDbPropertyNames[$dbFieldName]; |
358
|
|
|
} |
359
|
|
|
|
360
|
|
|
/** |
361
|
|
|
* @return bool |
362
|
|
|
*/ |
363
|
5 |
|
public function hasModifiedDbProperties() |
364
|
|
|
{ |
365
|
5 |
|
return !empty($this->modifiedDbProperties); |
366
|
|
|
} |
367
|
|
|
|
368
|
|
|
/** |
369
|
|
|
* @param string $property |
370
|
|
|
* @return bool |
371
|
|
|
*/ |
372
|
16 |
|
public function isDbPropertyModified($property) |
373
|
|
|
{ |
374
|
16 |
|
return in_array($property, $this->modifiedDbProperties); |
375
|
|
|
} |
376
|
|
|
|
377
|
|
|
/** |
378
|
|
|
* @return array |
379
|
|
|
*/ |
380
|
6 |
|
public function getModifiedDbData() |
381
|
|
|
{ |
382
|
6 |
|
return array_intersect_key($this->dbData, array_flip($this->modifiedDbProperties)); |
383
|
|
|
} |
384
|
|
|
|
385
|
|
|
/** |
386
|
|
|
* @param string $property |
387
|
|
|
*/ |
388
|
|
|
public function clearModifiedDbProperty($property) |
389
|
|
|
{ |
390
|
|
|
if (($key = array_search($property, $this->modifiedDbProperties))) { |
391
|
|
|
unset($this->modifiedDbProperties[$key]); |
392
|
|
|
} |
393
|
|
|
} |
394
|
|
|
|
395
|
4 |
|
public function clearModifiedDbProperties() |
396
|
|
|
{ |
397
|
4 |
|
$this->modifiedDbProperties = []; |
398
|
4 |
|
} |
399
|
|
|
|
400
|
1 |
|
public function setAllDbPropertiesAsModified() |
401
|
|
|
{ |
402
|
1 |
|
$this->modifiedDbProperties = array_keys(static::$dbProperties); |
403
|
1 |
|
} |
404
|
|
|
|
405
|
|
|
/** |
406
|
|
|
* Magic method used to automate getters & setters for row data. |
407
|
|
|
* |
408
|
|
|
* @param string $name |
409
|
|
|
* @param array $arguments |
410
|
|
|
* @return mixed |
411
|
|
|
*/ |
412
|
19 |
|
public function __call($name, array $arguments = []) |
413
|
|
|
{ |
414
|
19 |
|
$propertyName = lcfirst(substr($name, 3)); |
415
|
|
|
|
416
|
19 |
|
if (strpos($name, 'get') === 0 && isset(static::$dbProperties[$propertyName])) { |
417
|
5 |
|
return $this->getDbValue($propertyName); |
418
|
17 |
|
} elseif (strpos($name, 'set') === 0 && isset(static::$dbProperties[$propertyName])) { |
419
|
16 |
|
$argumentCount = count($arguments); |
420
|
16 |
|
if ($argumentCount >= 1 && $argumentCount <= 3) { |
421
|
15 |
|
return $this->setDbValue($propertyName, ...$arguments); |
422
|
|
|
} else { |
423
|
1 |
|
throw new \BadMethodCallException("Invalid argument count[{$argumentCount}] for {$name}()"); |
424
|
|
|
} |
425
|
|
|
} else { |
426
|
1 |
|
throw new \BadMethodCallException("No method named {$name}()"); |
427
|
|
|
} |
428
|
|
|
} |
429
|
|
|
|
430
|
|
|
/** |
431
|
|
|
* Set database fields' data. |
432
|
|
|
* |
433
|
|
|
* @param array $data |
434
|
|
|
*/ |
435
|
2 |
|
public function setDbData(array $data) |
436
|
|
|
{ |
437
|
2 |
View Code Duplication |
foreach (array_keys(static::$dbProperties) as $propertyName) { |
|
|
|
|
438
|
2 |
|
if (array_key_exists($propertyName, $data)) { |
439
|
2 |
|
$this->setDbValue($propertyName, $data[$propertyName], true); |
440
|
2 |
|
} |
441
|
2 |
|
} |
442
|
2 |
|
} |
443
|
|
|
|
444
|
|
|
/** |
445
|
|
|
* Set db data from raw database row data with field names in database format. |
446
|
|
|
* |
447
|
|
|
* @param array $rowData |
448
|
|
|
*/ |
449
|
7 |
|
public function setDbDataFromRow(array $rowData) |
450
|
|
|
{ |
451
|
|
|
// If there are less row data than properties, use rows as starting point (optimization) |
452
|
7 |
|
if (count($rowData) < count(static::$dbProperties)) { |
453
|
6 |
|
foreach ($rowData as $dbFieldName => $value) { |
454
|
6 |
|
$propertyName = static::getDbPropertyName($dbFieldName); |
455
|
6 |
|
if (isset(static::$dbProperties[$propertyName])) { |
456
|
6 |
|
$this->setDbValue($propertyName, $value, false); |
457
|
6 |
|
} |
458
|
6 |
|
} |
459
|
|
|
// If there are more row data than properties, use properties as starting point |
460
|
6 |
|
} else { |
461
|
2 |
View Code Duplication |
foreach (array_keys(static::$dbProperties) as $propertyName) { |
|
|
|
|
462
|
2 |
|
$fieldName = static::getDbFieldName($propertyName); |
463
|
2 |
|
if (array_key_exists($fieldName, $rowData)) { |
464
|
2 |
|
$this->setDbValue($propertyName, $rowData[$fieldName], false); |
465
|
2 |
|
} |
466
|
2 |
|
} |
467
|
|
|
} |
468
|
7 |
|
} |
469
|
|
|
|
470
|
|
|
/** |
471
|
|
|
* @return array |
472
|
|
|
*/ |
473
|
10 |
|
public function getDbData() |
474
|
|
|
{ |
475
|
10 |
|
return $this->dbData; |
476
|
|
|
} |
477
|
|
|
|
478
|
|
|
/** |
479
|
|
|
* @return array |
480
|
|
|
*/ |
481
|
2 |
|
public function getDbDataWithoutPrimary() |
482
|
|
|
{ |
483
|
2 |
|
$dbDataWithoutPrimary = $this->dbData; |
484
|
|
|
|
485
|
2 |
|
if (is_array(static::$primaryDbPropertyKey)) { |
486
|
1 |
|
foreach (static::$primaryDbPropertyKey as $keyPart) { |
487
|
1 |
|
unset($dbDataWithoutPrimary[$keyPart]); |
488
|
1 |
|
} |
489
|
1 |
|
} else { |
490
|
1 |
|
unset($dbDataWithoutPrimary[static::$primaryDbPropertyKey]); |
491
|
|
|
} |
492
|
|
|
|
493
|
2 |
|
return $dbDataWithoutPrimary; |
494
|
|
|
} |
495
|
|
|
|
496
|
|
|
/** |
497
|
|
|
* @param bool $deleteFromDbOnSave |
498
|
|
|
*/ |
499
|
3 |
|
public function setDeleteFromDbOnSave($deleteFromDbOnSave = true) |
500
|
|
|
{ |
501
|
3 |
|
$this->deleteFromDbOnSave = $deleteFromDbOnSave; |
502
|
3 |
|
} |
503
|
|
|
|
504
|
|
|
/** |
505
|
|
|
* @return bool |
506
|
|
|
*/ |
507
|
11 |
|
public function shouldBeDeletedFromDbOnSave() |
508
|
|
|
{ |
509
|
11 |
|
return $this->deleteFromDbOnSave; |
510
|
|
|
} |
511
|
|
|
|
512
|
|
|
/** |
513
|
|
|
* @return bool |
514
|
|
|
*/ |
515
|
1 |
|
public function isDeleted() |
516
|
|
|
{ |
517
|
1 |
|
return $this->deleted; |
518
|
|
|
} |
519
|
|
|
|
520
|
|
|
/** |
521
|
|
|
* @param bool $deleted |
522
|
|
|
*/ |
523
|
3 |
|
public function setDeleted($deleted = true) |
524
|
|
|
{ |
525
|
3 |
|
$this->deleted = $deleted; |
526
|
3 |
|
} |
527
|
|
|
|
528
|
|
|
/** |
529
|
|
|
* @param bool $forceDbInsertOnSave |
530
|
|
|
*/ |
531
|
5 |
|
public function setForceDbInsertOnSave($forceDbInsertOnSave) |
532
|
|
|
{ |
533
|
5 |
|
$this->forceDbInsertOnSave = $forceDbInsertOnSave; |
534
|
5 |
|
} |
535
|
|
|
|
536
|
|
|
/** |
537
|
|
|
* @return bool |
538
|
|
|
*/ |
539
|
10 |
|
public function shouldForceDbInsertOnSave() |
540
|
|
|
{ |
541
|
10 |
|
return $this->forceDbInsertOnSave; |
542
|
|
|
} |
543
|
|
|
|
544
|
|
|
/** |
545
|
|
|
* @return array |
546
|
|
|
*/ |
547
|
1 |
|
public static function getDbProperties() |
548
|
|
|
{ |
549
|
1 |
|
return static::$dbProperties; |
550
|
|
|
} |
551
|
|
|
|
552
|
|
|
/** |
553
|
|
|
* @param string $propertyName |
554
|
|
|
* @return int|null |
555
|
|
|
*/ |
556
|
6 |
|
public static function getDbPropertyMaxLength($propertyName) |
557
|
|
|
{ |
558
|
6 |
|
return isset(static::$dbProperties[$propertyName]['maxLength']) |
559
|
6 |
|
? static::$dbProperties[$propertyName]['maxLength'] |
560
|
6 |
|
: null; |
561
|
|
|
} |
562
|
|
|
|
563
|
|
|
/** |
564
|
|
|
* @param string $propertyName |
565
|
|
|
* @return bool |
566
|
|
|
*/ |
567
|
3 |
|
public static function getDbPropertyRequired($propertyName) |
568
|
|
|
{ |
569
|
3 |
|
return isset(static::$dbProperties[$propertyName]['required']) |
570
|
3 |
|
? static::$dbProperties[$propertyName]['required'] |
571
|
3 |
|
: false; |
572
|
|
|
} |
573
|
|
|
|
574
|
|
|
/** |
575
|
|
|
* @param string $propertyName |
576
|
|
|
* @return bool |
577
|
|
|
*/ |
578
|
4 |
|
public static function getDbPropertyNonEmpty($propertyName) |
579
|
|
|
{ |
580
|
4 |
|
return isset(static::$dbProperties[$propertyName]['nonEmpty']) |
581
|
4 |
|
? static::$dbProperties[$propertyName]['nonEmpty'] |
582
|
4 |
|
: false; |
583
|
|
|
} |
584
|
|
|
|
585
|
|
|
/** |
586
|
|
|
* @return string|array |
587
|
|
|
*/ |
588
|
18 |
|
public static function getPrimaryDbPropertyKey() |
589
|
|
|
{ |
590
|
18 |
|
return static::$primaryDbPropertyKey; |
591
|
|
|
} |
592
|
|
|
|
593
|
|
|
/** |
594
|
|
|
* @return string|array |
595
|
|
|
*/ |
596
|
10 |
|
public static function getPrimaryDbFieldKey() |
597
|
|
|
{ |
598
|
10 |
|
$primaryDbPropertyKey = static::getPrimaryDbPropertyKey(); |
599
|
|
|
|
600
|
10 |
|
if (is_array($primaryDbPropertyKey)) { |
601
|
3 |
|
$primaryDbFieldKey = []; |
602
|
3 |
|
foreach ($primaryDbPropertyKey as $propertyName) { |
603
|
3 |
|
$primaryDbFieldKey[] = static::getDbFieldName($propertyName); |
604
|
3 |
|
} |
605
|
|
|
|
606
|
3 |
|
return $primaryDbFieldKey; |
607
|
|
|
} else { |
608
|
7 |
|
return static::getDbFieldName($primaryDbPropertyKey); |
609
|
|
|
} |
610
|
|
|
} |
611
|
|
|
|
612
|
|
|
/** |
613
|
|
|
* Return array with db property names. |
614
|
|
|
* |
615
|
|
|
* @param array $exclude |
616
|
|
|
* @return array |
617
|
|
|
*/ |
618
|
1 |
|
public static function getDbPropertyNames(array $exclude = []) |
619
|
|
|
{ |
620
|
1 |
|
$dbPropertyNames = array_keys(static::$dbProperties); |
621
|
|
|
|
622
|
1 |
|
return $exclude ? array_diff($dbPropertyNames, $exclude) : $dbPropertyNames; |
623
|
|
|
} |
624
|
|
|
|
625
|
|
|
/** |
626
|
|
|
* Return array with raw db field names. |
627
|
|
|
* |
628
|
|
|
* @param array $exclude |
629
|
|
|
* @return array |
630
|
|
|
*/ |
631
|
3 |
|
public static function getDbFieldNames(array $exclude = []) |
632
|
|
|
{ |
633
|
3 |
|
$fieldNames = []; |
634
|
3 |
|
foreach (array_keys(static::$dbProperties) as $propertyName) { |
635
|
3 |
|
$fieldNames[] = static::getDbFieldName($propertyName); |
636
|
3 |
|
} |
637
|
|
|
|
638
|
3 |
|
return $exclude ? array_diff($fieldNames, $exclude) : $fieldNames; |
639
|
|
|
} |
640
|
|
|
|
641
|
|
|
|
642
|
|
|
/** |
643
|
|
|
* Get raw database field names prefixed (id, name becomes t.id, t.name etc.). |
644
|
|
|
* |
645
|
|
|
* @param string $dbTableAlias |
646
|
|
|
* @param array $exclude |
647
|
|
|
* @return array |
648
|
|
|
*/ |
649
|
1 |
|
public static function getPrefixedDbFieldNames($dbTableAlias, array $exclude = []) |
650
|
|
|
{ |
651
|
1 |
|
return Arr::valuesWithPrefix(static::getDbFieldNames($exclude), $dbTableAlias . '.'); |
652
|
|
|
} |
653
|
|
|
|
654
|
|
|
/** |
655
|
|
|
* Get database columns transformed from e.g. "productId, date" to "p.product_id AS p_product_id, p.date AS p_date". |
656
|
|
|
* |
657
|
|
|
* @param string $dbTableAlias |
658
|
|
|
* @param array $exclude |
659
|
|
|
* @return array |
660
|
|
|
*/ |
661
|
1 |
|
public static function getAliasedDbFieldNames($dbTableAlias, array $exclude = []) |
662
|
|
|
{ |
663
|
1 |
|
$newArray = []; |
664
|
1 |
|
foreach (static::getDbFieldNames($exclude) as $dbFieldName) { |
665
|
1 |
|
$fromCol = $dbTableAlias . '.' . $dbFieldName; |
666
|
1 |
|
$toCol = $dbTableAlias . '_' . $dbFieldName; |
667
|
1 |
|
$newArray[] = $fromCol . ' AS ' . $toCol; |
668
|
1 |
|
} |
669
|
|
|
|
670
|
1 |
|
return $newArray; |
671
|
|
|
} |
672
|
|
|
|
673
|
|
|
/** |
674
|
|
|
* Filters a full db item array by it's table alias and the strips the table alias. |
675
|
|
|
* |
676
|
|
|
* @param array $rowData |
677
|
|
|
* @param string $dbTableAlias |
678
|
|
|
* @param bool $skipStrip For cases when you want to filter only (no stripping) |
679
|
|
|
* @return array |
680
|
|
|
*/ |
681
|
1 |
|
public static function filterStripDbRowData(array $rowData, $dbTableAlias, $skipStrip = false) |
682
|
|
|
{ |
683
|
1 |
|
$columnPrefix = $dbTableAlias . '_'; |
684
|
|
|
|
685
|
1 |
|
$filteredAndStrippedRowData = []; |
686
|
1 |
|
foreach ($rowData as $key => $val) { |
687
|
1 |
|
if (strpos($key, $columnPrefix) === 0) { |
688
|
1 |
|
$strippedKey = $skipStrip ? $key : Str::stripLeft($key, $columnPrefix); |
689
|
1 |
|
$filteredAndStrippedRowData[$strippedKey] = $val; |
690
|
1 |
|
} |
691
|
1 |
|
} |
692
|
|
|
|
693
|
1 |
|
return $filteredAndStrippedRowData; |
694
|
|
|
} |
695
|
|
|
|
696
|
|
|
/** |
697
|
|
|
* @return string |
698
|
|
|
*/ |
699
|
8 |
|
public static function getDbTableName() |
700
|
|
|
{ |
701
|
8 |
|
return static::$dbTableName; |
702
|
|
|
} |
703
|
|
|
|
704
|
|
|
/** |
705
|
|
|
* Method to handle the serialization of this object. |
706
|
|
|
* |
707
|
|
|
* Implementation of Serializable interface. If descendant private properties |
708
|
|
|
* should be serialized, they need to be visible to this parent (i.e. not private). |
709
|
|
|
* |
710
|
|
|
* @return string |
711
|
|
|
*/ |
712
|
2 |
|
public function serialize() |
713
|
|
|
{ |
714
|
2 |
|
return serialize(get_object_vars($this)); |
715
|
|
|
} |
716
|
|
|
|
717
|
|
|
/** |
718
|
|
|
* Method to handle the unserialization of this object. |
719
|
|
|
* |
720
|
|
|
* Implementation of Serializable interface. If descendant private properties |
721
|
|
|
* should be unserialized, they need to be visible to this parent (i.e. not private). |
722
|
|
|
* |
723
|
|
|
* @param string $serializedObject |
724
|
|
|
*/ |
725
|
1 |
|
public function unserialize($serializedObject) |
726
|
|
|
{ |
727
|
1 |
|
$objectVars = unserialize($serializedObject); |
728
|
|
|
|
729
|
1 |
|
foreach ($objectVars as $key => $value) { |
730
|
1 |
|
$this->{$key} = $value; |
731
|
1 |
|
} |
732
|
1 |
|
} |
733
|
|
|
|
734
|
|
|
/** |
735
|
|
|
* Merges other object's modified database data into this object. |
736
|
|
|
* |
737
|
|
|
* @param AbstractDbEntity $otherEntity |
738
|
|
|
*/ |
739
|
1 |
|
public function mergeWith(AbstractDbEntity $otherEntity) |
740
|
|
|
{ |
741
|
1 |
|
$dataToMerge = $otherEntity->getModifiedDbData(); |
742
|
1 |
|
$this->setDbData($dataToMerge); |
743
|
1 |
|
} |
744
|
|
|
} |
745
|
|
|
|
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.