dkx/json-api

This package is abandoned and no longer maintained. No replacement package was suggested.

Json api transformers

2.0.2 2020-02-05 14:31 UTC

This package is auto-updated.

Last update: 2024-02-05 23:23:12 UTC


README

Framework agnostic PHP JsonApi transformer.

Installation

$ composer require dkx/json-api

Transformer

<?php

use DKX\JsonApi\Item;
use DKX\JsonApi\Transformer;
use DKX\JsonApi\TransformContext;

class BookTransformer implements Transformer
{    
    public function supports(object $item): bool 
    {
        return $item instanceof Book;
    }
    
    public function transform(object $item, TransformContext $ctx): Item
    {
        if (!$item instanceof Book) {
            throw new ShouldNotHappenException;
        }
        
        return new Item('book', $item->id, [
            Item::ATTRIBUTES => [
                'title' => $item->title,
                'content' => $item->content,
            ],
        ]);
    }
}

Usage

<?php

use DKX\JsonApi\Manager;

$manager = new Manager;
$manager->addTransformer(new BookTransformer);

$json = $manager->itemToArray($books->getById(5));

// or collection

$json = $manager->collectionToArray($books->getAll());

Relationships

<?php

use DKX\JsonApi\Item;

new Item('book', $item->id, [
    Item::ATTRIBUTES => [
        'title' => $item->title,
        'content' => $item->content,
    ],
    Item::RELATIONSHIPS => [
       'user' => $item->user,
   ],
]);

Now we can load the user relationship like this:

<?php

$manager->itemToArray($books->getOneById(5), ['user']);

Lazy relationships

<?php

use DKX\JsonApi\Item;

new Item('book', $item->id, [
    Item::ATTRIBUTES => [
        'title' => $item->title,
        'content' => $item->content,
    ],
    Item::RELATIONSHIPS => [
        'user' => function () use ($item) {
            return $users->getOneById($item->userId);
        },
    ],
]);

Solving N+1 Problem

<?php

use DKX\JsonApi\Deferred;
use DKX\JsonApi\Item;

new Item('book', $item->id, [
    Item::ATTRIBUTES => [
        'title' => $item->title,
        'content' => $item->content,
    ],
    Item::RELATIONSHIPS => [
        'user' => function () use ($item) {
            MyUserBuffer::add($item->userId);

            return new Deferred(function () use ($item) {
                MyUserBuffer::loadBuffered();
                return MyUserBuffer::get($item->userId);
            });
        },
    ],
]);

The logic behind solving the N+1 problem here is to store all IDs into a buffer, then load all entities with one query (with IN(?) SQL clause) and later select the requested entity from loaded array.

Batch:

Batch is just another form of Deferred with simpler usage.

<?php

use DKX\JsonApi\Batch;
use DKX\JsonApi\Item;

new Item('book', $item->id, [
    Item::ATTRIBUTES => [
        'title' => $item->title,
        'content' => $item->content,
    ],
    Item::RELATIONSHIPS => [
        'user' => function () use ($item) {
            return new Batch('bookUsersById', $item->userId, function (array $ids) {
                return $usersRepository->getByIds($ids);
            }, function (array $users) use ($item) {
                return $users[$item->userId];
            });
        },
    ],
]);

Each Batch must have a scope (eg. bookUsersById) which is used to collect all relationship ids (second argument of Batch).

First callback must return an array with all possible relationships selected by all ids (collected in scope).

Second callback receives data returned from the first callback and must return one relationship data for current item.