- ArrayOf errors now catched in EntityTrait (event soon to be attached to it)

- Fixed some non-nullable defaulted to null vars in functions
- Added a new diff method for entities which skips nullable values if they are not defined.
This commit is contained in:
Dave M. 2026-07-13 12:25:39 +00:00
parent 709ed63323
commit 13b7cf06f1
9 changed files with 52 additions and 26 deletions

View File

@ -25,22 +25,26 @@ class ArrayOf implements \Ulmus\Entity\EntityValueModifier
sprintf("Given item '%s' is not a valid enum for type %s ; try one of theses instead : %s", $item, $this->type, implode("', '", array_map(fn($e) => $e->value, $this->type::cases())))
);
} else {
$value = $this->type::from($item);
$obj = $this->type::from($item);
}
}
elseif (class_exists($this->type)) {
if (is_subclass_of($this->type, JsonUnserializable::class)) {
$value = (new \ReflectionClass($this->type))->newInstanceWithoutConstructor();
$value->jsonUnserialize($item);
$obj = (new \ReflectionClass($this->type))->newInstanceWithoutConstructor();
$obj->jsonUnserialize($item);
}
else {
$value = new $this->type($item);
$obj = new $this->type($item);
}
}
else {
throw new \InvalidArgumentException("Given type for ArrayOf is incompatible with it's use. An object should be provided");
}
$return[$index] = $value;
$return[$index] = $obj;
}
}
if (! $property->type->builtIn) {
$cls = $property->type->type;

View File

@ -6,7 +6,6 @@ use Ulmus\EntityCollection;
class ArrayCollection extends EntityCollection implements JsonUnserializable
{
public function jsonUnserialize(array $json): void
{
$this->fromArray($json);

View File

@ -17,7 +17,7 @@ class DatasetHandler
protected bool $entityStrictFieldsDeclaration = false,
) {}
public function pull(object $entity) : Generator
public function pull(object $entity, bool $nullable = true) : Generator
{
foreach($this->entityResolver->fieldList(EntityResolver::KEY_ENTITY_NAME, true) as $key => $field) {
$attribute = $this->entityResolver->searchFieldAnnotation($key,[ Field::class ]);
@ -25,7 +25,7 @@ class DatasetHandler
if ( $entity->__isset($key) ) {
yield $attribute->name ?? $key => $entity->$key;
}
elseif ( $field->allowsNull() ) {
elseif ( $nullable && $field->allowsNull() ) {
yield $attribute->name ?? $key => null;
}
}

View File

@ -18,7 +18,7 @@ interface EntityInterface extends SearchableInterface /* extends \JsonSerializab
public function isLoaded() : bool;
public function jsonSerialize() : mixed;
public static function resolveEntity() : EntityResolver;
public static function repository(string $alias = Repository::DEFAULT_ALIAS, ConnectionAdapter $adapter = null) : Repository;
public static function repository(string $alias = Repository::DEFAULT_ALIAS, ?ConnectionAdapter $adapter = null) : Repository;
public static function entityCollection(...$arguments) : EntityCollection;
public static function queryBuilder() : QueryBuilderInterface;
public static function field($name, null|string|false $alias = Repository::DEFAULT_ALIAS) : EntityField;

View File

@ -28,7 +28,7 @@ class Datetime extends \DateTime implements EntityObjectInterface, \JsonSerializ
return $obj;
}
public function setTime($hour, $minute, $second = 0, $microsecond = 0) : \DateTimeInterface
public function setTime($hour, $minute, $second = 0, $microsecond = 0) : \DateTime
{
$fromParent = parent::setTime($hour, $minute, $second, $microsecond);
@ -45,14 +45,15 @@ class Datetime extends \DateTime implements EntityObjectInterface, \JsonSerializ
return $this->format($this->format);
}
#[\Deprecated]
public function formatLocale(string $format) : string
{
return strftime($format, $this->getTimestamp());
}
public function formatLocaleIntl(int $dateFormatter = \IntlDateFormatter::LONG, int $timeFormatter = \IntlDateFormatter::NONE) : string
public function formatLocaleIntl(int $dateFormatter = \IntlDateFormatter::LONG, int $timeFormatter = \IntlDateFormatter::NONE, ? string $pattern = null) : string
{
$formatter = new \IntlDateFormatter(\Locale::getDefault(), $dateFormatter, $timeFormatter, \Locale::getRegion(""));
$formatter = new \IntlDateFormatter(\Locale::getDefault(), $dateFormatter, $timeFormatter, \date_default_timezone_get(), \IntlDateFormatter::GREGORIAN, $pattern);
return $formatter->format($this->getTimestamp());
}

View File

@ -4,20 +4,15 @@ namespace Ulmus;
use Notes\Attribute\Ignore;
use Psr\Http\Message\ServerRequestInterface;
use Ulmus\{Attribute\Obj\JsonSerialize,
Attribute\Property\Join,
Attribute\Property\Relation,
Attribute\Property\ResettablePropertyInterface,
Attribute\Property\Virtual,
Common\EntityResolver,
Common\EntityField,
Entity\DatasetHandler,
Entity\EntityInterface,
QueryBuilder\QueryBuilderInterface};
use Ulmus\{Attribute\RegisterEntityEventInterface, Entity\EntityValueModifier, SearchRequest, Event};
use Ulmus\{Attribute\RegisterEntityEventInterface, Entity\EntityValueModifier, };
#[JsonSerialize(includeRelations: true)]
trait EntityTrait {
@ -33,7 +28,7 @@ trait EntityTrait {
protected array $entityDatasetUnmatchedFields = [];
#[Ignore]
protected DatasetHandler $datasetHandler;
public DatasetHandler $datasetHandler;
#[Ignore]
public string $loadedFromAdapter;
@ -82,7 +77,16 @@ trait EntityTrait {
foreach($generator as $field => $value) {
foreach($properties[$field]->attributes as $tag) {
if ( $tag->object instanceof EntityValueModifier ) {
$value = $tag->object->push($value, $properties[$field]);
try {
$value = $tag->object->push($value, $properties[$field]);
}
catch(\Throwable $ex) {
throw new \LogicException(
message: sprintf("An error occured trying to push an EntityValueModifier results : %s", $ex->getMessage()),
code: $ex->getCode(),
previous: $ex,
);
}
}
}
@ -128,6 +132,18 @@ trait EntityTrait {
}
}
#[Ignore]
public function entityGetDiffDataset() : array
{
$return = [];
foreach($this->datasetHandler->pull($this, false) as $field => $value) {
$return[$field] = static::repository()->adapter->adapter()->writableValue($value);
}
return $return;
}
#[Ignore]
public function resetVirtualProperties() : self
{
@ -217,7 +233,12 @@ trait EntityTrait {
return true;
}
return isset($this->$name);
if (isset($this->$name)) {
return true;
}
# Handling undefined 'nullable'
return new \ReflectionProperty($this, $name)->isInitialized($this);
}
#[Ignore]
@ -289,7 +310,7 @@ trait EntityTrait {
}
#[Ignore]
public static function repository(string $alias = Repository::DEFAULT_ALIAS, ConnectionAdapter $adapter = null) : Repository
public static function repository(string $alias = Repository::DEFAULT_ALIAS, ?ConnectionAdapter $adapter = null) : Repository
{
return Ulmus::repository(static::class, $alias, $adapter);
}

View File

@ -491,7 +491,7 @@ class MysqlQueryBuilder extends SqlQueryBuilder
}
}
public function getFragments(QueryFragmentInterface|array|\Stringable|string $fragment = null) : array
public function getFragments(QueryFragmentInterface|array|\Stringable|string|null $fragment = null) : array
{
return $fragment !== null ? array_filter($this->queryStack, fn($e) => is_a($e, $fragment, true) ) : $this->queryStack;
}
@ -501,7 +501,7 @@ class MysqlQueryBuilder extends SqlQueryBuilder
return $this->render();
}
public function addParameter(mixed $value, string $key = null) : string
public function addParameter(mixed $value, ?string $key = null) : string
{
if ( $this->parent ?? false ) {
return $this->parent->addParameter($value, $key);
@ -540,4 +540,5 @@ class MysqlQueryBuilder extends SqlQueryBuilder
return $next + 1;
}
}

View File

@ -337,7 +337,7 @@ class Repository implements RepositoryInterface
public function generateDatasetDiff(object $entity, bool $oldValues = false) : array
{
$array = array_change_key_case($entity->toArray());
$array = array_change_key_case($entity->entityGetDiffDataset());
$dataset = array_change_key_case($entity->entityGetDataset(false, true));

View File

@ -108,7 +108,7 @@ abstract class Ulmus
return static::$resolved[$entityClass] ?? static::$resolved[$entityClass] = new Common\EntityResolver($entityClass, static::$cache);
}
public static function repository(string $entityClass, string $alias = Repository::DEFAULT_ALIAS, ConnectionAdapter $adapter = null) : Repository
public static function repository(string $entityClass, string $alias = Repository::DEFAULT_ALIAS, ?ConnectionAdapter $adapter = null) : Repository
{
$adapter ??= $entityClass::resolveEntity()->sqlAdapter();
$cls = $adapter->adapter()->repositoryClass();