JSON:API for Laravel applications

Related tags

Laravel laravel
Overview

Tests

JSON:API for Web Artisans

Implement feature-rich JSON:API compliant APIs in your Laravel applications. Build your next standards-compliant API today.

Why use JSON:API?

  • Standardised, consistent APIs.
  • Feature rich - some of which are filtering, pagination, eager loading and sparse fieldsets.
  • Easy to understand.

Why use Laravel JSON:API?

  • Saves a lot of development time.
  • Highly maintainable code.
  • Great, extensive documentation.
  • Strong conventions, but also highly customisable.
  • Makes use of native Laravel features such as policies and form requests to make the shift easier for developers.
  • Beautiful, expressive Nova-style schemas.
  • Fully testable via expressive test helpers.
class PostSchema extends Schema
{

    /**
     * The model the schema corresponds to.
     *
     * @var string
     */
    public static string $model = Post::class;

    /**
     * The maximum include path depth.
     *
     * @var int
     */
    protected int $maxDepth = 3;

    /**
     * Get the resource fields.
     *
     * @return array
     */
    public function fields(): array
    {
        return [
            ID::make(),
            BelongsTo::make('author')->type('users')->readOnly(),
            HasMany::make('comments')->readOnly(),
            Str::make('content'),
            DateTime::make('createdAt')->sortable()->readOnly(),
            DateTime::make('publishedAt')->sortable(),
            Str::make('slug'),
            BelongsToMany::make('tags'),
            Str::make('title')->sortable(),
            DateTime::make('updatedAt')->sortable()->readOnly(),
        ];
    }

    /**
     * Get the resource filters.
     *
     * @return array
     */
    public function filters(): array
    {
        return [
            WhereIdIn::make($this),
            WhereIn::make('author', 'author_id'),
        ];
    }

    /**
     * Get the resource paginator.
     *
     * @return Paginator|null
     */
    public function pagination(): ?Paginator
    {
        return PagePagination::make();
    }
}

Documentation

See our website, laraveljsonapi.io

Tutorial

New to JSON:API and/or Laravel JSON:API? Then the Laravel JSON:API tutorial is a great way to learn!

Follow the tutorial to build a blog application with a JSON:API compliant API.

Installation

Install using Composer

composer require laravel-json-api/laravel

See our documentation for further installation instructions.

Upgrading

When upgrading you typically want to upgrade this package and all our related packages. This is the recommended way:

