Git Product home page Git Product logo

laravel-json-api's Introduction

JsonApi - Laravel Resource

A Lightweight {JSON:API} Resource for Laravel.

example branch parameter codecov

Installation

composer require ark4ne/laravel-json-api

Config

Path Type Description
describer.nullable bool For describer notation, defined if a value is nullable by default.
describer.date string datetime format For describer notation, defined default date time format.
describer.precision int \ null For describer notation, decimal precision for float value. null for disable rounding.
describer.when-has bool \ string[] For describer notation, Apply automatically whenHas condition on attributes.
relationship.when-included bool Allow to disabled by default the loading of relationship data.

Usage

This package is a specialisation of Laravel's JsonResource class. All the underlying API's are still there, thus in your controller you can still interact with JsonApiResource classes as you would with the base JsonResource class

Request

This package allows the reading and dynamic inclusion of resources that will be requested in the requests via the "include" parameter.
@see {json:api} fetching-includes

Resource attributes will also be filtered according to the "fields" parameter.
@see {json:api} fetching-fields

You can also very simply validate your requests for a given resource via the rules Rules\Includes and Rules\Fields.

Include validation

use \Ark4ne\JsonApi\Requests\Rules\Includes;
use \Illuminate\Foundation\Http\FormRequest;

class UserFetchRequest extends FormRequest
{
    public function rules()
    {
        return [
            'include' => [new Includes(UserResource::class)],
        ]
    }
}

Rules\Includes will validate the include to exactly match the UserResource schema (determined by the relationships).

Fields validation

use \Ark4ne\JsonApi\Requests\Rules\Fields;
use \Illuminate\Foundation\Http\FormRequest;

class UserFetchRequest extends FormRequest
{
    public function rules()
    {
        return [
            'fields' => [new Fields(UserResource::class)],
        ]
    }
}

Rules\Fields will validate the fields to exactly match the UserResource schema (determined by the attributes and relationships).

Customize validation message

Trans key default
validation.custom.jsonapi.fields.invalid The selected :attribute is invalid.
validation.custom.jsonapi.fields.invalid_fields ":resource" doesn ' t have fields ":fields".
validation.custom.jsonapi.fields.invalid_resource ":resource" doesn ' t exists.
validation.custom.jsonapi.includes.invalid The selected :attribute is invalid.
validation.custom.jsonapi.includes.invalid_includes ":include" doesn ' t have relationship ":relation".

Resource

@see {json:api} resource-type

Implementable methods :

protected function toType(Request $request): string;

protected function toIdentifier(Request $request): int|string;

protected function toAttributes(Request $request): iterable;

protected function toRelationships(Request $request): iterable;

protected function toResourceMeta(Request $request): ?iterable;

protected function toMeta(Request $request): ?iterable;

Example:

use Ark4ne\JsonApi\Resources\JsonApiResource;
use Illuminate\Http\Request;

class UserResource extends JsonApiResource
{
    protected function toAttributes(Request $request): iterable
    {
        return [
            'name' => $this->name,
            'email' => $this->email,
        ];
    }

    protected function toResourceMeta(Request $request): ?iterable
    {
        return [
            'created_at' => $this->created_at->format(DateTimeInterface::ATOM),
            'updated_at' => $this->updated_at->format(DateTimeInterface::ATOM),
        ];
    }

    protected function toRelationships(Request $request): iterable
    {
        return [
            'posts' => PostResource::relationship(fn() => $this->posts, fn() => [
                'self' => "https://api.example.com/user/{$this->id}/relationships/posts",
                'related' => "https://api.example.com/user/{$this->id}/posts",
            ]),
            'comments' => CommentResource::relationship(fn() => $this->whenLoaded('comments')),
        ];
    }
}

toType

@see {json:api} resource-type

Returns resource type.

protected function toType(Request $request): string
{
    return 'user';
}

Default returns model class in kebab case : App\Models\MyPost => my-post

toIdentifier

@see {json:api} resource-identifier

Returns resource identifier.

protected function toIdentifier(Request $request): int|string
{
    return $this->id;
}

Default returns model id.

toAttributes

@see {json:api} resource-attributes

Returns resource attributes.

protected function toAttributes(Request $request): iterable
{
    return [
        'name' => $this->name,
        'email' => $this->email,
    ];
}

Laravel conditional attributes

@see laravel: eloquent-conditional-attributes

Support laravel conditional attributes.

protected function toAttributes(Request $request): array
{
    return [
        'name' => $this->name,
        'email' => $this->email,
        // with lazy evaluation
        'hash64' => fn() => base64_encode("{$this->id}-{$this->email}"),
        // Conditional attribute
        'secret' => $this->when($request->user()->isAdmin(), 'secret-value'),       
        // Merging Conditional Attributes
        // use applyWhen insteadof mergeWhen for keep fields
        // useful for fields request rules validation
        $this->applyWhen($request->user()->isAdmin(), [
            'first-secret' => 123,
            'second-secret' => 456.789,
        ]),
    ];
}

Described attributes

@see described notation

protected function toAttributes(Request $request): array
{
    return [
        'name' => $this->string(),
        // pass key to describer
        $this->string('email'),
        // with lazy evaluation
        'hash64' => $this->string(fn() => base64_encode("{$this->id}-{$this->email}")),
        // Conditional attribute
        $this->string('secret')->when($request->user()->isAdmin(), 'secret-value'),       
        // Merging Conditional Attributes
        $this->applyWhen($request->user()->isAdmin(), [
            'first-secret' => $this->integer(fn() => 123),
            'second-secret' => $this->float(fn() => 456.789),
        ]),
    ];
}

