sehrgut/laravel5-api

此包已被弃用且不再维护。未建议替换包。

用于将 Laravel 5 Eloquent 模型暴露为 REST API 的模块化控制器。

v0.7.10 2018-07-04 09:46 UTC

README

Gitter Chat Travis Build Status StyleCI Status Code Climate

用于将您的 Laravel 5 Eloquent 模型暴露为 REST API 的模块化控制器。您需要做的只是为每个模型创建一个控制器子类并设置路由。

免责声明:这是一个早期版本!在没有充分测试的情况下不要在生产环境中使用!API 可能会更改!

请使用 Github Issues 提交错误报告和功能请求。

目录

文档

API 参考 v0.7.8 (v0.6.5, v0.5.3, v0.4.2, v0.3.0)

入门

安装包

composer require sehrgut/laravel5-api

创建端点

控制器

扩展 SehrGut\Laravel5_Api\Controller 并设置控制器应暴露的 Eloquent 模型。示例

use SehrGut\Laravel5_Api\Controller as ApiController;
use App\Models\Post;

class PostsController extends ApiController
{
    protected $model = Post::class;
}

路由

现在您有一个控制器,其处理程序与 Laravel Resource Controller 相同。现在可以使用这些方法来处理以下路由

Route::get('/posts', 'PostsController@index');
Route::post('/posts', 'PostsController@store');
Route::get('/posts/{id}', 'PostsController@show');
Route::put('/posts/{id}', 'PostsController@update');
Route::delete('/posts/{id}', 'PostsController@destroy');

将路由参数映射到模型属性

默认情况下,假设所有指向单个资源(showupdatedestroy)的 URL 都有一个 {id} 参数。然后使用此参数通过其 id 属性找到相应的模型。

如果您的模型的主键或您的路由参数的名称不是 id,您需要在控制器中的 $key_mapping 中手动映射这些名称。示例

protected $key_mapping = [
	// Maps the `{post_id}` url parameter to the model's `primary_key` attribute/db column
	'post_id' => 'primary_key'
];

以同样的方式,可以将额外的 URL 参数映射到模型属性。这对于创建类似 /api/v1/posts/{post_id}/comments/{comment_id} 的嵌套资源端点特别有用。

验证器 & 转换器

您可能想要创建一个 验证器 和一个 转换器,并将它们分配给您的 ModelMapping 中的模型。关于如何工作的更多信息,请参阅组件

错误响应

为了正确显示异常,请确保在您的 app/Exceptions/Handler.php 中处理 SehrGut\Laravel5_Api\Exceptions\Exception

use SehrGut\Laravel5_Api\Exceptions\Exception as SehrGutApiException;

class Handler extends ExceptionHandler
{
    public function render($request, Exception $exception)
    {
        if ($exception instanceof SehrGutApiException) {
            return $exception->errorResponse();
        }

        // Possibly other checks

        return parent::render($request, $exception);
    }
}

结构

在一个包含多个端点的较大项目中,有一个公共的 ModelMapping 控制器是有意义的,其中定义了所有端点的模型映射。

示例目录结构

app/
|---- PublicApi
|		+---- V1
|			  |---- Controllers
|			  |		|---- BaseController.php
|			  |		|---- PostsController.php
|			  |		+---- PostCommentsController.php
|			  |---- Transformers
|			  |		|---- PostTransformer.php
|			  |		+---- CommentTransformer.php
|			  |---- Validators
|			  |		|---- PostValidator.php
|			  |		+---- CommentValidator.php
|			  |---- ModelMapping.php
|			  +---- RequestAdapter.php
+---- …

组件

逻辑被分成更小的组件,每个组件都有自己的职责。

  • 控制器 – 控制整个请求/响应流程
  • 验证器 – 确保请求负载有效
  • 转换器 – 对输出数据进行转换
  • ModelMapping – 知道为每个模型使用哪个验证器/转换器
  • RequestAdapter – 从请求中获取参数

控制器

可用处理器

  • index() - 获取所有资源
  • store() - 创建新的资源
  • show() - 获取单个资源
  • update() - 更新单个资源
  • destroy() - 删除单个资源

旁加载和计数关系

可以通过在控制器的 $relations$counts 属性上枚举关系的名称,将旁加载和关系计数添加到响应中。两者都递归工作,允许使用点符号旁加载/计数嵌套关系。

示例

use SehrGut\Laravel5_Api\Controller as ApiController;
use App\Models\Post;

class PostsController extends ApiController
{
    protected $model = Post::class;

    protected $relations = [
    	'comments',  // Side-load the 'comments' relation within posts
    	'comments.author'  // Side-load the 'author' relation in nested comments
    ];

    protected $counts = [
    	'comments',	 // Add 'comments_count' to posts
    	'comments.responses'  // Add `responses_count` to nested comments
    ];
}

