From 9f97f7cd5fe41636b65b518642bddd491c1b01b9 Mon Sep 17 00:00:00 2001 From: Wim Reckman Date: Mon, 20 Feb 2023 09:29:48 +0100 Subject: [PATCH] Refactor resolving of null information for custom casted attribute types (#1330) * Refactor resolving of null information for custom casted attribute types * composer fix-style --------- Co-authored-by: laravel-ide-helper --- src/Console/ModelsCommand.php | 40 +++++++++++++++++-- .../LaravelCustomCasts/Models/CustomCast.php | 1 + .../__snapshots__/Test__test__1.php | 7 +++- .../migrations/____custom_casts_table.php | 1 + 4 files changed, 43 insertions(+), 6 deletions(-) diff --git a/src/Console/ModelsCommand.php b/src/Console/ModelsCommand.php index 64ee077..8b29612 100644 --- a/src/Console/ModelsCommand.php +++ b/src/Console/ModelsCommand.php @@ -437,14 +437,46 @@ class ModelsCommand extends Command $realType = $this->checkForCastableCasts($realType, $params); $realType = $this->checkForCustomLaravelCasts($realType); $realType = $this->getTypeOverride($realType); - $this->properties[$name]['type'] = $this->getTypeInModel($model, $realType); + $realType = $this->getTypeInModel($model, $realType); + $realType = $this->applyNullability($realType, isset($this->nullableColumns[$name])); - if (isset($this->nullableColumns[$name])) { - $this->properties[$name]['type'] .= '|null'; - } + $this->properties[$name]['type'] = $realType; } } + protected function applyNullability(?string $type, bool $isNullable): ?string + { + if (!$type) { + return null; + } + + $nullString = null; + + // Find instance of: + // A) start of string or non-word character (like space or pipe) followed by 'null|' + // B) '|null' followed by end of string or non-word character (like space or pipe) + // This will find 'or null' instances at the beginning, middle or end of a type string, + // but will exclude solo/pure null instances and null being part of a type's name (e.g. class 'Benull'). + if (preg_match('/(?:(?:^|\W)(null\|))|(\|null(?:$|\W))/', $type, $matches) === 1) { + $nullString = array_pop($matches); + } + + // Return the current type string if: + // A) the type can be null and the type contains a null instance + // B) the type can not be null and the type does not contain a null instance + if (!($isNullable xor $nullString)) { + return $type; + } + + if ($isNullable) { + $type .= '|null'; + } else { + $type = str_replace($nullString, '', $type); + } + + return $type; + } + /** * Returns the override type for the give type. * diff --git a/tests/Console/ModelsCommand/LaravelCustomCasts/Models/CustomCast.php b/tests/Console/ModelsCommand/LaravelCustomCasts/Models/CustomCast.php index 48e3c19..92dde6f 100644 --- a/tests/Console/ModelsCommand/LaravelCustomCasts/Models/CustomCast.php +++ b/tests/Console/ModelsCommand/LaravelCustomCasts/Models/CustomCast.php @@ -31,6 +31,7 @@ class CustomCast extends Model 'casted_property_with_return_primitive' => CustomCasterWithPrimitiveReturn::class, 'casted_property_with_return_primitive_docblock' => CustomCasterWithPrimitiveDocblockReturn::class, 'casted_property_with_return_nullable_primitive' => CustomCasterWithNullablePrimitiveReturn::class, + 'casted_property_with_return_nullable_primitive_and_nullable_column' => CustomCasterWithNullablePrimitiveReturn::class, 'casted_property_without_return' => CustomCasterWithoutReturnType::class, 'casted_property_with_param' => CustomCasterWithParam::class . ':param', 'casted_property_with_static_return_docblock' => SelfCastingCasterWithStaticDocblockReturn::class, diff --git a/tests/Console/ModelsCommand/LaravelCustomCasts/__snapshots__/Test__test__1.php b/tests/Console/ModelsCommand/LaravelCustomCasts/__snapshots__/Test__test__1.php index ee2c02f..4fe3d26 100644 --- a/tests/Console/ModelsCommand/LaravelCustomCasts/__snapshots__/Test__test__1.php +++ b/tests/Console/ModelsCommand/LaravelCustomCasts/__snapshots__/Test__test__1.php @@ -29,8 +29,9 @@ use Illuminate\Database\Eloquent\Model; * @property \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\LaravelCustomCasts\Casts\CastedProperty $casted_property_with_return_docblock * @property \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\LaravelCustomCasts\Casts\CastedProperty $casted_property_with_return_docblock_fqn * @property array $casted_property_with_return_primitive - * @property array|null $casted_property_with_return_primitive_docblock - * @property array|null $casted_property_with_return_nullable_primitive + * @property array $casted_property_with_return_primitive_docblock + * @property array $casted_property_with_return_nullable_primitive + * @property array|null $casted_property_with_return_nullable_primitive_and_nullable_column * @property $casted_property_without_return * @property \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\LaravelCustomCasts\Casts\CastedProperty $casted_property_with_param * @property SelfCastingCasterWithStaticDocblockReturn $casted_property_with_static_return_docblock @@ -50,6 +51,7 @@ use Illuminate\Database\Eloquent\Model; * @method static \Illuminate\Database\Eloquent\Builder|CustomCast whereCastedPropertyWithReturnDocblock($value) * @method static \Illuminate\Database\Eloquent\Builder|CustomCast whereCastedPropertyWithReturnDocblockFqn($value) * @method static \Illuminate\Database\Eloquent\Builder|CustomCast whereCastedPropertyWithReturnNullablePrimitive($value) + * @method static \Illuminate\Database\Eloquent\Builder|CustomCast whereCastedPropertyWithReturnNullablePrimitiveAndNullableColumn($value) * @method static \Illuminate\Database\Eloquent\Builder|CustomCast whereCastedPropertyWithReturnPrimitive($value) * @method static \Illuminate\Database\Eloquent\Builder|CustomCast whereCastedPropertyWithReturnPrimitiveDocblock($value) * @method static \Illuminate\Database\Eloquent\Builder|CustomCast whereCastedPropertyWithReturnType($value) @@ -70,6 +72,7 @@ class CustomCast extends Model 'casted_property_with_return_primitive' => CustomCasterWithPrimitiveReturn::class, 'casted_property_with_return_primitive_docblock' => CustomCasterWithPrimitiveDocblockReturn::class, 'casted_property_with_return_nullable_primitive' => CustomCasterWithNullablePrimitiveReturn::class, + 'casted_property_with_return_nullable_primitive_and_nullable_column' => CustomCasterWithNullablePrimitiveReturn::class, 'casted_property_without_return' => CustomCasterWithoutReturnType::class, 'casted_property_with_param' => CustomCasterWithParam::class . ':param', 'casted_property_with_static_return_docblock' => SelfCastingCasterWithStaticDocblockReturn::class, diff --git a/tests/Console/ModelsCommand/migrations/____custom_casts_table.php b/tests/Console/ModelsCommand/migrations/____custom_casts_table.php index 845d9b9..7f531bf 100644 --- a/tests/Console/ModelsCommand/migrations/____custom_casts_table.php +++ b/tests/Console/ModelsCommand/migrations/____custom_casts_table.php @@ -17,6 +17,7 @@ class CustomCastsTable extends Migration $table->string('casted_property_with_return_primitive'); $table->string('casted_property_with_return_primitive_docblock'); $table->string('casted_property_with_return_nullable_primitive'); + $table->string('casted_property_with_return_nullable_primitive_and_nullable_column')->nullable(); $table->string('casted_property_without_return'); $table->string('casted_property_with_param'); $table->string('casted_property_with_static_return_docblock');