diff --git a/src/Cursor/CacheableCursor.php b/src/Cursor/CacheableCursor.php index fbb3cb78..1793385f 100644 --- a/src/Cursor/CacheableCursor.php +++ b/src/Cursor/CacheableCursor.php @@ -52,7 +52,12 @@ class CacheableCursor extends Cursor protected function getCursor(): Iterator { // Returns original (non-cached) cursor - if ($this->ignoreCache || $this->position >= self::DOCUMENT_LIMIT) { + if ($this->ignoreCache) { + return $this->getOriginalCursor(); + } + + // Returns original (non-cached) cursor + if ($this->position >= self::DOCUMENT_LIMIT) { return $this->getOriginalCursor(); } diff --git a/src/Cursor/SchemaCacheableCursor.php b/src/Cursor/SchemaCacheableCursor.php index 1d250618..3cc9fd58 100644 --- a/src/Cursor/SchemaCacheableCursor.php +++ b/src/Cursor/SchemaCacheableCursor.php @@ -53,7 +53,12 @@ class SchemaCacheableCursor extends SchemaCursor protected function getCursor(): Iterator { // Returns original (non-cached) cursor - if ($this->ignoreCache || $this->position >= self::DOCUMENT_LIMIT) { + if ($this->ignoreCache) { + return $this->getOriginalCursor(); + } + + // Returns original (non-cached) cursor + if ($this->position >= self::DOCUMENT_LIMIT) { return $this->getOriginalCursor(); } diff --git a/src/DataMapper/SchemaMapper.php b/src/DataMapper/SchemaMapper.php index eb631aad..7fff81f1 100644 --- a/src/DataMapper/SchemaMapper.php +++ b/src/DataMapper/SchemaMapper.php @@ -81,8 +81,14 @@ public function parseField(mixed $value, string $fieldType): mixed if (method_exists($this->schema, $fieldType)) { return $this->schema->$fieldType($value); } + + // Returns null or an empty array + if (null === $value) { + return $value; + } + // Returns null or an empty array - if (null === $value || is_array($value) && empty($value)) { + if (is_array($value) && empty($value)) { return $value; } @@ -106,8 +112,6 @@ public function parseField(mixed $value, string $fieldType): mixed * * @param mixed $value value to be casted * @param string $type type to which the $value should be casted to - * - * @return mixed */ protected function cast(mixed $value, string $type): mixed { diff --git a/src/LegacyRecord.php b/src/LegacyRecord.php index 856a0d2f..12eecc16 100644 --- a/src/LegacyRecord.php +++ b/src/LegacyRecord.php @@ -30,17 +30,13 @@ class LegacyRecord implements ModelInterface, HasSchemaInterface /** * Name of the collection where this kind of Entity is going to be saved or * retrieved from. - * - * @var string */ - protected $collection = null; + protected ?string $collection = null; /** * @see https://docs.mongodb.com/manual/reference/write-concern/ - * - * @var int */ - protected $writeConcern = 1; + protected int $writeConcern = 1; /** * Describes the Schema fields of the model. Optionally you can set it to @@ -48,9 +44,9 @@ class LegacyRecord implements ModelInterface, HasSchemaInterface * * @see Mongolid\Schema\Schema::$fields * - * @var string|string[] - */ - protected $fields = [ + * @var string[] | string + */ + protected array | string $fields = [ '_id' => 'objectId', 'created_at' => 'createdAtTimestamp', 'updated_at' => 'updatedAtTimestamp', @@ -61,10 +57,8 @@ class LegacyRecord implements ModelInterface, HasSchemaInterface * that are not specified in the $fields property. This is useful if you * does not have a strict document format or if you want to take full * advantage of the "schemaless" nature of MongoDB. - * - * @var bool */ - public $dynamic = true; + public bool $dynamic = true; /** * This attribute is used to eager load models for @@ -72,10 +66,8 @@ class LegacyRecord implements ModelInterface, HasSchemaInterface * models using this parameter. Every time this * model is queried, it will load its referenced * models together. - * - * @var array */ - public $with = []; + public array $with = []; /** * Whether the model should manage the `created_at` and `updated_at` @@ -220,26 +212,24 @@ public static function firstOrNew($id) /** * Handle dynamic method calls into the model. * - * @param mixed $method name of the method that is being called - * @param mixed $parameters parameters of $method + * @param string $method name of the method that is being called + * @param array $parameters parameters of $method * * @throws BadMethodCallException in case of invalid methods be called - * - * @return mixed */ - public function __call($method, $parameters) + public function __call(string $method, array $parameters): mixed { $value = $parameters[0] ?? null; // Alias to attach - if ('attachTo' == substr($method, 0, 8)) { + if (str_starts_with($method, 'attachTo')) { $field = lcfirst(substr($method, 8)); return $this->attach($field, $value); } // Alias to embed - if ('embedTo' == substr($method, 0, 7)) { + if (str_starts_with($method, 'embedTo')) { $field = lcfirst(substr($method, 7)); return $this->embed($field, $value); @@ -248,7 +238,7 @@ public function __call($method, $parameters) throw new BadMethodCallException( sprintf( 'The following method can not be reached or does not exist: %s@%s', - get_class($this), + static::class, $method ) ); @@ -277,7 +267,7 @@ public function getCollectionName(): string throw new NoCollectionNameException(); } - return $this->collection ? $this->collection : $this->getSchema()->collection; + return $this->getSchema()->collection; } /** @@ -308,7 +298,7 @@ public function getSchema(): Schema } $schema = new DynamicSchema(); - $schema->entityClass = get_class($this); + $schema->entityClass = static::class; $schema->fields = $this->fields; $schema->dynamic = $this->dynamic; $schema->collection = $this->collection; @@ -319,26 +309,24 @@ public function getSchema(): Schema /** * Will check if the current value of $fields property is the name of a * Schema class and instantiate it if possible. - * - * @return Schema|null */ - protected function instantiateSchemaInFields() + protected function instantiateSchemaInFields(): ?Schema { if (is_string($this->fields)) { if (is_subclass_of($instance = Container::make($this->fields), Schema::class)) { return $instance; } } + + return null; } /** * Performs the given action into database. * * @param string $action datamapper function to execute - * - * @return bool */ - protected function execute(string $action) + protected function execute(string $action): bool { if (!$this->getCollectionName()) { return false; @@ -364,7 +352,7 @@ protected function execute(string $action) */ protected static function getDataMapperInstance() { - $instance = Container::make(get_called_class()); + $instance = Container::make(static::class); if (!$instance->getCollectionName()) { throw new NoCollectionNameException(); @@ -389,10 +377,6 @@ public function bsonSerialize(): object|array ->map($this, array_merge($this->fillable, $this->guarded), $this->dynamic, $this->timestamps); } - /** - * @param array $data - * @return void - */ public function bsonUnserialize(array $data): void { $this->fill($data, true); diff --git a/src/Model/AbstractModel.php b/src/Model/AbstractModel.php index 81f197cd..3bb4c36b 100644 --- a/src/Model/AbstractModel.php +++ b/src/Model/AbstractModel.php @@ -171,11 +171,10 @@ public function delete(): bool /** * Query model on database to retrieve an updated version of its attributes. - * @return self */ public function fresh(): self { - return $this->first($this->_id); + return static::first($this->_id); } /** diff --git a/src/Model/AttributesService.php b/src/Model/AttributesService.php index e7ef0dbe..82dbdaf7 100644 --- a/src/Model/AttributesService.php +++ b/src/Model/AttributesService.php @@ -1,8 +1,11 @@ fillable || in_array($key, $object->fillable)) && !in_array($key, $object->guarded))) { if ($value instanceof stdClass) { - $value = json_decode(json_encode($value), true); // cast to array + $value = json_decode(json_encode($value, JSON_THROW_ON_ERROR), true, 512, JSON_THROW_ON_ERROR); // cast to array } $object->setDocumentAttribute($key, $value); diff --git a/src/Model/Casts/DateTime/BaseDateTimeCast.php b/src/Model/Casts/DateTime/BaseDateTimeCast.php index 91d39e06..85a0f0b2 100644 --- a/src/Model/Casts/DateTime/BaseDateTimeCast.php +++ b/src/Model/Casts/DateTime/BaseDateTimeCast.php @@ -9,14 +9,8 @@ abstract class BaseDateTimeCast implements CastInterface { - /** - * @param UTCDateTime|null $value - */ abstract public function get(mixed $value): ?DateTimeInterface; - /** - * @param DateTimeInterface|UTCDateTimeInterface|null $value - */ public function set(mixed $value): UTCDateTime|null { if (is_null($value)) { diff --git a/src/Model/Casts/DateTime/DateTimeCast.php b/src/Model/Casts/DateTime/DateTimeCast.php index 1ba6fac9..0c38dc40 100644 --- a/src/Model/Casts/DateTime/DateTimeCast.php +++ b/src/Model/Casts/DateTime/DateTimeCast.php @@ -8,9 +8,6 @@ class DateTimeCast extends BaseDateTimeCast { - /** - * @param UTCDateTime|null $value - */ public function get(mixed $value): ?DateTime { if (is_null($value)) { diff --git a/src/Model/Casts/DateTime/ImmutableDateTimeCast.php b/src/Model/Casts/DateTime/ImmutableDateTimeCast.php index 793659ab..dda0a707 100644 --- a/src/Model/Casts/DateTime/ImmutableDateTimeCast.php +++ b/src/Model/Casts/DateTime/ImmutableDateTimeCast.php @@ -8,9 +8,6 @@ class ImmutableDateTimeCast extends BaseDateTimeCast { - /** - * @param UTCDateTime|null $value - */ public function get(mixed $value): ?DateTimeImmutable { if (is_null($value)) { diff --git a/src/Model/Exception/InvalidFieldNameException.php b/src/Model/Exception/InvalidFieldNameException.php index b459a8e7..b57a4259 100644 --- a/src/Model/Exception/InvalidFieldNameException.php +++ b/src/Model/Exception/InvalidFieldNameException.php @@ -1,4 +1,5 @@ fillable || in_array($key, $object->fillable)) && !in_array($key, $object->guarded))) { if ($value instanceof stdClass) { - $value = json_decode(json_encode($value), true); // cast to array + $value = json_decode(json_encode($value, JSON_THROW_ON_ERROR), true, 512, JSON_THROW_ON_ERROR); // cast to array } $object->setDocumentAttribute($key, $value); diff --git a/src/Model/HasLegacyAttributesTrait.php b/src/Model/HasLegacyAttributesTrait.php index c4f279e0..37704a14 100644 --- a/src/Model/HasLegacyAttributesTrait.php +++ b/src/Model/HasLegacyAttributesTrait.php @@ -107,9 +107,15 @@ public function fill(array $input, bool $force = false): HasAttributesInterface continue; } - if ((empty($this->fillable) || in_array($key, $this->fillable)) && !in_array($key, $this->guarded)) { - $this->setAttribute($key, $value); + if (!(empty($this->fillable) || in_array($key, $this->fillable))) { + continue; + } + + if (in_array($key, $this->guarded)) { + continue; } + + $this->setAttribute($key, $value); } return $this; diff --git a/src/Model/HasLegacyRelationsTrait.php b/src/Model/HasLegacyRelationsTrait.php index 82cc89d6..87dfc6a1 100644 --- a/src/Model/HasLegacyRelationsTrait.php +++ b/src/Model/HasLegacyRelationsTrait.php @@ -61,13 +61,13 @@ protected function referencesOne(string $entity, string $field, bool $cacheable /** * Returns the cursor for the referenced documents as objects. * - * @param string $entity class of the entity or of the schema of the entity - * @param string $field the field where the _ids are stored - * @param bool $cacheable retrieves a CacheableCursor instead + * @param string $entity class of the entity or of the schema of the entity + * @param string $field the field where the _ids are stored + * @param bool $cacheable retrieves a CacheableCursor instead * - * @return array + * @throws BindingResolutionException */ - protected function referencesMany(string $entity, string $field, bool $cacheable = true) + protected function referencesMany(string $entity, string $field, bool $cacheable = true): CursorInterface { $referencedIds = (array) $this->$field; @@ -95,11 +95,10 @@ protected function referencesMany(string $entity, string $field, bool $cacheable * Return a embedded documents as object. * * @param string $entity class of the entity or of the schema of the entity - * @param string $field field where the embedded document is stored - * - * @return LegacyRecord|Schema|null + * @param string $field field where the embedded document is stored + * @throws BindingResolutionException */ - protected function embedsOne(string $entity, string $field) + protected function embedsOne(string $entity, string $field): LegacyRecord|Schema|null { if (is_subclass_of($entity, Schema::class)) { $entity = (new $entity())->entityClass; @@ -118,11 +117,12 @@ protected function embedsOne(string $entity, string $field) * Return array of embedded documents as objects. * * @param string $entity class of the entity or of the schema of the entity - * @param string $field field where the embedded documents are stored + * @param string $field field where the embedded documents are stored * * @return CursorInterface Array with the embedded documents + * @throws BindingResolutionException */ - protected function embedsMany(string $entity, string $field) + protected function embedsMany(string $entity, string $field): CursorInterface { if (is_subclass_of($entity, Schema::class)) { $entity = (new $entity())->entityClass; @@ -142,9 +142,10 @@ protected function embedsMany(string $entity, string $field) * _id for the document if it's not present. * * @param string $field field to where the $obj will be embedded - * @param mixed $obj document or model instance + * @param mixed $obj document or model instance + * @throws BindingResolutionException */ - public function embed(string $field, &$obj) + public function embed(string $field, mixed &$obj): void { $embedder = Container::make(DocumentEmbedder::class); $embedder->embed($this, $field, $obj); @@ -155,9 +156,10 @@ public function embed(string $field, &$obj) * the _id of the given $obj. * * @param string $field name of the field where the $obj is embeded - * @param mixed $obj document, model instance or _id + * @param mixed $obj document, model instance or _id + * @throws BindingResolutionException */ - public function unembed(string $field, &$obj) + public function unembed(string $field, mixed &$obj): void { $embedder = Container::make(DocumentEmbedder::class); $embedder->unembed($this, $field, $obj); @@ -168,9 +170,10 @@ public function unembed(string $field, &$obj) * _id for the document if it's not present. * * @param string $field name of the field where the reference will be stored - * @param mixed $obj document, model instance or _id to be referenced + * @param mixed $obj document, model instance or _id to be referenced + * @throws BindingResolutionException */ - public function attach(string $field, &$obj) + public function attach(string $field, mixed &$obj): void { $embedder = Container::make(DocumentEmbedder::class); $embedder->attach($this, $field, $obj); @@ -181,9 +184,10 @@ public function attach(string $field, &$obj) * _id of the given $obj from inside the given $field. * * @param string $field field where the reference is stored - * @param mixed $obj document, model instance or _id that have been referenced by $field + * @param mixed $obj document, model instance or _id that have been referenced by $field + * @throws BindingResolutionException */ - public function detach(string $field, &$obj) + public function detach(string $field, mixed &$obj): void { $embedder = Container::make(DocumentEmbedder::class); $embedder->detach($this, $field, $obj); @@ -191,9 +195,9 @@ public function detach(string $field, &$obj) /** * @return mixed|null - * @throws \Illuminate\Contracts\Container\BindingResolutionException + * @throws BindingResolutionException */ - private function getDocumentFromCache(ModelInterface $entityInstance, string $referencedId) + private function getDocumentFromCache(ModelInterface $entityInstance, string $referencedId): mixed { /** @var CacheComponentInterface $cacheComponent */ $cacheComponent = Container::make(CacheComponentInterface::class); diff --git a/src/Model/PolymorphableModelInterface.php b/src/Model/PolymorphableModelInterface.php index 97289af2..dfa61971 100644 --- a/src/Model/PolymorphableModelInterface.php +++ b/src/Model/PolymorphableModelInterface.php @@ -1,4 +1,5 @@ [ 'key' => '_id', 'model' => Price::class diff --git a/tests/Stubs/Legacy/Shop.php b/tests/Stubs/Legacy/Shop.php index a2cab867..6ee47180 100644 --- a/tests/Stubs/Legacy/Shop.php +++ b/tests/Stubs/Legacy/Shop.php @@ -5,5 +5,5 @@ class Shop extends LegacyRecord { - protected $collection = 'shops'; + protected ?string $collection = 'shops'; } diff --git a/tests/Unit/LegacyRecordTest.php b/tests/Unit/LegacyRecordTest.php index f1bb54da..d3fed5dd 100644 --- a/tests/Unit/LegacyRecordTest.php +++ b/tests/Unit/LegacyRecordTest.php @@ -22,7 +22,7 @@ public function setUp(): void { parent::setUp(); $this->entity = new class() extends LegacyRecord { - protected $collection = 'legacy_record'; + protected ?string $collection = 'legacy_record'; }; } @@ -372,7 +372,7 @@ public function testShouldRaiseExceptionWhenHasNoCollectionAndTryToCallWhereFunc public function testShouldGetCollectionName(): void { $entity = new class() extends LegacyRecord { - protected $collection = 'collection_name'; + protected ?string $collection = 'collection_name'; }; $this->assertEquals('collection_name', $entity->getCollectionName()); @@ -381,7 +381,7 @@ public function testShouldGetCollectionName(): void public function testShouldAttachToAttribute(): void { $entity = new class() extends LegacyRecord { - protected $collection = 'collection_name'; + protected ?string $collection = 'collection_name'; public function class() { @@ -399,7 +399,7 @@ public function class() public function testShouldEmbedToAttribute(): void { $entity = new class() extends LegacyRecord { - protected $collection = 'collection_name'; + protected ?string $collection = 'collection_name'; public function classes() { @@ -416,7 +416,7 @@ public function classes() public function testShouldThrowBadMethodCallExceptionWhenCallingInvalidMethod(): void { $entity = new class() extends LegacyRecord { - protected $collection = 'collection_name'; + protected ?string $collection = 'collection_name'; }; $this->expectException(BadMethodCallException::class); diff --git a/tests/Unit/Model/Casts/DateTime/ImmutableDateTimeCastTest.php b/tests/Unit/Model/Casts/DateTime/ImmutableDateTimeCastTest.php index 816a2758..f3349729 100644 --- a/tests/Unit/Model/Casts/DateTime/ImmutableDateTimeCastTest.php +++ b/tests/Unit/Model/Casts/DateTime/ImmutableDateTimeCastTest.php @@ -12,8 +12,11 @@ public function testShouldGet(): void { // Set $timestamp = new UTCDateTime( - DateTimeImmutable::createFromFormat('d/m/Y H:i:s', '08/10/2025 12:30:45') + DateTimeImmutable::createFromFormat( + 'd/m/Y H:i:s', '08/10/2025 12:30:45' + ) ); + $immutableDateTimeCast = new ImmutableDateTimeCast(); // Actions