Now the main job of server programmers is no longer to set templates, but to write a JSON-based API interface. Unfortunately, the styles of writing interfaces are often very different, which brings many unnecessary communication costs to system integration. If you have similar problems, then you might as well pay attention to JSONAPI, which is a standard for building APIs based on JSON. A simple API interface is roughly as follows:
JSONAPI
Let me briefly explain: the data in the root node is used to place the content of the main object. Type and id are necessary fields to represent the type and identity of the main object. Other simple attributes are placed in attributes. If the main object has one-to-one, one-to-many associated objects, then it is placed in relationships. However, it is only a link through the type and id fields, and the actual content of the associated object is placed in the included in the root contact.
With JSONAPI, the data parsing process becomes standardized, saving unnecessary communication costs. However, it is still very troublesome to build JSONAPI data manually. Fortunately, the implementation process can be relatively automated by using Fractal. If the above example is implemented using Fractal, it is probably like this:
<?php use League\Fractal\Manager; use League\Fractal\Resource\Collection; $articles = [ [ 'id' => 1, 'title' => 'JSON API paints my bikeshed!', 'body' => 'The shortest article. Ever.', 'author' => [ 'id' => 42, 'name' => 'John', ], ], ]; $manager = new Manager(); $resource = new Collection($articles, new ArticleTransformer()); $manager->parseIncludes('author'); $manager->createData($resource)->toArray(); ?>
If I were asked to choose my favorite PHP toolkit, Fractal would definitely be on the list, it hides implementation details, so that users don’t have to understand the JSONAPI protocol to get started. However, if you want to use it in your own project, you can try Fractalistic compared to using it directly, which encapsulates Fractal to make it better:
<?php Fractal::create() ->collection($articles) ->transformWith(new ArticleTransformer()) ->includeAuthor() ->toArray(); ?>
If you write PHP naked, then Fractalistic is basically the best choice. However, if you use some full-stack frameworks, then Fractalistic may not be elegant enough because it cannot be more perfectly integrated with the existing functions of the framework itself. Take Lavaral as an example, it has built-in API Resources function. On this basis, I implemented a JsonApiSerializer, which can be perfectly integrated with the framework. The code is as follows:
<?php namespace App\Http\Serializers; use Illuminate\Http\Resources\MissingValue; use Illuminate\Http\Resources\Json\Resource; use Illuminate\Http\Resources\Json\ResourceCollection; use Illuminate\Pagination\AbstractPaginator; class JsonApiSerializer implements \JsonSerializable { protected $resource; protected $resourceValue; protected $data = []; protected static $included = []; public function __construct($resource, $resourceValue) { $this->resource = $resource; $this->resourceValue = $resourceValue; } public function jsonSerialize() { foreach ($this->resourceValue as $key => $value) { if ($value instanceof Resource) { $this->serializeResource($key, $value); } else { $this->serializeNonResource($key, $value); } } if (!$this->isRootResource()) { return $this->data; } $result = [ 'data' => $this->data, ]; if (static::$included) { $result['included'] = static::$included; } if (!$this->resource->resource instanceof AbstractPaginator) { return $result; } $paginated = $this->resource->resource->toArray(); $result['links'] = $this->links($paginated); $result['meta'] = $this->meta($paginated); return $result; } protected function serializeResource($key, $value, $type = null) { if ($type === null) { $type = $key; } if ($value->resource instanceof MissingValue) { return; } if ($value instanceof ResourceCollection) { foreach ($value as $k => $v) { $this->serializeResource($k, $v, $type); } } elseif (is_string($type)) { $included = $value->resolve(); $data = [ 'type' => $included['type'], 'id' => $included['id'], ]; if (is_int($key)) { $this->data['relationships'][$type]['data'][] = $data; } else { $this->data['relationships'][$type]['data'] = $data; } static::$included[] = $included; } else { $this->data[] = $value->resolve(); } } protected function serializeNonResource($key, $value) { switch ($key) { case 'id': $value = (string)$value; case 'type': case 'links': $this->data[$key] = $value; break; default: $this->data['attributes'][$key] = $value; } } protected function links($paginated) { return [ 'first' => $paginated['first_page_url'] ?? null, 'last' => $paginated['last_page_url'] ?? null, 'prev' => $paginated['prev_page_url'] ?? null, 'next' => $paginated['next_page_url'] ?? null, ]; } protected function meta($paginated) { return [ 'current_page' => $paginated['current_page'] ?? null, 'from' => $paginated['from'] ?? null, 'last_page' => $paginated['last_page'] ?? null, 'per_page' => $paginated['per_page'] ?? null, 'to' => $paginated['to'] ?? null, 'total' => $paginated['total'] ?? null, ]; } protected function isRootResource() { return isset($this->resource->isRoot) && $this->resource->isRoot; } } ?>
The corresponding Resource is basically the same as before, except that the return value has been changed:
<?php namespace App\Http\Resources; use App\Article; use Illuminate\Http\Resources\Json\Resource; use App\Http\Serializers\JsonApiSerializer; class ArticleResource extends Resource { public function toArray($request) { $value = [ 'type' => 'articles', 'id' => $this->id, 'name' => $this->name, 'author' => $this->whenLoaded('author'), ]; return new JsonApiSerializer($this, $value); } } ?>
The corresponding controller is similar to the original one, except that an isRoot attribute is added to identify the root:
<?php namespace App\Http\Controllers; use App\Article; use App\Http\Resources\ArticleResource; class ArticleController extends Controller { protected $article; public function __construct(Article $article) { $this->article = $article; } public function show($id) { $article = $this->article->with('author')->findOrFail($id); $resource = new ArticleResource($article); $resource->isRoot = true; return $resource; } } ?>
The entire process did not invade the Laravel architecture too much. It can be said that it is the best solution for Laravel to implement JSONAPI. If you are interested, you can study the implementation of JsonApiSerializer. Although there are only more than 100 lines of code, I have put a lot of effort into implementing it. It can be said that everything is hard.
Summarize
The above is the application of JSONAPI in PHP that the editor introduced to you. I hope it will be helpful to you. If you have any questions, please leave me a message. The editor will reply to you in time!