深入浅析JSONAPI在PHP中的应用
现在服务端程序员的主要工作已经不再是套模版,而是编写基于JSON的API接口。可惜大家编写接口的风格往往迥异,这就给系统集成带来了很多不必要的沟通成本,如果你有类似的困扰,那么不妨关注一下JSONAPI,它是一个基于JSON构建API的规范标准,一个简单的API接口大致如下所示:
JSONAPI
简单说明一下:根节点中的data用来放置主对象的内容,其中type和id是必须要有的字段,用来表示主对象的类型和标识,其它简单的属性统统放置到attributes里,如果主对象存在一对一、一对多等关联对象,那么放置到relationships里,不过只是通过type和id字段放置一个链接,关联对象的实际内容统统放置在根接点中的included里。
有了JSONAPI,数据解析的过程变得规范起来,节省了不必要的沟通成本。不过如果要手动构建JSONAPI数据还是很麻烦的,好在通过使用Fractal可以让实现过程相对自动化一些,上面的例子如果用Fractal实现大概是这个样子:
1,
'title'=>'JSONAPIpaintsmybikeshed!',
'body'=>'Theshortestarticle.Ever.',
'author'=>[
'id'=>42,
'name'=>'John',
],
],
];
$manager=newManager();
$resource=newCollection($articles,newArticleTransformer());
$manager->parseIncludes('author');
$manager->createData($resource)->toArray();
?>
如果让我选最喜爱的PHP工具包,Fractal一定榜上有名,它隐藏了实现细节,让使用者完全不必了解JSONAPI协议即可上手。不过如果你想在自己的项目里使用的话,与直接使用Fractal相比,可以试试Fractalistic,它对Fractal进行了封装,使其更好用:
collection($articles) ->transformWith(newArticleTransformer()) ->includeAuthor() ->toArray(); ?>
如果你是裸写PHP的话,那么Fractalistic基本就是最佳选择了,不过如果你使用了一些全栈框架的话,那么Fractalistic可能还不够优雅,因为它无法和框架本身已有的功能更完美的融合,以Lavaral为例,它本身内置了一个APIResources功能,在此基础上我实现了一个JsonApiSerializer,可以和框架完美融合,代码如下:
resource=$resource;
$this->resourceValue=$resourceValue;
}
publicfunctionjsonSerialize()
{
foreach($this->resourceValueas$key=>$value){
if($valueinstanceofResource){
$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->resourceinstanceofAbstractPaginator){
return$result;
}
$paginated=$this->resource->resource->toArray();
$result['links']=$this->links($paginated);
$result['meta']=$this->meta($paginated);
return$result;
}
protectedfunctionserializeResource($key,$value,$type=null)
{
if($type===null){
$type=$key;
}
if($value->resourceinstanceofMissingValue){
return;
}
if($valueinstanceofResourceCollection){
foreach($valueas$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();
}
}
protectedfunctionserializeNonResource($key,$value)
{
switch($key){
case'id':
$value=(string)$value;
case'type':
case'links':
$this->data[$key]=$value;
break;
default:
$this->data['attributes'][$key]=$value;
}
}
protectedfunctionlinks($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,
];
}
protectedfunctionmeta($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,
];
}
protectedfunctionisRootResource()
{
returnisset($this->resource->isRoot)&&$this->resource->isRoot;
}
}
?>
对应的Resource基本还和以前一样,只是返回值改了一下:
'articles',
'id'=>$this->id,
'name'=>$this->name,
'author'=>$this->whenLoaded('author'),
];
returnnewJsonApiSerializer($this,$value);
}
}
?>
对应的Controller也和原来差不多,只是加入了一个isRoot属性,用来识别根:
article=$article;
}
publicfunctionshow($id)
{
$article=$this->article->with('author')->findOrFail($id);
$resource=newArticleResource($article);
$resource->isRoot=true;
return$resource;
}
}
?>
整个过程没有对Laravel的架构进行太大的侵入,可以说是目前Laravel实现JSONAPI的最优解决方案了,有兴趣的可以研究一下JsonApiSerializer的实现,虽然只有一百多行代码,但是我却费了好大的力气才实现,可以说是行行皆辛苦啊。
总结
以上所述是小编给大家介绍的JSONAPI在PHP中的应用,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小编会及时回复大家的!