composer require laravel-json-api/laravel --no-update
composer require laravel-json-api/testing --dev --no-update
composer up laravel-json-api/* cloudcreativity/json-api-testing

Example Application

To view an example Laravel application that uses this package, see the Tutorial Application.

License

Laravel JSON:API is open-sourced software licensed under the Apache 2.0 License.

Comments
  • Default or custom sort not working.

    Default or custom sort not working.

    I want to fetch records and it works fine, but I want to sort it with a particular column. so I tried this in my schema - protected $defaultSort = '-createdAt';

    but this is not sorting the records, I also tried with other column names, no luck.

    in my controller index function

    if I use

     $spaces = $spaces->where('user_id', $user->id);
     $spaces = $spaces->sort('createdAt');
    

    error attached below is received. (where condition works fine, only sort is triggering that error).

    I do not want to handle sort from the url like GET /spaces?sort=-createdAt HTTP/1.1 . Please guide.

    image

    opened by sahil-kindlebit 17
  • Resource ID with value zero throws the exception: No JSON API resource id name set on route

    Resource ID with value zero throws the exception: No JSON API resource id name set on route

    Trying to get a resource which ID value is zero, kinda similar to an issue found at the former cloudcreativity/laravel-json-api throws the LogicException No JSON API resource id name set on route. Turns out that on funtion modelOrResourceId in Route.php an assingnmet within the if stament is performed and it throws the exception because a logic value false is assumed, instead of the actual 0.

    Here's referenced code (at line 102):

        /**
         * @inheritDoc
         */
        public function modelOrResourceId()
        {
            if (!$name = $this->resourceIdName()) {
                throw new LogicException('No JSON API resource id name set on route.');
            }
    
            if ($modelOrResourceId = $this->route->parameter($name)) {
                return $modelOrResourceId;
            }
    
            throw new LogicException('No JSON API resource id set on route.');
        }
    

    And this is the workaround the I've implemented in order to prevent the exception being thrown:

        /**
         * @inheritDoc
         */
        public function modelOrResourceId()
        {
            if (!$name = $this->resourceIdName()) {
                throw new LogicException('No JSON API resource id name set on route.');
            }
    
            if (!is_null($this->route->parameter($name)) && $this->route->parameter($name)!='') {
                return $this->route->parameter($name);
            }
    
            throw new LogicException('No JSON API resource id set on route.');
        }
    

    I'm not sure if this is the best solution but, considering that it's plausible to have a resource ID = zero, it solves my issues without affecting any other values.

    Hope it helps!

    bug 
    opened by dacercoolex 16
  • Clear Support for Virtual Resources

    Clear Support for Virtual Resources

    Creating "virtual" resources for an API is a common need and I'm struggling creating one using this package. Any thoughts or workarounds on this? Is seems any schema NEEDS an eloquent model which seems very limiting in some very valid use cases.

    Thanks!

    opened by PeterCat12 16
  • How to get the related model in controller relationship hooks?

    How to get the related model in controller relationship hooks?

     public function updatingStatus(User $user, UserRequest $request, StatusQuery $query) {
            $data = $request->validatedForRelation();
           
            dd($status = $query->model());
        }
    

    I would expect $status to be the status model, but the variable contains the $user model instead Also, if u can explain what is the difference between the $request and the $query

    enhancement 
    opened by Mina-R-Meshriky 13
  • Policy & include

    Policy & include

    Hi,

    For example, i have resource users with patient relation. When i return false in user policy in viewPatient method i can't GET /users/id/patient. It's ok.

    But... i can still do that: GET /users/id?include=patient

    Shouldn't this also be forbidden?

    opened by bbprojectnet 13
  • Filtering data by relationship fields

    Filtering data by relationship fields

    Hi! First of all, thank you for the great package!

    I'm learning and testing it, and I wanted to know, if this package does support filtering data by model and its relationship (like a inner join) ?

    Example:

    Resources (Models) :

    • posts (id, title, author_id )
    • author (id,name,email) - Belongs to posts

    And so I want to get:

    • posts with title 'test'
    • including the author
    • additional filter by author, where id = 1
    GET https://myaddress.com/api/v1/posts?include=author&filter['title']=test&filter['author.id']=1
    

    (The url sintax is just an example, there are use cases where people would use [author][id]=1 for example.

    I've checked the issue #49 but could not find a conclusive answer.

    opened by lcsdms 13
  • Availability to hash ids

    Availability to hash ids

    Hey congrats for your work, it's great.

    I'm wondering if theres a way to show hashed (e.g. with https://github.com/vinkla/laravel-hashids) when sending response to the client.

    Screen Shot 2021-02-25 at 12 06 28 PM

    Thanks for your ideas.

    opened by juljupy 12
  • Errors with a PATCH request

    Errors with a PATCH request

    Hi,

    I've tried sending two POST request, one from my frontend and one from my Postman-like client:

    From the frontend I send this:

    XHR PATCH
    [HTTP/1.1 400 Bad Request 601ms]
    
    Referrer Policystrict-origin-when-cross-origin
    
        Host: 127.0.0.1:8000
    
        User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:100.0) Gecko/20100101 Firefox/100.0
    
        Accept: application/vnd.api+json
    
        Accept-Language: it,en-GB;q=0.5
    
        Accept-Encoding: gzip, deflate
    
        Content-Type: application/vnd.api+json
    
        X-XSRF-TOKEN: eyJpdiI6Ilc3MWlaTEk5Q2VORzh6dTFPQnVjWEE9PSIsInZhbHVlIjoiOWQrRitxaWIySTVCaEhTblZjWWNqT1lMMElSak40V0kvcnRSMTNrWExEQnRjQzRpUVAwc21oS29hNm5YWHBqOUtLR1JVbFZ1ZW15emFOQWRQUmhqTEhQNFRzUVFmc2dBUEkvaWVnbmZoVlF4dDFoQjl3akRiYmNsRVBUaTYwaGsiLCJtYWMiOiJmMTllYmFhZjhmZDAxYjY4MTg5ZmUxMmJiZGRkNDlmNzg4Y2ZmYzc5ZWYyZDIzY2UyYzQ5YTE3MWE5OTc3ZGY0IiwidGFnIjoiIn0=
    
        Content-Length: 225
    
        Origin: http://127.0.0.1:8000
    
        DNT: 1
    
        Connection: keep-alive
    
        Referer: http://127.0.0.1:8000/anagrafiche
    
        Cookie: remember_web_59ba36addc2b2f9401580f014c7f58ea4e30989d=eyJpdiI6Ikl4akNHNElvMkpQQ3Q4V2Y3aUJNUWc9PSIsInZhbHVlIjoiVUJWZFg2c3c3Q3FZZlkxODBGd3d1UW15MDhWWk04TmV2VGs3eGRPWGJzZjB5WEtmN1BncGQ1YytYYWk3aE13NXZUZmJVVk1QSUxqT0h6blp0MmY3MFlkWVdacjNmUnU0bGRONkhLZlA0c2NMc0JhUm5pa0pDc0wxay9reSt3cW9ma0NINi9XdWRhSDdiUDJyNWkwMEhaNFBJTm9Wd09HNXZvTFhkNTZMVi8xUVVlQVJ6NTZrYSsxTmJDQW1XZVlLZnZZS25CUFdpL09hKzZoaWFRVWUrQitVajYvUGxHTGZFdTZQazh4VHAvZz0iLCJtYWMiOiIwMjE4ZTMyOTBmOTgxNGQ4ZDE2NDk0ZjViNzdkNmRlODhhMDBmYzM5ZTkyNTA3YmMzZWNjZjgyMWU5YTA3Mjk2IiwidGFnIjoiIn0%3D; XSRF-TOKEN=eyJpdiI6Ilc3MWlaTEk5Q2VORzh6dTFPQnVjWEE9PSIsInZhbHVlIjoiOWQrRitxaWIySTVCaEhTblZjWWNqT1lMMElSak40V0kvcnRSMTNrWExEQnRjQzRpUVAwc21oS29hNm5YWHBqOUtLR1JVbFZ1ZW15emFOQWRQUmhqTEhQNFRzUVFmc2dBUEkvaWVnbmZoVlF4dDFoQjl3akRiYmNsRVBUaTYwaGsiLCJtYWMiOiJmMTllYmFhZjhmZDAxYjY4MTg5ZmUxMmJiZGRkNDlmNzg4Y2ZmYzc5ZWYyZDIzY2UyYzQ5YTE3MWE5OTc3ZGY0IiwidGFnIjoiIn0%3D; openstamanager_session=eyJpdiI6IkhrZGgxVGZoNmxyeEJKY1NSK1U1cUE9PSIsInZhbHVlIjoiOU9XYmRweFJyRkZ3c0tHUTZXSjBvS2xNekRFTk5xUTFSSGhPdU5oWTRRT3BRWkt4NThZdVRIUXNCbnpwVm5sUnQ4Vkl1Qi9sdnJId3YzbFU4aE05MEIzUGRYL3BvclI1MXJ5N3ZOVjN2UnplTE5NOFJKNGczM2QzZEFFRlA4cVAiLCJtYWMiOiI4MGVkMmZhNWRkOWI4NmQ4OGY2MjdlZmEwOTdiMjFiZDY4YjI1MjRlNzk2MTBjZGVlYzY2MDFjMDU0MDFjMjc5IiwidGFnIjoiIn0%3D
    
        Sec-Fetch-Dest: empty
    
        Sec-Fetch-Mode: cors
    
        Sec-Fetch-Site: same-origin
    

    With this data:

    {
      "data": {
        "type": "anagrafiche-privati",
        "attributes": {
          "id": "26",
          "nome": "dsaqwe",
          "codice_fiscale": "sdewqahjjjjj",
          "createdAt": "2022-01-29T16:25:24.000000Z",
          "updatedAt": "2022-01-29T16:25:24.000000Z"
        },
        "relationships": {},
        "id": "26"
      }
    }
    

    And I receive this:

    {
      "jsonapi": {
        "version": "1.0"
      },
      "errors": [
        {
          "source": {
            "pointer": "/data/attributes"
          },
          "status": "400"
        }
      ]
    }
    

    Instead, from my client I send these headers with the same data as above:

    Accept: application/vnd.api+json
    Content-Type: application/vnd.api+json
    

    And I receive this:

    {
      "jsonapi": {
        "version": "1.0"
      },
      "errors": [
        {
          "detail": "The request entity has a media type which the server or resource does not support.",
          "status": "415",
          "title": "Unsupported Media Type"
        }
      ]
    }
    

    Do you know what the error can be?

    Thanks

    opened by maicol07 11
  • Support PHP 8.1

    Support PHP 8.1

    Need to update the automated testing to check this package works with PHP 8.1, which is imminently about to be released.

    There is definitely one breaking change required, see this issue: https://github.com/laravel-json-api/eloquent/issues/21

    enhancement 
    opened by lindyhopchris 10
  • Dependency injection in server classes - problems and improvements

    Dependency injection in server classes - problems and improvements

    Just calling JsonApiRoute::server() to register the routes produces a lot of side effects as it immediately resolves the server instance which resolves recursively all of the server dependencies which especially impacts the testing.

    My use-case is that I have a client courses resource for which the index action needs to be automatically scoped so that the client can see only the courses that it has access to. The client in this case is the OAuth2 client which authenticates to the app (I'm using Passport for that). So in order to do this I'm adding a scope in the serving method of the server:

        public function serving(): void
        {
            ClientCourse::addGlobalScope($this->oAuthClientScope);
        }
    

    And the scope code looks like this:

    <?php
    
    declare(strict_types=1);
    
    namespace App\JsonApi\V1\ClientCourses;
    
    use Illuminate\Contracts\Config\Repository;
    use Illuminate\Database\Eloquent\Builder;
    use Illuminate\Database\Eloquent\Model;
    use Illuminate\Database\Eloquent\Scope;
    use Laravel\Passport\Client as PassportClient;
    use Laravel\Passport\Guards\TokenGuard;
    use LogicException;
    
    final class OAuthClientScope implements Scope
    {
        private $request;
        private TokenGuard $tokenGuard;
        private Repository $config;
    
        public function __construct(
            callable $request,
            TokenGuard $tokenGuard,
            Repository $config
        ) {
            $this->request = $request;
            $this->tokenGuard = $tokenGuard;
            $this->config = $config;
        }
    
        public function apply(Builder $builder, Model $model)
        {
            /** @var PassportClient|null $client */
            $client = $this->tokenGuard->client(call_user_func($this->request));
    
            if (null === $client) {
                throw new LogicException('This should not have happened.');
            }
    
            // there is actually some more code here that maps the OAuth client model (oauth_clients table)
            // to the application client model (clients table), but it's not relevant for the issue here
    
            $builder->where('client_id', '=', $client->getKey());
        }
    }
    
    

    The scope instance (which gets injected into the server) has a constructor dependency on the HTTP request and on the Passport Token Guard which can tell me what OAuth client is authenticated based on the access token that is sent in the request (https://github.com/laravel/passport/blob/v10.1.2/src/Guards/TokenGuard.php#L122).

    The problem here is that since the server gets resolved when the routes are registered it means that at that point in time the scope instance that is injected in the server has an empty HTTP request and for some reason the server instance is cached in the server repository (https://github.com/laravel-json-api/core/blob/v1.0.0-alpha.4/src/Core/Server/ServerRepository.php#L70-L72). That means I don't get a new server instance when I do the request in my feature test (which means that the request in the scope is empty as it was resolved before the actual request in the test was made). I was able to work around this by making the request a callable (as you can see in my scope code above) instead of directly typehinting the request class. That helped a bit as the request is now resolved only when it's actually needed instead of being resolved along with the server (and I don't consider this as a proper solution, it's more of a temporary hack), but now the problem is that the token guard depends on the OAuth2 Resource server which is supposed to be mocked by Passport in my test :

    $oauthClient = PassportClientFactory::new()->create();
    
    Passport::actingAsClient($oauthClient);
    

    The actingAsClient essentially does app()->instance(ResourceServer::class, $mock); (https://github.com/laravel/passport/blob/v10.1.2/src/Passport.php#L395), but since the resource server was also resolved before the mocked instance was set into the container in my test I have the same problem that I get the actual non mocked resource server injected into the token guard so my test explodes as the non mocked server does not return the expected client back.

    Removing the caching of the server in the server repository helps to solve this specific problem (as a new server instance gets created once my feature test does the request), but it still leaves the underlying problem unresolved and that is that the server still gets resolved on route registration and all recursive dependencies with it.

    This is a problem when for example typehinting \Illuminate\Contracts\Auth\Guard in the constructor of the server and I get the default guard every time there (which is the web guard) instead of the api guard which I wanted (because the auth:api middleware that is supposed to set the guard has not been run at that point in time yet). The workaround for this is to explicitly use contextual binding for the server:

    use Illuminate\Contracts\Auth\Factory as AuthFactory;
    
            $this->app->when(Server::class)
                ->needs(Guard::class)
                ->give(static function (Container $container): Guard {
                    /** @var AuthFactory $authFactory */
                    $authFactory = $container->make(AuthFactory::class);
                    return $authFactory->guard('api');
                });
    

    This works, but it only works under the assumption that the only guard that will ever be able to be used with this server is the api guard which is not a valid assumption for more complex projects where there can be multiple guards that could be used for the same API routes.

    So removing the caching layer in the server repository only solves part of the problem. The other part would be making the package work in a way that that the server instance is not instantiated just for route registration. This could be solved by configuring the stuff that is needed for route registration via the config (per each server) or by making the needed data for route registration available on the server via static method(s) so that a server instance is not needed and so that the server gets resolved only when the \LaravelJsonApi\Laravel\Http\Middleware\BootJsonApi middleware runs (which can be configured in the HTTP kernel to only run after the Laravel auth middleware).

    opened by X-Coder264 9
  • Conditional Relationships in API Resource breaks relationship endpoints

    Conditional Relationships in API Resource breaks relationship endpoints

    Using Conditional Relationships in a API Resource class breaks the relationship self/related endpoints.

    How to repro

    1. Have two related models, e.g. Users and Team
    2. In your UserSchema.php add the Team relationship like
    use LaravelJsonApi\Eloquent\Fields\Relations\BelongsTo;
    
    class UserSchema extends Schema
    {
        public function fields(): array
        {
            return [
                ...
                BelongsTo::make('team'),
                ...
            ];
        }
    }
    
    1. Setup routes, the following endpoints should now work:
    • /users/:id?include=team
    • /users/:id/team
    • /users/:id/relationships/team
    1. Now define a UserResource.php like so
    use Illuminate\Http\Request;
    use LaravelJsonApi\Core\Resources\JsonApiResource;
    
    class UserResource extends JsonApiResource
    {
    
        ...
        
        public function relationships($request): iterable
        {
            return [
                $this->relation('team')),
            ];
        }
    
    }
    
    1. Try the three endpoints again, they should still work (3)
    2. Now make the relationships in the UserResource conditional:
    return [
        $this->when(1===1, $this->relation('team')),
    ];
    
    1. Try the endpoints again, the relationship ones are now broken:
    • /users/:id?include=team => ✅ 200 OK
    • /users/:id/team => ☠️ 500 INTERNAL SERVER ERROR
    • /users/:id/relationships/team => ☠️500 INTERNAL SERVER ERROR

    Stacktrace

    [2021-10-07 10:15:21] local.ERROR: Call to undefined method LaravelJsonApi\Core\Resources\ConditionalField::fieldName() {"userId":"2f667dda-026e-4c8a-9acf-9e48523a2059","exception":"[object] (Error(code: 0): Call to undefined method LaravelJsonApi\\Core\\Resources\\ConditionalField::fieldName() at /var/www/html/vendor/laravel-json-api/core/src/Core/Resources/JsonApiResource.php:262)
    [stacktrace]
    #0 /var/www/html/vendor/laravel-json-api/core/src/Core/Responses/Internal/RelatedResourceResponse.php(108): LaravelJsonApi\\Core\\Resources\\JsonApiResource->relationship()
    #1 /var/www/html/vendor/laravel-json-api/core/src/Core/Responses/Internal/RelatedResourceResponse.php(97): LaravelJsonApi\\Core\\Responses\\Internal\\RelatedResourceResponse->metaForRelationship()
    #2 /var/www/html/vendor/laravel-json-api/core/src/Core/Responses/Internal/RelatedResourceResponse.php(81): LaravelJsonApi\\Core\\Responses\\Internal\\RelatedResourceResponse->allMeta()
    #3 /var/www/html/vendor/laravel-json-api/core/src/Core/Responses/RelatedResponse.php(110): LaravelJsonApi\\Core\\Responses\\Internal\\RelatedResourceResponse->toResponse()
    #4 /var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php(776): LaravelJsonApi\\Core\\Responses\\RelatedResponse->toResponse()
    #5 /var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php(763): Illuminate\\Routing\\Router::toResponse()
    #6 /var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php(695): Illuminate\\Routing\\Router->prepareResponse()
    #7 /var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(128): Illuminate\\Routing\\Router->Illuminate\\Routing\\{closure}()
    #8 /var/www/html/vendor/laravel-json-api/laravel/src/Http/Middleware/BootJsonApi.php(103): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
    #9 /var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): LaravelJsonApi\\Laravel\\Http\\Middleware\\BootJsonApi->handle()
    #10 /var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php(50): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
    #11 /var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Routing\\Middleware\\SubstituteBindings->handle()
    #12 /var/www/html/vendor/laravel/framework/src/Illuminate/Auth/Middleware/Authenticate.php(44): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
    #13 /var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Auth\\Middleware\\Authenticate->handle()
    #14 /var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php(78): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
    #15 /var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Foundation\\Http\\Middleware\\VerifyCsrfToken->handle()
    #16 /var/www/html/vendor/laravel/framework/src/Illuminate/View/Middleware/ShareErrorsFromSession.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
    #17 /var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\View\\Middleware\\ShareErrorsFromSession->handle()
    #18 /var/www/html/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(121): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
    #19 /var/www/html/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(64): Illuminate\\Session\\Middleware\\StartSession->handleStatefulRequest()
    #20 /var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Session\\Middleware\\StartSession->handle()
    #21 /var/www/html/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/AddQueuedCookiesToResponse.php(37): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
    #22 /var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Cookie\\Middleware\\AddQueuedCookiesToResponse->handle()
    #23 /var/www/html/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/EncryptCookies.php(67): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
    #24 /var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Cookie\\Middleware\\EncryptCookies->handle()
    #25 /var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(103): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
    #26 /var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php(697): Illuminate\\Pipeline\\Pipeline->then()
    #27 /var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php(672): Illuminate\\Routing\\Router->runRouteWithinStack()
    #28 /var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php(636): Illuminate\\Routing\\Router->runRoute()
    #29 /var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php(625): Illuminate\\Routing\\Router->dispatchToRoute()
    #30 /var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(166): Illuminate\\Routing\\Router->dispatch()
    #31 /var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(128): Illuminate\\Foundation\\Http\\Kernel->Illuminate\\Foundation\\Http\\{closure}()
    #32 /var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
    #33 /var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ConvertEmptyStringsToNull.php(31): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()
    #34 /var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull->handle()
    #35 /var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
    #36 /var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php(40): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()
    #37 /var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Foundation\\Http\\Middleware\\TrimStrings->handle()
    #38 /var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ValidatePostSize.php(27): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
    #39 /var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize->handle()
    #40 /var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php(86): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
    #41 /var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance->handle()
    #42 /var/www/html/vendor/fruitcake/laravel-cors/src/HandleCors.php(52): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
    #43 /var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Fruitcake\\Cors\\HandleCors->handle()
    #44 /var/www/html/vendor/fideloper/proxy/src/TrustProxies.php(57): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
    #45 /var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Fideloper\\Proxy\\TrustProxies->handle()
    #46 /var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(103): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
    #47 /var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(141): Illuminate\\Pipeline\\Pipeline->then()
    #48 /var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(110): Illuminate\\Foundation\\Http\\Kernel->sendRequestThroughRouter()
    #49 /var/www/html/public/index.php(52): Illuminate\\Foundation\\Http\\Kernel->handle()
    #50 /var/www/html/server.php(21): require_once('...')
    #51 {main}
    "} 
    

    Response

    500 INTERNAL SERVER ERROR
    
    {
        "jsonapi": {
            "version": "1.0"
        },
        "errors": [
            {
                "detail": "Call to undefined method LaravelJsonApi\\Core\\Resources\\ConditionalField::fieldName()",
                "meta": {
                    "exception": "Error",
                    "file": "/var/www/html/vendor/laravel-json-api/core/src/Core/Resources/JsonApiResource.php",
                    "line": 262,
                    "trace": [
                        {
                            "file": "/var/www/html/vendor/laravel-json-api/core/src/Core/Responses/Internal/RelatedResourceResponse.php",
                            "line": 108,
                            "function": "relationship",
                            "class": "LaravelJsonApi\\Core\\Resources\\JsonApiResource",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/vendor/laravel-json-api/core/src/Core/Responses/Internal/RelatedResourceResponse.php",
                            "line": 97,
                            "function": "metaForRelationship",
                            "class": "LaravelJsonApi\\Core\\Responses\\Internal\\RelatedResourceResponse",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/vendor/laravel-json-api/core/src/Core/Responses/Internal/RelatedResourceResponse.php",
                            "line": 81,
                            "function": "allMeta",
                            "class": "LaravelJsonApi\\Core\\Responses\\Internal\\RelatedResourceResponse",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/vendor/laravel-json-api/core/src/Core/Responses/RelatedResponse.php",
                            "line": 110,
                            "function": "toResponse",
                            "class": "LaravelJsonApi\\Core\\Responses\\Internal\\RelatedResourceResponse",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
                            "line": 776,
                            "function": "toResponse",
                            "class": "LaravelJsonApi\\Core\\Responses\\RelatedResponse",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
                            "line": 763,
                            "function": "toResponse",
                            "class": "Illuminate\\Routing\\Router",
                            "type": "::"
                        },
                        {
                            "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
                            "line": 695,
                            "function": "prepareResponse",
                            "class": "Illuminate\\Routing\\Router",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
                            "line": 128,
                            "function": "Illuminate\\Routing\\{closure}",
                            "class": "Illuminate\\Routing\\Router",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/vendor/laravel-json-api/laravel/src/Http/Middleware/BootJsonApi.php",
                            "line": 103,
                            "function": "Illuminate\\Pipeline\\{closure}",
                            "class": "Illuminate\\Pipeline\\Pipeline",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
                            "line": 167,
                            "function": "handle",
                            "class": "LaravelJsonApi\\Laravel\\Http\\Middleware\\BootJsonApi",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php",
                            "line": 50,
                            "function": "Illuminate\\Pipeline\\{closure}",
                            "class": "Illuminate\\Pipeline\\Pipeline",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
                            "line": 167,
                            "function": "handle",
                            "class": "Illuminate\\Routing\\Middleware\\SubstituteBindings",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Auth/Middleware/Authenticate.php",
                            "line": 44,
                            "function": "Illuminate\\Pipeline\\{closure}",
                            "class": "Illuminate\\Pipeline\\Pipeline",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
                            "line": 167,
                            "function": "handle",
                            "class": "Illuminate\\Auth\\Middleware\\Authenticate",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php",
                            "line": 78,
                            "function": "Illuminate\\Pipeline\\{closure}",
                            "class": "Illuminate\\Pipeline\\Pipeline",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
                            "line": 167,
                            "function": "handle",
                            "class": "Illuminate\\Foundation\\Http\\Middleware\\VerifyCsrfToken",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/View/Middleware/ShareErrorsFromSession.php",
                            "line": 49,
                            "function": "Illuminate\\Pipeline\\{closure}",
                            "class": "Illuminate\\Pipeline\\Pipeline",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
                            "line": 167,
                            "function": "handle",
                            "class": "Illuminate\\View\\Middleware\\ShareErrorsFromSession",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php",
                            "line": 121,
                            "function": "Illuminate\\Pipeline\\{closure}",
                            "class": "Illuminate\\Pipeline\\Pipeline",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php",
                            "line": 64,
                            "function": "handleStatefulRequest",
                            "class": "Illuminate\\Session\\Middleware\\StartSession",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
                            "line": 167,
                            "function": "handle",
                            "class": "Illuminate\\Session\\Middleware\\StartSession",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/AddQueuedCookiesToResponse.php",
                            "line": 37,
                            "function": "Illuminate\\Pipeline\\{closure}",
                            "class": "Illuminate\\Pipeline\\Pipeline",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
                            "line": 167,
                            "function": "handle",
                            "class": "Illuminate\\Cookie\\Middleware\\AddQueuedCookiesToResponse",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/EncryptCookies.php",
                            "line": 67,
                            "function": "Illuminate\\Pipeline\\{closure}",
                            "class": "Illuminate\\Pipeline\\Pipeline",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
                            "line": 167,
                            "function": "handle",
                            "class": "Illuminate\\Cookie\\Middleware\\EncryptCookies",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
                            "line": 103,
                            "function": "Illuminate\\Pipeline\\{closure}",
                            "class": "Illuminate\\Pipeline\\Pipeline",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
                            "line": 697,
                            "function": "then",
                            "class": "Illuminate\\Pipeline\\Pipeline",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
                            "line": 672,
                            "function": "runRouteWithinStack",
                            "class": "Illuminate\\Routing\\Router",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
                            "line": 636,
                            "function": "runRoute",
                            "class": "Illuminate\\Routing\\Router",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
                            "line": 625,
                            "function": "dispatchToRoute",
                            "class": "Illuminate\\Routing\\Router",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php",
                            "line": 166,
                            "function": "dispatch",
                            "class": "Illuminate\\Routing\\Router",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
                            "line": 128,
                            "function": "Illuminate\\Foundation\\Http\\{closure}",
                            "class": "Illuminate\\Foundation\\Http\\Kernel",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php",
                            "line": 21,
                            "function": "Illuminate\\Pipeline\\{closure}",
                            "class": "Illuminate\\Pipeline\\Pipeline",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ConvertEmptyStringsToNull.php",
                            "line": 31,
                            "function": "handle",
                            "class": "Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
                            "line": 167,
                            "function": "handle",
                            "class": "Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php",
                            "line": 21,
                            "function": "Illuminate\\Pipeline\\{closure}",
                            "class": "Illuminate\\Pipeline\\Pipeline",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php",
                            "line": 40,
                            "function": "handle",
                            "class": "Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
                            "line": 167,
                            "function": "handle",
                            "class": "Illuminate\\Foundation\\Http\\Middleware\\TrimStrings",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ValidatePostSize.php",
                            "line": 27,
                            "function": "Illuminate\\Pipeline\\{closure}",
                            "class": "Illuminate\\Pipeline\\Pipeline",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
                            "line": 167,
                            "function": "handle",
                            "class": "Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php",
                            "line": 86,
                            "function": "Illuminate\\Pipeline\\{closure}",
                            "class": "Illuminate\\Pipeline\\Pipeline",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
                            "line": 167,
                            "function": "handle",
                            "class": "Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/vendor/fruitcake/laravel-cors/src/HandleCors.php",
                            "line": 52,
                            "function": "Illuminate\\Pipeline\\{closure}",
                            "class": "Illuminate\\Pipeline\\Pipeline",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
                            "line": 167,
                            "function": "handle",
                            "class": "Fruitcake\\Cors\\HandleCors",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/vendor/fideloper/proxy/src/TrustProxies.php",
                            "line": 57,
                            "function": "Illuminate\\Pipeline\\{closure}",
                            "class": "Illuminate\\Pipeline\\Pipeline",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
                            "line": 167,
                            "function": "handle",
                            "class": "Fideloper\\Proxy\\TrustProxies",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
                            "line": 103,
                            "function": "Illuminate\\Pipeline\\{closure}",
                            "class": "Illuminate\\Pipeline\\Pipeline",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php",
                            "line": 141,
                            "function": "then",
                            "class": "Illuminate\\Pipeline\\Pipeline",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php",
                            "line": 110,
                            "function": "sendRequestThroughRouter",
                            "class": "Illuminate\\Foundation\\Http\\Kernel",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/public/index.php",
                            "line": 52,
                            "function": "handle",
                            "class": "Illuminate\\Foundation\\Http\\Kernel",
                            "type": "->"
                        },
                        {
                            "file": "/var/www/html/server.php",
                            "line": 21,
                            "function": "require_once"
                        }
                    ]
                },
                "status": "500",
                "title": "Internal Server Error"
            }
        ]
    }
    

    Other details

    I can repro both in PHPUnit and manually, using either a Web (Laravel Sanctum) or API (Laravel Passport) guard with the folllowing in composer.json:

    • "laravel-json-api/laravel": "^1.0"
    • "laravel-json-api/non-eloquent": "^1.0"
    • "laravel/framework": "^8.40"
    • "laravel/passport": "^10.1"
    • "laravel/sanctum": "^2.11"

    php -v:

    PHP 8.0.11 (cli) (built: Sep 23 2021 21:26:42) ( NTS )
    Copyright (c) The PHP Group
    Zend Engine v4.0.11, Copyright (c) Zend Technologies
        with Zend OPcache v8.0.11, Copyright (c), by Zend Technologies
        with Xdebug v3.0.4, Copyright (c) 2002-2021, by Derick Rethans
    
    bug 
    opened by helmroos 8
  • Error in documentation

    Error in documentation

    https://laraveljsonapi.io/docs/1.0/requests/authorization.html We have error in doc looking there *(https://www.screencast.com/t/j5Gd51zT, https://www.screencast.com/t/wbXqafapNs) need be use LaravelJsonApi\Laravel\LaravelJsonApi;

    use LaravelJsonApi\LaravelJsonApi; - not working

    Please fix it.

    opened by casemeby 0
  • Adding globalScope to Proxy models...

    Adding globalScope to Proxy models...

    For a while now I have been working around adding global scope to Proxy models since I managed to apply filters and other scheme methods that allowed me to filter through the indexing... yet the question still remained, is it fine to add a globalScope to the constructor of the proxy model? When I did test's I noticed that the global scope I applied earlier was still there, but that is, I think because of the test single thread, supposedly this would reset on each call... just a question I can't seem to find an answer to... thanks

    opened by proxymurder 1
  • Include relationship withData() throw error

    Include relationship withData() throw error

    Hi,

    I have "virtual" relationship declared as:

    	public function relationships($request): iterable
    	{
    		return [
    			...iterator_to_array(parent::relationships($request)),
    
    			$this->relation('currentCouple')->withData(fn (Patient $patient) => $patient->getCurrentCouple()),
    		];
    	}
    

    in my resource class. It's works fine when i invoke alwaysShowData(), but, when i want to include this relation via include query param i get:

    Relationship currentCouple does not exist on resource schema patients.
    

    why jsonapi trying to get data from model when i declared withData()? How to make it works without alwaysShowData()?

    According to https://laraveljsonapi.io/docs/2.0/resources/relationships.html#specifying-data it should works as i expected ;)

    opened by bbprojectnet 4
  • StoreContract in showRelated method of controller

    StoreContract in showRelated method of controller

    Is there an easier way of me accessing the Store from within my custom readingRelatedx method without overriding the showRelated method in the controller and passing it in like this;

    I need to override the data that is returned.

            if (method_exists($this, $hook = 'readingRelated' . Str::classify($fieldName))) {
                $response = $this->{$hook}($model, $request, $store);
            }
    
    question 
    opened by ale1981 3
  • Artisan commands give misleading message if no servers are registered

    Artisan commands give misleading message if no servers are registered

    Reported via Slack. A developer was getting the following message with the Artisan commands:

    You must use the server option when you have more than one API

    But they didn't have more than one API - that had no APIs! So we need to improve the message here if there are no APIs.

    enhancement 
    opened by lindyhopchris 0
Releases(v2.4.0)
  • v2.4.0(Jun 25, 2022)

    Upgrading

    composer require laravel-json-api/laravel --no-update
    composer up "laravel-json-api/*"
    

    Changelog

    Added

    • The JsonApiException class now has a context() method. Laravel's exception handler uses this to add log context when the exception is logged. This means logging of JSON:API exceptions will now include the HTTP status code and the JSON:API errors.
    • Moved the default 406 Not Acceptable and 415 Unsupported Media Type messages to the following two new exception classes:
      • Exceptions\HttpNotAcceptableException
      • Exceptions\HttpUnsupportedMediaTypeException

    Fixed

    • #184 Ensure that an Accept header with the media type application/json is rejected with a 406 Not Acceptable response. Previously this media type worked, which is incorrect as the JSON:API specification requires the media type application/vnd.api+json.
    • #197 Fix sending null for a to-one relationship update.
    Source code(tar.gz)
    Source code(zip)
  • v2.3.0(Apr 11, 2022)

    Upgrading

    composer require laravel-json-api/laravel --no-update
    composer up "laravel-json-api/*"
    

    Changelog

    Added

    • Added Spanish and Brazilian Portuguese translations for specification and validation error messages.
    Source code(tar.gz)
    Source code(zip)
  • v2.2.0(Apr 10, 2022)

    Upgrading

    composer require laravel-json-api/laravel
    

    Changelog

    Added

    • #181 The JsonApiController now extends the base Laravel controller.

    Fixed

    • #180 Add missing method to the Authorizer stub.
    Source code(tar.gz)
    Source code(zip)
  • v2.1.2(Apr 4, 2022)

    Upgrading

    composer require laravel-json-api/laravel --no-update
    composer up "laravel-json-api/*"
    

    Changelog

    Fixed

    • #175 Fix page URLs missing sparse field sets.
    Source code(tar.gz)
    Source code(zip)
  • v2.1.1(Apr 1, 2022)

  • v2.1.0(Feb 20, 2022)

    Upgrading

    composer require laravel-json-api/laravel --no-update
    composer up laravel-json-api/*
    

    Changelog

    Added

    • #110 For requests that modify a relationship, it is now possible to get the model or models referenced in the request JSON using the toOne() or toMany() methods on the resource request class.
    • #113 The Eloquent Number field can now be configured to accept numeric strings by calling the acceptStrings() method on the field.
    Source code(tar.gz)
    Source code(zip)
  • v2.0.0(Feb 12, 2022)

    Upgrading

    Refer to the upgrade guide on our website.

    Changelog

    Added

    • This package now supports Laravel 9.
    • This package now supports PHP 8.1.

    Changed

    • BREAKING PHP 8.1 introduces readonly as a keyword. It was therefore necessary to rename the following interface and trait:
      • LaravelJsonApi\Eloquent\Contracts\ReadOnly is now IsReadOnly.
      • LaravelJsonApi\Eloquent\Fields\Concerns\ReadOnly is now IsReadOnly.
    • Return types have been added to all internal methods in all child packages, to remove deprecation messages in PHP 8.1
    • #83 Amended container bindings to ensure package works with Laravel Octane. Most of these changes should have no impact on consuming applications. However, the following changes could potentially be breaking to the JSON:API Server class in an application:
      • The type-hint of the first constructor argument has changed to LaravelJsonApi\Core\Support\AppResolver.
      • The deprecated $container property has been removed, and the $app property is now private. To access the current application instance in your server class, use $this->app() instead.
    • BREAKING #110 The model() and modelOrFail() methods on the ResourceQuery request class have been changed from public to protected. These were not documented for use on this query class, and were only intended to be used publicly on the ResourceRequest class. Although technically breaking, this change is unlikely to affect the vast majority of applications which should not be using the method.
    Source code(tar.gz)
    Source code(zip)
  • v1.1.0(Jan 3, 2022)

    Upgrading

    composer require laravel-json-api/laravel --no-update
    composer up laravel-json-api/*
    

    Added

    • The default JSON:API resource class can now be changed via the LaravelJsonApi\Laravel\LaravelJsonApi::defaultResource() method. This should be set in a service provider's register() method.
    • #127 The JsonApiResource class now has a protected serializeRelation method that can be used to override the default serialization of relationships if needed.
    • #111 Relationship documents returned by relationship self routes will now include any non-standard links set on the resource relationship in the top-level links member.

    Fixed

    • #147 Related relationship response now correctly merge the relationship links into the top-level document links member.
    • #130 The JsonApiResource now correctly handles conditional fields when iterating over relationships to find a specific relation.
    • #105 The JSON:API document returned by a relationship self route now handles a relationship not existing if it is hidden. Previously an exception was thrown when attempting to merge relationship links into the document.
    • #111 Relationship documents now handle a relationship that does not have one or both of the self and related relationship links.
    Source code(tar.gz)
    Source code(zip)
  • v1.0.1(Dec 8, 2021)

    Upgrading

    composer up laravel-json-api/*
    

    Changed

    • The maximum PHP version is now 8.0. PHP 8.1 is not supported because it introduces a breaking change. The next major version of this package will add support for PHP 8.1.

    Fixed

    • #139 Fix the WhereHas and WhereDoesntHave filters. Previously these were not iterating over the filters from the correct resource schema - they were iterating over the filters from the schema to which the relationship belonged. They now correctly iterate over the filters from the schema for the resource that is on the inverse side of the relationship.
    Source code(tar.gz)
    Source code(zip)
  • v1.0.0(Jul 31, 2021)

    Finally, our initial stable release. Yes there's more features to work on, but this package is definitely production ready - so here at last is a stable release!

    Upgrade Instructions

    composer require laravel-json-api/laravel --no-update
    composer require laravel-json-api/testing --dev --no-update
    composer up laravel-json-api/* cloudcreativity/json-api-testing
    

    Added

    • New relationship filter classes: Has, WhereHas, WhereDoesntHave. Refer to the filter documentation for details.

    Changed

    • BREAKING: Countable Relationships. This feature is now turned off by default. Although included in the 1.0 release, this feature is not considered production-ready. This is because we plan to make breaking changes to it, which will change how the client requests countable relationships. As such, this feature is considered highly-experimental and developers must opt-in to it by calling the canCount() method on a relationship. Refer to the Countable relationships chapter in the documentation for more details.
    • BREAKING: Cursor Pagination. Laravel now has its own cursor pagination feature. We have therefore moved our implementation into its own package: laravel-json-api/cursor-pagination This change has been made because it makes sense for the in-built cursor pagination implementation to use Laravel's cursor pagination implementation rather than our own custom one. Support for Laravel's cursor pagination will be added during the 1.x release cycle. If you are already using our cursor implementation, you can migrate in two easy steps:
      1. Install the new package: composer require laravel-json-api/cursor-pagination
      2. In any schemas using the cursor pagination, change the import statement from LaravelJsonApi\Eloquent\Pagination\CursorPagination to LaravelJsonApi\CursorPagination\CursorPagination.
    Source code(tar.gz)
    Source code(zip)
  • v1.0.0-beta.5(Jul 10, 2021)

    Added

    • The authorizer now has separate showRelated() and showRelationship() methods. Previously both these controller actions were authorized via the single showRelationship() method. Adding the new showRelated method means developers can now implement separate authorization logic for these two actions if desired. Our default implementation remains unchanged - both are authorized using the view<RelationshipName> method on the relevant policy.
    • The request class now has a isCreatingOrUpdating() helper method to determine whether the request is to create or updated a resource.
    • Add stop on first failure to all validators in the resource request class.
    • #85 When running an application with debug mode turned on, the default JSON:API error object for an exception will now contain detailed exception information, including the stack trace, in the object's meta member.
    • #103 Can now fully customise attribute serialization to JSON using the extractUsing() callback. This receives the model, column name and value. This is useful if the developer needs to control the serialization of a few fields on their schema. However, the recommendation is to use a resource class for complete control over the serialization of a model to a JSON:API resource.

    Changed

    • Minimum Laravel version is now 8.30. This change was required to use the $stopOnFirstFailure property on Laravel's FormRequest class.
    • Schema classes no longer automatically sort their fields by name when iterating over them. This change was made to give the developer full control over the order of fields (particularly as this order affects the order in which fields are listed when serialized to a JSON:API resource). Developers can list fields in name order if that is the preferred order.
    • Removed the LaravelJsonApi\Spec\UnexpectedDocumentException which was thrown if there was a failure when decoding request JSON content before parsing it for compliance with the JSON:API specification. A JsonApiException will now be thrown instead.

    Fixed

    • #101 Ensure controller create action always returns a response that will result in a 201 Created response.
    • #102 The attach and detach to-many relationship controller actions now correctly resolve the collection query class using the relation's inverse resource type. Previously they were incorrectly using the primary resource type to resolve the query class.
    Source code(tar.gz)
    Source code(zip)
  • v1.0.0-beta.4(Jun 2, 2021)

  • v1.0.0-beta.3(Apr 26, 2021)

    Added

    • #14 Additional sort parameters can now be added to Eloquent schemas. Previously only sortable attributes were supported. These new classes are added to schemas in the sortables() method. Refer to the new Sorting chapter in the documentation.
    • Eloquent schemas now support a default sort order via the $defaultSort property.
    • New generator command jsonapi:sort-field to create a custom sort field class.
    • #74 Developers can now add default include paths to the query request classes (e.g. PostQuery and PostCollectionQuery) via the $defaultIncludePaths property. These include paths are used if the client does not provide any include paths.
    Source code(tar.gz)
    Source code(zip)
  • v1.0.0-beta.2(Apr 20, 2021)

    Added

    • #65 BREAKING The fill() method on Eloquent fields has been updated to receive all the validated data as its third argument. This change was made to allow fields to work out the value to fill into the model based on other JSON:API field values. If you have written any custom fields, you will need to update the fill() method on your field class.
    • BREAKING Eloquent attributes now support serializing and filling column values on related objects. This is primarily intended for use with the Eloquent belongsTo, hasOne, hasOneThrough and morphOne relationships that have a withDefault() method. As part of this change, the mustExist() method was added to the Fillable interface. If you have written any custom fields, you will need to add this method to your field class - it should return true if the attribute needs to be filled after the primary model has been persisted.
    • #58 Schema model classes can now be a parent class or an interface.

    Fixed

    • #69 Fixed the parsing of empty include, sort and withCount query parameters.
    Source code(tar.gz)
    Source code(zip)
  • v1.0.0-beta.1(Mar 30, 2021)

    Added

    • #18 Added a withCount query parameter. For Eloquent resources, this allows a client to request the relationship count for the primary data's relationships. Refer to documentation for implementation details.
    • #55 Encoding and decoding of resource IDs is now supported. The ID field needs to implement the LaravelJsonApi\Contracts\Schema\IdEncoder interface for this to work.
    • #41 Hash IDs are now supported by installing the laravel-json-api/hashids package and using the HashId field instead of the standard Eloquent ID field. Refer to documentation for details.
    • #30 Non-Eloquent resources are now supported via the laravel-json-api/non-eloquent package. Refer to documentation for implementation details.
    • There is now a Core\Reponses\RelatedResponse class for returning the result for a related resources endpoint. For example, the /api/v1/posts/1/comments endpoint. Previously the DataResponse class was used. While this class can still be used, the new RelatedResponse class merges relationship meta into the top-level meta member of the response document. For to-many relationships that are countable, this will mean the top-level meta member will contain the count of the relationship.
    • The schema generator Artisan command now has a --non-eloquent option to generate a schema for a non-Eloquent resource.

    Changed

    • The LaravelJsonApi::registerQuery(), LaravelJsonApi::registerCollectionQuery() and LaravelJsonApi::registerRequest() methods must now be used to register custom HTTP request classes for specified resource types. Previously methods could be called on the RequestResolver classes, but these have now been removed.

    Fixed

    • Relationship endpoints that return resource identifiers now correctly include page meta in the top-level meta member of the document, if the results are paginated. Previously the page meta was incorrectly omitted.
    Source code(tar.gz)
    Source code(zip)
  • v1.0.0-alpha.5(Mar 12, 2021)

    Added

    • #43 The package now supports soft-deleting resources. For full details on how to apply this to resource schemas, refer to the new Soft Deleting chapter in the documentation.
    • Multi-resource models are now supported. This allows developers to represent a single model class as multiple different JSON:API resource types within an API. Refer to documentation for details of how to implement.
    • #8 The new MorphToMany relation field can now be used to add polymorphic to-many relations to a schema. Refer to documentation for details.
    • Developers can now type-hint dependencies in their server's serving() method.
    • Can now manually register request, query and collection query classes using the RequestResolver::registerRequest(), RequestResolver::registerQuery() and RequestResolver::registerCollectionQuery() static methods.
    Source code(tar.gz)
    Source code(zip)
  • v1.0.0-alpha.4(Feb 27, 2021)

    Added

    • Added missing jsonapi:authorizer generator command.
    • The Eloquent schema now has indexQuery and relatableQuery methods. These allow filtering for authorization purposes when a list of resources is being retrieved. For instance, it could filter those queries so that only models belonging to the authenticated user are returned.
    • #23 The resource request class now does not need to exist for the destroy controller action. Previously the implementation was expecting the resource request class to exist, even though delete validation was optional.
    • #24 Controller actions will now stop executing and return a response if one is returned by the before action hooks: i.e. searching, reading, saving, creating, updating, deleting, readingRelated<Name>, reading<Name>, updating<Name>, attaching<Name> and detaching<Name>.
    • #37 Can now use constructor dependency injection in Server classes.
    • #40 There is now a new MetaResponse class that can be used when returning meta-only responses. In addition, response classes have been updated to add a withServer method. This can be used to specify the named server the response should use to encode the JSON:API document. This has to be used when returning responses from routes that have not run the JSON:API middleware (i.e. there is no default server available via the service container).
    • #9 The Laravel route registrar is now passed through to the resources, relationships and actions callbacks as the second function argument.
    • #36 Eloquent schemas now support complex singular filter logic, via the Schema::isSingular() method.
    • #33 Specification compliance will now reject an incorrect resource type in a relationship. For example, if a relationship expects tags but the client sends posts, the request will be rejected with an error message that posts are not supported.

    Changed

    • #22 BREAKING The index and store methods on the authorizer contract now receive the model class as their second argument. This is useful for authorizers that are used for multiple resource types.
    • BREAKING When querying or modifying models via the schema repository or store, calls to using() must be replaced with withRequest(). This change was made to make it clearer that the request class can be passed into query builders.
    • #28 The sparse field sets validation rule will now reject with a specific message identifying any resource types in the parameter that do not exist.
    • #35 The Relation::type() method must now be used when setting the inverse resource type for the relation.

    Fixed

    • Optional parameters to generator commands that require values now work correctly. Previously these were incorrectly set up as optional parameters that expected no values.
    • #25 The encoder now correctly handles conditional fields when iterating over a resource's relationships.
    • #26 Fix parsing the fields query parameter to field set value objects.
    • #34 Do not require server option when generating a generic authorizer with multiple servers present.
    • #29 Do not reject delete requests without a Content-Type header.
    • #11 Fixed iterating over an empty to-many generator twice in the underlying compound document encoder.

    Deprecated

    • The Relation::inverseType() method is deprecated and will be removed in 1.0-stable. Use Relation::type() instead.
    Source code(tar.gz)
    Source code(zip)
  • v1.0.0-alpha.3(Feb 9, 2021)

    Added

    • #12 Can now register routes for custom actions on a resource, using the actions() helper method when registering resources. See the PR for examples.
    • The JsonApiController now has the Laravel AuthorizesRequests, DispatchesJobs and ValidatesRequests traits applied.
    • #6 Resource class can now use conditional fields in their relationships. This works in the same way as conditional attributes: the resource's when() and mergeWhen() method should be used to add conditional relationships.
    • #13 Added French translations for JSON:API errors generated by specification parsing and resource/query parameter validation.
    • #7 Eloquent schemas now support default eager loading via their $with property.
    • #15 When parsing a JSON:API document for compliance with the specification, the client will now receive a clearer error message if they submit a to-one relationship object for a to-many relationship (and vice-versa).

    Changed

    • #2 BREAKING Improved the extraction of existing resource field values when constructing validation data for update requests:
      • The existingAttributes() and existingRelationships() methods on the resource request class has been removed. If you need to modify the existing values before the client values are merged, implement the withExisting() method instead. This receives the model and its JSON representation (as an array).
      • The mustValidate() method must now be called on a schema relationship field. (Previously this was on the resource relation.) By default, belongs-to and morph-to relations will be included when extracting existing values; all other relations will not. Use the mustValidate() or notValidated() method on the schema relation to alter whether a relation is included in the extracted values.
    Source code(tar.gz)
    Source code(zip)
  • v1.0.0-alpha.2(Feb 2, 2021)

    Added

    • #1 Resource classes are now optional. If one is not defined, the implementation falls-back to using the Eloquent schema to serialize a model. Eloquent schema fields now have new hidden and serializeUsing methods to customise the serialization of models by the schema.
    • Resource classes now support using conditional attributes in their meta() method.
    • New field classes ArrayList and ArrayHash have been added, to distinguish between PHP zero-indexed arrays that serialize to JSON arrays (ArrayList) and PHP associative arrays that serialize to JSON objects (ArrayHash). The distinction is required because an empty array list can be serialized to [] in JSON whereas an empty associative array must be serialized to null in JSON.

    Changed

    • BREAKING The JsonApiResource method signatures for the attributes(), relationships(), meta(), and links() methods have been changed so that they receive the HTTP request as the first (and only) parameter. This brings the implementation in line with Laravel's Eloquent resources, which receive the request to their toArray() method. The slight difference is our implementation allows the request to be null - this is to cover encoding resources outside of HTTP requests, e.g. queued broadcasting. When upgrading, you will need to either delete resource classes (as they are now optional), or update the method signatures on any classes you are retaining.

    Fixed

    • #3 Example server registration in the published configuration file prevented developer from creating a v1 server after adding this package to their Laravel application.
    • Package discovery for sub-packages that have service providers now works correctly.

    Removed

    • BREAKING The Arr schema field has been removed - use the new ArrayList or ArrayHash fields instead.
    • BREAKING The uri method on resource and relationship routes has been removed:
      • The resource type URI can now be set on the resource's schema (using the $uriType property).
      • Relationship URIs are now set on the schema field for the relationship (via the withUriFieldName method).
    Source code(tar.gz)
    Source code(zip)
Owner
Laravel JSON:API
Implement feature-rich JSON:API compliant APIs in your Laravel applications. Build your next standards-compliant API today.
Laravel JSON:API
Lightweight JSON:API resource for Laravel

JSON:API Resource for Laravel A lightweight Laravel implementation of JSON:API. This is a WIP project currently being built out via livestream on my Y

Tim MacDonald 241 Jan 5, 2023
JSON-RPC 2.0 API server for @Laravel framework

Sajya is an open-source project aiming to implement the JSON-RPC 2.0 server specification for the Laravel quickly.

Sajya 179 Dec 29, 2022
Renders consistent HTTP JSON responses for API-based projects

Laravel API Response is a package that helps to provide and render a consistent HTTP JSON responses to API calls as well as converting and formatting

Kennedy Osaze 43 Nov 20, 2022
A suit of basic files samples to build a light-weight , short-handed & a strong Laravel REST API Applications.

Concerning Laravel. I found myself repeating a very basic form of code each time i begin a new REST API. in which the whole Interface's pieces are dev

Adnane Kadri 1 Nov 22, 2021
Builds nice, normalized and easy to consume REST JSON responses for Laravel powered APIs.

REST API Response Builder for Laravel Master branch: Development branch: Table of contents Introduction Why should I use it? Usage examples Features E

Marcin Orlowski 614 Dec 26, 2022
🔐 JSON Web Token Authentication for Laravel & Lumen

Documentation Documentation for 1.* here For version 0.5.* See the WIKI for documentation. Supported by Auth0 If you want to easily add secure authent

Sean Tymon 10.7k Jan 3, 2023
⚙️Simple key/value typed settings for your Laravel app with synchronized json export

Simple key/value typed settings for your Laravel app Create, store and use key/value settings, typed from numbers over dates to array, cached for quic

elipZis 8 Jan 7, 2023
Source code behind the Laracasts Larabit: Using MySQL JSON Columns with Laravel

Using MySQL JSON Columns with Laravel This is the source code behind the Laracasts Larabit: Using MySQL JSON Columns with Laravel, and features all of

Andrew Schmelyun 2 Dec 24, 2021
Store your Laravel application settings in an on-disk JSON file

Store your Laravel application settings in an on-disk JSON file. This package provides a simple SettingsRepository class that can be used to store you

Ryan Chandler 24 Nov 16, 2022
Laravel Boilerplate provides a very flexible and extensible way of building your custom Laravel applications.

Laravel Boilerplate Project Laravel Boilerplate provides a very flexible and extensible way of building your custom Laravel applications. Table of Con

Labs64 848 Dec 28, 2022
Nebula is a minimalistic and easy to use administration tool for Laravel applications, made with Laravel, Alpine.js, and Tailwind CSS.

Nebula Nebula is a minimalistic and easy to use administration tool for Laravel applications, made with Laravel, Alpine.js, and Tailwind CSS. Nebula m

Nebula 228 Nov 11, 2022
Get estimated read time of an article. Similar to medium.com's "x min read". Multilingual including right-to-left written languages. Supports JSON, Array and String output.

Read Time Calculates the read time of an article. Output string e.g: x min read or 5 minutes read. Features Multilingual translations support. Static

Waqar Ahmed 8 Dec 9, 2022
PHP Simple Response, XML, JSON,... auto response with accept in request's header

simple-response Simple package to handle response properly in your API. This package does not include any dependency. Install Via Composer $ composer

Phuong Danh 3 Dec 8, 2021
Tool to convert from composer.yml to composer.json.

composer-yaml This project allows you to convert a composer.yml file into composer.json format. It will use those exact filenames of your current work

Igor 56 Sep 28, 2022
The list of all Algerian provinces and cities according to the official division in different formats: csv, xlsx, php, json, etc.

algeria-cities This repository contains the list of all the administrative provinces and cities in Algeria. The data is up-to-date according to the of

Ramtani Othmane 393 Jan 2, 2023
Generate Data Transfer Objects directly from JSON objects

Json 2 DTO Spatie's Data Transfer Object library is awesome, but typing out DTOs can quickly become a chore. Inspired by Json2Typescript style tools,

null 111 Jan 3, 2023
Iconify icon sets in JSON format

100+ open source icon sets. Icons are validated, cleaned up, optimised, ready to render as SVG

Iconify 255 Dec 29, 2022
A demo of how to use filament/forms to build a user-facing Form Builder which stores fields in JSON.

About Laravel Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experie

Dan Harrin 41 Dec 24, 2022
🧑‍🔬 The missing assertions for your views in your Laravel applications.

Laravel View Assertions The missing assertions for your views in your Laravel applications. Installation You'll have to follow a couple of simple step

Sven Luijten 4 Dec 21, 2022