agazzipietro / api-wrapper
一个用于快速开发围绕远程API的PHP包装客户端的系统(这是PHP 8.2的helium/api-wrapper的一个分支)
Requires
- php: ^7.4|^8.0
- ext-json: *
- guzzlehttp/guzzle: ^7.5.1
- illuminate/collections: ^v8.83.27
- nesbot/carbon: ^2.66.0
- petrknap/php-singleton: ^1.0
- symfony/string: ^5.4.22
Requires (Dev)
- helium/coding-standards: ^1.2
- phpunit/phpunit: ^9.6.7
This package is not auto-updated.
Last update: 2024-10-02 23:30:07 UTC
README
简介
API包装器是一个快速开发围绕远程API的PHP包装客户端的系统。在底层由Guzzle提供支持,这个库提供了所有必要的抽象,以快速配置API请求,无需不必要的样板代码。
要深入了解这个库的优势,请查看我们的Medium文章。
此README中的所有示例都使用JSONPlaceholder API,这是一个用于测试基本请求的免费REST API。
安装
要将此库添加到您的项目中,请运行
composer require helium/api-wrapper
用法
路由
基本路由
可以使用Route
类创建路由。所有路由方法都接受一个唯一名称和一个URL。
Route::get('post.all', 'https://jsonplaceholder.typicode.com/posts')
唯一名称用于引用此路由进行请求(见请求)。
可用的路由方法
您可以手动声明路由,或使用任何HTTP动词
Route::endpoint('GET', $name, $route);
Route::head($name, $route);
Route::get($name, $route);
Route::post($name, $route);
Route::put($name, $route);
Route::patch($name, $route);
Route::delete($name, $route);
Route::options($name, $route);
每个路由方法都返回一个Endpoint
实例,这是一个表示您路由所有配置选项的类。您可以使用其公共成员方法将额外的配置选项(如处理器)应用到端点上。
路由分组
要将某些配置选项应用于一组路由,请声明一个路由分组。路由分组可以接受基础URL、单个处理器或处理器组(见处理器),以及包含路由声明的回调。路由分组可以是嵌套的,并且在其内部声明的所有路由都将接收其所有父分组的所有配置选项。
Route::group('https://jsonplaceholder.typicode.com', [], function() {
Route::get('post.all', 'posts');
});
处理器
创建一个处理器
受Laravel中间件的启发,Processor
类在请求执行前后拦截请求。处理器可以在请求发送之前修改要发送的请求,并且可以在返回之前检查和修改响应对象。
处理器是应用共享配置选项(如授权头)到外出请求的首选方式(见请求)。
class AuthorizationProcessor extends Processor
{
public static function handle(Request $request, callable $next): Response
{
$request->headers(['Authorization' => 'token']);
return $next($request);
}
}
要处理请求发送后的响应,请获取由$next
回调返回的响应对象(见响应)。
class PostProcessor extends Processor
{
public static function handle(Request $request, callable $next): Response
{
$response = $next($request);
//Do Something
return $response;
}
}
应用处理器
您可以将处理器应用于路由和路由分组。无论是Route::group
还是$endpoint->processor
函数,都接受数组或单个处理器类名称。
Route::group('https://jsonplaceholder.typicode.com', [GroupProcessor::class], function() {
Route::get('post.all', 'posts')->processor(EndpointProcessor::class);
});
请求
创建一个请求
Request
类表示在Guzzle执行之前HTTP请求的完整配置。创建请求的最佳方法是使用静态route
方法,该方法接受您在声明路由时提供的唯一名称。
$request = Request::route('post.all');
请求路径参数
要设置请求路径参数,您必须在路由中使用大括号{}
声明该参数。
Route::get('post.get', 'posts/{id}');
在创建请求时,使用pathParams
方法设置参数值。
$request->pathParams(['id' => 1]);
此请求将解析为URL https://jsonplaceholder.typicode.com/posts/1
。
请求查询参数
要设置请求查询(URL)参数,请使用queryParams
方法。
$request->queryParams(['userId' => 1]);
此请求将解析为URL https://jsonplaceholder.typicode.com/posts?userId=1
。
请求正文
请求体可以通过多种方法之一进行设置,具体取决于您的需求。
$request->body('body');
$request->json(['key' => 'value']);
$request->formParams(['key' => 'value']);
$request->multipart(['key' => 'value']);
请求头
要配置请求头,请使用 headers
方法,该方法接受一个包含头名称和值的关联数组。
$request->headers(['Authorization' => 'token']);
认证
要配置Guzzle定义的认证选项(请参阅Guzzle文档),而无需手动设置 Authorization
头,请使用 auth
方法。
$request->auth(['username', 'password']);
选项
要将任何额外的配置选项传递给Guzzle,请使用 options
方法。
$request->options(['timeout' => 3.14]);
Guzzle客户端
要使用自定义的Guzzle客户端,请将您的自定义实例传递给 Request::route
方法。这通常用于调试和测试时的Guzzle模拟。
Request::route('post.all', $client);
发送
在您配置完请求的所有必要选项后,使用 send
方法执行它以接收响应。
$response = $request->send();
方法链
为了便于使用,所有之前提到的请求方法都可以链接到单个调用中。
$response = Request::route('post.get')
->pathParams(['id' => 1])
->headers(['Authorization' => 'token'])
...
->send();
响应
执行请求后,您将收到一个 Response
对象。该类实现了 Psr\Http\Message\ResponseInterface
,与Guzzle直接返回的响应对象相同(请参阅Guzzle文档),实际上将所有接口方法调用转发到Guzzle响应对象实例。然而,此响应对象包含一些用于提高易用性的额外方法。
获取内容
默认情况下,Guzzle仅以流的形式返回响应体。虽然这可以转换为普通的 string
,但请使用响应的 getContents
方法。
$body = $response->getContents();
JSON
要自动处理JSON字符串响应,请使用 json
方法。
$data = $response->json();
默认情况下,此方法将JSON字符串解码为关联数组,但若要使用 stdClass
实例代替,请将 false
传递给方法。
$data = $response->json(false);
资源
受Laravel的Eloquent和Stripe PHP库的启发,Resource
类将API数据表示为具有基本ORM-like功能的数据模型实例。
对于不适合或不遵守REST标准的API,资源可能是不必要的抽象。然而,对于处理关系对象数据的API,资源提供了很多有用的行为。
创建资源
您可以使用自定义的ApiResource类来表示API中的每个模型。
class Post extends Resource
{
...
}
可用的属性方法
与Eloquent模型一样,资源维护一个内部数组,该数组表示模型数据,并且有多种方法可以与之交互。
$post = new Post(['id' => 1]);
$post->id = 1;
$id = $post->id;
$post->setAttribute('id', 1);
$id = $post->getAttribute('id');
$post->mergeAttributes(['id' => 1]); //Merge the new attributes into the existing attributes.
//For existing keys, overwrite old values with new, but do not clear other keys.
$post->setAttributes(['id' => 1]); //Same effect as mergeAttributes.
$post->setAttributes(['id' => 1], true); //Clear all existing attributes.
属性转换
您可以配置ApiResource类,以便在设置属性值时将某些属性转换为其他类型。
可用的转换类型包括
数组
布尔值
collection
(请参阅Doctrine Collections)date
(请参阅Carbon)datetime
(请参阅Carbon)浮点数
整数
对象
字符串
timestamp
(请参阅Carbon)- ApiResource类(请参阅属性转换)
在您的ApiResource类中声明属性转换类型。
class Post extends Resource
{
protected $casts = [
'id' => 'int',
...
];
}
请注意,如果远程API返回的数据类型正确,则声明转换不是必需的。尽管这不会造成任何伤害,但除非需要进行转换,否则您无需麻烦地声明转换类型。
当设置了一个类型转换属性时,它的值会通过一个内部转换函数进行处理,并将其以正确的类型存储在内部。当你在未来检索此对象时,它将以存储的类型返回。
资源转换
为了自动将嵌套对象转换为正确的ApiResource类,使用目标类名作为转换类型。当使用数据关联数组设置属性时,它将自动创建目标资源类的实例。
class Post extends Resource
{
protected $casts = [
'user' => 'User::class',
...
];
}
默认操作
默认情况下,此库为您的ApiResource类提供了基本CRUD操作的实施。通过实现适当的接口合约和使用提供的特性来实现该行为。
use Helium\ApiWrapper\Resource\Contracts\All as AllContract;
use Helium\ApiWrapper\Resource\Contracts\Create as CreateContract;
use Helium\ApiWrapper\Resource\Contracts\Delete as DeleteContract;
use Helium\ApiWrapper\Resource\Contracts\Get as GetContract;
use Helium\ApiWrapper\Resource\Contracts\Update as UpdateContract;
use Helium\ApiWrapper\Resource\Operations\All;
use Helium\ApiWrapper\Resource\Operations\Create;
use Helium\ApiWrapper\Resource\Operations\Delete;
use Helium\ApiWrapper\Resource\Operations\Get;
use Helium\ApiWrapper\Resource\Operations\Update;
use Helium\ApiWrapper\Resource\Resource;
class Post extends ApiResource implements AllContract, CreateContract, DeleteContract, GetContract, UpdateContract
{
use All, Create, Delete, Get, Update;
}
在底层,这些操作映射到如上所述的请求层。要执行查询,请调用可用的操作方法。
$posts = Post::all(); //Queries post.all Route (usually GET)
//returns Collection of Post Resources
$post = Post::create([...]); //Queries post.create Route (usually POST) using provided attributes
//returns instance of Post Resource
$post = new Post([...]);
$post->store(); //Queries post.create Route (usually POST) using current attributes
//Fills $post with returned data
Post::delete(1); //Queries post.delete (usually DELETE) Route with id = 1
$post = new Post(['id' => 1]);
$post->destroy(); //Queries post.delete Route (usually DELETE) with id = 1
$post = Post::get(1); //Queries post.get Route (usually GET) with id = 1
$post = new Post(['id' => 1]);
$post = $post->fresh(); //Queries post.get Route (usually GET) with id = 1
//Returns new instance of Post Resource
$post = new Post(['id' => 1]);
$post->refresh(); //Queries post.get Route (usually GET) with id = 1
//Fills $post with returned data
Post::update(1, [...]); //Queries post.update Route (usually PUT/PATCH) with id = 1 and current attributes
$post = new Post(['id' => 1]);
$post->updateAttributes([...]); //Sets $post attribues using provided attributes
//Queries post.update Route (usually PUT/PATCH) with id = 1 and current attributes
//Fills $post with returned data
$post = new Post(['id' => 1]);
$post->saveChanges(); //Queries post.update Route (usually PUT/PATCH) with id = 1 and current attributes
//Fills $post with returned data
默认操作路由
默认情况下,每个操作都假设一个特定的路由名称,该名称包括模型名称和操作名称。例如,Post::all
假设一个名为 post.all
的路由。如果您需要为任何默认操作设置自定义路由名称,请设置您的ApiResource类上的相关属性。
class Post extends ApiResource implements AllContract, CreateContract, DeleteContract, GetContract, UpdateContract
{
use All, Create, Delete, Get, Update;
protected $allRoute = 'customAllRoute';
protected $createRoute = 'customCreateRoute';
protected $deleteRoute = 'customDeleteRoute';
protected $getRoute = 'customGetRoute';
protected $updateRoute = 'customUpdateRoute';
}
自定义操作
当然,您可以根据API的需求创建自己的自定义操作。只需定义您需要的函数,并在其中调用 Request::route
方法。或者,如果您需要覆盖默认操作实现的行为,您应该自己实现合约方法,而不是引入提供默认行为的特性。