Skip to content

Commit ceac3b1

Browse files
authored
Merge pull request #4 from ghonijee/hotfix/fixing-filter-relation
Fixing bug filter relation data
2 parents 396910e + f84237d commit ceac3b1

File tree

6 files changed

+133
-30
lines changed

6 files changed

+133
-30
lines changed

Models/TestModel.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ public function comments()
3737
return $this->hasMany(TestComment::class, 'test_model_id');
3838
}
3939

40+
public function dataComments()
41+
{
42+
return $this->hasMany(TestComment::class, 'test_model_id');
43+
}
44+
4045
public function scopeActive(Builder $query)
4146
{
4247
$query->where('active', 1);

src/Builders/FilterQuery.php

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use GhoniJee\DxAdapter\Data\FilterData;
66
use GhoniJee\DxAdapter\FilterClass\BuilderFilterData;
77
use GhoniJee\DxAdapter\FilterClass\BuilderFilterQuery;
8+
use GhoniJee\DxAdapter\FilterClass\BuilderRelationFilterQuery;
89
use Illuminate\Database\Eloquent\Builder;
910
use Illuminate\Database\Eloquent\Relations\Relation;
1011
use Illuminate\Support\Collection;
@@ -21,6 +22,7 @@ protected function parseFilter()
2122
$this->setArray();
2223

2324
$this->buildFilterData();
25+
2426
$this->query = $this->buildFilterQuery($this->query, $this->filter);
2527

2628
return $this;
@@ -38,7 +40,7 @@ private function buildFilterData()
3840
$this->filter = BuilderFilterData::fromRequest($this->filter);
3941
}
4042

