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.
ValravnJsonResource
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\Contracts\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.
ValravnResourceCollection
The resource collection class is the same as ValravnJsonResource
.
1use Hans\Valravn\Http\Resources\Contracts\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.
Resource Query
This contract can be uses in both ValravnJsonResource
and ValravnResourceCollection
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\ResourceQuery;
2use Illuminate\Database\Eloquent\Model;
3
4class FirstExampleQuery extends ResourceQuery {
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.
Collection Query
Collection Query just can be registered on ValravnResourceCollection
instances.
1use Hans\Valravn\Http\Resources\Contracts\VJsonResource;
2use Hans\Valravn\Http\Resources\Contracts\CollectionQuery;
3
4class RelatedExamplesCollectionQuery extends CollectionQuery {
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 getAvailableQueries
method and add you
queries.
1public function getAvailableQueries(): 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();
Includes
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\Includes
class. next, you should
implement the required methods.
1use Hans\Valravn\Http\Resources\Contracts\Includes;
2
3class ExampleIncludes extends Includes {
4
5 public function apply( Model $model ): Builder {
6 return $model->example();
7 }
8
9 public function toResource(): ValravnJsonResource {
10 return ExampleResource::make( $this->getBuilder()->first() );
11 }
12
13}
Includes registration
To register a include, you just need to override getAvailableIncludes
method
on the resource class and then register your includes.
1public function getAvailableIncludes(): 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)