petrelli / eloquent-consumer
使用Eloquent语法风格的API消费者和查询构建器
Requires
- php: ^7.0
- guzzlehttp/guzzle: 6.3.0
- jenssegers/model: ^1.2
This package is not auto-updated.
Last update: 2024-09-20 21:17:09 UTC
README
Eloquent API消费者将帮助您解决Laravel中API的两个主要问题
- 以清晰和简单的方式生成和执行API调用
- 处理API响应并生成类似Eloquent的模型和集合(包括分页集合)
您可以在以下示例应用程序中看到: Eloquent Consumer Example APP。
开发注意事项
此包仍处于alpha状态,并且正在进行大量开发。将来可能会有所变化。
创建此包的动机
我们的CMS被设计成使用类似Eloquent的模型作为数据源。
因此,我们需要一种方法来无缝地集成API源来构建一些列表,而无需修改CMS。
此库创建的模型将具有与Eloquent兼容的接口,因此构建查询、分页、作用域、筛选以及与Eloquent相关的大部分内容都将可用。
使用此包,您将能够管理API端点、缓存策略、底层API查询配置、模型属性、作用域和函数,以及生成查询、获取处理后的最终结果的每个相关元素。
目录
概述
让我们假设我们有一个Book API模型,并想从API中读取一些数据。让我们现在执行一些调用
// Call to the collection endpoint, and return a collection of Books \App\Book::query()->get(); // Call to the collection endpoint, and return a paginated collection of 10 elements \App\Book::query()->paginate(10); // Call to the resource endpoint, {id} will be replaced with the value of $id and will return a Test object. \App\Book::query()->find($id); // Call to the resource endpoint, {id} will be replaced with the value of $id and will return a Test object. Throw a 404 if not found. \App\Book::query()->findOrFail($id); // Let's get a collection of search results for a term. This will simply add a 'q=Julio Cortazar' parameter to the query by default. \App\Book::query()->search('Julio Cortazar')->get() // Call to the collection endpoint, and let's just add a customized parameter to this call to search books by ISBN. \App\Book::query()->rawQuery(['ISBN' => 123456])->get() // Call to the collection endpoint, and return a collection of Books with only id, and title columns \App\Book::query()->get(['id', 'title']);
如您所见,这种语法非常熟悉。
假设您想添加自己的类似Eloquent的作用域以仅获取已发布的元素。只需遵循与Laravel中通常使用的相同语法即可
<?php // ... class Book extends ApiModel { //... public function scopePublished($query) { return $query->rawQuery(['published' => true]); } //... }
// Call to the published scope and then call to the collection endpoint \App\Book::query()->published()->get();
这将传递一个published=true参数来执行API调用(因为我们只是像之前看到的那样使用了rawQuery函数)
为了配置此模型,我们必须为我们特定的API创建一个Endpoint类。您可以手动创建或使用以下命令
php eloquent-consumer:endpoint Main
让我们编辑一些选项
<?php namespace App\ApiConsumer\Endpoints; use \Petrelli\EloquentConsumer\Endpoints\BaseEndpoint; class Main extends BaseEndpoint { protected $baseUri = 'https://baseapi.com'; protected $defaultTTL = 200; }
如您所见,我们在这里只定义了一个$baseUri和一个默认的缓存TTL(200秒)。
现在我们已经配置了端点,我们应该创建实际的Book模型。此模型必须继承我们的伪Eloquent类。
<?php namespace App; use \Petrelli\EloquentConsumer\Models\ApiModel; class Book extends ApiModel { protected $endpointClass = \App\ApiConsumer\Endpoints\Main::class; protected $endpoints = [ 'collection' => '/books', 'resource' => '/books/{id}', ]; }
在这里,我们只需配置两件事
- 要使用的端点类
- 此类型资源使用的实际URL
集合和资源是包使用的默认端点。当然,您可以定义自己的并稍后使用它们,但这两个就足够了。
这足以让您开始。
如果您继续阅读,您将了解如何修改调用的方式以及如何处理响应。
核心概念
您将为项目有一个默认的配置文件,定义这里将展示的所有内容。但当然,您可以通过分别配置每个类来覆盖这些值。
建议在创建新元素时使用默认命名空间ApiConsumer。
我们将提到以下实体
- 端点
- 消费者
- 语法
端点
这将是您的主要实体。
您可以在每个项目中拥有任意多个,允许您使用多个API和/或同一API中的多个配置。
您可以在以下选项中定义以下内容
use \Petrelli\EloquentConsumer\Endpoints\BaseEndpoint; class MainEndpoint extends BaseEndpoint { //Mandadory if not added at the general config file //This will be your baseline URL. E.g. http://apibase.com protected $baseUri; //Mandadory if not added at the general config file. Number of seconds each call will be cached. protected $defaultTTL; //Custom Grammar or Consumer classes protected $grammarClass; protected $consumerClass; //...... }
请注意以下选项
- 消费者类
- 语法类
消费者
消费者是执行实际API调用的客户端。这个包包括一个默认客户端,它使用Guzzle。
通常情况下,使用Guzzle的默认客户端已经足够。
如果你需要添加新的选项,例如认证头、超时配置等,创建一个新的消费者类可能会有用,基本上,任何你可能希望Guzzle或你选择的客户端支持的配置。
语法
语法类基本上是一个收集器,它将你提供给查询构建器的信息转换为将在API请求中发送的字段。
这是一个非常简单的类,你可以扩展并调整以适应你的API规范。
让我们看看这个语法类的示例函数
protected function compileSearchText($query, $text) { if ($text) return ['q' => $text]; else return []; }
在这里,你可以看到函数search('term')将被转换为名为q的参数。
请参考我们的默认语法类代码,你将看到我们如何处理所有选项。
安装
- 通过composer安装此包
composer require petrelli/eloquent-consumer
- 导出配置文件
php artisan vendor:publish --provider="Petrelli\EloquentConsumer\EloquentConsumerServiceProvider"
在那里,你可以定义默认选项和实体(端点、语法和连接)。
配置
让我们生成一个端点
php artisan eloquent-consumer:endpoint Base
或者,如果你想使用自己的命名空间
php artisan eloquent-consumer:endpoint EndpointName Namespace1/Namespace2
现在让我们向端点添加一些配置值
<?php namespace App\ApiConsumer\Endpoints; use \Petrelli\EloquentConsumer\Endpoints\BaseEndpoint; class Base extends BaseEndpoint { protected $baseUri = 'https://baseapi.com'; protected $defaultTTL = 200; }
这里你可以看到这是一个非常基本的端点。没有定制语法或消费者。
如果你想扩展默认的语法,可以添加一个$grammarClass变量
protected $connectionClass = \App\ApiConsumer\Grammar\MyOwnGrammar::class;
然后我们需要创建这个语法类
<?php namespace App\ApiConsumer\Grammar; use \Petrelli\EloquentConsumer\Grammar\BaseGrammar; class MyOwnGrammar extends BaseGrammar { protected function compilePage($query, $page) { return ['page_number' => $page]; } }
所以在这里,我们重新定义了compilePage,它将在API查询中发送一个page_number参数,而不是默认的page。
使用
配置完成后,我们可以开始使用这个模型,几乎就像它是一个eloquent实体一样。
// Call to the collection endpoint, and return a collection of Books \App\Book::query()->get(); // Call to the collection endpoint, and return a paginated collection of 10 elements \App\Book::query()->paginate(10); // Call to the resource endpoint, {id} will be replaced with the value of $id and will return a Test object. \App\Book::query()->find($id); // Call to the resource endpoint, {id} will be replaced with the value of $id and will return a Test object. Throw a 404 if not found. \App\Book::query()->findOrFail($id); // Let's get a collection of search results for a term. This will simply add a 'q=Julio Cortazar' parameter to the query by default. \App\Book::query()->search('Julio Cortazar')->get(); // Call to the collection endpoint, and let's just add a customized parameter to this call to search books by ISBN. \App\Book::query()->rawQuery(['ISBN' => 123456])->get(); // Call to the collection endpoint, and return a collection of Books with only id, and title columns \App\Book::query()->get(['id', 'title']); // Call to the customized 'something' endpoint, and return a collection of Books with only id, and title columns \App\Book::query()->forceEndpoint('something')->get(['id', 'title']);
如您所见,这种语法非常熟悉。
假设您想添加自己的类似Eloquent的作用域以仅获取已发布的元素。只需遵循与Laravel中通常使用的相同语法即可
<?php // ... class Book extends ApiModel { //... public function scopePublished($query) { return $query->rawQuery(['published' => true]); } //... }
// Call to the published scope and then call to the collection endpoint \App\Book::query()->published()->get();
这将传递一个published=true参数来执行API调用(因为我们只是像之前看到的那样使用了rawQuery函数)
关系
文档待完成。
转换器类
所以我们生成了查询,现在我们得到了正确的响应。
现在我们需要重构这个响应,使其符合库的标准,以便一切按预期工作。
这可以通过转换器类轻松完成。
<?php namespace App\ApiConsumer\Transformers; use Petrelli\EloquentConsumer\Transformers\BaseTransformer; class Base extends BaseTransformer { /** * Transform Grants API response to a format we can read */ public function transform() { $original = $this->response->body; $original->pagination = (object) [ 'total' => $original->recordsTotal, 'page' => $original->draw, ]; return $this->response; } }
转换器类只有一个函数:transform()。在那里,你有可用的$this->response来使用它并将你的响应转换为我们正确的格式。
这将使你能够透明地使用任何API及其底层响应格式。
期望的格式如下所示
{
pagination: {
total: 100,
limit: 12,
offset: 0,
total_pages: 520,
current_page: 1,
},
data: [
{},
{},
{},
{},
...
]
}
所以为了能够充分利用,你需要将你的响应调整得像这样。
扩展参考
待办事项
许可
MIT许可证(MIT)。有关更多信息,请参阅许可证文件。