petrelli/eloquent-consumer

使用Eloquent语法风格的API消费者和查询构建器

v0.0.5-alpha 2018-10-11 15:50 UTC

This package is not auto-updated.

Last update: 2024-09-20 21:17:09 UTC


README

Eloquent API消费者将帮助您解决Laravel中API的两个主要问题

  1. 以清晰和简单的方式生成和执行API调用
  2. 处理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的参数。

请参考我们的默认语法类代码,你将看到我们如何处理所有选项。

安装

  1. 通过composer安装此包
composer require petrelli/eloquent-consumer
  1. 导出配置文件
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)。有关更多信息,请参阅许可证文件