toRelationships

@see {json:api} resources-relationships

Returns resource relationships.

All relationships must be created with ModelResource::relationship. This allows the generation of the schema representing the resource and thus the validation of request includes.

If your relation should have been a collection created via the ::collection(...) method, you can simply use ->asCollection().

If you want the relation data to be loaded only when it is present in the request include, you can use the ->whenIncluded() method.

protected function toRelationships(Request $request): array
{
    return [
        'avatar' => AvatarResource::relationship($this->avatar),
        // with conditional relationship
        'administrator' => $this->when($request->user()->isAdmin(), UserResource::relationship(fn() => $this->administrator),
        // as collection, with conditional value
        'comments' => CommentResource::relationship(fn() => $this->whenLoaded('comments'))->asCollection(),
        // with relationship (allow to include links and meta on relation)
        'posts' => PostResource::relationship(fn() => $this->posts)->withLinks(fn() => [
            'self' => "https://api.example.com/user/{$this->id}/relationships/posts",
            'related' => "https://api.example.com/user/{$this->id}/posts",
        ])->asCollection(),
    ];
}

toRelationships must returns an array, keyed by string, of JsonApiResource or JsonApiCollection.

Laravel conditional relationships

@see laravel: eloquent-conditional-relationships

Support laravel conditional relationships.

protected function toRelationships(Request $request): array
{
    return [
        'avatar' => AvatarResource::relationship($this->avatar),
        // as collection, with condition
        'comments' => CommentResource::relationship(fn() => $this->whenLoaded('comments'))->asCollection(),
        // with relationship (allow to include links and meta on relation)
        'posts' => PostResource::relationship(fn() => $this->posts)
                ->asCollection(),
    ];
}

Described attributes

@see described notation

protected function toRelationships(Request $request): array
{
    return [
        'avatar' => $this->one(AvatarResource::class),
        // custom relation name
        'my-avatar' => $this->one(AvatarResource::class, 'avatar'),
        // as collection, with condition
        'comments' => $this->many(CommentResource::class)
                           ->whenLoaded(),
        // with relationship (allow to include links and meta on relation)
        'posts' => $this->many(PostResource::class)
                ->links(fn() => [
                    'self' => "https://api.example.com/posts/{$this->resource->id}/relationships/posts",
                    'related' => "https://api.example.com/posts/{$this->resource->id}/posts",
                ])
                ->meta(fn() => [
                    'total' => $this->integer(fn() => $this->resource->posts()->count()),
                ]),
    ];
}

Relation links and meta

@see {json:api}: relation-linkage
@see {json:api}: relation-meta

Returns links and meta for a relation.

protected function toRelationships(Request $request): array
{
    return [
        'posts' => PostResource::relationship(fn() => $this->posts)->withLinks(fn() => [
            // links
            'self' => "https://api.example.com/user/{$this->id}/relationships/posts",
            'related' => "https://api.example.com/user/{$this->id}/posts",
        ])->withMeta(fn() => [
            // meta
            'creator' => $this->name,
        ])
        ->asCollection(),
    ];
}

toLinks

@see {json:api}: resource-linkage

Returns resource links.

protected function toLinks(Request $request): ?array
{
    return [
        'self' => route('api.user.show', ['id' => $this->id]),
    ];
}

toResourceMeta

@see {json:api}: resource-meta
@see {json:api}: document-meta

Returns resource meta.

protected function toResourceMeta(Request $request): ?iterable
{
    return [
        'created_at' => $this->created_at->format(DateTimeInterface::ATOM),
        'updated_at' => $this->updated_at->format(DateTimeInterface::ATOM),
    ];
}

toMeta

@see {json:api}: document-meta

Returns document meta.

protected function toMeta(Request $request): ?iterable
{
    return [
        "copyright": "Copyright 2022 My Awesome Api",
    ];
}

Collection

@see laravel: resource-collection

Collection are implemented in JsonApiCollection.

Usage is the same as laravel collections.

UserResource::collection(User::all()); // => JsonApiCollection

Described notation

Value methods

Method Description
bool Cast to boolean
integer Cast to integer
float Cast to float
string Cast to string
date Cast to date, allow to use custom format
array Cast to array
mixed Don't cast, return as is
enum Get enum value.

Relation methods

Method Description
one For relationship with a single value: HasOne, BelongsTo, ...
many For relationship with many value: HasMany, BelongsToMany, ...

Enum

Method enum allow to get enum value for backed enum or name for unit enum.

According to structure:

/// Role.php
enum Role {
    case ADMIN;
    case USER;
}
/// State.php
enum State:int {
    case ACTIVE = 1;
    case INACTIVE = 0;
}
/// User.php
class User extends Model
{
    $casts = [
        'role' => Role::class,
        'state' => State::class,
    ];
}

The following attributes resource:

// UserResource.php
protected function toAttributes(Request $request): array
{
    return [
        'status' => $this->enum(),
        'role' => $this->enum(),
    ];
}

Will return:

[
    "status": 1,
    "role": "ADMIN"
]

laravel-json-api's People

Contributors

ark4ne avatar

Stargazers

 avatar  avatar

Watchers

 avatar

Forkers

bchalier

laravel-json-api's Issues

v1.4.0

  • Auto check has attribute
  • Implement whenHas method
  • Implement unless method
  • Support Enum

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.