Initial
This commit is contained in:
38
system/Entity/Cast/ArrayCast.php
Normal file
38
system/Entity/Cast/ArrayCast.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CodeIgniter 4 framework.
|
||||
*
|
||||
* (c) CodeIgniter Foundation <admin@codeigniter.com>
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Entity\Cast;
|
||||
|
||||
/**
|
||||
* Class ArrayCast
|
||||
*/
|
||||
class ArrayCast extends BaseCast
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function get($value, array $params = []): array
|
||||
{
|
||||
if (is_string($value) && (strpos($value, 'a:') === 0 || strpos($value, 's:') === 0)) {
|
||||
$value = unserialize($value);
|
||||
}
|
||||
|
||||
return (array) $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function set($value, array $params = []): string
|
||||
{
|
||||
return serialize($value);
|
||||
}
|
||||
}
|
||||
44
system/Entity/Cast/BaseCast.php
Normal file
44
system/Entity/Cast/BaseCast.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CodeIgniter 4 framework.
|
||||
*
|
||||
* (c) CodeIgniter Foundation <admin@codeigniter.com>
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Entity\Cast;
|
||||
|
||||
/**
|
||||
* Class BaseCast
|
||||
*/
|
||||
abstract class BaseCast implements CastInterface
|
||||
{
|
||||
/**
|
||||
* Get
|
||||
*
|
||||
* @param mixed $value Data
|
||||
* @param array $params Additional param
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function get($value, array $params = [])
|
||||
{
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set
|
||||
*
|
||||
* @param mixed $value Data
|
||||
* @param array $params Additional param
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function set($value, array $params = [])
|
||||
{
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
26
system/Entity/Cast/BooleanCast.php
Normal file
26
system/Entity/Cast/BooleanCast.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CodeIgniter 4 framework.
|
||||
*
|
||||
* (c) CodeIgniter Foundation <admin@codeigniter.com>
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Entity\Cast;
|
||||
|
||||
/**
|
||||
* Class BooleanCast
|
||||
*/
|
||||
class BooleanCast extends BaseCast
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function get($value, array $params = []): bool
|
||||
{
|
||||
return (bool) $value;
|
||||
}
|
||||
}
|
||||
34
system/Entity/Cast/CSVCast.php
Normal file
34
system/Entity/Cast/CSVCast.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CodeIgniter 4 framework.
|
||||
*
|
||||
* (c) CodeIgniter Foundation <admin@codeigniter.com>
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Entity\Cast;
|
||||
|
||||
/**
|
||||
* Class CSVCast
|
||||
*/
|
||||
class CSVCast extends BaseCast
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function get($value, array $params = []): array
|
||||
{
|
||||
return explode(',', $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function set($value, array $params = []): string
|
||||
{
|
||||
return implode(',', $value);
|
||||
}
|
||||
}
|
||||
38
system/Entity/Cast/CastInterface.php
Normal file
38
system/Entity/Cast/CastInterface.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CodeIgniter 4 framework.
|
||||
*
|
||||
* (c) CodeIgniter Foundation <admin@codeigniter.com>
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Entity\Cast;
|
||||
|
||||
/**
|
||||
* Interface CastInterface
|
||||
*/
|
||||
interface CastInterface
|
||||
{
|
||||
/**
|
||||
* Get
|
||||
*
|
||||
* @param mixed $value Data
|
||||
* @param array $params Additional param
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function get($value, array $params = []);
|
||||
|
||||
/**
|
||||
* Set
|
||||
*
|
||||
* @param mixed $value Data
|
||||
* @param array $params Additional param
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function set($value, array $params = []);
|
||||
}
|
||||
48
system/Entity/Cast/DatetimeCast.php
Normal file
48
system/Entity/Cast/DatetimeCast.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CodeIgniter 4 framework.
|
||||
*
|
||||
* (c) CodeIgniter Foundation <admin@codeigniter.com>
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Entity\Cast;
|
||||
|
||||
use CodeIgniter\I18n\Time;
|
||||
use DateTime;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Class DatetimeCast
|
||||
*/
|
||||
class DatetimeCast extends BaseCast
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function get($value, array $params = [])
|
||||
{
|
||||
if ($value instanceof Time) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if ($value instanceof DateTime) {
|
||||
return Time::createFromInstance($value);
|
||||
}
|
||||
|
||||
if (is_numeric($value)) {
|
||||
return Time::createFromTimestamp($value);
|
||||
}
|
||||
|
||||
if (is_string($value)) {
|
||||
return Time::parse($value);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
26
system/Entity/Cast/FloatCast.php
Normal file
26
system/Entity/Cast/FloatCast.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CodeIgniter 4 framework.
|
||||
*
|
||||
* (c) CodeIgniter Foundation <admin@codeigniter.com>
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Entity\Cast;
|
||||
|
||||
/**
|
||||
* Class FloatCast
|
||||
*/
|
||||
class FloatCast extends BaseCast
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function get($value, array $params = []): float
|
||||
{
|
||||
return (float) $value;
|
||||
}
|
||||
}
|
||||
26
system/Entity/Cast/IntegerCast.php
Normal file
26
system/Entity/Cast/IntegerCast.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CodeIgniter 4 framework.
|
||||
*
|
||||
* (c) CodeIgniter Foundation <admin@codeigniter.com>
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Entity\Cast;
|
||||
|
||||
/**
|
||||
* Class IntegerCast
|
||||
*/
|
||||
class IntegerCast extends BaseCast
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function get($value, array $params = []): int
|
||||
{
|
||||
return (int) $value;
|
||||
}
|
||||
}
|
||||
65
system/Entity/Cast/JsonCast.php
Normal file
65
system/Entity/Cast/JsonCast.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CodeIgniter 4 framework.
|
||||
*
|
||||
* (c) CodeIgniter Foundation <admin@codeigniter.com>
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Entity\Cast;
|
||||
|
||||
use CodeIgniter\Entity\Exceptions\CastException;
|
||||
use JsonException;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Class JsonCast
|
||||
*/
|
||||
class JsonCast extends BaseCast
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function get($value, array $params = [])
|
||||
{
|
||||
$associative = in_array('array', $params, true);
|
||||
|
||||
$tmp = $value !== null ? ($associative ? [] : new stdClass()) : null;
|
||||
|
||||
if (function_exists('json_decode')
|
||||
&& (
|
||||
(is_string($value)
|
||||
&& strlen($value) > 1
|
||||
&& in_array($value[0], ['[', '{', '"'], true))
|
||||
|| is_numeric($value)
|
||||
)
|
||||
) {
|
||||
try {
|
||||
$tmp = json_decode($value, $associative, 512, JSON_THROW_ON_ERROR);
|
||||
} catch (JsonException $e) {
|
||||
throw CastException::forInvalidJsonFormat($e->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
return $tmp;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function set($value, array $params = []): string
|
||||
{
|
||||
if (function_exists('json_encode')) {
|
||||
try {
|
||||
$value = json_encode($value, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
|
||||
} catch (JsonException $e) {
|
||||
throw CastException::forInvalidJsonFormat($e->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
26
system/Entity/Cast/ObjectCast.php
Normal file
26
system/Entity/Cast/ObjectCast.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CodeIgniter 4 framework.
|
||||
*
|
||||
* (c) CodeIgniter Foundation <admin@codeigniter.com>
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Entity\Cast;
|
||||
|
||||
/**
|
||||
* Class ObjectCast
|
||||
*/
|
||||
class ObjectCast extends BaseCast
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function get($value, array $params = []): object
|
||||
{
|
||||
return (object) $value;
|
||||
}
|
||||
}
|
||||
26
system/Entity/Cast/StringCast.php
Normal file
26
system/Entity/Cast/StringCast.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CodeIgniter 4 framework.
|
||||
*
|
||||
* (c) CodeIgniter Foundation <admin@codeigniter.com>
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Entity\Cast;
|
||||
|
||||
/**
|
||||
* Class StringCast
|
||||
*/
|
||||
class StringCast extends BaseCast
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function get($value, array $params = []): string
|
||||
{
|
||||
return (string) $value;
|
||||
}
|
||||
}
|
||||
34
system/Entity/Cast/TimestampCast.php
Normal file
34
system/Entity/Cast/TimestampCast.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CodeIgniter 4 framework.
|
||||
*
|
||||
* (c) CodeIgniter Foundation <admin@codeigniter.com>
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Entity\Cast;
|
||||
|
||||
use CodeIgniter\Entity\Exceptions\CastException;
|
||||
|
||||
/**
|
||||
* Class TimestampCast
|
||||
*/
|
||||
class TimestampCast extends BaseCast
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function get($value, array $params = [])
|
||||
{
|
||||
$value = strtotime($value);
|
||||
|
||||
if ($value === false) {
|
||||
throw CastException::forInvalidTimestamp();
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
28
system/Entity/Cast/URICast.php
Normal file
28
system/Entity/Cast/URICast.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CodeIgniter 4 framework.
|
||||
*
|
||||
* (c) CodeIgniter Foundation <admin@codeigniter.com>
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Entity\Cast;
|
||||
|
||||
use CodeIgniter\HTTP\URI;
|
||||
|
||||
/**
|
||||
* Class URICast
|
||||
*/
|
||||
class URICast extends BaseCast
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function get($value, array $params = []): URI
|
||||
{
|
||||
return $value instanceof URI ? $value : new URI($value);
|
||||
}
|
||||
}
|
||||
535
system/Entity/Entity.php
Normal file
535
system/Entity/Entity.php
Normal file
@@ -0,0 +1,535 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CodeIgniter 4 framework.
|
||||
*
|
||||
* (c) CodeIgniter Foundation <admin@codeigniter.com>
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Entity;
|
||||
|
||||
use CodeIgniter\Entity\Cast\ArrayCast;
|
||||
use CodeIgniter\Entity\Cast\BooleanCast;
|
||||
use CodeIgniter\Entity\Cast\CastInterface;
|
||||
use CodeIgniter\Entity\Cast\CSVCast;
|
||||
use CodeIgniter\Entity\Cast\DatetimeCast;
|
||||
use CodeIgniter\Entity\Cast\FloatCast;
|
||||
use CodeIgniter\Entity\Cast\IntegerCast;
|
||||
use CodeIgniter\Entity\Cast\JsonCast;
|
||||
use CodeIgniter\Entity\Cast\ObjectCast;
|
||||
use CodeIgniter\Entity\Cast\StringCast;
|
||||
use CodeIgniter\Entity\Cast\TimestampCast;
|
||||
use CodeIgniter\Entity\Cast\URICast;
|
||||
use CodeIgniter\Entity\Exceptions\CastException;
|
||||
use CodeIgniter\I18n\Time;
|
||||
use Exception;
|
||||
use JsonSerializable;
|
||||
use ReturnTypeWillChange;
|
||||
|
||||
/**
|
||||
* Entity encapsulation, for use with CodeIgniter\Model
|
||||
*/
|
||||
class Entity implements JsonSerializable
|
||||
{
|
||||
/**
|
||||
* Maps names used in sets and gets against unique
|
||||
* names within the class, allowing independence from
|
||||
* database column names.
|
||||
*
|
||||
* Example:
|
||||
* $datamap = [
|
||||
* 'class_name' => 'db_name'
|
||||
* ];
|
||||
*/
|
||||
protected $datamap = [];
|
||||
|
||||
protected $dates = [
|
||||
'created_at',
|
||||
'updated_at',
|
||||
'deleted_at',
|
||||
];
|
||||
|
||||
/**
|
||||
* Array of field names and the type of value to cast them as when
|
||||
* they are accessed.
|
||||
*/
|
||||
protected $casts = [];
|
||||
|
||||
/**
|
||||
* Custom convert handlers
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $castHandlers = [];
|
||||
|
||||
/**
|
||||
* Default convert handlers
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
private $defaultCastHandlers = [
|
||||
'array' => ArrayCast::class,
|
||||
'bool' => BooleanCast::class,
|
||||
'boolean' => BooleanCast::class,
|
||||
'csv' => CSVCast::class,
|
||||
'datetime' => DatetimeCast::class,
|
||||
'double' => FloatCast::class,
|
||||
'float' => FloatCast::class,
|
||||
'int' => IntegerCast::class,
|
||||
'integer' => IntegerCast::class,
|
||||
'json' => JsonCast::class,
|
||||
'object' => ObjectCast::class,
|
||||
'string' => StringCast::class,
|
||||
'timestamp' => TimestampCast::class,
|
||||
'uri' => URICast::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Holds the current values of all class vars.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $attributes = [];
|
||||
|
||||
/**
|
||||
* Holds original copies of all class vars so we can determine
|
||||
* what's actually been changed and not accidentally write
|
||||
* nulls where we shouldn't.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $original = [];
|
||||
|
||||
/**
|
||||
* Holds info whenever properties have to be casted
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $_cast = true;
|
||||
|
||||
/**
|
||||
* Allows filling in Entity parameters during construction.
|
||||
*/
|
||||
public function __construct(?array $data = null)
|
||||
{
|
||||
$this->syncOriginal();
|
||||
|
||||
$this->fill($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an array of key/value pairs and sets them as class
|
||||
* properties, using any `setCamelCasedProperty()` methods
|
||||
* that may or may not exist.
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function fill(?array $data = null)
|
||||
{
|
||||
if (! is_array($data)) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
$this->__set($key, $value);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* General method that will return all public and protected values
|
||||
* 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 $cast If true, properties will be casted.
|
||||
* @param bool $recursive If true, inner entities will be casted as array as well.
|
||||
*/
|
||||
public function toArray(bool $onlyChanged = false, bool $cast = true, bool $recursive = false): array
|
||||
{
|
||||
$this->_cast = $cast;
|
||||
|
||||
$keys = array_filter(array_keys($this->attributes), static function ($key) {
|
||||
return strpos($key, '_') !== 0;
|
||||
});
|
||||
|
||||
if (is_array($this->datamap)) {
|
||||
$keys = array_unique(
|
||||
array_merge(array_diff($keys, $this->datamap), array_keys($this->datamap))
|
||||
);
|
||||
}
|
||||
|
||||
$return = [];
|
||||
|
||||
// Loop over the properties, to allow magic methods to do their thing.
|
||||
foreach ($keys as $key) {
|
||||
if ($onlyChanged && ! $this->hasChanged($key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$return[$key] = $this->__get($key);
|
||||
|
||||
if ($recursive) {
|
||||
if ($return[$key] instanceof self) {
|
||||
$return[$key] = $return[$key]->toArray($onlyChanged, $cast, $recursive);
|
||||
} elseif (is_callable([$return[$key], 'toArray'])) {
|
||||
$return[$key] = $return[$key]->toArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->_cast = true;
|
||||
|
||||
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 $recursive If true, inner entities will be casted as array as well.
|
||||
*/
|
||||
public function toRawArray(bool $onlyChanged = false, bool $recursive = false): array
|
||||
{
|
||||
$return = [];
|
||||
|
||||
if (! $onlyChanged) {
|
||||
if ($recursive) {
|
||||
return array_map(static function ($value) use ($onlyChanged, $recursive) {
|
||||
if ($value instanceof self) {
|
||||
$value = $value->toRawArray($onlyChanged, $recursive);
|
||||
} elseif (is_callable([$value, 'toRawArray'])) {
|
||||
$value = $value->toRawArray();
|
||||
}
|
||||
|
||||
return $value;
|
||||
}, $this->attributes);
|
||||
}
|
||||
|
||||
return $this->attributes;
|
||||
}
|
||||
|
||||
foreach ($this->attributes as $key => $value) {
|
||||
if (! $this->hasChanged($key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($recursive) {
|
||||
if ($value instanceof self) {
|
||||
$value = $value->toRawArray($onlyChanged, $recursive);
|
||||
} elseif (is_callable([$value, 'toRawArray'])) {
|
||||
$value = $value->toRawArray();
|
||||
}
|
||||
}
|
||||
|
||||
$return[$key] = $value;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures our "original" values match the current values.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function syncOriginal()
|
||||
{
|
||||
$this->original = $this->attributes;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 $key
|
||||
*/
|
||||
public function hasChanged(?string $key = null): bool
|
||||
{
|
||||
// If no parameter was given then check all attributes
|
||||
if ($key === null) {
|
||||
return $this->original !== $this->attributes;
|
||||
}
|
||||
|
||||
// Key doesn't exist in either
|
||||
if (! array_key_exists($key, $this->original) && ! array_key_exists($key, $this->attributes)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// It's a new element
|
||||
if (! array_key_exists($key, $this->original) && array_key_exists($key, $this->attributes)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->original[$key] !== $this->attributes[$key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set raw data array without any mutations
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setAttributes(array $data)
|
||||
{
|
||||
$this->attributes = $data;
|
||||
|
||||
$this->syncOriginal();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the datamap to see if this column name is being mapped,
|
||||
* and returns the mapped name, if any, or the original name.
|
||||
*
|
||||
* @return mixed|string
|
||||
*/
|
||||
protected function mapProperty(string $key)
|
||||
{
|
||||
if (empty($this->datamap)) {
|
||||
return $key;
|
||||
}
|
||||
|
||||
if (! empty($this->datamap[$key])) {
|
||||
return $this->datamap[$key];
|
||||
}
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given string|timestamp|DateTime|Time instance
|
||||
* into the "CodeIgniter\I18n\Time" object.
|
||||
*
|
||||
* @param mixed $value
|
||||
*
|
||||
* @throws Exception
|
||||
*
|
||||
* @return mixed|Time
|
||||
*/
|
||||
protected function mutateDate($value)
|
||||
{
|
||||
return DatetimeCast::get($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the ability to cast an item as a specific data type.
|
||||
* Add ? at the beginning of $type (i.e. ?string) to get NULL
|
||||
* instead of casting $value if $value === null
|
||||
*
|
||||
* @param mixed $value Attribute value
|
||||
* @param string $attribute Attribute name
|
||||
* @param string $method Allowed to "get" and "set"
|
||||
*
|
||||
* @throws CastException
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function castAs($value, string $attribute, string $method = 'get')
|
||||
{
|
||||
if (empty($this->casts[$attribute])) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$type = $this->casts[$attribute];
|
||||
|
||||
$isNullable = false;
|
||||
|
||||
if (strpos($type, '?') === 0) {
|
||||
$isNullable = true;
|
||||
|
||||
if ($value === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$type = substr($type, 1);
|
||||
}
|
||||
|
||||
// In order not to create a separate handler for the
|
||||
// json-array type, we transform the required one.
|
||||
$type = $type === 'json-array' ? 'json[array]' : $type;
|
||||
|
||||
if (! in_array($method, ['get', 'set'], true)) {
|
||||
throw CastException::forInvalidMethod($method);
|
||||
}
|
||||
|
||||
$params = [];
|
||||
|
||||
// Attempt to retrieve additional parameters if specified
|
||||
// type[param, param2,param3]
|
||||
if (preg_match('/^(.+)\[(.+)\]$/', $type, $matches)) {
|
||||
$type = $matches[1];
|
||||
$params = array_map('trim', explode(',', $matches[2]));
|
||||
}
|
||||
|
||||
if ($isNullable) {
|
||||
$params[] = 'nullable';
|
||||
}
|
||||
|
||||
$type = trim($type, '[]');
|
||||
|
||||
$handlers = array_merge($this->defaultCastHandlers, $this->castHandlers);
|
||||
|
||||
if (empty($handlers[$type])) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if (! is_subclass_of($handlers[$type], CastInterface::class)) {
|
||||
throw CastException::forInvalidInterface($handlers[$type]);
|
||||
}
|
||||
|
||||
return $handlers[$type]::$method($value, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Support for json_encode()
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function jsonSerialize()
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the value of the private $_cast property
|
||||
*
|
||||
* @return bool|Entity
|
||||
*/
|
||||
public function cast(?bool $cast = null)
|
||||
{
|
||||
if ($cast === null) {
|
||||
return $this->_cast;
|
||||
}
|
||||
|
||||
$this->_cast = $cast;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic method to all protected/private class properties to be
|
||||
* easily set, either through a direct access or a
|
||||
* `setCamelCasedProperty()` method.
|
||||
*
|
||||
* Examples:
|
||||
* $this->my_property = $p;
|
||||
* $this->setMyProperty() = $p;
|
||||
*
|
||||
* @param mixed|null $value
|
||||
*
|
||||
* @throws Exception
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function __set(string $key, $value = null)
|
||||
{
|
||||
$key = $this->mapProperty($key);
|
||||
|
||||
// Check if the field should be mutated into a date
|
||||
if (in_array($key, $this->dates, true)) {
|
||||
$value = $this->mutateDate($value);
|
||||
}
|
||||
|
||||
$value = $this->castAs($value, $key, 'set');
|
||||
|
||||
// if a set* method exists for this key, use that method to
|
||||
// insert this value. should be outside $isNullable check,
|
||||
// so maybe wants to do sth with null value automatically
|
||||
$method = 'set' . str_replace(' ', '', ucwords(str_replace(['-', '_'], ' ', $key)));
|
||||
|
||||
if (method_exists($this, $method)) {
|
||||
$this->{$method}($value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
// Otherwise, just the value. This allows for creation of new
|
||||
// class properties that are undefined, though they cannot be
|
||||
// saved. Useful for grabbing values through joins, assigning
|
||||
// relationships, etc.
|
||||
$this->attributes[$key] = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic method to allow retrieval of protected and private class properties
|
||||
* either by their name, or through a `getCamelCasedProperty()` method.
|
||||
*
|
||||
* Examples:
|
||||
* $p = $this->my_property
|
||||
* $p = $this->getMyProperty()
|
||||
*
|
||||
* @throws Exception
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get(string $key)
|
||||
{
|
||||
$key = $this->mapProperty($key);
|
||||
|
||||
$result = null;
|
||||
|
||||
// Convert to CamelCase for the method
|
||||
$method = 'get' . str_replace(' ', '', ucwords(str_replace(['-', '_'], ' ', $key)));
|
||||
|
||||
// if a set* method exists for this key,
|
||||
// use that method to insert this value.
|
||||
if (method_exists($this, $method)) {
|
||||
$result = $this->{$method}();
|
||||
}
|
||||
|
||||
// Otherwise return the protected property
|
||||
// if it exists.
|
||||
elseif (array_key_exists($key, $this->attributes)) {
|
||||
$result = $this->attributes[$key];
|
||||
}
|
||||
|
||||
// Do we need to mutate this into a date?
|
||||
if (in_array($key, $this->dates, true)) {
|
||||
$result = $this->mutateDate($result);
|
||||
}
|
||||
// Or cast it as something?
|
||||
elseif ($this->_cast) {
|
||||
$result = $this->castAs($result, $key);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a property exists names $key, or a getter method
|
||||
* exists named like for __get().
|
||||
*/
|
||||
public function __isset(string $key): bool
|
||||
{
|
||||
$key = $this->mapProperty($key);
|
||||
|
||||
$method = 'get' . str_replace(' ', '', ucwords(str_replace(['-', '_'], ' ', $key)));
|
||||
|
||||
if (method_exists($this, $method)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return isset($this->attributes[$key]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsets an attribute property.
|
||||
*/
|
||||
public function __unset(string $key): void
|
||||
{
|
||||
unset($this->attributes[$key]);
|
||||
}
|
||||
}
|
||||
78
system/Entity/Exceptions/CastException.php
Normal file
78
system/Entity/Exceptions/CastException.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CodeIgniter 4 framework.
|
||||
*
|
||||
* (c) CodeIgniter Foundation <admin@codeigniter.com>
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Entity\Exceptions;
|
||||
|
||||
use CodeIgniter\Exceptions\FrameworkException;
|
||||
|
||||
/**
|
||||
* CastException is thrown for invalid cast initialization and management.
|
||||
*/
|
||||
class CastException extends FrameworkException
|
||||
{
|
||||
/**
|
||||
* Thrown when the cast class does not extends BaseCast.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function forInvalidInterface(string $class)
|
||||
{
|
||||
return new static(lang('Cast.baseCastMissing', [$class]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when the Json format is invalid.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function forInvalidJsonFormat(int $error)
|
||||
{
|
||||
switch ($error) {
|
||||
case JSON_ERROR_DEPTH:
|
||||
return new static(lang('Cast.jsonErrorDepth'));
|
||||
|
||||
case JSON_ERROR_STATE_MISMATCH:
|
||||
return new static(lang('Cast.jsonErrorStateMismatch'));
|
||||
|
||||
case JSON_ERROR_CTRL_CHAR:
|
||||
return new static(lang('Cast.jsonErrorCtrlChar'));
|
||||
|
||||
case JSON_ERROR_SYNTAX:
|
||||
return new static(lang('Cast.jsonErrorSyntax'));
|
||||
|
||||
case JSON_ERROR_UTF8:
|
||||
return new static(lang('Cast.jsonErrorUtf8'));
|
||||
|
||||
default:
|
||||
return new static(lang('Cast.jsonErrorUnknown'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when the cast method is not `get` or `set`.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function forInvalidMethod(string $method)
|
||||
{
|
||||
return new static(lang('Cast.invalidCastMethod', [$method]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when the casting timestamp is not correct timestamp.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function forInvalidTimestamp()
|
||||
{
|
||||
return new static(lang('Cast.invalidTimestamp'));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user