• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

codeigniter4 / CodeIgniter4 / 20589198684

30 Dec 2025 04:55AM UTC coverage: 84.503% (-0.03%) from 84.53%
20589198684

Pull #9853

github

web-flow
Merge f1d8312ec into e2fc5243b
Pull Request #9853: feat(encryption): Add previous keys fallback feature

65 of 77 new or added lines in 5 files covered. (84.42%)

38 existing lines in 3 files now uncovered.

21572 of 25528 relevant lines covered (84.5%)

203.31 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.51
/system/Entity/Entity.php
1
<?php
2

3
declare(strict_types=1);
4

5
/**
6
 * This file is part of CodeIgniter 4 framework.
7
 *
8
 * (c) CodeIgniter Foundation <admin@codeigniter.com>
9
 *
10
 * For the full copyright and license information, please view
11
 * the LICENSE file that was distributed with this source code.
12
 */
13

14
namespace CodeIgniter\Entity;
15

16
use BackedEnum;
17
use CodeIgniter\DataCaster\DataCaster;
18
use CodeIgniter\Entity\Cast\ArrayCast;
19
use CodeIgniter\Entity\Cast\BooleanCast;
20
use CodeIgniter\Entity\Cast\CSVCast;
21
use CodeIgniter\Entity\Cast\DatetimeCast;
22
use CodeIgniter\Entity\Cast\EnumCast;
23
use CodeIgniter\Entity\Cast\FloatCast;
24
use CodeIgniter\Entity\Cast\IntBoolCast;
25
use CodeIgniter\Entity\Cast\IntegerCast;
26
use CodeIgniter\Entity\Cast\JsonCast;
27
use CodeIgniter\Entity\Cast\ObjectCast;
28
use CodeIgniter\Entity\Cast\StringCast;
29
use CodeIgniter\Entity\Cast\TimestampCast;
30
use CodeIgniter\Entity\Cast\URICast;
31
use CodeIgniter\Entity\Exceptions\CastException;
32
use CodeIgniter\I18n\Time;
33
use DateTime;
34
use DateTimeInterface;
35
use Exception;
36
use JsonSerializable;
37
use ReturnTypeWillChange;
38
use Traversable;
39
use UnitEnum;
40

41
/**
42
 * Entity encapsulation, for use with CodeIgniter\Model
43
 *
44
 * @see \CodeIgniter\Entity\EntityTest
45
 */
