diff --git a/src/Actions/WriteColumnAttribute.php b/src/Actions/WriteColumnAttribute.php index 639c62e..6079ed4 100644 --- a/src/Actions/WriteColumnAttribute.php +++ b/src/Actions/WriteColumnAttribute.php @@ -110,7 +110,28 @@ public function __invoke(ReflectionClass $reflectionModel, array $attribute, arr } } } else { - if ($attribute['type'] !== null) { + // No get callback: reads go through the model's cast, + // so fall back to it before the database column type + $cast = $reflectionModel->newInstance()->getCasts()[$attribute['name']] ?? null; + + if (! is_null($cast)) { + if (Str::contains($cast, '\\')) { + $castReflection = new ReflectionClass($cast); + + if ($castReflection->isEnum()) { + $type = $this->getClassName($cast); + $enumRef = $castReflection; + } + } else { + $cleanStr = Str::of($cast)->before(':')->lower()->toString(); + + if (isset($mappings[$cleanStr])) { + $type = $returnType($cleanStr, $mappings); + } + } + } + + if ($type === 'unknown' && $attribute['type'] !== null) { $type = $returnType($attribute['type'], $mappings); } } diff --git a/src/Actions/WriteEnumConst.php b/src/Actions/WriteEnumConst.php index f89be47..f2eacb1 100644 --- a/src/Actions/WriteEnumConst.php +++ b/src/Actions/WriteEnumConst.php @@ -25,7 +25,8 @@ public function __invoke(ReflectionClass $reflection, string $indent = '', bool $comments = array_map(fn ($match) => trim(str_replace('@property', '', $match)), $matches[0]); } - $cases = collect($reflection->getConstants()); + $cases = collect($reflection->getConstants()) + ->filter(fn ($case) => $case instanceof \BackedEnum); if ($cases->isNotEmpty()) { if ($useEnums) { diff --git a/test/input/expectations/complex-model-camel-case.ts b/test/input/expectations/complex-model-camel-case.ts index c4de839..7325ade 100644 --- a/test/input/expectations/complex-model-camel-case.ts +++ b/test/input/expectations/complex-model-camel-case.ts @@ -26,6 +26,7 @@ export interface Complex { string: string castedUppercaseString: unknown stringWithMutatorAndNoAccessor: string + enumWithMutatorAndNoAccessor: Roles text: string time: string timestamp: string @@ -42,3 +43,14 @@ export interface Complex { // exists complexRelationshipsExists: boolean } + +const Roles = { + /** Can do anything */ + ADMIN: 'admin', + /** Standard readonly */ + USER: 'user', + /** Value that needs string escaping */ + USERCLASS: 'App\\Models\\User', +} as const; + +export type Roles = typeof Roles[keyof typeof Roles] diff --git a/test/input/expectations/complex-model-pascal-case.ts b/test/input/expectations/complex-model-pascal-case.ts index 7c4e5db..207164b 100644 --- a/test/input/expectations/complex-model-pascal-case.ts +++ b/test/input/expectations/complex-model-pascal-case.ts @@ -26,6 +26,7 @@ export interface Complex { String: string CastedUppercaseString: unknown StringWithMutatorAndNoAccessor: string + EnumWithMutatorAndNoAccessor: Roles Text: string Time: string Timestamp: string @@ -42,3 +43,14 @@ export interface Complex { // exists ComplexRelationshipsExists: boolean } + +const Roles = { + /** Can do anything */ + ADMIN: 'admin', + /** Standard readonly */ + USER: 'user', + /** Value that needs string escaping */ + USERCLASS: 'App\\Models\\User', +} as const; + +export type Roles = typeof Roles[keyof typeof Roles] diff --git a/test/input/expectations/complex-model-with-cast.ts b/test/input/expectations/complex-model-with-cast.ts index e17d35c..2889d1a 100644 --- a/test/input/expectations/complex-model-with-cast.ts +++ b/test/input/expectations/complex-model-with-cast.ts @@ -26,6 +26,7 @@ export interface Complex { string: string casted_uppercase_string: string string_with_mutator_and_no_accessor: string + enum_with_mutator_and_no_accessor: Roles text: string time: string timestamp: string @@ -42,3 +43,14 @@ export interface Complex { // exists complex_relationships_exists: boolean } + +const Roles = { + /** Can do anything */ + ADMIN: 'admin', + /** Standard readonly */ + USER: 'user', + /** Value that needs string escaping */ + USERCLASS: 'App\\Models\\User', +} as const; + +export type Roles = typeof Roles[keyof typeof Roles] diff --git a/test/input/expectations/complex-model.ts b/test/input/expectations/complex-model.ts index 364725e..4d1eed6 100644 --- a/test/input/expectations/complex-model.ts +++ b/test/input/expectations/complex-model.ts @@ -26,6 +26,7 @@ export interface Complex { string: string casted_uppercase_string: unknown string_with_mutator_and_no_accessor: string + enum_with_mutator_and_no_accessor: Roles text: string time: string timestamp: string @@ -42,3 +43,14 @@ export interface Complex { // exists complex_relationships_exists: boolean } + +const Roles = { + /** Can do anything */ + ADMIN: 'admin', + /** Standard readonly */ + USER: 'user', + /** Value that needs string escaping */ + USERCLASS: 'App\\Models\\User', +} as const; + +export type Roles = typeof Roles[keyof typeof Roles] diff --git a/test/laravel-skeleton/app/Enums/Roles.php b/test/laravel-skeleton/app/Enums/Roles.php index 0f3ccb7..8404d14 100644 --- a/test/laravel-skeleton/app/Enums/Roles.php +++ b/test/laravel-skeleton/app/Enums/Roles.php @@ -15,6 +15,11 @@ enum Roles: string case USER = 'user'; case USERCLASS = User::class; + public const NON_ADMIN_ROLES = [ + self::USER, + self::USERCLASS, + ]; + public static function fromValue(string $value): self { return match ($value) { diff --git a/test/laravel-skeleton/app/Models/Complex.php b/test/laravel-skeleton/app/Models/Complex.php index 15376e1..c819348 100644 --- a/test/laravel-skeleton/app/Models/Complex.php +++ b/test/laravel-skeleton/app/Models/Complex.php @@ -3,6 +3,8 @@ namespace App\Models; use App\Casts\UpperCast; +use App\Enums\Roles; +use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\HasMany; @@ -15,6 +17,7 @@ class Complex extends Model 'jsonb' => 'json', 'year' => 'int', 'casted_uppercase_string' => UpperCast::class, + 'enum_with_mutator_and_no_accessor' => Roles::class, 'immutableDateTime' => 'immutable_date', 'immutableDate' => 'immutable_datetime', 'immutableCustomDateTime' => 'immutable_custom_datetime', @@ -31,4 +34,11 @@ protected function stringWithMutatorAndNoAccessor(): Attribute set: fn (string $value): string => strtolower($value), ); } + + protected function enumWithMutatorAndNoAccessor(): Attribute + { + return Attribute::make( + set: fn (Roles|string $value): string => $value instanceof Roles ? $value->value : $value, + ); + } } diff --git a/test/laravel-skeleton/database/migrations/0001_01_01_000003_create_complex_model_table.php b/test/laravel-skeleton/database/migrations/0001_01_01_000003_create_complex_model_table.php index 30c4fe4..cd5bcc2 100644 --- a/test/laravel-skeleton/database/migrations/0001_01_01_000003_create_complex_model_table.php +++ b/test/laravel-skeleton/database/migrations/0001_01_01_000003_create_complex_model_table.php @@ -38,6 +38,7 @@ public function up(): void $table->string('string'); $table->string('casted_uppercase_string'); $table->string('string_with_mutator_and_no_accessor'); + $table->string('enum_with_mutator_and_no_accessor'); $table->text('text'); $table->time('time'); $table->timestamp('timestamp');