首頁 >web前端 >js教程 >JSONAPI在PHP的應用

JSONAPI在PHP的應用

小云云
小云云原創
2017-12-25 14:54:561461瀏覽

現在服務端程式設計師的主要工作已經不再是套模版,而是編寫基於 JSON 的 API 介面。可惜大家寫介面的風格往往迥異,這就為系統整合帶來了很多不必要的溝通成本,如果你有類似的困擾,那麼不妨關註一下JSONAPI ,它是一個基於JSON 構建API 的規範標準,一個簡單的API 介面大致如下所示:

JSONAPI

#簡單說明一下:根節點中的data 用來放置主物件的內容,其中type 和id 是必須要有的字段,用來表示主對象的類型和標識,其它簡單的屬性統統放置到attributes 裡,如果主對象存在一對一、一對多等關聯對象,那麼放置到relationships 裡,不過只是通過type 和id 字段放置一個鏈接,關聯物件的實際內容統統放置在根接點中的included 裡。

有了 JSONAPI,資料解析的過程變得規範起來,節省了不必要的溝通成本。不過如果要手動建立JSONAPI 資料還是很麻煩的,好在透過使用Fractal 可以讓實作過程相對自動化一些,上面的範例如果用Fractal 實現大概是這個樣子:


<?php
use League\Fractal\Manager;
use League\Fractal\Resource\Collection;
$articles = [
  [
    &#39;id&#39; => 1,
    &#39;title&#39; => &#39;JSON API paints my bikeshed!&#39;,
    &#39;body&#39; => &#39;The shortest article. Ever.&#39;,
    &#39;author&#39; => [
      &#39;id&#39; => 42,
      &#39;name&#39; => &#39;John&#39;,
    ],
  ],
];
$manager = new Manager();
$resource = new Collection($articles, new ArticleTransformer());
$manager->parseIncludes(&#39;author&#39;);
$manager->createData($resource)->toArray();
?>

如果讓我選最愛的PHP 工具包,Fractal 一定榜上有名,它隱藏了實作細節,讓使用者完全不必了解JSONAPI 協定即可上手。不過如果你想在自己的專案裡使用的話,與直接使用Fractal 相比,可以試試Fractalistic ,它對Fractal 進行了封裝,使其更好用:


<?php
Fractal::create()
  ->collection($articles)
  ->transformWith(new ArticleTransformer())
  ->includeAuthor()
  ->toArray();
?>

如果你是裸寫PHP 的話,那麼Fractalistic 基本上就是最佳選擇了,不過如果你使用了一些全棧框架的話,那麼Fractalistic 可能還不夠優雅,因為它無法和框架本身已有的功能更完美的融合,以Lavaral 為例,它本身內建了一個API Resources 功能,在此基礎上我實作了一個JsonApiSerializer,可以和框架完美融合,程式碼如下:


#
<?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 = [
      &#39;data&#39; => $this->data,
    ];
    if (static::$included) {
      $result[&#39;included&#39;] = static::$included;
    }
    if (!$this->resource->resource instanceof AbstractPaginator) {
      return $result;
    }
    $paginated = $this->resource->resource->toArray();
    $result[&#39;links&#39;] = $this->links($paginated);
    $result[&#39;meta&#39;] = $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 = [
        &#39;type&#39; => $included[&#39;type&#39;],
        &#39;id&#39; => $included[&#39;id&#39;],
      ];
      if (is_int($key)) {
        $this->data[&#39;relationships&#39;][$type][&#39;data&#39;][] = $data;
      } else {
        $this->data[&#39;relationships&#39;][$type][&#39;data&#39;] = $data;
      }
      static::$included[] = $included;
    } else {
      $this->data[] = $value->resolve();
    }
  }
  protected function serializeNonResource($key, $value)
  {
    switch ($key) {
      case &#39;id&#39;:
        $value = (string)$value;
      case &#39;type&#39;:
      case &#39;links&#39;:
        $this->data[$key] = $value;
        break;
      default:
        $this->data[&#39;attributes&#39;][$key] = $value;
    }
  }
  protected function links($paginated)
  {
    return [
      &#39;first&#39; => $paginated[&#39;first_page_url&#39;] ?? null,
      &#39;last&#39; => $paginated[&#39;last_page_url&#39;] ?? null,
      &#39;prev&#39; => $paginated[&#39;prev_page_url&#39;] ?? null,
      &#39;next&#39; => $paginated[&#39;next_page_url&#39;] ?? null,
    ];
  }
  protected function meta($paginated)
  {
    return [
      &#39;current_page&#39; => $paginated[&#39;current_page&#39;] ?? null,
      &#39;from&#39; => $paginated[&#39;from&#39;] ?? null,
      &#39;last_page&#39; => $paginated[&#39;last_page&#39;] ?? null,
      &#39;per_page&#39; => $paginated[&#39;per_page&#39;] ?? null,
      &#39;to&#39; => $paginated[&#39;to&#39;] ?? null,
      &#39;total&#39; => $paginated[&#39;total&#39;] ?? null,
    ];
  }
  protected function isRootResource()
  {
    return isset($this->resource->isRoot) && $this->resource->isRoot;
  }
}
?>

對應的Resource 基本上還跟以前一樣,只是回傳值改了一下:


<?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 = [
      &#39;type&#39; => &#39;articles&#39;,
      &#39;id&#39; => $this->id,
      &#39;name&#39; => $this->name,
      &#39;author&#39; => $this->whenLoaded(&#39;author&#39;),
    ];
    return new JsonApiSerializer($this, $value);
  }
}
?>

對應的Controller 也跟原來差不多,只是加入了一個isRoot 屬性,用來識別根:


<?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(&#39;author&#39;)->findOrFail($id);
    $resource = new ArticleResource($article);
    $resource->isRoot = true;
    return $resource;
  }
}
?>

整個過程沒有對Laravel 的架構進行太大的侵入,可以說是目前Laravel 實作JSONAPI 的最優解決方案了,有興趣的可以研究一下JsonApiSerializer 的實現,雖然只有一百多行程式碼,但是我卻費了好大的力氣才實現,可以說是行行皆辛苦啊。

相關推薦:

Ajax與jsonp使用方法總結

實例詳解javascript將json格式陣列下載為excel表格

JSON傳值與PHP接收的幾種情況

#

以上是JSONAPI在PHP的應用的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn