Skip to content
Merged
70 changes: 31 additions & 39 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 @@ -131,17 +130,19 @@ class Entity implements JsonSerializable
protected DataCaster $dataCaster;

/**
* 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)
{
Expand All @@ -162,7 +163,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 +185,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;
$lastCastStatus = $this->_cast;
Comment thread
neznaika0 marked this conversation as resolved.
Outdated
$this->_cast = $cast;

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

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

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

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 +376,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 +504,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 +519,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|Time instance
* into the "CodeIgniter\I18n\Time" object.
*
* @param DateTime|float|int|string|Time $value
* @param DateTimeInterface|float|int|string|Time $value
Comment thread
neznaika0 marked this conversation as resolved.
Outdated
*
* @return Time
*
Expand All @@ -568,7 +562,7 @@ 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
*/
Expand All @@ -581,9 +575,9 @@ protected function castAs($value, string $attribute, string $method = 'get')
}

/**
* Support for json_encode()
* Support for json_encode().
*
* @return array
* @return array<string, mixed>
*/
#[ReturnTypeWillChange]
public function jsonSerialize()
Expand All @@ -592,7 +586,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 @@ -616,7 +610,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 +640,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 +661,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
64 changes: 62 additions & 2 deletions tests/system/Entity/EntityTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,32 @@ public function testSetArrayToPropertyNamedAttributes(): void
$this->assertSame($expected, $entity->toRawArray());
}

public function testSetGetAttributesMethod(): void
{
$entity = new class () extends Entity {
protected $attributes = [
'foo' => null,
'attributes' => null,
];

public function setAttributes(string $value): self
{
$this->attributes['attributes'] = $value;

return $this;
}

public function getAttributes(): string
{
return $this->attributes['attributes'];
}
};

$entity->setAttributes('attributes');

$this->assertSame('attributes', $entity->getAttributes());
}

public function testSimpleSetAndGet(): void
{
$entity = $this->getEntity();
Expand Down Expand Up @@ -358,11 +384,11 @@ public function testCastIntBool(): void
];
};

$entity->setAttributes(['active' => '1']);
$entity->injectRawData(['active' => '1']);

$this->assertTrue($entity->active);

$entity->setAttributes(['active' => '0']);
$entity->injectRawData(['active' => '0']);

$this->assertFalse($entity->active);

Expand Down Expand Up @@ -1078,6 +1104,40 @@ public function testAsArraySwapped(): void
], $result);
}

public function testAsArrayRestoringCastStatus(): void
{
$entity = new class () extends Entity {
protected $attributes = [
'first' => null,
];
protected $original = [
'first' => null,
];
protected $casts = [
'first' => 'integer',
];
};
$entity->first = '2026 Year';

// Disabled casting properties, but we will allow casting in the method.
$entity->cast(false);
$beforeCast = $this->getPrivateProperty($entity, '_cast');
Comment thread
neznaika0 marked this conversation as resolved.
Outdated
$result = $entity->toArray(true, true);
$afterCast = $this->getPrivateProperty($entity, '_cast');

$this->assertSame(2026, $result['first']);
$this->assertSame($beforeCast, $afterCast);

// Enabled casting properties, but we will disallow casting in the method.
$entity->cast(true);
$beforeCast = $this->getPrivateProperty($entity, '_cast');
$result = $entity->toArray(true, false);
$afterCast = $this->getPrivateProperty($entity, '_cast');

$this->assertSame('2026 Year', $result['first']);
$this->assertSame($beforeCast, $afterCast);
}

public function testDataMappingIssetSwapped(): void
{
$entity = $this->getSimpleSwappedEntity();
Expand Down
6 changes: 3 additions & 3 deletions tests/system/Models/ValidationModelRuleGroupTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,7 @@ public function testUpdateEntityWithPropertyCleanValidationRulesTrueAndCallingCl

// Simulate to get the entity from the database.
$entity = new SimpleEntity();
$entity->setAttributes([
$entity->injectRawData([
'id' => '1',
'field1' => 'value1',
'field2' => 'value2',
Expand Down Expand Up @@ -421,7 +421,7 @@ public function testUpdateEntityWithPropertyCleanValidationRulesFalse(): void

// Simulate to get the entity from the database.
$entity = new SimpleEntity();
$entity->setAttributes([
$entity->injectRawData([
'id' => '1',
'field1' => 'value1',
'field2' => 'value2',
Expand Down Expand Up @@ -457,7 +457,7 @@ public function testInsertEntityValidateEntireRules(): void
};

$entity = new SimpleEntity();
$entity->setAttributes([
$entity->injectRawData([
'field1' => 'value1',
// field2 is missing
'field3' => '',
Expand Down
6 changes: 3 additions & 3 deletions tests/system/Models/ValidationModelTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,7 @@ public function testUpdateEntityWithPropertyCleanValidationRulesTrueAndCallingCl

// Simulate to get the entity from the database.
$entity = new SimpleEntity();
$entity->setAttributes([
$entity->injectRawData([
'id' => '1',
'field1' => 'value1',
'field2' => 'value2',
Expand Down Expand Up @@ -434,7 +434,7 @@ public function testUpdateEntityWithPropertyCleanValidationRulesFalse(): void

// Simulate to get the entity from the database.
$entity = new SimpleEntity();
$entity->setAttributes([
$entity->injectRawData([
'id' => '1',
'field1' => 'value1',
'field2' => 'value2',
Expand Down Expand Up @@ -470,7 +470,7 @@ public function testInsertEntityValidateEntireRules(): void
};

$entity = new SimpleEntity();
$entity->setAttributes([
$entity->injectRawData([
'field1' => 'value1',
// field2 is missing
'field3' => '',
Expand Down
3 changes: 1 addition & 2 deletions user_guide_src/source/changelogs/v4.7.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ Removed Deprecated Items
- **BaseModel:** The deprecated method ``transformDataRowToArray()`` has been removed.
- **Cache:** The deprecated return type ``false`` for ``CodeIgniter\Cache\CacheInterface::getMetaData()`` has been replaced with ``null`` type.
- **CodeIgniter:** The deprecated ``CodeIgniter\CodeIgniter::resolvePlatformExtensions()`` has been removed.
- **Entity:** The deprecated ``CodeIgniter\Entity\Entity::setAttributes()`` has been removed. Use ``CodeIgniter\Entity\Entity::injectRawData()`` instead.
- **IncomingRequest:** The deprecated methods has been removed:
- ``CodeIgniter\HTTP\IncomingRequest\detectURI()``
- ``CodeIgniter\HTTP\IncomingRequest\detectPath()``
Expand Down Expand Up @@ -250,7 +251,6 @@ Libraries
- **View:** Added the ability to override namespaced views (e.g., from modules/packages) by placing a matching file structure within the **app/Views/overrides** directory. See :ref:`Overriding Namespaced Views <views-overriding-namespaced-views>` for details.
- **Toolbar:** Fixed an issue where the Debug Toolbar was incorrectly injected into responses generated by third-party libraries (e.g., Dompdf) that use native PHP headers instead of the framework's Response object.


Commands
========

Expand Down Expand Up @@ -306,7 +306,6 @@ Changes
- **Paths:** Added support for changing the location of the ``.env`` file via the ``Paths::$envDirectory`` property.
- **Toolbar:** Added ``$disableOnHeaders`` property to **app/Config/Toolbar.php**.


************
Deprecations
************
Expand Down
Loading
Loading