gbradley/laravel-api-resource 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

gbradley/laravel-api-resource

Composer 安装命令:

composer require gbradley/laravel-api-resource

包简介

Easier Laravel APIs

README 文档

README

This package expands on Laravel's base classes to provide a more succinct yet powerful way to control your API resources.

Goals

  • Less code repetition; API resources take less time to write and maintain
  • Improved relation handling including loading on-demand
  • More control over data wrapping
  • Won't affect your existing API by default

Requirements

  • Laravel 5.8+

Installation

$ composer require gbradley/laravel-api-resource

Usage

Extending the new resource class

In your resource, instead of extending Laravel's Resource, change this to extend GBradley\ApiResource\Resource instead:

namespace App\Http\Resources;

use GBradley\ApiResource\Resource;

class PostResource extends Resource
{
	...
}

That's it! You can now use this resource as you would any normal resource. A main goal of this package is not to change resources' existing behaviour.

Quicker attribute definitions

In our example resource, lets define some attributes. Normally you would have to do this individually, which can be quite verbose with a lot of repetition. Instead, you can now use mergeAtrributes() to quickly define the attributes you wish to use from your underlying model. This reduces something like:

class PostResource extends Resource
{

	public function toArray($request)
	{
		return [
			'id'		=> $this->id,
			'title'		=> $this->title,
			...
		];
	}

to this:

class PostResource extends Resource
{

	public function toArray($request)
	{
		return [
			$this->mergeAttributes('id', 'title', ...),
		];
	}

This has the additional benefit of retaining date formats in casted date or datetime attributes. Normally you would have to specify the format again in your resource, but this will now be handled for you.

Of course, you can mix both the new and old styles if needed:

class PostResource extends Resource
{

	public function toArray($request)
	{
		return [
			$this->mergeAttributes('id', 'title', ...),
			'dated'		=> $this->dated->format('d/m/Y'),
		];
	}

Relation handling

Laravel's resources have two strategies for adding relations:

Direct loading - this always loads relations, often resulting in non-eager loaded queries being executed to generate data which the front-end may not need.

Conditional loading - this returns relations only when they are already loaded, deffering loading to the controller. However, your business logic may also load relations (for example inside an event handler), meaning your response structure can be affected by internal side-effects.

Instead, this package allows controllers to explicitly define which relations the resource can expose, with the resource determining which relations it will expose. These can either be required relations, which will always be exposed, or optional relations, which will be exposed if specified in the current request. The result is a flexible system which also utilises eager-loading.

To start, open your controller. Instead of instantiating a resource, or using the collection() method, use the static build method which can accept a model, a collection or a paginator instance:

return PostResource::build($model);

This exposes a fluid interface for specifying the relations. To specify relations which should always be exposed, pass an array of names to withRelations():

return PostResource::build($model)
	->withRelations('blog', 'author');

Use withOptionalRelations() in the same manner to define relations which will only be exposed if found in the load parameter of the current request:

return PostResource::build($model)
	->withRelations('blog', 'author')
	->withOptionalRelations('comments.author');

As you can see from the above example, these methods also accept nested relations.

Now the controller has defined what it allows the resource to expose, you can configure the resource to do so using mergeWhenExplicitlyLoaded(). This method accepts the array of relation names that the resource can expose:

class PostResource extends Resource
{

	public function toArray($request)
	{
		return [
			$this->mergeAttributes('id', 'title'),
			$this->mergeWhenExplicitlyLoaded([
				'blog', 'author', 'comments'
			]),
		];
	}

Modifying related models

You may wish to modify models that have been loaded via these relations, before they are converted to their resource representations. You can do this by passing an associative array, with the relation name as the key and a closure as the value:

return PostResource::build($model)
    ->withRelations('blog', 'author')
    ->withOptionalRelations([
        'comments.author' => fn($author) => $author->append('avatar')
    ]);

Transforming relations

When passing items sequentially, they will be exposed using the related object's toArray method. If you would like to transform them into other resources, pass the relation as the key and the desired resource class as the value. These techniques can be combined:

class PostResource extends Resource
{

	public function toArray($request)
	{
		return [
			$this->mergeAttributes('id', 'title'),
			$this->mergeWhenExplicitlyLoaded([
				'blog',
				'author',
				'comments' => CommentResource::class,
			]),
		];
	}

In this case, we can now also define CommentResource to expose its author relation when explicitly loaded.

class CommentResource extends Resource
{

	public function toArray($request)
	{
		return [
			$this->mergeAttributes('id', 'content'),
			$this->mergeWhenExplicitlyLoaded([
				'author',
			]),
		];
	}

This results in a request like this:

`GET /post/1?load[]=comments.author`

returning something like this:

{
	'id' : 1,
	'name' : 'Blog post 1',
	'blog'	: {
		'id' => 123,
		'title' => 'My Blog',
	},
	'author' : {
		'id' : 456,
		'name' : 'Graham',
	},
	'comments' : [
		{
			'id' : 789,
			'content' : 'Nice post!',
			'author' : {
				'id' : 987,
				'name' : 'Taylor Otwell'
			}
		}
	]
}

Polymorphic relations

For polymirphic relations, first add a {relation}_type property to your resource. Then, specify a map of the possible classes and their relations inside mergeWhenExplicitlyLoaded():

class CommentResource extends Resource
{

	public function toArray($request)
	{
		return [
			$this->mergeAttributes('id', 'content', 'commentable_type'),
			$this->mergeWhenExplicitlyLoaded([
				'author',
				'commentable' => [
				    Post::class => PostResource::class,
				    Page::class => PageResource::class,
				]
			]),
		];
	}

Contextual data

Sometimes you may want to modify your resource's transformation based on information that isn't found in the request or the model being transformed.

To make arbitary data available to your resource, call withContext() on the resource builder returned from build():

$context = [
	'foo' => [
		'bar' => 1
	]
];

return PostResource::build($model)
	->withRelations('blog')
	->withContext($context);

Your context data can be accessed inside the resource with getContext(). You can also use dot notation to retrive a subset of the data:

$data = $this->getContext('foo.bar');

You may use mergeContext() to merge all or part of the context into your representation:

class PostResource extends Resource
{

	public function toArray($request)
	{
		return [
			$this->mergeAttributes('id', 'title'),
			$this->mergeWhenExplicitlyLoaded([
				'blog' => BlogResource::class,
			]),
			$this->mergeContext('foo.bar')
		];
	}

Any context data provided to a resource will be passed down to any other resources loaded via relations. In the above example, the context data will be available on the instances of BlogResource.

Data wrapping

Laravel's resources allow you to disable data wrapping, however this does so for both models and collections. Although the security risks of returning top-level JSON arrays appear to have been resolved in all browers, some may prefer to avoid wrapping single models but retain wrapping for collections.

Do to this, call withoutWrapping() on the new Resource class, followed by wrapCollection():

public function boot()
{
	Resource::withoutWrapping();
	Resource::wrapCollection('data');
}

gbradley/laravel-api-resource 适用场景与选型建议

gbradley/laravel-api-resource 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 18.04k 次下载、GitHub Stars 达 1, 最近一次更新时间为 2019 年 12 月 16 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「rest」 「api」 「laravel」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 gbradley/laravel-api-resource 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 gbradley/laravel-api-resource 我们能提供哪些服务?
定制开发 / 二次开发

基于 gbradley/laravel-api-resource 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 18.04k
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 1
  • 点击次数: 1
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 1
  • Watchers: 1
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2019-12-16