Skip to content

Commit 7260a81

Browse files
committed
add params applier
1 parent a81b4bd commit 7260a81

File tree

6 files changed

+214
-19
lines changed

6 files changed

+214
-19
lines changed

src/BuilderParamsApplierTrait.php

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
<?php
2+
3+
namespace LumenApiQueryParser;
4+
5+
use Illuminate\Database\Eloquent\Builder;
6+
use LumenApiQueryParser\Params\Filter;
7+
use LumenApiQueryParser\Params\RequestParamsInterface;
8+
use LumenApiQueryParser\Params\Sort;
9+
10+
trait BuilderParamsApplierTrait
11+
{
12+
public function applyParams(Builder $query, RequestParamsInterface $params)
13+
{
14+
if ($params->hasFilter()) {
15+
foreach ($params->getFilters() as $filter) {
16+
$this->applyFilter($query, $filter);
17+
}
18+
}
19+
20+
if ($params->hasSort()) {
21+
foreach ($params->getSorts() as $sort) {
22+
$this->applySort($query, $sort);
23+
}
24+
}
25+
26+
if ($params->hasPagination()) {
27+
$pagination = $params->getPagination();
28+
$query->limit($pagination->getLimit());
29+
$query->offset($pagination->getPage() * $pagination->getLimit());
30+
}
31+
32+
if ($params->hasConnection()) {
33+
$with = [];
34+
foreach ($params->getConnections() as $connection) {
35+
$with[] = $connection->getName();
36+
}
37+
$query->with($with);
38+
}
39+
40+
return $query->get();
41+
}
42+
43+
protected function applyFilter(Builder $query, Filter $filter): void
44+
{
45+
$table = $query->getModel()->getTable();
46+
$field = sprintf('%s.%s', $table, $filter->getField());
47+
$operator = $filter->getOperator();
48+
$value = $filter->getValue();
49+
$method = 'where';
50+
$clauseOperator = null;
51+
$databaseField = null;
52+
53+
switch ($operator) {
54+
case 'ct':
55+
$value = '%' . $value . '%';
56+
$clauseOperator = 'LIKE';
57+
break;
58+
case 'sw':
59+
$value = $value . '%';
60+
$clauseOperator = 'LIKE';
61+
break;
62+
case 'ew':
63+
$value = '%' . $value;
64+
$clauseOperator = 'LIKE';
65+
break;
66+
case 'eq':
67+
default:
68+
$clauseOperator = '=';
69+
break;
70+
case 'ne':
71+
$clauseOperator = '!=';
72+
break;
73+
case 'gt':
74+
$clauseOperator = '>';
75+
break;
76+
case 'ge':
77+
$clauseOperator = '>=';
78+
break;
79+
case 'bt':
80+
$clauseOperator = 'between';
81+
break;
82+
case 'lt':
83+
$clauseOperator = '<';
84+
break;
85+
case 'le':
86+
$clauseOperator = '<=';
87+
break;
88+
}
89+
90+
if ($operator === 'in') {
91+
call_user_func_array([$query, 'whereIn'], [
92+
$field, $value
93+
]);
94+
} else {
95+
call_user_func_array([$query, $method], [
96+
$field, $clauseOperator, $value
97+
]);
98+
}
99+
}
100+
101+
protected function applySort(Builder $query, Sort $sort)
102+
{
103+
$query->orderBy($sort->getField(), $sort->getDirection());
104+
}
105+
}

src/Params/RequestParams.php

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ public function addFilter(FilterInterface $filter): void
1414
$this->filters[] = $filter;
1515
}
1616

17+
public function hasFilter(): bool
18+
{
19+
return (bool) count($this->filters);
20+
}
21+
1722
public function getFilters(): array
1823
{
1924
return $this->filters;
@@ -24,6 +29,11 @@ public function addSort(SortInterface $sort): void
2429
$this->sorts[] = $sort;
2530
}
2631

