Skip to content

Future design and request for comments #411

@lindyhopchris

Description

@lindyhopchris

Plan for Version 2

We are starting to turn our attention to version 2 of this package. This issue describes the thinking and invites any comments/suggestions about the approach. We're aware a lot of people are using this package, so we want to get this right.

Background

When we first wrote this Laravel package, it was based on a framework-agnostic implementation because we weren't just using it in a Laravel application. One of the best decisions of the 1.0.0-alpha/beta series was to move the code into this package and develop it as a solely-Laravel package.

During the 1.0 development we started making it feel more like a Laravel package, however there was a limit to how much we could do that without introducing more breaking changes than we were comfortable with.

The other bit of background is that we use the neomerx/json-api package, predominantly for the encoding. That package has a great attention on making encoding performant, so we still want to use it. However, we are not entirely confident that decisions made on that package fit with our overall approach and unfortunately recent changes to that package make extending some of the internals complex.

Additionally, exposing the neomerx interfaces to the Laravel developer makes this package feel less intuitively like a Laravel package - as there's more code to wrap your head around.

Aims

Based on this background, there are two main things we want 2.0 to achieve:

  1. Make the use of this package align closely with existing Laravel approaches (wherever possible); and
  2. Hide the use of the neomerx package so it becomes an internal implementation detail, rather than something the developer needs to know about.

Laravel Alignment

Currently we have:

- App
    - Http
        - Controllers
            - Api
                - PostsController (optional)
    - JsonApi
        - Posts
            - Adapter
            - Schema
            - Validators
            - Authorizer (optional)
            - ContentNegotiator (optional)

This will still work on 2.0, requiring only minor interface changes and PHP 7 type-hinting to upgrade. Any code relating to the implementation will be marked as deprecated and removed in 3.0.

Our new style for 2.0 will be this:

- App
    - Http
        - Controllers
            - Api
                - PostController (optional)
        - Resources
            - JsonApi
                - PostAdapter
                - PostResource
                - PostCollection (optional)
        - Requests
            - JsonApi
                - PostRequest
                - PostQuery

The reason for the JsonApi namespaces is to prevent collisions with existing classes, e.g
an Eloquent PostResource, or an existing PostRequest form request. It also makes it entirely clear which classes are to do with your JSON-API implementation.

There will be an option to sub-namespace things if you have multiple JSON APIs. E.g. if we have
a v1 and v2 API, the resources will be JsonApi\V1\PostResource, JsonApi\V2\PostResource etc.

This means the following differences from the current implementation:

  • Schema is replaced by PostResource, which will have a similar style to Eloquent resources. Similar is intentionally used, because the Eloquent approach will not fully work for JSON API, and it has some aspects that would undermine the performance of the JSON API encoder. Resource classes will implement Responsable and JsonSerialize so that they can be used in other contexts as required.
  • Validators is split into PostRequest for validating request JSON content and PostQuery to validate the query parameters for a response that will contain posts resources.
  • Authorizers cease to exist and use standard Laravel authorization via middleware, form request authorize methods, controller middleware and controller helpers.
  • ContentNegotiators are removed but the use-case is fully supported (and documented). Different media-types will be easier to wire in as there will be a specific PostRequest and PostQuery on which validation can be customised or skipped.
  • Adapters will return either the resource class, or the resource collection class, or null. These will allow the adapter to provide meta and links as needed.
  • Standard Laravel route bindings will be used.
  • The default controller will be composed using traits, allowing a developer to create their own custom controllers if they want. We will offload the running of the HTTP handling logic into a Server class, so it's easier to call our default handling of JSON API requests from within a custom controller if needed.
  • Controller and adapter hooks will be replaced with standard Laravel events. For resource update events, we will provide data about the before and after state, allowing listeners to decide to do something if a resource field has changed.
  • We will enable auto-generation of API docs by inspecting defined routes and the request/query classes. We will show example resources by serializing models created via Eloquent factories. This means the docs output will need to be created in development, and can then be committed to version control.

As an example, our new default controller would look like this:

<?php

namespace CloudCreatvity\LaravelJsonApi\Http\Controllers;

use CloudCreativity\LaravelJsonApi\Http\Actions;

class ResourceController
{

    use Actions\Index;
    use Actions\Store;
    use Actions\Show;
    use Actions\Update;
    use Actions\Destroy;
    use Actions\Related\Show;
    use Actions\Relationships\Show;
    use Actions\Relationships\Replace;
    use Actions\Relationships\Add;
    use Actions\Relationships\Remove;
}

The action names here match Laravel's default names for resource controllers - except for relationships, as they are a JSON API concept.

This means that if the developer wanted to write their own controller, changing the method signatures of some of the actions, they could compose their own controller using our traits - but omit the ones they want to change. E.g.

<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Http\Resources\JsonApi\PostRequest;
use App\Http\Resources\JsonApi\PostQuery;
use CloudCreativity\LaravelJsonApi\Http\Actions;
use CloudCreativity\LaravelJsonApi\Facades\JsonApi;
use Illuminate\Contracts\Support\Responsable;

class PostController extends Controller
{

    // default actions, e.g.
    use Actions\Index;

    public function store(\Illuminate\Http\Request $request): Responsable
    {
        if ($something) {
            // return something custom.
        }

       // use our standard implementation via the `server()` helper.
       // lazily resolving the request/query classes means JSON API validation only happens now.
       return JsonApi::server()->store(
            app(PostRequest::class),
            app(PostQuery::class)
        );
    }
}

Outside of HTTP requests, you can query your resources using a fluent JSON API query syntax. This effectively allows you to run the logic within your adapters in a style familiar to a Laravel Query Builder (but using JSON API terms, such as include). Say we wanted to get a PostResource from a post id:

$resource = JsonApi::resources()->posts()->include('comments', 'author')->find($postId);

return \json_encode($resource);

Neomerx Package

We are currently using v1 of the neomerx package, but it is currently on v3 - which contains a lot of performance improvements. We therefore desperately need to upgrade.

However, as the current implementation involves developers extending or type-hinting classes/interfaces from the neomerx package, this change will be extremely breaking.

As we plan to change our use of the neomerx package to an internal implementation detail, 2.0 of our package will set the neomerx Composer constraint to ^1.0.3|^3.0. We will detect the version installed, caching the answer in config for performance reasons, and adjust the implementation internally to use either.

Developers will therefore be able to upgrade to 2.0 without changing their existing Schemas etc. And can then upgrade to neomerx v3 once they have converted to the new PostResource pattern described above.

How Can You Help?

Provide comments on this approach and highlight potential problems, so that we know whether we're on the right track!

We think that with these changes, this package will be one of the best options out there for building standard-compliant APIs in Laravel. So we could really do with a great website with docs that follow the Laravel docs sub-headings (i.e. so that they are instantly familiar to Laravel developers). If you're able to help out with a public website, let me know.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      pFad - Phonifier reborn

      Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

      Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


      Alternative Proxies:

      Alternative Proxy

      pFad Proxy

      pFad v3 Proxy

      pFad v4 Proxy