mdantas / express-request
CakePHP 的 ExpressRequest 插件
Requires
- php: >=7.4
- ext-json: *
- cakephp/cakephp: >3.2
Requires (Dev)
- phpunit/phpunit: ^9
README
关于此包的一些内容
这个包在查询 URL 方面帮助我很多,也许它也会帮到你。我经常在尝试制作 API 时,试图制作复杂的 URL 和查询字符串参数,我感到很沮丧,因为有些东西没有意义。在 poc 和 poc 以及好伴侣的帮助下,我得出以下结论。
这个包如何帮助你
与其制作越来越复杂的 URL 查询参数或许多端点,不如尝试制作真正适合客户的需求模型,进入代码。
安装
您可以使用 composer 将此插件安装到您的 CakePHP 应用程序中。
安装 composer 包的推荐方法是
composer require mdantas/express-request
将插件添加到我们的 Application.php
public function bootstrap(): void { //... other codes. $this->addPlugin('ExpressRequest'); }
在模型上表达过滤条件
我们的表对象需要实现 ExpressRepositoryInterface
并创建新方法。
//Model/Table/DomainsTable.php class DomainsTable extends Table implements ExpressRepositoryInterface { //...code... public function getFilterable(): FiltersCollection { return new FiltersCollection([ new BooleanFilter('active'), new SearchFilter('name', SearchFilter::START_STRATEGY), new SearchDateFilter('created_at'), new NumberFilter('price'), new SearchInFilter('type') ]); } public function getSelectables(): array { return [ 'name', 'created_at', 'price', 'type', 'active' ]; } }
控制器
在 Controller/AppController.php
中加载名为: ExpressRequest.ExpressParams
的组件,现在,向我们的 DomainsController 添加一些代码。
//Controller/DomainsController.php public function index() { // The finder need to be a method what return a Cake\ORM\Query object. return $this ->responseJson( $this->ExpressRequest->search( $this->request, $this->Domains, 'findDomainsByCompany', // Finder model method optional $companyId // Optional ) ); } public function anotherWay() { // We can make our filtering by request expressions $collection = $this->ExpressRequest->search( $this->request, $this->Domains ); $query = $collection->getQuery(); // And can implement our needed things above user api expressions. $query->where(['id' => 1]); return $this->responseJson($collection); } public function alternative() { // Request and Model now is taked from controller. // Need status code // Return a ResponseInterface from psr with json data on body. return this->ExpressRequest->getResponse(200); }
*当然,别忘了为这个控制器添加路由。
//routes.php $routes->scope('/', function (RouteBuilder $builder) { $builder->get('/domains', ['controller' => 'Domains', 'action' => 'index']); }
请求
让我们看看您的 /domains
端点现在对请求/资源更友好。
在浏览器中打开: https://:8765/domains?price=140..3000&sort[price]=asc&type[not]=profit&size=1
// https://:8765/domains?price=140..3000&sort[price]=asc&type[not]=profit&size=1 { "data": [ { "id": "dee7b40b-df70-33b7-90e0-1d13a4b13693", "name": "Azevedo e Pacheco", "company_id": "2130a414-828a-4ae3-a3a6-5f153fe25ad8", "city_id": 2507705, "created_at": "2010-02-12T21:18:44+00:00", "modified_at": null, "price": "291.600", "type": "purchase" } ], "meta": { "total": 103, "per_page": 1, "current_page": 1, "last_page": 103, "first_page_url": "https://:8765/domains?price=140..3000&sort%5Bprice%5D=asc&type%5Bnot%5D=profit&size=1&page=1", "next_page_url": "https://:8765/domains?price=140..3000&sort%5Bprice%5D=asc&type%5Bnot%5D=profit&size=1&page=2", "last_page_url": "https://:8765/domains?price=140..3000&sort%5Bprice%5D=asc&type%5Bnot%5D=profit&size=1&page=103", "prev_page_url": null, "path": "https://:8765/domains", "from": 1, "to": 1 } }
详细信息
我刚才提到这个帮助了我很多,现在,看看这些内容是如何工作的。
每个请求都在 ExpressRequest.ExpressParams
上进行处理。该组件试图理解请求需要什么,并借助模型生成响应、一些操作或用户可以引入的简单请求错误。因为请求错误,所以模型向组件表达它能做什么,如果它不能,则不会发生任何事情。想想看,如果我们尝试在布尔类型的数据中搜索 'A' 会怎样?因此,实现了一些类型过滤。
仅选择字段。
// localhost/domains?props=name,price,created_at - Select only this three fields.
数据排序
对于每个字段,有两个值:asc,desc。
// localhost/domains?sort[name]=asc // localhost/domains?sort[name]=asc&sort[price]=desc
包含数据
有时我们需要检索具有关联的数据,当然,这很容易。
// https://:8765/domains?nested=users,comments //Get data with relationship
需要向相关数据添加复杂或条件,简单的答案是:您不能,也不应该尝试。如果您需要一些复杂的相关数据,请参阅:CakePHP 文档
过滤器
强大且安全的过滤器
布尔过滤器
通过布尔值进行过滤?例如 'true', 'false', '1', '0'
数字过滤器
可以通过一些帮助过滤数据。
// localhost/domains?price=100 - Exact by 100 // localhost/domains?price=100..200 - Between 100 and 200 // localhost/domains?price[lt|gt|lte|gte]=100 - filter less, great, less than or great than.
空值过滤器
此过滤器简单地执行查询中的 is 或 is not null。
new SearchFilter('name') // localhost/domains?name=null - WHERE name IS NULL // localhost/domains?name[is]=null - WHERE name IS NULL // localhost/domains?name[not]=null - WHERE name IS NOT NULL
搜索过滤器
此过滤器有四种工作方法
class SearchFilter implements FilterTypeInterface { use ProcessableFilterTrait; const PARTIAL_STRATEGY = 'partial'; const START_STRATEGY = 'start'; const END_STRATEGY = 'end'; const EXACT_STRATEGY = 'exact'; ...code }
new SearchFilter('name') - Exact is default // localhost/domains?name=marcos - Exact by marcos new SearchFilter('name', SearchFilter::PARTIAL_STRATEGY) // localhost/domains?name=marcos - add a %value% by like method. new SearchFilter('name', SearchFilter::START_STRATEGY) // localhost/domains?name=marcos - add a value% by like method. new SearchFilter('name', SearchFilter::END_STRATEGY) // localhost/domains?name=marcos - add a %value by like method.
搜索过滤器
通过值或组值进行过滤。
// localhost/domains?name=marcos - name in('marcos') // localhost/domains?name=marcos,github - name in('marcos', 'github') // localhost/domains?name[not]=marcos,github - name not in('marcos', 'github')
日期搜索过滤器
2.0.1 可以使用 [gte, gt, lt, lte]
操作符。
// localhost/domains?created_at=2019 - by init of 2019 year. // localhost/domains?created_at=2019-01 - by init of Jan/2019 year. // localhost/domains?created_at=201903 - by init of Mar/2019 year. // localhost/domains?created_at=2019-01-12 - by day 12 of Jan/2019 year. // localhost/domains?created_at=20190315 - by day 15 of Mar/2019 year. // localhost/domains?created_at[lte]=20190315 - search by operators: [lt, lte, gt, gte] // localhost/domains?created_at[lte]=20190315&created_at[gte]=20190315 - search by operators: [lt, lte, gt, gte]
自定义过滤器?
通过实现 FilterTypeInterface
并如果您想要 ProcessableFilterTrait
,您可以为所需的内容创建一个过滤器。
关于组件
组件有一系列配置值,用于处理查询参数,如果没有 reserved
键,则尝试过滤内容,如果模型接受它,则执行 URL 查询
[ 'pagination' => true, 'maxSize' => 100, // Max number of per page. 'size' => 20, // default numbers of items per page 'ssl' => true, // generate routes with ssl by default, 'cacheConfig' => 'default', // cache config 'cache' => true, // Enable or disable cache 'reserved' => [ // If you need to use one o more of this keywords, change to alias. 'size' => 'size', 'page' => 'page', 'props' => 'props', 'nested' => 'nested', 'sort' => 'sort' ] ];
更改一些配置...
//Controller/AppController.php public function initialize() { // ...code $this->loadComponent('ExpressRequest.ExpressRequest', [ 'ssl' => false, 'maxSize' => 30, 'reserved' => [ 'props' => 'fields' ] ]); }