Resources
Resources have built on top of laravel's resource and resource collection classes but there are more features! You can create resource classes on your own or just using our resources command.
VJsonResource
it's same as JsonResource class on laravel. to start using this, first you
need to create resource class which should see something like this.
1use Hans\Valravn\Http\Resources\VJsonResource;
2use Illuminate\Database\Eloquent\Model;
3
4class SampleResource extends VJsonResource {
5
6 public function extract( Model $model ): ?array {
7 return [
8 'id' => $model->id,
9 //
10 ];
11 }
12
13 public function type(): string {
14 return 'samples';
15 }
16
17}
You can specify the model's attributes in extract method that you wand to see
in response. the type method determines related entity on this response for
the front-end dev.
VResourceCollection
The resource collection class is the same as ValravnJsonResource.
1use Hans\Valravn\Http\Resources\VResourceCollection;
2use Illuminate\Database\Eloquent\Model;
3
4class SampleCollection extends VResourceCollection {
5
6 public function extract( Model $model ): ?array {
7 return null;
8 }
9
10 public function type(): string {
11 return 'samples';
12 }
13
14}
Available methods
Resource classes contain several methods and in continue, we will introduce them.
only
If you just don't need some fields in a specific response, you can reduce your exported fields using this method.
1SampleResource::make( $this->model )->only( 'name','email' );
2// or on a collection class
3SampleCollection::make( $this->models )->only( [ 'email' ] );
loaded
It's a hook on both resource and collection classes that will execute when each item extracted. you can
override loaded method on your instance to write your code there.
There is a bit of difference on using loaded hook in resource and collection classes. In resource class we have only
data parameter.
1protected function loaded( &$data ): void {
2 $data[ 'sober' ] = "i might regret this when tomorrow comes";
3}
But in collection class, we have one more parameter named resource that let us manipulate loaded pivot data or loaded
relations through related methods.
1protected function loaded( &$data, ValravnJsonResource $resource = null ): void {
2 $this->addExtra( [
3 'sober' => 'i might regret this when tomorrow comes'
4 ] );
5}
addAdditional
Using this method, you can add additional data to your response.
1SampleResource::make( $model )
2 ->addAdditional( [
3 'tulips&roses' => "My star's back shining bright, I just polished it",
4 ] );
This data will be merged out of data wrapper. to merge some data into data wrapper, use addExtra method.
addExtra
It's similar to addAdditional method but the difference is, addExtra merge data into data wrapper.
1SampleResource::make( $this->model )
2 ->addExtra( [
3 'all facts' => "love don't cost a thing, this is all facts",
4 ] );
allLoaded
This hook will execute when all collection's items extracted.
1protected function allLoaded( Collection &$response ): void {
2 $this->addAdditional( [
3 'all-loaded' => 'will you still love me when i no longer young and beautiful?'
4 ] );
5}
Queries
A Query is a class that defines for a resource class and front-end dev can trigger that using a specific query string.
VResourceQuery
This contract can be uses in both VJsonResource and VResourceCollection
classes. first you need to create a class. it's recommended to create the class
in a sub-folder where the resource classes are exist. in addition, it's better
that suffix the classes with Query to make recognition easier.
1use Hans\Valravn\Http\Resources\Contracts\VResourceQuery;
2use Illuminate\Database\Eloquent\Model;
3
4class FirstExampleQuery extends VResourceQuery {
5
6 public function apply( Model $model ): array {
7 return [
8 'first_example' => ExampleResource::make( $model )
9 ];
10 }
11
12}
in apply method, you should return an array. we merge the data into the
related resource instance using the array key that you defined.
data key on response.
VCollectionQuery
Collection Query just can be registered on VResourceCollection instances.
1use Hans\Valravn\Http\Resources\Contracts\VJsonResource;
2use Hans\Valravn\Http\Resources\Contracts\VCollectionQuery;
3
4class RelatedExamplesCollectionQuery extends VCollectionQuery {
5
6 public function apply( VJsonResource $resource ): array {
7 $ids = $resource->resource instanceof Collection ?
8 $resource->resource->map( fn( $value ) => [ 'id' => $value->id ] )->flatten() :
9 [ $resource->resource->id ];
10
11 return [
12 'related_examples' => ExampleCollection::make(
13 app(ExampleService::class)->relatedToTheseIds($ids)
14 )
15 ];
16 }
17
18}
CollectionQuery to avoid any conflict and mistake.
Queries registration
After all, you should register your queries on the resource class. to register a
query, just you need to override the getAvailableVQueries method and add you
queries.
1public function getAvailableVQueries(): array {
2 return [
3 'with_first_example' => FirstExampleQuery::class
4 ];
5}
the array's key is the query string that a front-end dev can trigger this query and the value must be the query class.
with_ to avoid any conflict.
Parse queries
By default, the resource classes don't parse queries. to enable this feature,
you should call parseQueries on the resource instance that you are returning
as a response.
1SampleResource::make($model)->parseQueries();
Manual trigger
Also, you can manually trigger a query on the resource instance by
calling registerQuery method.
1SampleResource::make($model)->registerQuery(SampleQuery::class);
or just create a method on resource class for ease of use.
1public function withSampleQuery(): self {
2 $this->registerQuery( FirstCommentQuery::class );
3
4 return $this;
5}
and then you can call this on a resource instance.
1SampleResource::make($model)->withSampleQuery();
VIncludes
Valravn allows you to eager load relationships using query strings and apply
actions on them. first you need to create a class and
extends Hans\Valravn\Http\Resources\Contracts\VIncludes class. next, you should
implement the required methods.
1use Hans\Valravn\Http\Resources\Contracts\VIncludes;
2
3class ExampleIncludes extends VIncludes {
4
5 public function apply( Model $model ): Builder {
6 return $model->example();
7 }
8
9 public function toVResource(): VJsonResource {
10 return ExampleResource::make( $this->getBuilder()->first() );
11 }
12
13}
VIncludes registration
To register a include, you just need to override getAvailableVIncludes method
on the resource class and then register your includes.
1public function getAvailableVIncludes(): array {
2 return [
3 'example' => ExampleIncludes::class,
4 ];
5}
Parse includes
By default, resource classes don't parse includes. if you want to parse
includes, just call parseIncludes method on your resource instance.
1SampleResource::make($model)->parseIncludes();
Manual trigger
Like queries, you can trigger your needed includes by calling registerInclude
method.
1SampleResource::make($model)->registerInclude(ExampleIncludes::class);
or just register your include using a method.
1public function withExampleInclude(): self {
2 $this->registerInclude( ExampleIncludes::class );
3
4 return $this;
5}
and then you can call this on a resource instance.
1SampleResource::make($model)->withExampleInclude();
Trigger through API
let's talk about how The front-end dev should work with defined includes. let's assume we just want to include a relationship. all we need to do is this:
1domain/api/namespace/name?includes=example
includes key.
Nested eager loads
Valravn allows you to use nested eager loads. for that you just need to pass the
nested includes after the first one and split them using a . character.
1domain/api/namespace/name?includes=example.owner
owner include. otherwise this will not work.
Actions
There are some default actions that you can use on your api calls. you are free
to use an action for two relationship or different actions for any includes.
actions must split using : character.
select
This action allows you just fetch columns that you want. it might help create optimized requests.
1domain/api/blog/posts?includes=comments:select(content).user:select(first_name|last_name)
select
action, don't forget to select the foreign key too. otherwise the relationship
doesn't resolve by ORM.
order
The order action can set an ordering to the relationship result data.
1domain/api/blog/posts?includes=comments:select(content):order(created_at)
Also, To change the direction, pass 'asc' or 'desc' options as the second parameter.
If none passed, the asc option set as default.
1domain/api/blog/posts?includes=comments:select(content):order(created_at|desc)
limit
Using this action, you can limit the number of rows must fetch from related relationship.
1domain/api/blog/categories?includes=posts:limit(5)
this request will return the categories while each category loaded with at least
5 related posts. however the limit action accepts two parameter. the second
parameter acts like page number.
1domain/api/blog/categories?includes=posts:limit(5|2)
For example, the above request will skip the first 5 posts and loads the second 5 posts of each category.
Interact with relations
You can interact with relations and manipulate the relationships' data.
resolveRelationsUsing
Accepts relations and their custom resource class to be response to the client.
1PostResource::make( $this->post )
2->resolveRelationsUsing(
3 [
4 'comments' => CommentCustomCollection::class
5 ]
6)
skipRelationsForModel
Accept models and their related relationships that should be skipped and not present in the output.
1PostResource::make( $this->post )
2->skipRelationsForModel(
3 [
4 Post::class => 'comments',
5 Comment::class => [ 'post', 'user' ]
6 ]
7)