diff --git a/readme.md b/readme.md index 0dd8c34..cfbf9e7 100644 --- a/readme.md +++ b/readme.md @@ -20,35 +20,35 @@ will output ```ts export interface User { - // columns - id: number; - email: string; - name: string; - created_at?: Date; - updated_at?: Date; - // mutators - first_name: string; - initials: string; - // relations - teams: Teams; + // columns + id: number + email: string + name: string + created_at?: Date + updated_at?: Date + // mutators + first_name: string + initials: string + // relations + teams: Teams } -export type Users = Array; +export type Users = Array export interface Team { - // columns - id: number; - name: string; - logo: string; - created_at?: Date; - updated_at?: Date; - // mutators - initials: string; - slug: string; - url: string; - // relations - users: Users; + // columns + id: number + name: string + logo: string + created_at?: Date + updated_at?: Date + // mutators + initials: string + slug: string + url: string + // relations + users: Users } -export type Teams = Array; +export type Teams = Array ``` ### What does this do? @@ -61,11 +61,11 @@ Starting support is for Laravel v9.21+ and PHP v8.1+ > **Note** > This package may require you to install Doctrine DBAL. If so you can run + ```bash composer require doctrine/dbal ``` - 1. You must have a [return type](https://www.php.net/manual/en/language.types.declarations.php) for your model relationships ```php @@ -84,6 +84,16 @@ public function getFirstNameAttribute(): string // <- this } ``` +### Additional Options + +``` +--no-relations : Do not include relations +--optional-relations : Make relations optional fields on the model type +--no-hidden : Do not include hidden model attributes +--timestamps-date : Output timestamps as a Date object type +--optional-nullables : Output nullable attributes as optional fields +``` + ### Custom Interfaces If you have custom interfaces you are using for your models you can specify them in a reserved `interfaces` array @@ -102,11 +112,11 @@ public array $interfaces = [ And it should generate: ```ts -import { Point } from "@/types/api"; +import { Point } from '@/types/api' export interface Location { - // columns - coordinate: Point; + // columns + coordinate: Point } ``` @@ -138,8 +148,8 @@ And it should generate: ```ts export interface Location { - // columns - choices: "good" | "bad"; + // columns + choices: 'good' | 'bad' } ``` @@ -159,10 +169,10 @@ This will generate: ```ts export interface Location { - // ... - // overrides - state: "found" | "not_found" | "searching" | "reset"; - // ... + // ... + // overrides + state: 'found' | 'not_found' | 'searching' | 'reset' + // ... } ``` @@ -192,13 +202,14 @@ declare global { ```bash artisan model:typer --plurals ``` + Exports for example, when a `User` model exists: ```ts export type Users = User[] ``` -### Output Api.MetApi* resources +### Output Api.MetApi\* resources ```bash artisan model:typer --api-resources @@ -207,10 +218,18 @@ artisan model:typer --api-resources Exports: ```ts -export interface UserResults extends api.MetApiResults { data: Users } -export interface UserResult extends api.MetApiResults { data: User } -export interface UserMetApiData extends api.MetApiData { data: User } -export interface UserResponse extends api.MetApiResponse { data: UserMetApiData } +export interface UserResults extends api.MetApiResults { + data: Users +} +export interface UserResult extends api.MetApiResults { + data: User +} +export interface UserMetApiData extends api.MetApiData { + data: User +} +export interface UserResponse extends api.MetApiResponse { + data: UserMetApiData +} ``` ### Enable all output options @@ -248,16 +267,16 @@ This will generate something like: ```ts export interface User { - // columns - id: number; - email: string; - name?: string; - created_at?: Date; - updated_at?: Date; - // mutators - is_captain: boolean; - // relations - teams: TeamUsers; + // columns + id: number + email: string + name?: string + created_at?: Date + updated_at?: Date + // mutators + is_captain: boolean + // relations + teams: TeamUsers } ``` @@ -292,10 +311,9 @@ namespace App\Enums; * @property ADMIN - Can do anything * @property USER - Standard read-only */ -enum UserRoleEnum: string -{ - case ADMIN = 'admin'; - case USER = 'user'; +enum UserRoleEnum: string { + case ADMIN = 'admin'; + case USER = 'user'; } ``` diff --git a/src/Actions/GenerateCliOutput.php b/src/Actions/GenerateCliOutput.php index 74dfdd9..9a2562d 100644 --- a/src/Actions/GenerateCliOutput.php +++ b/src/Actions/GenerateCliOutput.php @@ -27,7 +27,7 @@ class GenerateCliOutput * * @param Collection $models */ - public function __invoke(Collection $models, bool $global = false, bool $plurals = false, bool $apiResources = false, bool $optionalRelations = false, bool $noRelations = false, bool $noHidden = false, bool $timestampStrings = false, bool $optionalNullables = false): string + public function __invoke(Collection $models, bool $global = false, bool $plurals = false, bool $apiResources = false, bool $optionalRelations = false, bool $noRelations = false, bool $noHidden = false, bool $timestampsDate = false, bool $optionalNullables = false): string { $modelBuilder = app(BuildModelDetails::class); $colAttrWriter = app(WriteColumnAttribute::class); @@ -38,7 +38,7 @@ public function __invoke(Collection $models, bool $global = false, bool $plurals $this->indent = ' '; } - $models->each(function (SplFileInfo $model) use ($modelBuilder, $colAttrWriter, $relationWriter, $plurals, $apiResources, $optionalRelations, $noRelations, $noHidden, $timestampStrings, $optionalNullables) { + $models->each(function (SplFileInfo $model) use ($modelBuilder, $colAttrWriter, $relationWriter, $plurals, $apiResources, $optionalRelations, $noRelations, $noHidden, $timestampsDate, $optionalNullables) { $entry = ''; [ @@ -57,8 +57,8 @@ public function __invoke(Collection $models, bool $global = false, bool $plurals if ($columns->isNotEmpty()) { $entry .= "{$this->indent} // columns\n"; - $columns->each(function ($att) use (&$entry, $reflectionModel, $colAttrWriter, $noHidden, $timestampStrings, $optionalNullables) { - [$line, $enum] = $colAttrWriter(reflectionModel: $reflectionModel, attribute: $att, indent: $this->indent, noHidden: $noHidden, timestampStrings: $timestampStrings, optionalNullables: $optionalNullables); + $columns->each(function ($att) use (&$entry, $reflectionModel, $colAttrWriter, $noHidden, $timestampsDate, $optionalNullables) { + [$line, $enum] = $colAttrWriter(reflectionModel: $reflectionModel, attribute: $att, indent: $this->indent, noHidden: $noHidden, timestampsDate: $timestampsDate, optionalNullables: $optionalNullables); if (!empty($line)) { $entry .= $line; if ($enum) { @@ -70,8 +70,8 @@ public function __invoke(Collection $models, bool $global = false, bool $plurals if ($nonColumns->isNotEmpty()) { $entry .= "{$this->indent} // mutators\n"; - $nonColumns->each(function ($att) use (&$entry, $reflectionModel, $colAttrWriter, $noHidden, $timestampStrings, $optionalNullables) { - [$line, $enum] = $colAttrWriter(reflectionModel: $reflectionModel, attribute: $att, indent: $this->indent, noHidden: $noHidden, timestampStrings: $timestampStrings, optionalNullables: $optionalNullables); + $nonColumns->each(function ($att) use (&$entry, $reflectionModel, $colAttrWriter, $noHidden, $timestampsDate, $optionalNullables) { + [$line, $enum] = $colAttrWriter(reflectionModel: $reflectionModel, attribute: $att, indent: $this->indent, noHidden: $noHidden, timestampsDate: $timestampsDate, optionalNullables: $optionalNullables); if (!empty($line)) { $entry .= $line; if ($enum) { @@ -83,8 +83,8 @@ public function __invoke(Collection $models, bool $global = false, bool $plurals if ($interfaces->isNotEmpty()) { $entry .= "{$this->indent} // overrides\n"; - $interfaces->each(function ($interface) use (&$entry, $reflectionModel, $colAttrWriter, $timestampStrings) { - [$line] = $colAttrWriter(reflectionModel: $reflectionModel, attribute: $interface, indent: $this->indent, timestampStrings: $timestampStrings); + $interfaces->each(function ($interface) use (&$entry, $reflectionModel, $colAttrWriter, $timestampsDate) { + [$line] = $colAttrWriter(reflectionModel: $reflectionModel, attribute: $interface, indent: $this->indent, timestampsDate: $timestampsDate); $entry .= $line; }); } diff --git a/src/Actions/Generator.php b/src/Actions/Generator.php index 6e8918c..5213ee0 100644 --- a/src/Actions/Generator.php +++ b/src/Actions/Generator.php @@ -13,11 +13,11 @@ class Generator * * @return string */ - public function __invoke(?string $specificModel = null, bool $global = false, bool $json = false, bool $plurals = false, bool $apiResources = false, bool $optionalRelations = false, bool $noRelations = false, bool $noHidden = false, bool $timestampStrings = false, bool $optionalNullables = false) + public function __invoke(?string $specificModel = null, bool $global = false, bool $json = false, bool $plurals = false, bool $apiResources = false, bool $optionalRelations = false, bool $noRelations = false, bool $noHidden = false, bool $timestampsDate = false, bool $optionalNullables = false) { $models = $this->getModels($specificModel); - return $this->display($models, $global, $json, $plurals, $apiResources, $optionalRelations, $noRelations, $noHidden, $timestampStrings, $optionalNullables); + return $this->display($models, $global, $json, $plurals, $apiResources, $optionalRelations, $noRelations, $noHidden, $timestampsDate, $optionalNullables); } /** @@ -41,7 +41,7 @@ protected function getModels(?string $model = null): Collection * * @param Collection $models */ - protected function display(Collection $models, bool $global = false, bool $json = false, bool $plurals = false, bool $apiResources = false, bool $optionalRelations = false, bool $noRelations = false, bool $noHidden = false, bool $timestampStrings = false, bool $optionalNullables = false): string + protected function display(Collection $models, bool $global = false, bool $json = false, bool $plurals = false, bool $apiResources = false, bool $optionalRelations = false, bool $noRelations = false, bool $noHidden = false, bool $timestampsDate = false, bool $optionalNullables = false): string { if ($models->isEmpty()) { return 'ERROR: No models found.'; @@ -51,6 +51,6 @@ protected function display(Collection $models, bool $global = false, bool $json return app(GenerateJsonOutput::class)($models); } - return app(GenerateCliOutput::class)($models, $global, $plurals, $apiResources, $optionalRelations, $noRelations, $noHidden, $timestampStrings, $optionalNullables); + return app(GenerateCliOutput::class)($models, $global, $plurals, $apiResources, $optionalRelations, $noRelations, $noHidden, $timestampsDate, $optionalNullables); } } diff --git a/src/Actions/MapReturnType.php b/src/Actions/MapReturnType.php index 3fbb117..b210bfd 100644 --- a/src/Actions/MapReturnType.php +++ b/src/Actions/MapReturnType.php @@ -12,12 +12,12 @@ class MapReturnType * @param string $returnType * @return string */ - public function __invoke(string $returnType, bool $timestampStrings = false): string + public function __invoke(string $returnType, bool $timestampsDate = false): string { $mappings = TypescriptMappings::$mappings; - if ($timestampStrings) { - $mappings['datetime'] = 'string'; - $mappings['date'] = 'string'; + if ($timestampsDate) { + $mappings['datetime'] = 'Date'; + $mappings['date'] = 'Date'; } $returnType = explode(' ', $returnType)[0]; diff --git a/src/Actions/WriteColumnAttribute.php b/src/Actions/WriteColumnAttribute.php index 17a39ce..472ceba 100644 --- a/src/Actions/WriteColumnAttribute.php +++ b/src/Actions/WriteColumnAttribute.php @@ -21,7 +21,7 @@ class WriteColumnAttribute * @param bool $jsonOutput * @return array */ - public function __invoke(ReflectionClass $reflectionModel, array $attribute, string $indent = '', bool $jsonOutput = false, bool $noHidden = false, bool $timestampStrings = false, bool $optionalNullables = false): array + public function __invoke(ReflectionClass $reflectionModel, array $attribute, string $indent = '', bool $jsonOutput = false, bool $noHidden = false, bool $timestampsDate = false, bool $optionalNullables = false): array { $enumRef = null; $returnType = app(MapReturnType::class); @@ -39,7 +39,7 @@ public function __invoke(ReflectionClass $reflectionModel, array $attribute, str } else { if (! is_null($attribute['cast']) && $attribute['cast'] !== $attribute['type']) { if (isset(TypescriptMappings::$mappings[$attribute['cast']])) { - $type = $returnType($attribute['cast'], $timestampStrings); + $type = $returnType($attribute['cast'], $timestampsDate); } else { if ($attribute['type'] === 'json' || $this->getClassName($attribute['cast']) === 'AsCollection' || $this->getClassName($attribute['cast']) === 'AsArrayObject') { $type = $returnType('json'); @@ -54,7 +54,7 @@ public function __invoke(ReflectionClass $reflectionModel, array $attribute, str if (! is_null($closure->get)) { $rf = new ReflectionFunction($closure->get); if ($rf->hasReturnType()) { - $type = $returnType($rf->getReturnType()->getName(), $timestampStrings); + $type = $returnType($rf->getReturnType()->getName(), $timestampsDate); } } } else { @@ -72,7 +72,7 @@ public function __invoke(ReflectionClass $reflectionModel, array $attribute, str $cleanStr = Str::of($attribute['cast'])->before(':')->__toString(); if (isset(TypescriptMappings::$mappings[$cleanStr])) { - $type = $returnType($cleanStr, $timestampStrings); + $type = $returnType($cleanStr, $timestampsDate); } else { dump('Unknown cast type: ' . $attribute['cast']); } @@ -81,7 +81,7 @@ public function __invoke(ReflectionClass $reflectionModel, array $attribute, str } } } else { - $type = $returnType($attribute['type'], $timestampStrings); + $type = $returnType($attribute['type'], $timestampsDate); } } diff --git a/src/Commands/ModelTyperCommand.php b/src/Commands/ModelTyperCommand.php index f6fd86a..1fac4db 100644 --- a/src/Commands/ModelTyperCommand.php +++ b/src/Commands/ModelTyperCommand.php @@ -25,9 +25,9 @@ class ModelTyperCommand extends Command {--json : Output the result as json} {--plurals : Output model plurals} {--no-relations : Do not include relations} - {--optional-relations : Make relations optional types} + {--optional-relations : Make relations optional fields on the model type} {--no-hidden : Do not include hidden model attributes} - {--timestamp-strings : Output timestamps as strings} + {--timestamps-date : Output timestamps as a Date object type} {--optional-nullables : Output nullable attributes as optional fields} {--api-resources : Output api.MetApi interfaces} {--all : Enable all output options (equivalent to --plurals --api-resources)}'; @@ -66,7 +66,7 @@ public function handle(Generator $generator): int $plurals = $this->option('plurals') || $this->option('all'); $apiResources = $this->option('api-resources') || $this->option('all'); - echo $generator($this->option('model'), $this->option('global'), $this->option('json'), $plurals, $apiResources, $this->option('optional-relations'), $this->option('no-relations'), $this->option('no-hidden'), $this->option('timestamp-strings'), $this->option('optional-nullables')); + echo $generator($this->option('model'), $this->option('global'), $this->option('json'), $plurals, $apiResources, $this->option('optional-relations'), $this->option('no-relations'), $this->option('no-hidden'), $this->option('timestamps-date'), $this->option('optional-nullables')); return Command::SUCCESS; } diff --git a/src/Constants/TypescriptMappings.php b/src/Constants/TypescriptMappings.php index b930e34..09f5244 100644 --- a/src/Constants/TypescriptMappings.php +++ b/src/Constants/TypescriptMappings.php @@ -12,8 +12,8 @@ class TypescriptMappings 'float' => 'number', 'string' => 'string', 'decimal' => 'number', - 'datetime' => 'Date', - 'date' => 'Date', + 'datetime' => 'string', + 'date' => 'string', 'bool' => 'boolean', 'boolean' => 'boolean', 'json' => 'Record',