From c2ad5bbb77cb041cee9e810b919d4b6406eef7d7 Mon Sep 17 00:00:00 2001 From: Tim Chandler Date: Tue, 3 Jan 2023 02:15:32 +1100 Subject: [PATCH 1/2] Allow Eloquent cast to static using @ide-helper-eloquent-cast-to-specified-class --- CHANGELOG.md | 1 + src/Console/ModelsCommand.php | 28 ++++++++++ .../SimpleCasts/Castables/ChildObject.php | 18 +++++++ .../SimpleCasts/Castables/CustomCaster.php | 53 +++++++++++++++++++ .../SimpleCasts/Castables/ParentObject.php | 48 +++++++++++++++++ .../SimpleCasts/Models/SimpleCast.php | 4 ++ .../__snapshots__/Test__test__1.php | 6 +++ 7 files changed, 158 insertions(+) create mode 100644 tests/Console/ModelsCommand/SimpleCasts/Castables/ChildObject.php create mode 100644 tests/Console/ModelsCommand/SimpleCasts/Castables/CustomCaster.php create mode 100644 tests/Console/ModelsCommand/SimpleCasts/Castables/ParentObject.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 957ef7954..43813ea8d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ All notable changes to this project will be documented in this file. ### Added - Add support for custom casts that implement `CastsInboundAttributes` [#1329 / sforward](https://github.com/barryvdh/laravel-ide-helper/pull/1329) +- A solution to detect property types when casting Eloquent attributes to objects. Add the `@ide-helper-eloquent-cast-to-specified-class` tag to the class being cast to 2022-03-06, 2.12.3 ------------------ diff --git a/src/Console/ModelsCommand.php b/src/Console/ModelsCommand.php index 9d78583a5..85c578092 100644 --- a/src/Console/ModelsCommand.php +++ b/src/Console/ModelsCommand.php @@ -1211,6 +1211,30 @@ protected function getReturnTypeFromReflection(\ReflectionMethod $reflection): ? return $type; } + /** + * Check to see if a class (or its parents) has a @ide-helper-eloquent-cast-to-specified-class tag + * + * @param \Reflector $reflector + * + * @return boolean + */ + protected function classHasCastSpecifiedTag(\Reflector $reflector = null): bool + { + $phpDocContext = (new ContextFactory())->createFromReflector($reflector); + $context = new Context( + $phpDocContext->getNamespace(), + $phpDocContext->getNamespaceAliases() + ); + $phpdoc = new DocBlock($reflector, $context); + + if ($phpdoc->hasTag('ide-helper-eloquent-cast-to-specified-class')) { + return true; + } + + $parentReflector = $reflector->getParentClass(); + + return $parentReflector ? $this->classHasCastSpecifiedTag($parentReflector) : false; + } /** * Generates methods provided by the SoftDeletes trait @@ -1316,6 +1340,10 @@ protected function checkForCastableCasts(string $type, array $params = []): stri return $type; } + if ($this->classHasCastSpecifiedTag($reflection)) { + return $type; + } + $cast = call_user_func([$type, 'castUsing'], $params); if (is_string($cast) && !is_object($cast)) { diff --git a/tests/Console/ModelsCommand/SimpleCasts/Castables/ChildObject.php b/tests/Console/ModelsCommand/SimpleCasts/Castables/ChildObject.php new file mode 100644 index 000000000..4db473115 --- /dev/null +++ b/tests/Console/ModelsCommand/SimpleCasts/Castables/ChildObject.php @@ -0,0 +1,18 @@ +value); + } +} diff --git a/tests/Console/ModelsCommand/SimpleCasts/Castables/CustomCaster.php b/tests/Console/ModelsCommand/SimpleCasts/Castables/CustomCaster.php new file mode 100644 index 000000000..b644992c7 --- /dev/null +++ b/tests/Console/ModelsCommand/SimpleCasts/Castables/CustomCaster.php @@ -0,0 +1,53 @@ +castToClass = $castToClass; + } + + /** + * Transform the attribute from the underlying model values. + * + * @param \Illuminate\Database\Eloquent\Model $model + * @param string $key + * @param mixed $value + * @param array $attributes + * @return mixed + */ + public function get($model, string $key, $value, array $attributes) + { + return new $this->castToClass($value); + } + + /** + * Transform the attribute to its underlying model values. + * + * @param \Illuminate\Database\Eloquent\Model $model + * @param string $key + * @param mixed $value + * @param array $attributes + * @return mixed + */ + public function set($model, string $key, $value, array $attributes) + { + /** @var ParentObject $value */ + return [$key => $value->getValue()]; + } +} diff --git a/tests/Console/ModelsCommand/SimpleCasts/Castables/ParentObject.php b/tests/Console/ModelsCommand/SimpleCasts/Castables/ParentObject.php new file mode 100644 index 000000000..1c5912184 --- /dev/null +++ b/tests/Console/ModelsCommand/SimpleCasts/Castables/ParentObject.php @@ -0,0 +1,48 @@ +value = $value; + } + /** + * Retrieve the value stored in this Value object. + * + * @return mixed + */ + public function getValue() + { + return mb_strtoupper($this->value); + } + + /** + * Get the name of the caster class to use when casting from / to this cast target. + * + * @param array $arguments + * @return string|CastsAttributes|CastsInboundAttributes + */ + public static function castUsing(array $arguments): CastsAttributes + { + return new CustomCaster(static::class); + } +} diff --git a/tests/Console/ModelsCommand/SimpleCasts/Models/SimpleCast.php b/tests/Console/ModelsCommand/SimpleCasts/Models/SimpleCast.php index 420ab7437..45bbc8bae 100644 --- a/tests/Console/ModelsCommand/SimpleCasts/Models/SimpleCast.php +++ b/tests/Console/ModelsCommand/SimpleCasts/Models/SimpleCast.php @@ -4,6 +4,8 @@ namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SimpleCasts\Models; +use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SimpleCasts\Castables\ChildObject; +use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SimpleCasts\Castables\ParentObject; use Illuminate\Database\Eloquent\Model; class SimpleCast extends Model @@ -36,5 +38,7 @@ class SimpleCast extends Model 'cast_to_encrypted_collection' => 'encrypted:collection', 'cast_to_encrypted_json' => 'encrypted:json', 'cast_to_encrypted_object' => 'encrypted:object', + 'cast_to_parent_object_using_cast_static_tag' => ParentObject::class, + 'cast_to_child_object_using_cast_static_tag' => ChildObject::class, ]; } diff --git a/tests/Console/ModelsCommand/SimpleCasts/__snapshots__/Test__test__1.php b/tests/Console/ModelsCommand/SimpleCasts/__snapshots__/Test__test__1.php index ea2617a64..c36cb5ec9 100644 --- a/tests/Console/ModelsCommand/SimpleCasts/__snapshots__/Test__test__1.php +++ b/tests/Console/ModelsCommand/SimpleCasts/__snapshots__/Test__test__1.php @@ -4,6 +4,8 @@ namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SimpleCasts\Models; +use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SimpleCasts\Castables\ChildObject; +use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SimpleCasts\Castables\ParentObject; use Illuminate\Database\Eloquent\Model; /** @@ -36,6 +38,8 @@ * @property \Illuminate\Support\Collection $cast_to_encrypted_collection * @property array $cast_to_encrypted_json * @property object $cast_to_encrypted_object + * @property ParentObject $cast_to_parent_object_using_cast_static_tag + * @property ChildObject $cast_to_child_object_using_cast_static_tag * @method static \Illuminate\Database\Eloquent\Builder|SimpleCast newModelQuery() * @method static \Illuminate\Database\Eloquent\Builder|SimpleCast newQuery() * @method static \Illuminate\Database\Eloquent\Builder|SimpleCast query() @@ -98,5 +102,7 @@ class SimpleCast extends Model 'cast_to_encrypted_collection' => 'encrypted:collection', 'cast_to_encrypted_json' => 'encrypted:json', 'cast_to_encrypted_object' => 'encrypted:object', + 'cast_to_parent_object_using_cast_static_tag' => ParentObject::class, + 'cast_to_child_object_using_cast_static_tag' => ChildObject::class, ]; } From f2a7fba41ff39c6c9c472bd86df4c959c05d4c7b Mon Sep 17 00:00:00 2001 From: Tim Chandler Date: Tue, 3 Jan 2023 02:16:01 +1100 Subject: [PATCH 2/2] Ran: composer fix-style --- src/Console/ModelsCommand.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Console/ModelsCommand.php b/src/Console/ModelsCommand.php index 85c578092..455445a0c 100644 --- a/src/Console/ModelsCommand.php +++ b/src/Console/ModelsCommand.php @@ -935,14 +935,14 @@ protected function createPhpDocs($class) // remove the already existing tag to prevent duplicates foreach ($phpdoc->getTagsByName('mixin') as $tag) { - if($tag->getContent() === $eloquentClassNameInModel) { + if ($tag->getContent() === $eloquentClassNameInModel) { $phpdoc->deleteTag($tag); } } $phpdoc->appendTag(Tag::createInstance('@mixin ' . $eloquentClassNameInModel, $phpdoc)); } - + if ($this->phpstorm_noinspections) { /** * Facades, Eloquent API