Skip to content
4 changes: 2 additions & 2 deletions system/Entity/Cast/DatetimeCast.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
namespace CodeIgniter\Entity\Cast;

use CodeIgniter\I18n\Time;
use DateTime;
use DateTimeInterface;
use Exception;

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

if ($value instanceof DateTime) {
if ($value instanceof DateTimeInterface) {
return Time::createFromInstance($value);
}

Expand Down
117 changes: 67 additions & 50 deletions system/Entity/Entity.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
use CodeIgniter\Entity\Cast\URICast;
use CodeIgniter\Entity\Exceptions\CastException;
use CodeIgniter\I18n\Time;
use DateTime;
use DateTimeInterface;
use Exception;
use JsonSerializable;
Expand Down Expand Up @@ -79,14 +78,14 @@ class Entity implements JsonSerializable
protected $casts = [];

/**
* Custom convert handlers
* Custom convert handlers.
*
* @var array<string, string>
*/
protected $castHandlers = [];

/**
* Default convert handlers
* Default convert handlers.
*
* @var array<string, string>
*/
Expand Down Expand Up @@ -128,29 +127,26 @@ class Entity implements JsonSerializable
/**
* The data caster.
*/
protected DataCaster $dataCaster;
protected ?DataCaster $dataCaster = null;

/**
* Holds info whenever properties have to be casted
* Holds info whenever properties have to be casted.
*/
private bool $_cast = true;

/**
* Indicates whether all attributes are scalars (for optimization)
* Indicates whether all attributes are scalars (for optimization).
*/
private bool $_onlyScalars = true;

/**
* Allows filling in Entity parameters during construction.
*
* @param array<string, mixed> $data
*/
public function __construct(?array $data = null)
{
$this->dataCaster = new DataCaster(
array_merge($this->defaultCastHandlers, $this->castHandlers),
null,
null,
false,
);
$this->dataCaster = $this->dataCaster();

$this->syncOriginal();

Expand All @@ -162,7 +158,7 @@ public function __construct(?array $data = null)
* properties, using any `setCamelCasedProperty()` methods
* that may or may not exist.
*
* @param array<string, array|bool|float|int|object|string|null> $data
* @param array<string, array<int|string, mixed>|bool|float|int|object|string|null> $data
*
* @return $this
*/
Expand All @@ -184,13 +180,16 @@ public function fill(?array $data = null)
* of this entity as an array. All values are accessed through the
* __get() magic method so will have any casts, etc applied to them.
*
* @param bool $onlyChanged If true, only return values that have changed since object creation
* @param bool $onlyChanged If true, only return values that have changed since object creation.
* @param bool $cast If true, properties will be cast.
* @param bool $recursive If true, inner entities will be cast as array as well.
*
* @return array<string, mixed>
*/
public function toArray(bool $onlyChanged = false, bool $cast = true, bool $recursive = false): array
{
$this->_cast = $cast;
$originalCast = $this->_cast;
$this->_cast = $cast;

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

Expand Down Expand Up @@ -219,16 +218,18 @@ public function toArray(bool $onlyChanged = false, bool $cast = true, bool $recu
}
}

$this->_cast = true;
$this->_cast = $originalCast;

return $return;
}

/**
* Returns the raw values of the current attributes.
*
* @param bool $onlyChanged If true, only return values that have changed since object creation
* @param bool $onlyChanged If true, only return values that have changed since object creation.
* @param bool $recursive If true, inner entities will be cast as array as well.
*
* @return array<string, mixed>
*/
public function toRawArray(bool $onlyChanged = false, bool $recursive = false): array
{
Expand Down Expand Up @@ -370,8 +371,6 @@ public function syncOriginal()
* Checks a property to see if it has changed since the entity
* was created. Or, without a parameter, checks if any
* properties have changed.
*
* @param string|null $key class property
*/
public function hasChanged(?string $key = null): bool
{
Expand Down Expand Up @@ -500,7 +499,9 @@ private function normalizeValue(mixed $data): mixed
}

/**
* Set raw data array without any mutations
* Set raw data array without any mutations.
*
* @param array<string, mixed> $data
*
* @return $this
*/
Expand All @@ -513,42 +514,30 @@ public function injectRawData(array $data)
return $this;
}

/**
* Set raw data array without any mutations
*
* @return $this
*
* @deprecated Use injectRawData() instead.
*/
public function setAttributes(array $data)
{
return $this->injectRawData($data);
}

/**
* Checks the datamap to see if this property name is being mapped,
* and returns the db column name, if any, or the original property name.
* and returns the DB column name, if any, or the original property name.
*
* @return string db column name
* @return string Database column name.
*/
protected function mapProperty(string $key)
{
if ($this->datamap === []) {
return $key;
}

if (! empty($this->datamap[$key])) {
if (array_key_exists($key, $this->datamap) && $this->datamap[$key] !== '') {
return $this->datamap[$key];
}

return $key;
}

/**
* Converts the given string|timestamp|DateTime|Time instance
* Converts the given string|timestamp|DateTimeInterface instance
* into the "CodeIgniter\I18n\Time" object.
*
* @param DateTime|float|int|string|Time $value
* @param DateTimeInterface|float|int|string $value
*
* @return Time
*
Expand All @@ -568,22 +557,49 @@ protected function mutateDate($value)
* @param string $attribute Attribute name
* @param string $method Allowed to "get" and "set"
*
* @return array|bool|float|int|object|string|null
* @return array<int|string, mixed>|bool|float|int|object|string|null
*
* @throws CastException
*/
protected function castAs($value, string $attribute, string $method = 'get')
{
return $this->dataCaster
// @TODO if $casts is readonly, we don't need the setTypes() method.
->setTypes($this->casts)
->castAs($value, $attribute, $method);
if ($this->dataCaster() instanceof DataCaster) {
return $this->dataCaster
// @TODO if $casts is readonly, we don't need the setTypes() method.
->setTypes($this->casts)
->castAs($value, $attribute, $method);
}

return $value;
}

/**
* This method allows you to refuse to contain an unnecessary DataCaster if you do not use casting.
*/
Comment on lines +576 to +578
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/**
* This method allows you to refuse to contain an unnecessary DataCaster if you do not use casting.
*/
/**
* Returns a DataCaster instance when casts are defined.
* If no casts are configured, no DataCaster is created and null is returned.
*/

protected function dataCaster(): ?DataCaster
{
if ($this->casts === []) {
$this->dataCaster = null;

return null;
}

if (! $this->dataCaster instanceof DataCaster) {
$this->dataCaster = new DataCaster(
array_merge($this->defaultCastHandlers, $this->castHandlers),
null,
null,
false,
);
}

return $this->dataCaster;
}

/**
* Support for json_encode()
* Support for json_encode().
*
* @return array
* @return array<string, mixed>
*/
#[ReturnTypeWillChange]
public function jsonSerialize()
Expand All @@ -592,7 +608,7 @@ public function jsonSerialize()
}

/**
* Change the value of the private $_cast property
* Change the value of the private $_cast property.
*
* @return bool|Entity
*/
Expand All @@ -604,6 +620,9 @@ public function cast(?bool $cast = null)

$this->_cast = $cast;

// Synchronize option with DataCaster initialization
$this->dataCaster();

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not needed as $_cast does not decide about DataCaster initialization.

return $this;
}

Expand All @@ -616,7 +635,7 @@ public function cast(?bool $cast = null)
* $this->my_property = $p;
* $this->setMyProperty() = $p;
*
* @param array|bool|float|int|object|string|null $value
* @param array<int|string, mixed>|bool|float|int|object|string|null $value
*
* @return void
*
Expand Down Expand Up @@ -646,7 +665,7 @@ public function __set(string $key, $value = null)
}

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

return;
Expand All @@ -667,11 +686,9 @@ public function __set(string $key, $value = null)
* $p = $this->my_property
* $p = $this->getMyProperty()
*
* @return array|bool|float|int|object|string|null
* @return array<int|string, mixed>|bool|float|int|object|string|null
*
* @throws Exception
*
* @params string $key class property
*/
public function __get(string $key)
{
Expand Down
Loading
Loading