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\ValravnJsonResource;
 2use Illuminate\Database\Eloquent\Model;
 3
 4class SampleResource extends ValravnJsonResource {
 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\ValravnResourceCollection;
 2use Illuminate\Database\Eloquent\Model;
 3
 4class SampleCollection extends ValravnResourceCollection {
 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.

Make sure there is not any conflict with extracted attributes of the related resource.
this data will merge into the data key on response.

Collection Query

Collection Query just can be registered on ValravnResourceCollection instances.

 1use Hans\Valravn\Http\Resources\Contracts\ValravnJsonResource;
 2use Hans\Valravn\Http\Resources\Contracts\CollectionQuery;
 3
 4class RelatedExamplesCollectionQuery extends CollectionQuery {
 5
 6  public function apply( ValravnJsonResource $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}
It's recommended that suffix the class with 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.

It's recommended to prefix query strings with 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}
It's recommended to create includes in a sub folder where the related resource classes are locate.

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
To eager load a relationship, you must pass the registered include using 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
We consider the ExampleResource class registered 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.

Columns you pass as parameter to actions, must be in filterable list of related model.
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)
If there is a belongs to relationship, and you want to use select action, don't forget to select the foreign key too. otherwise the relationship doesn't resolve by ORM.
order

The order action ables you to set order for your relationship results.

1domain/api/blog/posts?includes=comments:select(content):order(created_at)
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.

These methods just effect on current resource instance and other instances that created automatically using current resource.
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)