nvmcommunity / alchemist-restful-api
这是一个帮助您快速获取严格且灵活的基于RESTful的应用程序API接口的库。
Requires
- php: >=7.4
- nette/utils: *
- psr/http-message: ^2.0
Requires (Dev)
- phpstan/phpstan: ^1.10
- phpunit/phpunit: 9.6.15
- symfony/var-dumper: ^6.2
- dev-master
- 2.0.24
- 2.0.23
- 2.0.22
- 2.0.21
- 2.0.20
- 2.0.19
- 2.0.18
- 2.0.17
- 2.0.16
- 2.0.15
- 2.0.14
- 2.0.12
- 2.0.11
- 2.0.10
- 2.0.9
- 2.0.8
- 2.0.7
- 2.0.6
- 2.0.5
- 2.0.4
- 2.0.3
- 2.0.2
- 2.0.1
- 2.0.0
- 2.0.0-beta240304
- 2.0.0-beta240303
- 2.0.0-beta240302
- 2.0.0-beta240301
- 2.0.0-beta240205
- 2.0.0-beta240116
- 2.0.0-beta240115
- 2.0.0-beta240114
- 2.0.0-beta240113
- 2.0.0-beta240112
- 2.0.0-beta240111
- 2.0.0-beta240110
- 2.0.0-beta240108
- 2.0.0-beta231229
- 2.0.0-beta231228
- 2.0.0-beta23731
- 2.0.0-beta23721
- 2.0.0-beta23720
- 2.0.0-beta23719
- 1.0.4
- 1.0.3
- 1.0.2
- 1.0.1
- 1.0.0
- 0.2.1-beta2313100
- 0.1.5-beta8123316
- 0.1.4-beta8123425
- 0.0.2-beta7113634
- 0.0.2-beta7113624
- 0.0.2-beta7113622
- 0.0.1-beta743349
This package is auto-updated.
Last update: 2024-09-25 09:33:15 UTC
README
这是一个帮助您快速获取严格且灵活的基于RESTful的应用程序API接口的库。
测试
此软件包已经准备好投入生产,包含193个测试和1151个断言。
./vendor/phpunit/phpunit/phpunit
目录
变更日志
- 1.0.0: 首次发布
- 2.0.0: 添加对新方式定义对象和集合字段结构的支持
- 2.0.1: 添加对AlchemistAdapter的支持,以更改默认组件的请求输入中的参数名
- 2.0.16: (2024-04-28) 添加更多测试和修复意外的行为
- 2.0.17: (2024-06-14) 修复字段解析器中的错误模式
- 2.0.19: (2024-06-14) 字段解析语法支持空格
- 2.0.20: (2024-06-14) 添加API类的非静态版本:StatefulAlchemistQueryable
简介
Alchemist Restful API是一个帮助您快速获取严格且灵活的基于RESTful的应用程序API接口的库。
该库提供了一套组件,您可以使用它们来构建API接口,包括
- 字段选择器:允许您的API客户端选择他们想要检索的字段,确保所有检索到的字段都在您的控制之下。
- 资源筛选:关注检查您的API客户端使用的筛选是否在定义的可筛选列表中。
- 资源排序:支持基于API客户端指定的排序和方向灵活返回数据。
- 资源搜索:在筛选时,API客户端需要明确指定筛选标准。然而,在搜索的情况下,API客户端只需要传入要搜索的值,后端将自动从内部定义筛选标准。
- 资源偏移分页器:支持通过偏移量和限制机制进行分页。
先决条件
- PHP 7.4
- 已安装Composer (https://getcomposer.org.cn)
安装
composer require nvmcommunity/alchemist-restful-api
Laravel集成
如果您正在使用Laravel,您可以使用名为laravel-eloquent-api
的另一个软件包将Alchemist Restful API与Laravel中的Eloquent ORM集成。
composer require nvmcommunity/laravel-eloquent-api
有关如何使用nvmcommunity/laravel-eloquent-api
软件包的更多详细信息,请参阅Laravel Eloquent API文档
基本用法
以下是如何使用Alchemist Restful API构建订单API的RESTful API接口的示例。
步骤1:定义API类
您需要定义一个扩展AlchemistQueryable
类的API类,并实现字段选择器、资源筛选、资源分页、资源搜索和资源排序的方法。
<?php use Nvmcommunity\Alchemist\RestfulApi\Common\Integrations\AlchemistQueryable; use Nvmcommunity\Alchemist\RestfulApi\FieldSelector\Handlers\FieldSelector; use Nvmcommunity\Alchemist\RestfulApi\ResourceFilter\Handlers\ResourceFilter; use Nvmcommunity\Alchemist\RestfulApi\ResourceFilter\Objects\FilteringRules; use Nvmcommunity\Alchemist\RestfulApi\ResourcePaginations\OffsetPaginator\Handlers\ResourceOffsetPaginator; use Nvmcommunity\Alchemist\RestfulApi\ResourceSearch\Handlers\ResourceSearch; use Nvmcommunity\Alchemist\RestfulApi\ResourceSort\Handlers\ResourceSort; /** * Example of Order Api Query */ class OrderApiQuery extends AlchemistQueryable { /** * @param FieldSelector $fieldSelector * @return void */ public static function fieldSelector(FieldSelector $fieldSelector): void { $fieldSelector->defineFieldStructure([ 'id', 'order_date', 'order_status', 'order_items' => [ 'item_id', 'product_id', 'price', 'quantity' ] ]); } /** * @param ResourceFilter $resourceFilter * @return void */ public static function resourceFilter(ResourceFilter $resourceFilter): void { $resourceFilter->defineFilteringRules([ FilteringRules::String('product_name', ['eq', 'contains']), FilteringRules::Date('order_date', ['eq', 'lte', 'gte'], ['Y-m-d']), FilteringRules::Integer('product_id', ['eq']), FilteringRules::Boolean('is_best_sales', ['eq']), ]); } /** * @param ResourceOffsetPaginator $resourceOffsetPaginator * @return void */ public static function resourceOffsetPaginator(ResourceOffsetPaginator $resourceOffsetPaginator): void { $resourceOffsetPaginator ->defineDefaultLimit(10) ->defineMaxLimit(1000); } /** * @param ResourceSearch $resourceSearch * @return void */ public static function resourceSearch(ResourceSearch $resourceSearch): void { $resourceSearch ->defineSearchCondition('product_name'); } /** * @param ResourceSort $resourceSort * @return void */ public static function resourceSort(ResourceSort $resourceSort): void { $resourceSort ->defineDefaultSort('id') ->defineDefaultDirection('desc') ->defineSortableFields(['id', 'created_at']); } }
步骤2:验证输入参数
务必使用validate
方法验证从请求输入传递的输入参数。
<?php use Nvmcommunity\Alchemist\RestfulApi\AlchemistRestfulApi; // Assuming that the input parameters are passed in from the request input $input = [ 'fields' => 'id,order_date,order_status,order_items{item_id,product_id,price,quantity}', 'filtering' => [ 'order_date:lte' => '2023-02-26', 'product_name:contains' => 'clothes hanger' ], 'search' => 'clothes hanger', 'sort' => 'id', 'direction' => 'desc', 'limit' => 10, 'offset' => 0, ]; $restfulApi = AlchemistRestfulApi::for(OrderApiQuery::class, $input); // Check if the input parameters are not valid, the validator will collect all errors and return them to you. if (! $restfulApi->validate($errorBag)->passes()) { // var_dump(json_encode($errorBag->getErrors())); echo "validate failed"; die(); }
步骤3:好了,所有输入参数都已经经过仔细验证
最后,您将收到的是来自API客户端的字段、筛选、偏移、搜索和排序参数。所有这些都已经经过仔细验证。
// Get all string fields that user want to retrieve $restfulApi->fieldSelector()->flatFields(); // Get all filtering conditions that client passed in $restfulApi->resourceFilter()->filtering(); $offsetPaginate = $restfulApi->resourceOffsetPaginator()->offsetPaginate(); // Get pagination limit and offset $offsetPaginate->getLimit(); $offsetPaginate->getOffset(); $search = $restfulApi->resourceSearch()->search(); // Get search condition (defined in the API class) and search value (provided by user) $search->getSearchCondition(); $search->getSearchValue(); $sort = $restfulApi->resourceSort()->sort(); // Get sort field and direction $sort->getSortField(); $sort->getDirection();
每个组件的更多说明
以下是您可以使用来构建API接口的每个组件的详细说明。
字段选择器
字段选择器组件允许您的API客户端选择他们想要检索的字段,确保所有检索的字段都在您的控制范围内。
use Nvmcommunity\Alchemist\RestfulApi\AlchemistRestfulApi; $restfulApi = new AlchemistRestfulApi([ // The fields are passed in from the request input, fields are separated by commas, and subsidiary fields // are enclosed in `{}`. 'fields' => 'id,order_date,order_status,order_items{item_id,product_id,price,quantity}' ]); // Call `$restfulApi->fieldSelector()` to start the field selector builder, then your API definitions can be // defined based on the chain of builder. $fieldSelector = $restfulApi->fieldSelector() // If no field is passed up by the API Client, the default field will present. ->defineDefaultFields(['id']) // Your API client can be able to retrieve any fields in the list ->defineFieldStructure([ 'id', 'order_date', 'order_status', 'order_items' => [ 'item_id', 'product_id', 'price', 'quantity' ] ]); // The important thing here is that your API will be rigorously checked by the validator, which will check things like // whether the selected fields are in the list of "selectable" fields, the same for subsidiary fields, and also // whether your API client is selecting subsidiary fields on atomic fields that do not have subsidiary fields, // for example: "id{something}", where id is an atomic field and has no subsidiary fields. if (! $restfulApi->validate($errorBag)->passes()) { // var_dump(json_encode($errorBag->getErrors())); echo "validate failed"; die(); } // Finally, what you will receive by call the `$fieldSelector->fields()` method is a list of field objects, and everything has been carefully checked. // Combine with the use of an ORM/Query Builder $result = ExampleOrderQueryBuilder::select($fieldSelector->flatFields())->get(); // For other purposes, use `$fieldSelector->fields()` to obtain a complete map list of field object. var_dump($fieldSelector->fields());
资源筛选
资源过滤主要关注检查您的API客户端使用的过滤是否在定义的可过滤列表中。
use Nvmcommunity\Alchemist\RestfulApi\AlchemistRestfulApi; use Nvmcommunity\Alchemist\RestfulApi\ResourceFilter\Objects\FilteringRules; use Nvmcommunity\Alchemist\RestfulApi\ResourceFilter\Objects\FilteringOptions; $restfulApi = new AlchemistRestfulApi([ // The filtering are passed in from the request input. // Use a colon `:` to separate filtering and operator. 'filtering' => [ 'order_date:lte' => '2023-02-26', 'product_name:contains' => 'clothes hanger' ] ]); $resourceFilter = $restfulApi->resourceFilter() // Defining options for your API ->defineFilteringOptions(new FilteringOptions([ // Your API client needs to pass order_date filtering with any operation in order to pass the validator 'required' => ['order_date:any'] ])) // Defining filtering for your API ->defineFilteringRules([ // Your API allows filtering by product_name with the operations "eq" and "contains", and the data of the // filtering must be a string type FilteringRules::String('product_name', ['eq', 'contains']), // Your API allows filtering by product_name with the operations "eq", "lte" and "gte", and the data of // the filtering must be a valid date in `Y-m-d` format FilteringRules::Date('order_date', ['eq', 'lte', 'gte'], ['Y-m-d']), // Your API allows filtering by product_id with the operations "eq" and the data of the filtering must // be an integer type FilteringRules::Integer('product_id', ['eq']), // Your API allows filtering by is_best_sales with the operations "eq" and the data of the filtering must // be an integer type with value of: `0` (represent for false) or `1` (represent for true) FilteringRules::Boolean('is_best_sales', ['eq']), ]); // Support for setting a default filtering in case your API client does not pass data to a specific filtering. $resourceFilter->addFilteringIfNotExists('is_best_sales', 'eq', 1); // Validate your API client filtering, the same concept with field selector above if (! $restfulApi->validate($errorBag)->passes()) { // var_dump(json_encode($errorBag->getErrors())); echo "validate failed"; die(); } // And finally, what you will receive is a list of filtering objects, and everything has been carefully checked. // Combine with the use of an ORM/Query Builder $conditions = array_map(static fn($filteringObj) => $filteringObj->flatArray(), $resourceFilter->filtering()); $result = ExampleOrderQueryBuilder::where($conditions)->get(); // For other purposes, use `$resourceFilter->filtering()` to obtain a complete map list of filtering object. var_dump($resourceFilter->filtering());
过滤规则
过滤规则是根据FilteringRules
类定义的,其中包含以下信息
- 过滤名称:API客户端将传递的过滤名称。
- 支持的运算符:API客户端可以使用来过滤数据的运算符列表。
- 数据类型:过滤数据的类型。
支持的过滤规则
// `String` type is a special type that allows you to filter the data based on the string value. FilteringRules::String(string $filtering, array $supportedOperators) // `Integer` type is a special type that allows you to filter the data based on the integer value. FilteringRules::Integer(string $filtering, array $supportedOperators) // `Number` type is a special type that allows you to filter the data based on the numeric value. FilteringRules::Number(string $filtering, array $supportedOperators) // `Date` type allow you to define the date format that your API client can use to filter the data, default format: 'Y-m-d' FilteringRules::Date(string $filtering, array $supportedOperators, array $formats = ['Y-m-d']) // // `Datetime` type allow you to define the date and time format that your API client can use to filter the data, default format: 'Y-m-d H:i:s' FilteringRules::Datetime(string $filtering, array $supportedOperators, array $formats = ['Y-m-d H:i:s']) // `Enum` type is a special type that allows you to define a list of values that your API client can use to filter the data. FilteringRules::Enum(string $filtering, array $supportedOperators, array $enums) // `Boolean` type is a special type that allows you to define the boolean value that your API client can use to filter the data. FilteringRules::Boolean(string $filtering, array $supportedOperators = [])
过滤运算符
在请求输入中使用运算符的过滤可以表示为:<filtering>:<operator>
从请求输入中传递的运算符(请求运算符)将被转换为目标运算符。此表还描述了特殊数据类型(如:between
、not between
、in
、not in
)的过滤值结构。
支持的运算符
(*) 注意这些运算符,因为它们不是任何数据库管理系统的原生运算符;您需要手动处理它们。
资源分页
支持通过偏移量和限制机制进行分页。
$restfulApi = new AlchemistRestfulApi([ // The limit and offset are passed in from the request input. 'limit' => 10, 'offset' => 0, ]); $resourceOffsetPaginator = $restfulApi->resourceOffsetPaginator() // Define default limit for resource ->defineDefaultLimit(10) // Define max limit for resource (set max limit to `0` or not define it will disable max limit) ->defineMaxLimit(1000); // Validate your API client pagination parameters (limit, offset), Check if the offset value passed in is negative // or not, and whether the limit parameter passed in exceeds the max limit (if max limit defined). if (! $resourceOffsetPaginator->validate($notification)->passes()) { // var_dump(json_encode($errorBag->getErrors())); echo "validate failed"; die(); } // Receive an object containing parameters for offset, limit, and max limit. $offsetPaginate = $resourceOffsetPaginator->offsetPaginate(); // Combine with the use of an ORM/Query Builder $result = ExampleOrderQueryBuilder::limit($offsetPaginate->getLimit())->offset($offsetPaginate->getOffset())->get();
资源排序
支持根据API客户端指定的排序和方向灵活返回结果。
$restfulApi = new AlchemistRestfulApi([ // The sort and direction are passed in from the request input. 'sort' => 'id', 'direction' => 'desc', ]); $resourceSort = $restfulApi->resourceSort() // define default sort field ->defineDefaultSort('id') // define default sort direction ->defineDefaultDirection('desc') // define list of field that client able to sort ->defineSortableFields(['id', 'created_at']); // Validate your API client sort parameters (sort, direction), Check if the sort field passed in is in the list of // sortable fields, and whether the direction parameter passed in is valid. if (! $restfulApi->validate($errorBag)->passes()) { // var_dump(json_encode($errorBag->getErrors())); echo "validate failed"; die(); } $sort = $resourceSort->sort(); // Combine with the use of an ORM/Query Builder if (! empty($sort->getSortField())) { ExampleOrderQueryBuilder::orderBy($sort->getSortField(), $sort->getDirection()); }
资源搜索
当通过过滤进行过滤时,API客户端需要清楚地指定过滤标准。然而,在搜索的情况下,API客户端只需要传递要搜索的值,后端将自动从内部定义过滤标准。
$restfulApi = new AlchemistRestfulApi([ // The search are passed in from the request input. 'search' => 'clothes hanger', ]); $resourceSearch = $restfulApi->resourceSearch() // define the search criteria ->defineSearchCondition('product_name'); $search = $resourceSearch->search(); // Combine with the use of an ORM/Query Builder ExampleOrderQueryBuilder::where($search->getSearchCondition(), 'like', "%{$search->getSearchValue()}%");
贡献
有关详细信息,请参阅CONTRIBUTING。
贡献者
代码贡献者
这个项目之所以存在,要归功于所有贡献者。贡献。
许可证
本项目采用MIT许可证。