32+
public function hasSort(): bool
33+
{
34+
return (bool) count($this->sorts);
35+
}
36+
2737
public function getSorts(): array
2838
{
2939
return $this->sorts;
@@ -34,6 +44,11 @@ public function addPagination(PaginationInterface $pagination): void
3444
$this->pagination = $pagination;
3545
}
3646

47+
public function hasPagination(): bool
48+
{
49+
return $this->pagination !== null;
50+
}
51+
3752
public function getPagination(): PaginationInterface
3853
{
3954
return $this->pagination;
@@ -44,7 +59,12 @@ public function addConnection(ConnectionInterface $connection): void
4459
$this->connections[] = $connection;
4560
}
4661

47-
public function getConnection(): array
62+
public function hasConnection(): bool
63+
{
64+
return (bool) count($this->connections);
65+
}
66+
67+
public function getConnections(): array
4868
{
4969
return $this->connections;
5070
}

src/Params/RequestParamsInterface.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,19 @@
44

55
interface RequestParamsInterface
66
{
7+
public function hasFilter(): bool;
8+
79
public function getFilters(): array;
810

11+
public function hasSort(): bool;
12+
913
public function getSorts(): array;
1014

15+
public function hasPagination(): bool;
16+
1117
public function getPagination(): PaginationInterface;
1218

13-
public function getConnection(): array;
19+
public function hasConnection(): bool;
20+
21+
public function getConnections(): array;
1422
}