41-
private function buildFilterQuery(Builder $query, $collection)
43+
private function buildFilterQuery(Builder &$query, $collection)
4244
{
4345
$collection->each(function ($item) use ($query) {
4446
if (is_string($item)) {
@@ -54,19 +56,7 @@ private function buildFilterQuery(Builder $query, $collection)
5456
}
5557

5658
if ($this->isRelationFilter($query, $item)) {
57-
58-
[$relation,] = collect(explode('.', $item->field))
59-
->pipe(function (Collection $parts) {
60-
return [
61-
$parts->except(count($parts) - 1)->implode('.'),
62-
$parts->last(),
63-
];
64-
});
65-
66-
$this->query->whereHas($relation, function (Builder $relationQuery) use ($item) {
67-
$relationQuery = BuilderFilterQuery::fromDataType($relationQuery, $item, $this->conjungtion)->query();
68-
});
69-
59+
$query = BuilderRelationFilterQuery::fromDataType($query, $item, $this->conjungtion)->query();
7060
return true;
7161
}
7262

@@ -78,16 +68,14 @@ private function buildFilterQuery(Builder $query, $collection)
7868

7969
private function isRelationFilter(Builder $query, FilterData $item)
8070
{
81-
if (!Str::contains($item->field, '.')) {
71+
if (!$item->isRelation) {
8272
return false;
8373
}
8474

85-
$firstRelationship = explode('.', $item->field)[0];
86-
87-
if (!method_exists($query->getModel(), $firstRelationship)) {
75+
if (!method_exists($query->getModel(), $item->relationMethod)) {
8876
return false;
8977
}
9078

91-
return is_a($query->getModel()->{$firstRelationship}(), Relation::class);
79+
return is_a($query->getModel()->{$item->relationMethod}(), Relation::class);
9280
}
9381
}

src/Data/FilterData.php

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
use DateTime;
66
use GhoniJee\DxAdapter\Enums\ValueDataType;
7+
use Illuminate\Support\Collection;
8+
use Illuminate\Support\Str;
79

810
class FilterData
911
{
@@ -15,11 +17,14 @@ class FilterData
1517

1618
public $type;
1719

18-
public $relation;
20+
public $relationMethod;
21+
22+
public bool $isRelation = false;
1923

2024
public function __construct($data)
2125
{
2226
list($this->field, $this->condition, $this->value) = $data;
27+
$this->isRelationFilter();
2328
$this->setValueType();
2429
}
2530

@@ -81,4 +86,18 @@ private function isNumeric()
8186
}
8287
return false;
8388
}
89+
90+
public function isRelationFilter()
91+
{
92+
if (Str::contains($this->field, '.')) {
93+
$this->isRelation = true;
94+
[$this->relationMethod, $this->field] = collect(explode('.', $this->field))
95+
->pipe(function (Collection $parts) {
96+
return [
97+
$parts->except(count($parts) - 1)->implode('.'),
98+
$parts->last(),
99+
];
100+
});
101+
}
102+
}
84103
}

src/FilterClass/BuilderFilterQuery.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public function __construct($query, FilterData $filterData, $conjungtion)
2626
$this->conjungtion = $conjungtion;
2727
}
2828

29-
public static function fromDataType($query, FilterData $filterData, $conjungtion)
29+
public static function fromDataType($query, FilterData $filterData, $conjungtion = null)
3030
{
3131
return new self($query, $filterData, $conjungtion);
3232
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
3+
namespace GhoniJee\DxAdapter\FilterClass;
4+
5+
use Exception;
6+
use GhoniJee\DxAdapter\Data\FilterData;
7+
use GhoniJee\DxAdapter\Enums\ValueDataType;
8+
use GhoniJee\DxAdapter\FilterClass\QueryClass\BooleanFilter;
9+
use GhoniJee\DxAdapter\FilterClass\QueryClass\DateFilter;
10+
use GhoniJee\DxAdapter\FilterClass\QueryClass\NullFilter;
11+
use GhoniJee\DxAdapter\FilterClass\QueryClass\NumericFilter;
12+
use GhoniJee\DxAdapter\FilterClass\QueryClass\StringFilter;
13+
14+
class BuilderRelationFilterQuery
15+
{
16+
protected $query;
17+
18+
protected FilterData $filterData;
19+
20+
protected $conjungtion;
21+
22+
public function __construct($query, FilterData $filterData, $conjungtion)
23+
{
24+
$this->query = $query;
25+
$this->filterData = $filterData;
26+
$this->conjungtion = $conjungtion;
27+
}
28+
29+
public static function fromDataType($query, FilterData $filterData, $conjungtion = null)
30+
{
31+
return new self($query, $filterData, $conjungtion);
32+
}
33+
34+
public function query()
35+
{
36+
switch ($this->conjungtion) {
37+
case '!':
38+
$this->query->whereDoesntHave($this->filterData->relationMethod, function ($relationQuery) {
39+
$relationQuery = BuilderFilterQuery::fromDataType($relationQuery, $this->filterData)->query();
40+
});
41+
break;
42+
case 'or':
43+
$this->query->orWhereHas($this->filterData->relationMethod, function ($relationQuery) {
44+
$relationQuery = BuilderFilterQuery::fromDataType($relationQuery, $this->filterData)->query();
45+
});
46+
break;
47+
default:
48+
$this->query->whereHas($this->filterData->relationMethod, function ($relationQuery) {
49+
$relationQuery = BuilderFilterQuery::fromDataType($relationQuery, $this->filterData)->query();
50+
});
51+
break;
52+
}
53+
return $this->query;
54+
}
55+
}

tests/Feature/FilterTest.php

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@
237237
$query = TestModel::query();
238238
$queryBuilder = DxAdapter::load($query, $this->request)->toSql();
239239
$expected = TestModel::whereHas('comments', function ($queryComment) {
240-
$queryComment->where('comments.comment', 'like', 'ahmad');
240+
$queryComment->where('comment', 'like', 'ahmad');
241241
})->where(function ($q) {
242242
$q->where('active', 0);
243243
$q->orWhere('active', 1);
@@ -247,18 +247,54 @@
247247
});
248248

249249

250-
test('can filter with string params', function () {
251-
$filter = [['comments.comment', 'contains', 'test'], 'and', [['active', '=', 0], 'or', ['active', '=', 1]]];
250+
test('can filter multi relation data with conjungtion OR', function () {
251+
$filter = [[['dataComments.comment', 'contains', 'test'], 'or', ['dataComments.comment', 'contains', 'est']], 'and', ['active', '=', 1]];
252252
$this->request->replace(['filter' => json_encode($filter)]);
253253

254254
$query = TestModel::query();
255255
$queryBuilder = DxAdapter::load($query, $this->request)->toSql();
256-
$expected = TestModel::whereHas('comments', function ($queryComment) {
257-
$queryComment->where('comments.comment', 'like', 'ahmad');
258-
})->where(function ($q) {
259-
$q->where('active', 0);
260-
$q->orWhere('active', 1);
261-
})->toSql();
256+
257+
$expected = TestModel::where(function ($q) {
258+
$q->whereHas('dataComments', function ($queryComment) {
259+
$queryComment->where('comment', 'like', 'test');
260+
})->orWhereHas('dataComments', function ($queryCommentNew) {
261+
$queryCommentNew->where('comment', 'like', 'est');
262+
});
263+
})->where('active', 1)->toSql();
264+
265+
expect($queryBuilder)->toEqual($expected);
266+
});
267+
test('can filter multi relation data with conjungtion AND', function () {
268+
$filter = [[['dataComments.comment', 'contains', 'test'], 'and', ['dataComments.comment', 'contains', 'est']], 'and', ['active', '=', 1]];
269+
$this->request->replace(['filter' => json_encode($filter)]);
270+
271+
$query = TestModel::query();
272+
$queryBuilder = DxAdapter::load($query, $this->request)->toSql();
273+
274+
$expected = TestModel::where(function ($q) {
275+
$q->whereHas('dataComments', function ($queryComment) {
276+
$queryComment->where('comment', 'like', 'test');
277+
})->whereHas('dataComments', function ($queryCommentNew) {
278+
$queryCommentNew->where('comment', 'like', 'est');
279+
});
280+
})->where('active', 1)->toSql();
281+
282+
expect($queryBuilder)->toEqual($expected);
283+
});
284+
test('can filter multi relation data with conjungtion NOT', function () {
285+
$filter = [[['dataComments.comment', 'contains', 'test'], '!', ['dataComments.comment', 'contains', 'est']], 'and', ['active', '=', 1]];
286+
$this->request->replace(['filter' => json_encode($filter)]);
287+
288+
$query = TestModel::query();
289+
$queryBuilder = DxAdapter::load($query, $this->request)->toSql();
290+
291+
$expected = TestModel::where(function ($q) {
292+
$q->whereHas('dataComments', function ($queryComment) {
293+
$queryComment->where('comment', 'like', 'test');
294+
})->whereDoesntHave('dataComments', function ($queryCommentNew) {
295+
$queryCommentNew->where('comment', 'like', 'est');
296+
});
297+
})->where('active', 1)->toSql();
262298

263299
expect($queryBuilder)->toEqual($expected);
264300
});

0 commit comments

Comments
 (0)