验证器

为了为模型创建自定义验证器,您可以继承 Validator 类并设置 $rules 数组。之后,验证器需要在分配给控制器的 ModelMapping 中进行注册。有关如何操作的更多信息,请参阅ModelMapping 部分。验证器可能看起来像这样

use SehrGut\Laravel5_Api\Validator;

class PostValidator extends Validator
{
    protected static $rules = [
        'title' => 'required|min:3|max:100',
        'body' => 'max:65536'
    ];
}

转换器

为了定义模型在 API 中的表示方式,您可以在生成响应时对模型进行一些转换。这与验证器的工作方式相同。只需继承 Transformer 并通过 ModelMapping 将它们分配给模型即可。

在您的转换器子类中,您可以定义以下属性来自定义输出

use SehrGut\Laravel5_Api\Transformers\Transformer;

class PostTransformer extends Transformer
{
	// Rename Attributes:
	protected $aliases = [
		'original_attribute_name' => 'new_attribute_name',
		'id' => 'post_id'
	];

	// Remove Attributes:
	protected $drop_attributes = [
		'private_email'
	];

	// Remove Relations:
	protected $drop_relations = [
		'comments'
	];
}

此外,您可以通过在转换器上定义 formatAttribute 方法来更改模型单个属性值,其中 Attribute 是您想要转换的属性的驼峰式名称。该方法应接受一个参数(原始值)并返回转换后的属性。示例

use SehrGut\Laravel5_Api\Transformers\Transformer;

class PostTransformer extends Transformer
{
	/**
	 * Correct the date format of the member_since attribute
	 */
	formatMemberSince($value)
	{
		return $value->toDateString();
	}
}

ModelMapping

控制器询问ModelMapping应该为每个模型及其相应的关联使用哪个验证器和转换器。如果未为模型分配转换器/验证器,则返回相应的默认值(无验证,无转换)。

为了将自定义转换器或验证器应用到您的模型上,您必须创建一个自定义模型映射,并将其分配给控制器(最好通过一个共同的BaseController)。

use SehrGut\Laravel5_Api\ModelMapping as BaseModelMapping;

use App\Models\Post;
use App\PublicApi\V1\Transformers\PostTransformer;
use App\PublicApi\V1\Validators\PostValidator;

class ModelMapping extends BaseModelMapping
{
	protected $transformers = [
		Post::class =>	PostTransformer::class
	];

	protected $validators = [
		Post::class =>	PostValidator::class
	];
}
use SehrGut\Laravel5_Api\Controller;

class BaseController extends Controller
{
	protected $model_mapping_class = ModelMapping::class;
}

请求适配器

待定

定制

插件

插件是一种“挂钩”并操作控制器行为的方式。它们替代了旧版本≤0.3中使用的“钩子”。

使用方法

可以通过在$plugins属性中指定它们来在Controller内部注册插件。执行顺序将是它们在此处列出的顺序。

示例

use SehrGut\Laravel5_Api\Plugins\Paginator;
use SehrGut\Laravel5_Api\Plugins\SearchFilter;

class PostsController extends BaseController
{
	protected $plugins = [
		Paginator::class,
		SearchFilter::class,
	];
}
在控制器上声明钩子

除了使用插件外,控制器还可以实现任何钩子,以影响请求/响应生命周期。在这种情况下,它的行为与插件相同

  1. 声明控制器实现适当的钩子接口
  2. 声明接口所需的方法

如果控制器以这种方式订阅钩子,则在传递给插件之前,将首先在控制器上调用钩子。

配置

一些插件具有可配置的选项,可以通过控制器设置。这可以在afterConstruct()方法内部完成,如下所示

use SehrGut\Laravel5_Api\Plugins\SearchFilter;

class PostsController extends BaseController
{
	protected $plugins = [SearchFilter::class];

	protected function afterConstruct()
	{
	    $this->configurePlugin(SearchFilter::class, [
	        'searchable' => ['name', 'description'],  // compare to those fields on the model
	        'search_param' => 'query',                // `?query=some+search+query`
	    ]);
	}
}

请参阅各个插件的源代码或API参考以查看可用的配置选项。

可用的插件

使用源代码以获得确定的答案。不过,这里有一份(可能过时)的插件列表

  • 授权在所有五个默认控制器操作中使用Laravel内置的授权进行授权检查。
  • 分页器允许对index结果集进行分页。默认情况下,它使用limitpage查询参数来确定请求的子集。
  • 关系拆分器是一位来自南方的优秀离婚律师……开玩笑的,它使相关对象在响应中作为单独的键出现,而不是嵌套在其相关对象内部。
  • 搜索过滤器index查询添加文本搜索。您可以配置要与搜索词进行比较的模型属性。

有关使用各个插件的帮助,请检查它们各自的源文件API参考

