Skip to content

Commit 1b56deb

Browse files
authored
refactor: Rework Entity class (#9878)
1 parent 41a92ec commit 1b56deb

File tree

9 files changed

+213
-119
lines changed

9 files changed

+213
-119
lines changed

system/Entity/Cast/DatetimeCast.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
namespace CodeIgniter\Entity\Cast;
1515

1616
use CodeIgniter\I18n\Time;
17-
use DateTime;
17+
use DateTimeInterface;
1818
use Exception;
1919

2020
class DatetimeCast extends BaseCast
@@ -32,7 +32,7 @@ public static function get($value, array $params = [])
3232
return $value;
3333
}
3434

35-
if ($value instanceof DateTime) {
35+
if ($value instanceof DateTimeInterface) {
3636
return Time::createFromInstance($value);
3737
}
3838

system/Entity/Entity.php

Lines changed: 65 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
use CodeIgniter\Entity\Cast\URICast;
3131
use CodeIgniter\Entity\Exceptions\CastException;
3232
use CodeIgniter\I18n\Time;
33-
use DateTime;
3433
use DateTimeInterface;
3534
use Exception;
3635
use JsonSerializable;
@@ -79,14 +78,14 @@ class Entity implements JsonSerializable
7978
protected $casts = [];
8079

8180
/**
82-
* Custom convert handlers
81+
* Custom convert handlers.
8382
*
8483
* @var array<string, string>
8584
*/
8685
protected $castHandlers = [];
8786

8887
/**
89-
* Default convert handlers
88+
* Default convert handlers.
9089
*
9190
* @var array<string, string>
9291
*/
@@ -128,29 +127,26 @@ class Entity implements JsonSerializable
128127
/**
129128
* The data caster.
130129
*/
131-
protected DataCaster $dataCaster;
130+
protected ?DataCaster $dataCaster = null;
132131

133132
/**
134-
* Holds info whenever properties have to be casted
133+
* Holds info whenever properties have to be casted.
135134
*/
136135
private bool $_cast = true;
137136

138137
/**
139-
* Indicates whether all attributes are scalars (for optimization)
138+
* Indicates whether all attributes are scalars (for optimization).
140139
*/
141140
private bool $_onlyScalars = true;
142141

143142
/**
144143
* Allows filling in Entity parameters during construction.
144+
*
145+
* @param array<string, mixed> $data
145146
*/
146147
public function __construct(?array $data = null)
147148
{
148-
$this->dataCaster = new DataCaster(
149-
array_merge($this->defaultCastHandlers, $this->castHandlers),
150-
null,
151-
null,
152-
false,
153-
);
149+
$this->dataCaster = $this->dataCaster();
154150

155151
$this->syncOriginal();
156152

@@ -162,7 +158,7 @@ public function __construct(?array $data = null)
162158
* properties, using any `setCamelCasedProperty()` methods
163159
* that may or may not exist.
164160
*
165-
* @param array<string, array|bool|float|int|object|string|null> $data
161+
* @param array<string, array<int|string, mixed>|bool|float|int|object|string|null> $data
166162
*
167163
* @return $this
168164
*/
@@ -184,13 +180,16 @@ public function fill(?array $data = null)
184180
* of this entity as an array. All values are accessed through the
185181
* __get() magic method so will have any casts, etc applied to them.
186182
*
187-
* @param bool $onlyChanged If true, only return values that have changed since object creation
183+
* @param bool $onlyChanged If true, only return values that have changed since object creation.
188184
* @param bool $cast If true, properties will be cast.
189185
* @param bool $recursive If true, inner entities will be cast as array as well.
186+
*
187+
* @return array<string, mixed>
190188
*/
191189
public function toArray(bool $onlyChanged = false, bool $cast = true, bool $recursive = false): array
192190
{
193-
$this->_cast = $cast;
191+
$originalCast = $this->_cast;
192+
$this->_cast = $cast;
194193

195194
$keys = array_filter(array_keys($this->attributes), static fn ($key): bool => ! str_starts_with($key, '_'));
196195

@@ -219,16 +218,18 @@ public function toArray(bool $onlyChanged = false, bool $cast = true, bool $recu
219218
}
220219
}
221220

222-
$this->_cast = true;
221+
$this->_cast = $originalCast;
223222

224223
return $return;
225224
}
226225

227226
/**
228227
* Returns the raw values of the current attributes.
229228
*
230-
* @param bool $onlyChanged If true, only return values that have changed since object creation
229+
* @param bool $onlyChanged If true, only return values that have changed since object creation.
231230
* @param bool $recursive If true, inner entities will be cast as array as well.
231+
*
232+
* @return array<string, mixed>
232233
*/
233234
public function toRawArray(bool $onlyChanged = false, bool $recursive = false): array
234235
{
@@ -370,8 +371,6 @@ public function syncOriginal()
370371
* Checks a property to see if it has changed since the entity
371372
* was created. Or, without a parameter, checks if any
372373
* properties have changed.
373-
*
374-
* @param string|null $key class property
375374
*/
376375
public function hasChanged(?string $key = null): bool
377376
{
@@ -500,7 +499,9 @@ private function normalizeValue(mixed $data): mixed
500499
}
501500

502501
/**
503-
* Set raw data array without any mutations
502+
* Set raw data array without any mutations.
503+
*
504+
* @param array<string, mixed> $data
504505
*
505506
* @return $this
506507
*/
@@ -513,42 +514,30 @@ public function injectRawData(array $data)
513514
return $this;
514515
}
515516

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);
526-
}
527-
528517
/**
529518
* 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.
519+
* and returns the DB column name, if any, or the original property name.
531520
*
532-
* @return string db column name
521+
* @return string Database column name.
533522
*/
534523
protected function mapProperty(string $key)
535524
{
536525
if ($this->datamap === []) {
537526
return $key;
538527
}
539528

540-
if (! empty($this->datamap[$key])) {
529+
if (array_key_exists($key, $this->datamap) && $this->datamap[$key] !== '') {
541530
return $this->datamap[$key];
542531
}
543532

544533
return $key;
545534
}
546535

547536
/**
548-
* Converts the given string|timestamp|DateTime|Time instance
537+
* Converts the given string|timestamp|DateTimeInterface instance
549538
* into the "CodeIgniter\I18n\Time" object.
550539
*
551-
* @param DateTime|float|int|string|Time $value
540+
* @param DateTimeInterface|float|int|string $value
552541
*
553542
* @return Time
554543
*
@@ -568,22 +557,50 @@ protected function mutateDate($value)
568557
* @param string $attribute Attribute name
569558
* @param string $method Allowed to "get" and "set"
570559
*
571-
* @return array|bool|float|int|object|string|null
560+
* @return array<int|string, mixed>|bool|float|int|object|string|null
572561
*
573562
* @throws CastException
574563
*/
575564
protected function castAs($value, string $attribute, string $method = 'get')
576565
{
577-
return $this->dataCaster
578-
// @TODO if $casts is readonly, we don't need the setTypes() method.
579-
->setTypes($this->casts)
580-
->castAs($value, $attribute, $method);
566+
if ($this->dataCaster() instanceof DataCaster) {
567+
return $this->dataCaster
568+
// @TODO if $casts is readonly, we don't need the setTypes() method.
569+
->setTypes($this->casts)
570+
->castAs($value, $attribute, $method);
571+
}
572+
573+
return $value;
574+
}
575+
576+
/**
577+
* Returns a DataCaster instance when casts are defined.
578+
* If no casts are configured, no DataCaster is created and null is returned.
579+
*/
580+
protected function dataCaster(): ?DataCaster
581+
{
582+
if ($this->casts === []) {
583+
$this->dataCaster = null;
584+
585+
return null;
586+
}
587+
588+
if (! $this->dataCaster instanceof DataCaster) {
589+
$this->dataCaster = new DataCaster(
590+
array_merge($this->defaultCastHandlers, $this->castHandlers),
591+
null,
592+
null,
593+
false,
594+
);
595+
}
596+
597+
return $this->dataCaster;
581598
}
582599

583600
/**
584-
* Support for json_encode()
601+
* Support for json_encode().
585602
*
586-
* @return array
603+
* @return array<string, mixed>
587604
*/
588605
#[ReturnTypeWillChange]
589606
public function jsonSerialize()
@@ -592,7 +609,7 @@ public function jsonSerialize()
592609
}
593610