46
class Entity implements JsonSerializable
47
{
48
    /**
49
     * Maps names used in sets and gets against unique
50
     * names within the class, allowing independence from
51
     * database column names.
52
     *
53
     * Example:
54
     *  $datamap = [
55
     *      'class_property_name' => 'db_column_name'
56
     *  ];
57
     *
58
     * @var array<string, string>
59
     */
60
    protected $datamap = [];
61

62
    /**
63
     * The date fields.
64
     *
65
     * @var list<string>
66
     */
67
    protected $dates = [
68
        'created_at',
69
        'updated_at',
70
        'deleted_at',
71
    ];
72

73
    /**
74
     * Array of field names and the type of value to cast them as when
75
     * they are accessed.
76
     *
77
     * @var array<string, string>
78
     */
79
    protected $casts = [];
80

81
    /**
82
     * Custom convert handlers
83
     *
84
     * @var array<string, string>
85
     */
86
    protected $castHandlers = [];
87

88
    /**
89
     * Default convert handlers
90
     *
91
     * @var array<string, string>
92
     */
93
    private array $defaultCastHandlers = [
94
        'array'     => ArrayCast::class,
95
        'bool'      => BooleanCast::class,
96
        'boolean'   => BooleanCast::class,
97
        'csv'       => CSVCast::class,
98
        'datetime'  => DatetimeCast::class,
99
        'double'    => FloatCast::class,
100
        'enum'      => EnumCast::class,
101
        'float'     => FloatCast::class,
102
        'int'       => IntegerCast::class,
103
        'integer'   => IntegerCast::class,
104
        'int-bool'  => IntBoolCast::class,
105
        'json'      => JsonCast::class,
106
        'object'    => ObjectCast::class,
107
        'string'    => StringCast::class,
108
        'timestamp' => TimestampCast::class,
109
        'uri'       => URICast::class,
110
    ];
111

112
    /**
113
     * Holds the current values of all class vars.
114
     *
115
     * @var array<string, mixed>
116
     */
117
    protected $attributes = [];
118

119
    /**
120
     * Holds original copies of all class vars so we can determine
121
     * what's actually been changed and not accidentally write
122
     * nulls where we shouldn't.
123
     *
124
     * @var array<string, mixed>
125
     */
126
    protected $original = [];
127

128
    /**
129
     * The data caster.
130
     */
131
    protected DataCaster $dataCaster;
132

133
    /**
134
     * Holds info whenever properties have to be casted
135
     */
136
    private bool $_cast = true;
137

138
    /**
139
     * Indicates whether all attributes are scalars (for optimization)
140
     */
141
    private bool $_onlyScalars = true;
142

143
    /**
144
     * Allows filling in Entity parameters during construction.
145
     */
146
    public function __construct(?array $data = null)
147
    {
148
        $this->dataCaster = new DataCaster(
164✔
149
            array_merge($this->defaultCastHandlers, $this->castHandlers),
164✔
150
            null,
164✔
151
            null,
164✔
152
            false,
164✔
153
        );
164✔
154

155
        $this->syncOriginal();
164✔
156

157
        $this->fill($data);
164✔
158
    }
159

160
    /**
161
     * Takes an array of key/value pairs and sets them as class
162
     * properties, using any `setCamelCasedProperty()` methods
163
     * that may or may not exist.
164
     *
165
     * @param array<string, array|bool|float|int|object|string|null> $data
166
     *
167
     * @return $this
168
     */
169
    public function fill(?array $data = null)
170
    {
171
        if (! is_array($data)) {
164✔
172
            return $this;
158✔
173
        }
174

175
        foreach ($data as $key => $value) {
18✔
176
            $this->__set($key, $value);
18✔
177
        }
178

179
        return $this;
18✔
180
    }
181

182
    /**
183
     * General method that will return all public and protected values
184
     * of this entity as an array. All values are accessed through the
185
     * __get() magic method so will have any casts, etc applied to them.
186
     *
187
     * @param bool $onlyChanged If true, only return values that have changed since object creation
188
     * @param bool $cast        If true, properties will be cast.
189
     * @param bool $recursive   If true, inner entities will be cast as array as well.
190
     */
191
    public function toArray(bool $onlyChanged = false, bool $cast = true, bool $recursive = false): array
192
    {
193
        $this->_cast = $cast;
10✔
194

195
        $keys = array_filter(array_keys($this->attributes), static fn ($key): bool => ! str_starts_with($key, '_'));
10✔
196

197
        if (is_array($this->datamap)) {
10✔
198
            $keys = array_unique(
10✔
199
                [...array_diff($keys, $this->datamap), ...array_keys($this->datamap)],
10✔
200
            );
10✔
201
        }
202

203
        $return = [];
10✔
204

205
        // Loop over the properties, to allow magic methods to do their thing.
206
        foreach ($keys as $key) {
10✔
207
            if ($onlyChanged && ! $this->hasChanged($key)) {
10✔
208
                continue;
2✔
209
            }
210

211
            $return[$key] = $this->__get($key);
10✔
212

213
            if ($recursive) {
10✔
214
                if ($return[$key] instanceof self) {
1✔
215
                    $return[$key] = $return[$key]->toArray($onlyChanged, $cast, $recursive);
1✔
216
                } elseif (is_callable([$return[$key], 'toArray'])) {
1✔
217
                    $return[$key] = $return[$key]->toArray();
×
218
                }
219
            }
220
        }
221

222
        $this->_cast = true;
10✔
223

224
        return $return;
10✔
225
    }
226

227
    /**
228
     * Returns the raw values of the current attributes.
229
     *
230
     * @param bool $onlyChanged If true, only return values that have changed since object creation
231
     * @param bool $recursive   If true, inner entities will be cast as array as well.
232
     */
233
    public function toRawArray(bool $onlyChanged = false, bool $recursive = false): array
234
    {
235
        $convert = static function ($value) use (&$convert, $recursive) {
44✔
236
            if (! $recursive) {
8✔
UNCOV
237
                return $value;
×
238
            }
239

240
            if ($value instanceof self) {
8✔
241
                // Always output full array for nested entities
242
                return $value->toRawArray(false, true);
2✔
243
            }
244

245
            if (is_array($value)) {
8✔
246
                $result = [];
1✔
247

248
                foreach ($value as $k => $v) {
1✔
249
                    $result[$k] = $convert($v);
1✔
250
                }
251

252
                return $result;
1✔
253
            }
254

255
            if (is_object($value) && is_callable([$value, 'toRawArray'])) {
8✔
UNCOV
256
                return $value->toRawArray();
×
257
            }
258

259
            return $value;
8✔
260
        };
44✔
261

262
        // When returning everything
263
        if (! $onlyChanged) {
44✔
264
            return $recursive
36✔
265
                ? array_map($convert, $this->attributes)
8✔
266
                : $this->attributes;
36✔
267
        }
268

269
        // When filtering by changed values only
270
        $return = [];
15✔
271

272
        foreach ($this->attributes as $key => $value) {
15✔
273
            // Special handling for arrays of entities in recursive mode
274
            // Skip hasChanged() and do per-entity comparison directly
275
            if ($recursive && is_array($value) && $this->containsOnlyEntities($value)) {
15✔
276
                $originalValue = $this->original[$key] ?? null;
4✔
277

278
                if (! is_string($originalValue)) {
4✔
279
                    // No original or invalid format, export all entities
UNCOV
280
                    $converted = [];
×
281

UNCOV
282
                    foreach ($value as $idx => $item) {
×
UNCOV
283
                        $converted[$idx] = $item->toRawArray(false, true);
×
284
                    }
UNCOV
285
                    $return[$key] = $converted;
×
286

UNCOV
287
                    continue;
×
288
                }
289

290
                // Decode original array structure for per-entity comparison
291
                $originalArray = json_decode($originalValue, true);
4✔
292
                $converted     = [];
4✔
293

294
                foreach ($value as $idx => $item) {
4✔
295
                    // Compare current entity against its original state
296
                    $currentNormalized  = $this->normalizeValue($item);
4✔
297
                    $originalNormalized = $originalArray[$idx] ?? null;
4✔
298

299
                    // Only include if changed, new, or can't determine
300
                    if ($originalNormalized === null || $currentNormalized !== $originalNormalized) {
4✔
301
                        $converted[$idx] = $item->toRawArray(false, true);
3✔
302
                    }
303
                }
304

305
                // Only include this property if at least one entity changed
306
                if ($converted !== []) {
4✔
307
                    $return[$key] = $converted;
3✔
308
                }
309

310
                continue;
4✔
311
            }
312

313
            // For all other cases, use hasChanged()
314
            if (! $this->hasChanged($key)) {
15✔
315
                continue;
13✔
316
            }
317

318
            if ($recursive) {
11✔
319
                // Special handling for arrays (mixed or not all entities)
UNCOV
320
                if (is_array($value)) {
×
UNCOV
321
                    $converted = [];
×
322

UNCOV
323
                    foreach ($value as $idx => $item) {
×
UNCOV
324
                        $converted[$idx] = $item instanceof self ? $item->toRawArray(false, true) : $convert($item);
×
325
                    }
UNCOV
326
                    $return[$key] = $converted;
×
327

UNCOV
328
                    continue;
×
329
                }
330

331
                // default recursive conversion
UNCOV
332
                $return[$key] = $convert($value);
×
333

UNCOV
334
                continue;
×
335
            }
336

337
            // non-recursive changed value
338
            $return[$key] = $value;
11✔
339
        }
340

341
        return $return;
15✔
342
    }
343

344
    /**
345
     * Ensures our "original" values match the current values.
346
     *
347
     * Objects and arrays are normalized and JSON-encoded for reliable change detection,
348
     * while scalars are stored as-is for performance.
349
     *
350
     * @return $this
351
     */
352
    public function syncOriginal()
353
    {
354
        $this->original     = [];
164✔
355
        $this->_onlyScalars = true;
164✔
356

357
        foreach ($this->attributes as $key => $value) {
164✔
358
            if (is_object($value) || is_array($value)) {
139✔
359
                $this->original[$key] = json_encode($this->normalizeValue($value), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
36✔
360
                $this->_onlyScalars   = false;
36✔
361
            } else {
362
                $this->original[$key] = $value;
135✔
363
            }
364
        }
365

366
        return $this;
164✔
367
    }
368

369
    /**
370
     * Checks a property to see if it has changed since the entity
371
     * was created. Or, without a parameter, checks if any
372
     * properties have changed.
373
     *
374
     * @param string|null $key class property
375
     */
376
    public function hasChanged(?string $key = null): bool
377
    {
378
        // If no parameter was given then check all attributes
379
        if ($key === null) {
50✔
380
            if ($this->_onlyScalars) {
6✔
381
                return $this->original !== $this->attributes;
5✔
382
            }
383

384
            foreach (array_keys($this->attributes) as $attributeKey) {
1✔
385
                if ($this->hasChanged($attributeKey)) {
1✔
386
                    return true;
1✔
387
                }
388
            }
389

390
            return false;
1✔
391
        }
392

393
        $dbColumn = $this->mapProperty($key);
46✔
394

395
        // Key doesn't exist in either
396
        if (! array_key_exists($dbColumn, $this->original) && ! array_key_exists($dbColumn, $this->attributes)) {
46✔
397
            return false;
1✔
398
        }
399

400
        // It's a new element
401
        if (! array_key_exists($dbColumn, $this->original) && array_key_exists($dbColumn, $this->attributes)) {
45✔
402
            return true;
4✔
403
        }
404

405
        // It was removed
406
        if (array_key_exists($dbColumn, $this->original) && ! array_key_exists($dbColumn, $this->attributes)) {
43✔
407
            return true;
1✔
408
        }
409

410
        $originalValue = $this->original[$dbColumn];
42✔
411
        $currentValue  = $this->attributes[$dbColumn];
42✔
412

413
        // If original is a string, it was JSON-encoded (object or array)
414
        if (is_string($originalValue) && (is_object($currentValue) || is_array($currentValue))) {
42✔
415
            return $originalValue !== json_encode($this->normalizeValue($currentValue), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
23✔
416
        }
417

418
        // For scalars, use direct comparison
419
        return $originalValue !== $currentValue;
22✔
420
    }
421

422
    /**
423
     * Checks if an array contains only Entity instances.
424
     * This allows optimization for per-entity change tracking.
425
     *
426
     * @param array<int|string, mixed> $data
427
     */
428
    private function containsOnlyEntities(array $data): bool
429
    {
430
        if ($data === []) {
4✔
UNCOV
431
            return false;
×
432
        }
433

434
        foreach ($data as $item) {
4✔
435
            if (! $item instanceof self) {
4✔
UNCOV
436
                return false;
×
437
            }
438
        }
439

440
        return true;
4✔
441
    }
442

443
    /**
444
     * Recursively normalize a value for comparison.
445
     * Converts objects and arrays to a JSON-encodable format.
446
     */
447
    private function normalizeValue(mixed $data): mixed
448
    {
449
        if (is_array($data)) {
36✔
450
            $normalized = [];
29✔
451

452
            foreach ($data as $key => $value) {
29✔
453
                $normalized[$key] = $this->normalizeValue($value);
27✔
454
            }
455

456
            return $normalized;
29✔
457
        }
458

459
        if (is_object($data)) {
35✔
460
            // Check for Entity instance (use raw values, recursive)
461
            if ($data instanceof self) {
30✔
462
                $objectData = $data->toRawArray(false, true);
6✔
463
            } elseif ($data instanceof JsonSerializable) {
24✔
464
                $objectData = $data->jsonSerialize();
1✔
465
            } elseif (method_exists($data, 'toArray')) {
23✔
466
                $objectData = $data->toArray();
1✔
467
            } elseif ($data instanceof Traversable) {
22✔
468
                $objectData = iterator_to_array($data);
1✔
469
            } elseif ($data instanceof DateTimeInterface) {
22✔
470
                return [
8✔
471
                    '__class'    => $data::class,
8✔
472
                    '__datetime' => $data->format(DATE_RFC3339_EXTENDED),
8✔
473
                ];
8✔
474
            } elseif ($data instanceof UnitEnum) {
14✔
475
                return [
5✔
476
                    '__class' => $data::class,
5✔
477
                    '__enum'  => $data instanceof BackedEnum ? $data->value : $data->name,
5✔
478
                ];
5✔
479
            } else {
480
                $objectData = get_object_vars($data);
9✔
481

482
                // Fallback for value objects with __toString()
483
                // when properties are not accessible
484
                if ($objectData === [] && method_exists($data, '__toString')) {
9✔
485
                    return [
1✔
486
                        '__class'  => $data::class,
1✔
487
                        '__string' => (string) $data,
1✔
488
                    ];
1✔
489
                }
490
            }
491

492
            return [
16✔
493
                '__class' => $data::class,
16✔
494
                '__data'  => $this->normalizeValue($objectData),
16✔
495
            ];
16✔
496
        }
497

498
        // Return scalars and null as-is
499
        return $data;
27✔
500
    }
501

502
    /**
503
     * Set raw data array without any mutations
504
     *
505
     * @return $this
506
     */
507
    public function injectRawData(array $data)
508
    {
509
        $this->attributes = $data;
26✔
510

511
        $this->syncOriginal();
26✔
512

513
        return $this;
26✔
514
    }
515

516
    /**
517
     * Set raw data array without any mutations
518
     *
519
     * @return $this
520
     *
521
     * @deprecated Use injectRawData() instead.
522
     */
523
    public function setAttributes(array $data)
524
    {
525
        return $this->injectRawData($data);
7✔
526
    }
527

528
    /**
529
     * Checks the datamap to see if this property name is being mapped,
530
     * and returns the db column name, if any, or the original property name.
531
     *
532
     * @return string db column name
533
     */
534
    protected function mapProperty(string $key)
535
    {
536
        if ($this->datamap === []) {
152✔
537
            return $key;
110✔
538
        }
539

540
        if (! empty($this->datamap[$key])) {
42✔
541
            return $this->datamap[$key];
17✔
542
        }
543

544
        return $key;
35✔
545
    }
546

547
    /**
548
     * Converts the given string|timestamp|DateTime|Time instance
549
     * into the "CodeIgniter\I18n\Time" object.
550
     *
551
     * @param DateTime|float|int|string|Time $value
552
     *
553
     * @return Time
554
     *
555
     * @throws Exception
556
     */
557
    protected function mutateDate($value)
558
    {
559
        return DatetimeCast::get($value);
32✔
560
    }
561

562
    /**
563
     * Provides the ability to cast an item as a specific data type.
564
     * Add ? at the beginning of the type (i.e. ?string) to get `null`
565
     * instead of casting $value when $value is null.
566
     *
567
     * @param bool|float|int|string|null $value     Attribute value
568
     * @param string                     $attribute Attribute name
569
     * @param string                     $method    Allowed to "get" and "set"
570
     *
571
     * @return array|bool|float|int|object|string|null
572
     *
573
     * @throws CastException
574
     */
575
    protected function castAs($value, string $attribute, string $method = 'get')
576
    {
577
        return $this->dataCaster
146✔
578
            // @TODO if $casts is readonly, we don't need the setTypes() method.
146✔
579
            ->setTypes($this->casts)
146✔
580
            ->castAs($value, $attribute, $method);
146✔
581
    }
582

583
    /**
584
     * Support for json_encode()
585
     *
586
     * @return array
587
     */
588
    #[ReturnTypeWillChange]
589
    public function jsonSerialize()
590
    {
591
        return $this->toArray();
1✔
592
    }
593

594
    /**
595
     * Change the value of the private $_cast property
596
     *
597
     * @return bool|Entity
598
     */
599
    public function cast(?bool $cast = null)
600
    {
601
        if ($cast === null) {
11✔
602
            return $this->_cast;
10✔
603
        }
604

605
        $this->_cast = $cast;
10✔
606

607
        return $this;
10✔
608
    }
609

610
    /**
611
     * Magic method to all protected/private class properties to be
612
     * easily set, either through a direct access or a
613
     * `setCamelCasedProperty()` method.
614
     *
615
     * Examples:
616
     *  $this->my_property = $p;
617
     *  $this->setMyProperty() = $p;
618
     *
619
     * @param array|bool|float|int|object|string|null $value
620
     *
621
     * @return void
622
     *
623
     * @throws Exception
624
     */
625
    public function __set(string $key, $value = null)
626
    {
627
        $dbColumn = $this->mapProperty($key);
126✔
628

629
        // Check if the field should be mutated into a date
630
        if (in_array($dbColumn, $this->dates, true)) {
126✔
631
            $value = $this->mutateDate($value);
17✔
632
        }
633

634
        $value = $this->castAs($value, $dbColumn, 'set');
126✔
635

636
        // if a setter method exists for this key, use that method to
637
        // insert this value. should be outside $isNullable check,
638
        // so maybe wants to do sth with null value automatically
639
        $method = 'set' . str_replace(' ', '', ucwords(str_replace(['-', '_'], ' ', $dbColumn)));
118✔
640

641
        // If a "`_set` + $key" method exists, it is a setter.
642
        if (method_exists($this, '_' . $method)) {
118✔
643
            $this->{'_' . $method}($value);
1✔
644

645
            return;
1✔
646
        }
647

648
        // If a "`set` + $key" method exists, it is also a setter.
649
        if (method_exists($this, $method) && $method !== 'setAttributes') {
117✔
650
            $this->{$method}($value);
12✔
651

652
            return;
12✔
653
        }
654

655
        // Otherwise, just the value. This allows for creation of new
656
        // class properties that are undefined, though they cannot be
657
        // saved. Useful for grabbing values through joins, assigning
658
        // relationships, etc.
659
        $this->attributes[$dbColumn] = $value;
108✔
660
    }
661

662
    /**
663
     * Magic method to allow retrieval of protected and private class properties
664
     * either by their name, or through a `getCamelCasedProperty()` method.
665
     *
666
     * Examples:
667
     *  $p = $this->my_property
668
     *  $p = $this->getMyProperty()
669
     *
670
     * @return array|bool|float|int|object|string|null
671
     *
672
     * @throws Exception
673
     *
674
     * @params string $key class property
675
     */
676
    public function __get(string $key)
677
    {
678
        $dbColumn = $this->mapProperty($key);
88✔
679

680
        $result = null;
88✔
681

682
        // Convert to CamelCase for the method
683
        $method = 'get' . str_replace(' ', '', ucwords(str_replace(['-', '_'], ' ', $dbColumn)));
88✔
684

685
        // if a getter method exists for this key,
686
        // use that method to insert this value.
687
        if (method_exists($this, '_' . $method)) {
88✔
688
            // If a "`_get` + $key" method exists, it is a getter.
689
            $result = $this->{'_' . $method}();
1✔
690
        } elseif (method_exists($this, $method)) {
87✔
691
            // If a "`get` + $key" method exists, it is also a getter.
692
            $result = $this->{$method}();
9✔
693
        }
694

695
        // Otherwise return the protected property
696
        // if it exists.
697
        elseif (array_key_exists($dbColumn, $this->attributes)) {
84✔
698
            $result = $this->attributes[$dbColumn];
82✔
699
        }
700

701
        // Do we need to mutate this into a date?
702
        if (in_array($dbColumn, $this->dates, true)) {
88✔
703
            $result = $this->mutateDate($result);
18✔
704
        }
705
        // Or cast it as something?
706
        elseif ($this->_cast) {
82✔
707
            $result = $this->castAs($result, $dbColumn);
82✔
708
        }
709

710
        return $result;
83✔
711
    }
712

713
    /**
714
     * Returns true if a property exists names $key, or a getter method
715
     * exists named like for __get().
716
     */
717
    public function __isset(string $key): bool
718
    {
719
        if ($this->isMappedDbColumn($key)) {
6✔
720
            return false;
2✔
721
        }
722

723
        $dbColumn = $this->mapProperty($key);
6✔
724

725
        $method = 'get' . str_replace(' ', '', ucwords(str_replace(['-', '_'], ' ', $dbColumn)));
6✔
726

727
        if (method_exists($this, $method)) {
6✔
728
            return true;
1✔
729
        }
730

731
        return isset($this->attributes[$dbColumn]);
6✔
732
    }
733

734
    /**
735
     * Unsets an attribute property.
736
     */
737
    public function __unset(string $key): void
738
    {
739
        if ($this->isMappedDbColumn($key)) {
4✔
740
            return;
1✔
741
        }
742

743
        $dbColumn = $this->mapProperty($key);
4✔
744

745
        unset($this->attributes[$dbColumn]);
4✔
746
    }
747

748
    /**
749
     * Whether this key is mapped db column name?
750
     */
751
    protected function isMappedDbColumn(string $key): bool
752
    {
753
        $dbColumn = $this->mapProperty($key);
8✔
754

755
        // The $key is a property name which has mapped db column name
756
        if ($key !== $dbColumn) {
8✔
757
            return false;
5✔
758
        }
759

760
        return $this->hasMappedProperty($key);
6✔
761
    }
762

763
    /**
764
     * Whether this key has mapped property?
765
     */
766
    protected function hasMappedProperty(string $key): bool
767
    {
768
        $property = array_search($key, $this->datamap, true);
6✔
769

770
        return $property !== false;
6✔
771
    }
772
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc