sehrgut / laravel5-api
用于将 Laravel 5 Eloquent 模型暴露为 REST API 的模块化控制器。
Requires
- php: >=5.4
Requires (Dev)
- laravel/laravel: ~5.6
- mockery/mockery: ~1.0
- phpunit/phpunit: ~7.0
This package is auto-updated.
Last update: 2020-12-13 20:46:19 UTC
README
用于将您的 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');
将路由参数映射到模型属性
默认情况下,假设所有指向单个资源(show
、update
、destroy
)的 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, ]; }
在控制器上声明钩子
除了使用插件外,控制器还可以实现任何钩子,以影响请求/响应生命周期。在这种情况下,它的行为与插件相同
- 声明控制器
实现
适当的钩子接口 - 声明接口所需的方法
如果控制器以这种方式订阅钩子,则在传递给插件之前,将首先在控制器上调用钩子。
配置
一些插件具有可配置的选项,可以通过控制器设置。这可以在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
结果集进行分页。默认情况下,它使用limit
和page
查询参数来确定请求的子集。 - 关系拆分器是一位来自南方的优秀离婚律师……开玩笑的,它使相关对象在响应中作为单独的键出现,而不是嵌套在其相关对象内部。
- 搜索过滤器为
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()
定制获取单个资源(show
、update
和 destroy
操作)的查询。
AdaptRelations::adaptRelations(array $relations)
此钩子接收要与查询模型一起预加载的关系数组。返回修改后的数组。
BeginAction::beginAction()
这是任何操作的第一个钩子,在 $context->action
设置之后。
AuthorizeAction::authorizeAction()
在 index
和 store
操作上执行授权。此钩子仅在这些操作上调用。
AuthorizeResource::authorizeResource()
在从数据库中获取资源并存储到 $this->resource
后,在 show
、update
和 destroy
处理程序中调用此钩子以执行资源级别的授权。
FormatCollection::formatCollection()
在转换之前接收资源集合。
FormatResource::formatResource()
在转换之前接收单个资源。
ResponseHeaders::responseHeaders()
在此处操作响应头。
BeforeSave::beforeSave()
在模型从 $context->input
填充并在调用 $context->resource->save()
之前,在每次 create
和 update
操作上调用。
AfterSave::afterSave()
在每次 create
和 update
操作后调用,在调用 $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。