594611
/**
595-
* Change the value of the private $_cast property
612+
* Change the value of the private $_cast property.
596613
*
597614
* @return bool|Entity
598615
*/
@@ -616,7 +633,7 @@ public function cast(?bool $cast = null)
616633
* $this->my_property = $p;
617634
* $this->setMyProperty() = $p;
618635
*
619-
* @param array|bool|float|int|object|string|null $value
636+
* @param array<int|string, mixed>|bool|float|int|object|string|null $value
620637
*
621638
* @return void
622639
*
@@ -646,7 +663,7 @@ public function __set(string $key, $value = null)
646663
}
647664

648665
// If a "`set` + $key" method exists, it is also a setter.
649-
if (method_exists($this, $method) && $method !== 'setAttributes') {
666+
if (method_exists($this, $method)) {
650667
$this->{$method}($value);
651668

652669
return;
@@ -667,11 +684,9 @@ public function __set(string $key, $value = null)
667684
* $p = $this->my_property
668685
* $p = $this->getMyProperty()
669686
*
670-
* @return array|bool|float|int|object|string|null
687+
* @return array<int|string, mixed>|bool|float|int|object|string|null
671688
*
672689
* @throws Exception
673-
*
674-
* @params string $key class property
675690
*/
676691
public function __get(string $key)
677692
{

0 commit comments

Comments
 (0)