src/RequestQueryParser.php

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,12 @@ protected function parseFilters(Request $request): void
3434
{
3535
if ($request->has('filter')) {
3636
foreach ($request->get('filter') as $filter) {
37-
if (!isset($filter['field']) || !isset($filter['value'])) {
37+
$filterDatas = explode(':', $filter);
38+
39+
if (count($filterDatas) < 3) {
3840
throw new UnprocessableEntityHttpException('Filter must contains field and value!');
3941
}
40-
41-
$field = $filter['field'];
42-
$operator = $filter['operator'] ?? 'eq';
43-
$value = $filter['value'];
42+
list($field, $operator, $value) = $filterDatas;
4443

4544
$this->requestParams->addFilter(new Filter($field, $operator, $value));
4645
}
@@ -51,13 +50,12 @@ protected function parseSort(Request $request): void
5150
{
5251
if ($request->has('sort')) {
5352
foreach ($request->get('sort') as $sort) {
54-
if (!isset($sort['field'])) {
53+
list($field, $direction) = explode(':', $sort);
54+
55+
if (empty($field)) {
5556
throw new UnprocessableEntityHttpException('Sort must contains field!');
5657
}
5758

58-
$field = $sort['field'];
59-
$direction = $sort['direction'];
60-
6159
$this->requestParams->addSort(new Sort($field, $direction));
6260
}
6361
}

tests/AbstractQueryParserTest.php

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public function goodRequests(): array
1717
{
1818
return [
1919
[
20-
new Request(['filter' => [['field' => 'name', 'operator' => 'eq', 'value' => 'John Doe']]]),
20+
new Request(['filter' => ['name:eq:John Doe']]),
2121
[
2222
'filters' => [['field' => 'name', 'operator' => 'eq', 'value' => 'John Doe']],
2323
'sorts' => [],
@@ -27,7 +27,7 @@ public function goodRequests(): array
2727
]
2828
],
2929
[
30-
new Request(['sort' => [['field' => 'name', 'direction' => 'DESC']]]),
30+
new Request(['sort' => ['name:DESC']]),
3131
[
3232
'filters' => [],
3333
'sorts' => [['field' => 'name', 'direction' => 'DESC']],
@@ -58,14 +58,14 @@ public function goodRequests(): array
5858
],
5959
[
6060
new Request([
61-
'filter' => [['field' => 'email', 'operator' => 'lk', 'value' => '@gmail.com']],
62-
'sort' => [['field' => 'updated', 'direction' => 'ASC']],
61+
'filter' => ['email:ew:@gmail.com'],
62+
'sort' => ['updated:ASC'],
6363
'limit' => 50,
6464
'page' => 11,
6565
'connection' => ['profile'],
6666
]),
6767
[
68-
'filters' => [['field' => 'email', 'operator' => 'lk', 'value' => '@gmail.com']],
68+
'filters' => [['field' => 'email', 'operator' => 'ew', 'value' => '@gmail.com']],
6969
'sorts' => [['field' => 'updated', 'direction' => 'ASC']],
7070
'limit' => 50,
7171
'page' => 11,
@@ -79,15 +79,15 @@ public function badRequests(): array
7979
{
8080
return [
8181
[
82-
new Request(['filter' => [['field' => 'name']]]),
82+
new Request(['filter' => ['name']]),
8383
UnprocessableEntityHttpException::class
8484
],
8585
[
86-
new Request(['sort' => [['direction' => 'DESC']]]),
86+
new Request(['sort' => [':DESC']]),
8787
UnprocessableEntityHttpException::class
8888
],
8989
[
90-
new Request(['sort' => [[]], 'filter' => [[]]]),
90+
new Request(['sort' => [''], 'filter' => ['']]),
9191
UnprocessableEntityHttpException::class
9292
],
9393
];
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php
2+
3+
namespace Test;
4+
5+
use Illuminate\Database\Eloquent\Builder;
6+
use Illuminate\Database\Eloquent\Model;
7+
use LumenApiQueryParser\BuilderParamsApplierTrait;
8+
use LumenApiQueryParser\Params\Connection;
9+
use LumenApiQueryParser\Params\Filter;
10+
use LumenApiQueryParser\Params\Pagination;
11+
use LumenApiQueryParser\Params\RequestParams;
12+
use LumenApiQueryParser\Params\Sort;
13+
use Mockery;
14+
use PHPUnit\Framework\TestCase;
15+
16+
class BuilderParamsApplierTraitTest extends TestCase
17+
{
18+
use BuilderParamsApplierTrait;
19+
20+
/**
21+
* @test
22+
*/
23+
public function parametersAppliedCorrectly()
24+
{
25+
$mock = Mockery::mock(Builder::class);
26+
$mock->shouldReceive('with')->once()->with(Mockery::mustBe([
27+
'children1', 'children2'
28+
]));
29+
$mock->shouldReceive('orderBy')->once()->with('property', 'ASC');
30+
$mock->shouldReceive('limit')->once()->with(20);
31+
$mock->shouldReceive('offset')->once()->with(40);
32+
$mock->shouldReceive('where')->times(3);
33+
$modelMock = Mockery::mock(Model::class);
34+
$modelMock->shouldReceive('getTable')->times(3)->andReturn('user');
35+
$mock->shouldReceive('getModel')->times(3)->andReturn($modelMock);
36+
$mock->shouldReceive('get')->once()->andReturn(true);
37+
38+
$params = new RequestParams();
39+
$params->addConnection(new Connection('children1'));
40+
$params->addConnection(new Connection('children2'));
41+
$params->addFilter(new Filter('name', 'eq', 'foo'));
42+
$params->addFilter(new Filter('name', 'ct', 'bar'));
43+
$params->addFilter(new Filter('name', 'ne', 'baz'));
44+
$params->addSort(new Sort('property'));
45+
$params->addPagination(new Pagination(20, 2));
46+
47+
$this->assertTrue($this->applyParams($mock, $params));
48+
}
49+
50+
/**
51+
* @test
52+
*/
53+
public function noParamersApplied()
54+
{
55+
$mock = Mockery::mock(Builder::class);
56+
$mock->shouldNotReceive('orderBy');
57+
$mock->shouldNotReceive('limit');
58+
$mock->shouldNotReceive('offset');
59+
$mock->shouldNotReceive('where');
60+
$mock->shouldReceive('get')->once()->andReturn(true);
61+
62+
$this->assertTrue($this->applyParams($mock, new RequestParams()));
63+
}
64+
}

0 commit comments

Comments
 (0)