diff --git a/README.md b/README.md index 0be5beb..54a31e4 100644 --- a/README.md +++ b/README.md @@ -214,7 +214,7 @@ With options: - --`R`|`routes-path`: Path to routes directory. - --`O`|`output-path`: Path to output. -## More +## Advanced ### MongoDB @@ -222,9 +222,7 @@ With options: Database isn't parsed like with relational databases. The package will parse `key`, `fillable` and `hidden` to get all fields. If some fields are missing, you can [override them manually](https://github.com/kiwilan/typescriptable-laravel#override-models). All relations and accessors are supported. -### Troubleshooting - -#### Database prefix +### Database prefix You can use `prefix` variable into `config/database.php` file. @@ -236,7 +234,7 @@ You can use `prefix` variable into `config/database.php` file. ], ``` -#### Override models +### Override models `kiwilan/typescriptable-laravel` will cover many cases, but if you want to override some models, you can just create a type like `resources/js/types/index.ts` and extends `Model` type. diff --git a/docs/examples.md b/docs/examples.md index e9a39d6..b789af8 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -7,87 +7,195 @@ An example of Eloquent model. ```php 'array', + 'budget' => PublishStatusEnum::class, + 'homepage' => PublishStatusEnum::class, + 'revenue' => 'integer', + 'is_multilingual' => 'boolean', + 'added_at' => 'datetime:Y-m-d', ]; - protected $withCount = [ - 'chapters', + protected $appends = [ + 'show_route', + 'api_route', ]; - protected $casts = [ - 'status' => PublishStatusEnum::class, - 'published_at' => 'datetime:Y-m-d', + protected $hidden = [ + 'budget', + 'edition', ]; - public function getTimeToReadAttribute(): int + public function registerMediaConversions(?Media $media = null): void + { + $this + ->addMediaConversion('preview') + ->fit(Fit::Contain, 300, 300) + ->nonQueued(); + } - public function chapters(): HasMany + public function getShowRouteAttribute(): string + { + return 'movies.show'; + } - public function category(): BelongsTo + /** + * @return string + */ + protected function apiRoute(): Attribute + { + return Attribute::make( + get: fn (?string $value) => ucfirst($value), + ); + } - public function author(): BelongsTo + public function similars(): \Illuminate\Database\Eloquent\Relations\BelongsToMany + { + return $this->belongsToMany(Movie::class, 'similars', 'movie_id', 'similar_id'); + } + + public function recommendations(): \Illuminate\Database\Eloquent\Relations\BelongsToMany + { + return $this->belongsToMany(Movie::class, 'recommendations', 'movie_id', 'recommendation_id'); + } - public function tags(): BelongsToMany + public function members(): \Illuminate\Database\Eloquent\Relations\MorphToMany + { + return $this + ->morphToMany(Member::class, 'memberable') + ->withPivot([ + 'character', + 'job', + 'department', + 'order', + 'is_adult', + 'known_for_department', + 'is_crew', + ]) + ->orderBy('order'); + } + + public function author(): BelongsTo + { + return $this->belongsTo(Author::class, 'author_id'); + } } ``` TS file generated at `resources/js/types-eloquent.d.ts` -```typescript -declare namespace App { - declare namespace Models { - export type Story = { - id: number; - title: string; - slug?: string; - abstract?: string; - original_link?: string; - picture?: string; - status: "draft" | "scheduled" | "published"; - published_at?: Date; - meta_title?: string; - meta_description?: string; - created_at?: Date; - updated_at?: Date; - author_id?: number; - category_id?: number; - time_to_read?: number; - chapters?: Chapter[]; - category?: Category; - author?: Author; - tags?: Tag[]; - chapters_count?: number; - tags_count?: number; - }; +```ts +declare namespace App.Models { + export interface Movie { + id: string; + tmdb_id?: number; + title?: string; + year?: number; + subtitles: any[]; + slug: string; + french_title?: string; + original_title?: string; + release_date?: string; + original_language?: string; + overview?: string; + popularity?: number; + is_adult?: number; + homepage?: "draft" | "scheduled" | "published"; + tagline?: string; + status?: string; + certification?: string; + tmdb_url?: string; + imdb_id?: string; + runtime?: number; + budget: "draft" | "scheduled" | "published"; + revenue?: number; + edition?: string; + version?: string; + library?: string; + is_multilingual: boolean; + poster?: string; + poster_tmdb?: string; + poster_color?: string; + backdrop?: string; + backdrop_tmdb?: string; + author_id?: number; + added_at?: string; + fetched_at?: string; + fetched_has_failed: number; + created_at?: string; + updated_at?: string; + show_route?: string; + api_route?: string; + similars_count?: number; + recommendations_count?: number; + members_count?: number; + media_count?: number; + similars?: App.Models.Movie[]; + recommendations?: App.Models.Movie[]; + members?: App.Models.Member[]; + author?: App.Models.NestedAuthor; + media?: any[]; } - // With `paginate` option. - export type PaginateLink = { +} + +declare namespace App { + export interface PaginateLink { url: string; label: string; active: boolean; - }; - export type Paginate = { + } + export interface Paginate { data: T[]; current_page: number; first_page_url: string; @@ -101,7 +209,26 @@ declare namespace App { prev_page_url: string; to: number; total: number; - }; + } + export interface ApiPaginate { + data: T[]; + links: { + first?: string; + last?: string; + prev?: string; + next?: string; + }; + meta: { + current_page: number; + from: number; + last_page: number; + links: App.PaginateLink[]; + path: string; + per_page: number; + to: number; + total: number; + }; + } } ``` @@ -112,7 +239,7 @@ Usage in Vue component. ```vue ``` @@ -122,7 +249,7 @@ Or with paginate option. ```vue ``` diff --git a/tests/EloquentTypeArtisanMysqlTest.php b/tests/EloquentTypeArtisanMysqlTest.php new file mode 100644 index 0000000..368156d --- /dev/null +++ b/tests/EloquentTypeArtisanMysqlTest.php @@ -0,0 +1,25 @@ +execute(); + + $eloquent = outputDir(TypescriptableConfig::eloquentFilename()); + expect($eloquent)->toBeFile(); +}); diff --git a/tests/EloquentArtisanTest.php b/tests/EloquentTypeArtisanTest.php similarity index 100% rename from tests/EloquentArtisanTest.php rename to tests/EloquentTypeArtisanTest.php diff --git a/tests/EloquentParserTest.php b/tests/EloquentTypeParserTest.php similarity index 100% rename from tests/EloquentParserTest.php rename to tests/EloquentTypeParserTest.php diff --git a/tests/docs/Movie.php b/tests/docs/Movie.php new file mode 100644 index 0000000..3a9d328 --- /dev/null +++ b/tests/docs/Movie.php @@ -0,0 +1,68 @@ + 'array', + 'budget' => \Kiwilan\Typescriptable\Tests\Data\Enums\BudgetEnum::class, + 'homepage' => \Kiwilan\Typescriptable\Tests\Data\Enums\HomePageEnum::class, + 'revenue' => 'integer', + 'is_multilingual' => 'boolean', + 'added_at' => 'datetime:Y-m-d', + ]; + + protected $appends = [ + 'show_route', + 'api_route', + ]; + + protected $hidden = [ + 'budget', + 'edition', + ]; + + public function getShowRouteAttribute(): string + { + return 'movies.show'; + } + + /** + * @return string + */ + protected function apiRoute(): \Illuminate\Database\Eloquent\Casts\Attribute + { + return \Illuminate\Database\Eloquent\Casts\Attribute::make( + get: fn (?string $value) => ucfirst($value), + ); + } + + public function recommendations(): \Illuminate\Database\Eloquent\Relations\BelongsToMany + { + return $this->belongsToMany(Movie::class, 'recommendations', 'movie_id', 'recommendation_id'); + } + + public function members(): \Illuminate\Database\Eloquent\Relations\MorphToMany + { + return $this->morphToMany(Member::class, 'memberable'); + } + + public function author(): \Illuminate\Database\Eloquent\Relations\BelongsTo + { + return $this->belongsTo(\Kiwilan\Typescriptable\Tests\Data\Models\Nested\Author::class, 'author_id'); + } +} diff --git a/tests/docs/create_movie_table.php b/tests/docs/create_movie_table.php new file mode 100644 index 0000000..5d1c8ca --- /dev/null +++ b/tests/docs/create_movie_table.php @@ -0,0 +1,25 @@ +ulid('id')->primary(); + $table->string('title')->nullable()->unique(); + $table->json('subtitles'); + $table->string('homepage')->nullable(); + $table->enum('budget', [ + \Kiwilan\Typescriptable\Tests\Data\Enums\BudgetEnum::high->value, + \Kiwilan\Typescriptable\Tests\Data\Enums\BudgetEnum::middle->value, + \Kiwilan\Typescriptable\Tests\Data\Enums\BudgetEnum::low->value, + ])->default(\Kiwilan\Typescriptable\Tests\Data\Enums\BudgetEnum::high); + $table->bigInteger('revenue')->nullable(); + $table->boolean('is_multilingual')->default(false); + $table->foreignId('author_id')->nullable()->constrained('authors')->nullOnDelete(); + $table->dateTime('added_at')->nullable(); + $table->dateTime('fetched_at')->nullable(); + $table->timestamps(); + }); + } +}; diff --git a/tests/docs/freeze.md b/tests/docs/freeze.md new file mode 100644 index 0000000..fdc9475 --- /dev/null +++ b/tests/docs/freeze.md @@ -0,0 +1,7 @@ +# Freeze + +```sh +freeze tests/docs/create_movie_table.php --theme github-dark --window --font.family "Victor Mono" -o tests/docs/output/create_movie_table.png +freeze tests/docs/Movie.php --theme github-dark --window --font.family "Victor Mono" -o tests/docs/output/movie.png +freeze tests/docs/types-eloquent.d.ts --theme github-dark --window --font.family "Victor Mono" -o tests/docs/output/types-eloquent.png +``` diff --git a/tests/docs/output/.gitignore b/tests/docs/output/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/tests/docs/output/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/tests/docs/types-eloquent.d.ts b/tests/docs/types-eloquent.d.ts new file mode 100644 index 0000000..04fb63c --- /dev/null +++ b/tests/docs/types-eloquent.d.ts @@ -0,0 +1,25 @@ +declare namespace App.Models { + export interface Movie { + id: string + title?: string + subtitles: any[] + homepage?: 'imdb' | 'tmdb' + budget: 'high' | 'middle' | 'low' + revenue?: number + is_multilingual: boolean + added_at?: string + fetched_at?: string + author_id?: number + created_at?: string + updated_at?: string + show_route?: string + api_route?: string + similars_count?: number + recommendations_count?: number + members_count?: number + media_count?: number + recommendations?: App.Models.Movie[] + members?: App.Models.Member[] + author?: App.Models.NestedAuthor + } +}