编写插件

插件只是一个扩展SehrGut\Laravel5_Api\Plugins\Plugin的类。它可以实现一个或多个钩子,以影响控制器的行为。

插件配置

基本 Plugin 类提供了一个 protected $config 属性,用于存储配置选项,这些选项可以通过控制器的方法 configurePlugin($name, $options) 进行设置。使用此功能将插件参数暴露给用户(编写控制器的人)。在插件内部使用 $this->config['option_name'] 来检索配置选项。默认值应通过 protected $default_config 属性设置。

示例
<?php
namespace App\Api\V1\Plugins;

use SehrGut\Laravel5_Api\Plugins\Plugin;
use SehrGut\Laravel5_Api\Hooks\AdaptCollectionQuery;
use SehrGut\Laravel5_Api\Hooks\AdaptResourceQuery;

/**
 * Just an example: `dd()` all queries instead of executing them.
 */
class DieAndDumpQuery extends Plugin implements AdaptCollectionQuery, AdaptResourceQuery
{
	$default_config = [
		'option' => 'Reasonable default',
	];

	protected function adaptCollectionQuery()
	{
		dd($this->context->query);
	}

	protected function adaptResourceQuery()
	{
		dd($this->context->query);
	}
}
插件钩子

可用的钩子列表在 "Hooks" 目录中。

为了使用钩子,您只需声明您的插件类 implements 对应的接口,然后实现该接口的适当方法。查看现有插件的源代码,了解如何实现此功能。

每个钩子接口声明了一个精确的方法。该方法的名称与接口名称相同,只是首字母小写。例如:钩子接口 AdaptResourceQuery 声明了一个名为 adaptResourceQuery 的方法。

可用钩子

AdaptCollectionQuery::adaptCollectionQuery() 定制获取资源集合(index 操作)的查询。

AdaptResourceQuery::adaptResourceQuery() 定制获取单个资源(showupdatedestroy 操作)的查询。

AdaptRelations::adaptRelations(array $relations) 此钩子接收要与查询模型一起预加载的关系数组。返回修改后的数组。

BeginAction::beginAction() 这是任何操作的第一个钩子,在 $context->action 设置之后。

AuthorizeAction::authorizeAction()indexstore 操作上执行授权。此钩子仅在这些操作上调用。

AuthorizeResource::authorizeResource() 在从数据库中获取资源并存储到 $this->resource 后,在 showupdatedestroy 处理程序中调用此钩子以执行资源级别的授权。

FormatCollection::formatCollection() 在转换之前接收资源集合。

FormatResource::formatResource() 在转换之前接收单个资源。

ResponseHeaders::responseHeaders() 在此处操作响应头。

BeforeSave::beforeSave() 在模型从 $context->input 填充并在调用 $context->resource->save() 之前,在每次 createupdate 操作上调用。

AfterSave::afterSave() 在每次 createupdate 操作后调用,在调用 $context->resource->save() 之后。

BeforeCreate::beforeCreate()store 动作中仅在调用 beforeSave() 之前调用此钩子。

BeforeUpdate::beforeUpdate()update 动作中仅在调用 beforeSave() 之前调用此钩子。

控制器/插件上下文

每个插件都有一个对控制器 Context 对象的引用,该对象存储在 Plugin::$context 中。Context 包含了插件可能需要读取或写入的所有相关数据。

// Read-only:
$context->model;
$context->request;

// Read-write:
$context->input;
$context->action;
$context->query;
$context->resource;
$context->collection;
$context->response;

已弃用:钩子方法

警告:钩子(Hooks)已被插件(Plugins)取代(见上文),使用时请注意:以下列出的方法将很快从控制器中移除,并替换为相应的插件钩子。 在“插件”的上下文中,“钩子”指的是一个接口,而不是像旧概念中那样是一个控制器方法。

控制器中有多个钩子,可以帮助您自定义其行为。您只需在控制器中实现所需的方法即可。有关钩子的详细信息,请浏览代码并参考API参考

makeModelMapping()

根据Auth/Roles等动态自定义ModelMapping。

makeRequestAdapter(Request $request)

动态自定义RequestAdapter。

adaptRules(Array $rules)

在从验证器获取验证规则后对其进行适配。返回适配后的规则。

afterConstruct()

控制器中__construct()方法的最后调用。

变更日志

请参阅CHANGELOG.md

兼容性

  • 与Laravel 5.3 - 5.6进行了测试。
  • 与PHP 7.1 - 7.2进行了测试。

测试

测试基于phpunit,并使用内存中的sqlite数据库。由于测试依赖于Laravel框架,因此需要首先安装composer(dev-)依赖项。

composer install
vendor/bin/phpunit

许可证

本软件根据MIT许可证许可。有关详细信息,请参阅LICENSE.txt