Skip to content

Commit a00a19f

Browse files
committed
Add additional backend filtering
1 parent e04d094 commit a00a19f

3 files changed

Lines changed: 161 additions & 28 deletions

File tree

ProcessMaker/Filters/Filter.php

Lines changed: 70 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22

33
namespace ProcessMaker\Filters;
44

5+
use Illuminate\Database\Eloquent\Builder;
56
use Illuminate\Support\Arr;
7+
use ProcessMaker\Models\ProcessRequestToken;
8+
use ProcessMaker\Models\User;
69

7-
// Represents a subject, operator, and value
810
class Filter
911
{
1012
const TYPE_PARTICIPANTS = 'Participants';
@@ -13,6 +15,8 @@ class Filter
1315

1416
const TYPE_FIELD = 'Field';
1517

18+
const TYPE_PROCESS = 'Process';
19+
1620
public string|null $subjectValue;
1721

1822
public string $subjectType;
@@ -23,7 +27,7 @@ class Filter
2327

2428
public array $or;
2529

26-
public static function filter($query, string $filterDefinitions)
30+
public static function filter(Builder $query, string $filterDefinitions)
2731
{
2832
$filterDefinitions = json_decode($filterDefinitions, true);
2933
$query->where(function ($query) use ($filterDefinitions) {
@@ -42,7 +46,7 @@ public function __construct($definition)
4246
$this->or = Arr::get($definition, 'or', []);
4347
}
4448

45-
public function addToQuery($query)
49+
public function addToQuery(Builder $query)
4650
{
4751
if (!empty($this->or)) {
4852
$query->where(function ($query) {
@@ -55,10 +59,10 @@ public function addToQuery($query)
5559

5660
private function apply($query)
5761
{
58-
if ($this->subjectType === self::TYPE_PARTICIPANTS) {
59-
$this->participants($query);
60-
} elseif ($this->subjectType === self::TYPE_STATUS) {
61-
$this->status($query);
62+
if ($valueAliasMethod = $this->valueAliasMethod()) {
63+
$this->valueAliasAdapter($valueAliasMethod, $query);
64+
} elseif ($this->subjectType === self::TYPE_PROCESS) {
65+
$this->filterByProcessId($query);
6266
} else {
6367
$this->applyQueryBuilderMethod($query);
6468
}
@@ -133,6 +137,10 @@ private function subject()
133137
return 'user_id';
134138
}
135139

140+
if ($this->subjectType === self::TYPE_PROCESS) {
141+
return 'process_id';
142+
}
143+
136144
return $this->subjectValue;
137145
}
138146

@@ -149,29 +157,69 @@ private function value()
149157
return $this->value;
150158
}
151159

152-
private function participants($query)
160+
/**
161+
* Forward Status and Participant subjects to PMQL methods on the models.
162+
*
163+
* For now, we only need Participants and Status because Request and Requester
164+
* are columns on the tables (process_request_id and user_id).
165+
*/
166+
private function valueAliasMethod()
153167
{
154-
$query->whereIn('id', function ($subQuery) {
155-
$subQuery->select('process_request_id')->from('process_request_tokens')
156-
->whereIn('element_type', ['task', 'userTask', 'startEvent']);
157-
$this->applyQueryBuilderMethod($subQuery);
158-
});
159-
}
160-
161-
private function taskStatus($query)
162-
{
163-
}
168+
$method = null;
164169

165-
private function valueAliasAdapter($query)
166-
{
167-
$expression = (object) ['operator' => $this->operator];
168170
switch ($this->subjectType) {
169171
case self::TYPE_PARTICIPANTS:
170-
$method = 'valueAliasParticipants';
172+
$method = 'valueAliasParticipant';
171173
break;
172174
case self::TYPE_STATUS:
173175
$method = 'valueAliasStatus';
174176
break;
175177
}
178+
179+
return $method;
180+
}
181+
182+
private function valueAliasAdapter(string $method, Builder $query)
183+
{
184+
$operator = $this->operator();
185+
if ($operator === 'in') {
186+
$operator = '=';
187+
}
188+
$values = (array) $this->value();
189+
190+
$expression = (object) ['operator' => $operator];
191+
192+
$model = $query->getModel();
193+
194+
if ($method === 'valueAliasParticipant') {
195+
$values = $this->convertUserIdsToUsernames($values);
196+
}
197+
198+
foreach ($values as $i => $value) {
199+
if ($i === 0) {
200+
$query->where($model->$method($value, $expression));
201+
} else {
202+
$query->orWhere($model->$method($value, $expression));
203+
}
204+
}
205+
}
206+
207+
private function convertUserIdsToUsernames($values)
208+
{
209+
return array_map(function ($value) {
210+
return User::find($value)?->username;
211+
}, $values);
212+
}
213+
214+
private function filterByProcessId(Builder $query)
215+
{
216+
if ($query->getModel() instanceof ProcessRequestToken) {
217+
$query->whereIn('process_request_id', function ($query) {
218+
$query->select('id')->from('process_requests')
219+
->whereIn('process_id', (array) $this->value());
220+
});
221+
} else {
222+
$this->applyQueryBuilderMethod($query);
223+
}
176224
}
177225
}

ProcessMaker/Models/ProcessRequest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -698,7 +698,7 @@ private function valueAliasRequester($value, $expression)
698698
*
699699
* @return callable
700700
*/
701-
private function valueAliasParticipant($value, $expression)
701+
public function valueAliasParticipant($value, $expression)
702702
{
703703
$user = User::where('username', $value)->get()->first();
704704

tests/unit/ProcessMaker/FilterTest.php

Lines changed: 90 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,17 @@
22

33
namespace Tests;
44

5+
use Illuminate\Support\Facades\Auth;
56
use ProcessMaker\Filters\Filter;
67
use ProcessMaker\Models\ProcessRequest;
8+
use ProcessMaker\Models\ProcessRequestToken;
9+
use ProcessMaker\Models\User;
710

811
class FilterTest extends TestCase
912
{
10-
private function filter($filterDefinition)
13+
private function filter($filterDefinition, $model = ProcessRequest::class)
1114
{
12-
$query = ProcessRequest::query();
15+
$query = $model::query();
1316
Filter::filter($query, json_encode($filterDefinition));
1417

1518
return $query->toRawSql();
@@ -104,23 +107,105 @@ public function testAdditionalOperators()
104107

105108
public function testParticipants()
106109
{
110+
$user1 = User::factory()->create();
111+
$user2 = User::factory()->create();
112+
$user3 = User::factory()->create();
113+
107114
$sql = $this->filter([
108115
[
109116
'subject' => ['type' => 'Participants'],
110117
'operator' => 'in',
111-
'value' => [1, 2, 3],
118+
'value' => [$user1->id, $user2->id],
112119
'or' => [
113120
[
114121
'subject' => ['type' => 'Participants'],
115122
'operator' => '=',
116-
'value' => 5,
123+
'value' => $user3->id,
117124
],
118125
],
119126
],
120127
]);
121128

122129
$this->assertEquals(
123-
"select * from `process_requests` where ((`id` in (select `process_request_id` from `process_request_tokens` where `element_type` in ('task', 'userTask', 'startEvent') and `user_id` in (1, 2, 3)) or (`id` in (select `process_request_id` from `process_request_tokens` where `element_type` in ('task', 'userTask', 'startEvent') and `user_id` = 5))))",
130+
'select * from `process_requests` where (((' .
131+
"`id` in (select `process_request_id` from `process_request_tokens` where `user_id` = {$user1->id} and `element_type` in ('task', 'userTask', 'startEvent'))) " .
132+
"or (`id` in (select `process_request_id` from `process_request_tokens` where `user_id` = {$user2->id} and `element_type` in ('task', 'userTask', 'startEvent'))) " .
133+
"or ((`id` in (select `process_request_id` from `process_request_tokens` where `user_id` = {$user3->id} and `element_type` in ('task', 'userTask', 'startEvent'))))))",
134+
$sql
135+
);
136+
}
137+
138+
public function testRequestStatus()
139+
{
140+
$sql = $this->filter([
141+
[
142+
'subject' => ['type' => 'Status'],
143+
'operator' => 'in',
144+
'value' => ['In Progress', 'Completed'],
145+
],
146+
]);
147+
148+
$this->assertEquals(
149+
"select * from `process_requests` where ((`status` = 'ACTIVE') or (`status` = 'COMPLETED'))",
150+
$sql
151+
);
152+
}
153+
154+
public function testTaskStatus()
155+
{
156+
$user = User::factory()->create();
157+
158+
$selfServiceTask = ProcessRequestToken::factory()->create([
159+
'is_self_service' => true,
160+
'status' => 'ACTIVE',
161+
'user_id' => null,
162+
'self_service_groups' => ['users' => [$user->id]],
163+
]);
164+
165+
Auth::shouldReceive('user')->andReturn($user);
166+
167+
$sql = $this->filter([
168+
[
169+
'subject' => ['type' => 'Status'],
170+
'operator' => '=',
171+
'value' => 'Self Service',
172+
],
173+
], ProcessRequestToken::class);
174+
175+
$this->assertEquals(
176+
"select * from `process_request_tokens` where ((`id` in ({$selfServiceTask->id})))",
177+
$sql
178+
);
179+
}
180+
181+
public function testRequestProcess()
182+
{
183+
$sql = $this->filter([
184+
[
185+
'subject' => ['type' => 'Process'],
186+
'operator' => 'in',
187+
'value' => [5, 6],
188+
],
189+
]);
190+
191+
$this->assertEquals(
192+
'select * from `process_requests` where (`process_id` in (5, 6))',
193+
$sql
194+
);
195+
}
196+
197+
public function testTaskProcess()
198+
{
199+
$sql = $this->filter([
200+
[
201+
'subject' => ['type' => 'Process'],
202+
'operator' => '=',
203+
'value' => 5,
204+
],
205+
], ProcessRequestToken::class);
206+
207+
$this->assertEquals(
208+
'select * from `process_request_tokens` where (`process_request_id` in (select `id` from `process_requests` where `process_id` in (5)))',
124209
$sql
125210
);
126211
}

0 commit comments

Comments
 (0)