Artisan Commands
The package ships generators for each class it uses. The classes they write pass Pint with the laravel preset and Larastan at level 10: they import the classes they use and mark each overriding method with #[\Override].
| Command | Writes |
|---|---|
rest:quick-start | the resources, controllers, policies and routes of the models you choose |
rest:resource PostResource --model=Post | app/Rest/Resources/PostResource.php |
rest:controller PostsController --resource=PostResource | app/Rest/Controllers/PostsController.php |
rest:policy PostPolicy --model=Post | app/Policies/PostPolicy.php |
rest:action ExtendSubscriptions | app/Rest/Actions/ExtendSubscriptions.php |
rest:instruction OddEvenId | app/Rest/Instructions/OddEvenId.php |
rest:metric OrdersByStatus --type=partition --group-by=status | app/Rest/Metrics/OrdersByStatus.php |
rest:response ArticleResponse | app/Rest/Responses/ArticleResponse.php |
rest:base-resource, rest:base-controller | the abstract base classes app/Rest/Resources/Resource.php and app/Rest/Controllers/Controller.php |
rest:check writes nothing: it checks the application.
Every generator keeps an existing class: it prints <Type> already exists. and exits with 0. Pass --force (-f) to replace it.
Quick start
rest:quick-start asks which models to expose, then writes their resources from their tables, their controllers, a policy that allows every ability when Gate finds none, and one route per model behind the auth middleware of the guard, auth:sanctum by default. It checks the result in a new process before it reports success.
php artisan rest:quick-start
php artisan rest:quick-start --no-interaction --model=Post --model=Tag --install-api
See Quick start for every option.
Checking the application
rest:check checks the routes, policies, perimeters and action row checks of your resources. Run it in CI:
php artisan rest:check
php artisan rest:check "App\Rest\Controllers\PostsController" --json
routes and policies ... 14 routes authenticated, 2 policies found
rows ... 1 resource with a perimeter, 1 marked #[WithoutPerimeter]
App\Rest\Resources\CountryResource ... #[WithoutPerimeter]
actions ... 1 action checks each row
Without arguments, it checks every controller that a route uses. It exits with 1 when:
- a route of a controller has no authentication middleware, or a model has no policy;
- no route uses a controller of the package;
- a resource that the routes reach, through their relations too, does not override
perimeterQuery(), so every row is visible to users who passviewAny; - an action on the
SearchorResourcestarget returnsnullfromauthorizeEach(), so no policy ability is checked on each row.
rows ... FAILED
⇂ App\Rest\Resources\JournalResource does not override perimeterQuery(): every row is visible to users who pass viewAny. Override perimeterQuery() to limit the rows, or mark the resource with #[WithoutPerimeter] when every row is meant for these users
- Override
perimeterQuery(), or mark a resource whose rows are meant for all these users, such as countries, with#[Lomkit\Rest\Attributes\WithoutPerimeter]. A subclass inherits the attribute. - An action that only reads returns
'view'fromauthorizeEach(). --jsonprints the report as an object:routes,policies,problems,search, thenrows(perimeter,without_perimeter,problems) andactions(checked,problems).rest:quick-startchecks the routes and the policies only.
Resources
php artisan rest:resource PostResource --model=Post --generate
- Without
--model, the model is guessed from the class name:PostResourcegivesApp\Models\Postwhen that class exists. - The key field is
ID::make()with the key name of the model. rest:resourcecreates the base resource when it is missing.
Generating the fields and relations
--generate (-G) reads the table of the model and declares one field per column, in the order of the table, then one relation per relation method whose related model has a resource. Run it after the migrations.
PostResource ... 8 fields, 1 relation
Skipped: author: no resource for App\Models\User. Generate one first with php artisan rest:resource UserResource --model=User --generate, then run this command again with --force.
- A cast gives the field type when one matches: a backed enum gives
Enum, the JSON casts giveJson,boolean,integer,decimal,float,dateanddatetimegiveBoolean,Integer,Number,DateandDateTime. Otherwise the column type decides. - The columns that the authentication flow writes are left out, with a note:
remember_token(or thegetRememberTokenName()of anAuthenticatablemodel),two_factor_secret,two_factor_recovery_codesandtwo_factor_confirmed_at.passwordkeeps its field. A hidden column that the model does not let fill, when the model declares$fillableor$guarded, is left out with a note too. - A nullable column gets
nullable(). The timestamps, auto-increment and generated columns getreadonly(). A hidden column getswriteOnly(). - A string column whose length the database reports gets
rules('max:<length>'). A column that is not nullable and has no default getscreationRules('required'), and a column with a unique index getsRule::unique(). - A generated
BelongsTorelation makes its foreign keyreadonly(), so clients set it through the relation. - A
MorphTorelation is skipped, with a note that prints the declaration to write by hand. The note names the resources of the morph map models that point back to the relation, through amorphOne(), amorphMany()or amorphToMany()of the same morph name:
Skipped: commentable: MorphTo relations are not generated, declare it in relations() by hand with its targets, for example MorphTo::make('commentable', [PostResource::class, VideoResource::class]).
- A many-to-many relation with
withPivot()columns getspivotFields([...]).
When the command runs interactively without --generate and the table exists, it asks Generate the fields and relations from the <table> table?. Read the generated fields before you expose the resource.
Controllers
php artisan rest:controller PostsController --resource=PostResource
Without --resource, the resource is guessed: PostsController gives App\Rest\Resources\PostResource. When nothing is found, the $resource line is left out and the command warns: set it before Rest::resource() registers the controller.
Policies
php artisan rest:policy PostPolicy --model=Post
The policy allows every ability to a signed-in user: viewAny(), view(), create(), update() and delete(), plus restore() and forceDelete() for a model that uses SoftDeletes. The user parameter has the type of the user model of the default guard.
When Gate already finds another policy for the model, the command prints The model [App\Models\Post] already has the policy [App\Policies\ContentPolicy]. and writes nothing.
Metrics
php artisan rest:metric OrdersByStatus --type=partition --group-by=status
--type | calculate() returns |
|---|---|
value | $this->count($query), without ranges |
trend | $this->countByDays($query), with the ranges 7, 30 and 90 days |
partition | $this->count($query, '<group-by>') |
progress | $this->count($query, target: 100) |
table | $this->rows($query, '<group-by>', ['count' => TableColumn::count()]) |
custom | a ValueResult, in a class that extends Metric |
--group-by is required for partition and table. With --no-interaction, a metric without --type is a value metric. See Metrics.
Actions, instructions and responses
rest:actionwrites an action withhandle(ActionFields $fields, Collection $models)and afields()method that returns[]. It has theSearchtarget. See Actions.rest:instructionwrites an instruction withhandle(InstructionFields $fields, Builder $query). See Scopes and instructions.rest:responsewrites a response with itsmap()method. See Custom responses.
Published stubs
The generators read the stubs that php artisan stub:publish copies to stubs/rest.<name>.stub, and fall back to the package stubs. A published stub keeps the placeholders that the generator fills, such as {{ namespace }} and {{ class }}. A missing placeholder throws Lomkit\Rest\Exceptions\InvalidStubException, which names the stub and the placeholder.
rest:metric reads one stub per type: rest.metric-value.stub, rest.metric-trend.stub, rest.metric-partition.stub, rest.metric-progress.stub, rest.metric-table.stub, and rest.metric.stub for custom.