Skip to content

Commit 0cb38e6

Browse files
authored
Merge pull request #8781 from ProcessMaker/bugfix/FOUR-30703
FOUR-30703 | Save Search Shows “Error Loading Items” in Available Columns
2 parents 16a066c + 212c11d commit 0cb38e6

2 files changed

Lines changed: 118 additions & 6 deletions

File tree

ProcessMaker/Models/ProcessRequestToken.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -640,21 +640,21 @@ public function scopeFilter($query, $filter)
640640
$setting = Setting::byKey('indexed-search');
641641
if ($setting && $setting->config['enabled'] === true) {
642642
if (is_numeric($filter)) {
643-
$query->whereIn('id', [$filter]);
643+
$query->whereIn('process_request_tokens.id', [$filter]);
644644
} else {
645645
$matches = self::search($filter)->take(10000)->get()->pluck('id');
646-
$query->whereIn('id', $matches);
646+
$query->whereIn('process_request_tokens.id', $matches);
647647
}
648648
} else {
649649
$filter = '%' . mb_strtolower($filter) . '%';
650650
$query->where(function ($query) use ($filter) {
651651
$query->where(DB::raw('LOWER(element_name)'), 'like', $filter)
652652
->orWhere(DB::raw('LOWER(data)'), 'like', $filter)
653653
->orWhere(DB::raw('LOWER(status)'), 'like', $filter)
654-
->orWhere('id', 'like', $filter)
655-
->orWhere('created_at', 'like', $filter)
656-
->orWhere('due_at', 'like', $filter)
657-
->orWhere('updated_at', 'like', $filter)
654+
->orWhere('process_request_tokens.id', 'like', $filter)
655+
->orWhere('process_request_tokens.created_at', 'like', $filter)
656+
->orWhere('process_request_tokens.due_at', 'like', $filter)
657+
->orWhere('process_request_tokens.updated_at', 'like', $filter)
658658
->orWhereHas('processRequest', function ($query) use ($filter) {
659659
$query->where(DB::raw('LOWER(name)'), 'like', $filter);
660660
})

tests/Feature/Api/TasksTest.php

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
use ProcessMaker\Models\ProcessTaskAssignment;
2121
use ProcessMaker\Models\Screen;
2222
use ProcessMaker\Models\ScreenVersion;
23+
use ProcessMaker\Models\Setting;
2324
use ProcessMaker\Models\User;
2425
use ProcessMaker\Notifications\ActivityActivatedNotification;
2526
use ProcessMaker\Providers\AuthServiceProvider;
@@ -913,4 +914,115 @@ public function testGetAllTaskTypes()
913914
$this->assertEquals($scriptTask->id, $response['data'][1]['id']);
914915
$this->assertEquals($gatewayTask->id, $response['data'][2]['id']);
915916
}
917+
918+
/**
919+
* Test that the free-text filter parameter works on the tasks endpoint.
920+
*/
921+
public function testFilterByFreeTextSearch()
922+
{
923+
$request = ProcessRequest::factory()->create();
924+
925+
$matchingTask = ProcessRequestToken::factory()->create([
926+
'element_type' => 'task',
927+
'element_name' => 'UniqueSearchableTaskName',
928+
'status' => 'ACTIVE',
929+
'process_request_id' => $request->id,
930+
]);
931+
932+
ProcessRequestToken::factory()->create([
933+
'element_type' => 'task',
934+
'element_name' => 'OtherTask',
935+
'status' => 'ACTIVE',
936+
'process_request_id' => $request->id,
937+
]);
938+
939+
$response = $this->apiCall('GET', route('api.' . $this->resource . '.index', [
940+
'filter' => 'UniqueSearchableTaskName',
941+
'per_page' => 50,
942+
]));
943+
944+
$response->assertStatus(200);
945+
$data = $response->json('data');
946+
$this->assertNotEmpty($data);
947+
$this->assertTrue(
948+
collect($data)->contains('id', $matchingTask->id),
949+
'Expected matching task to appear in filtered results'
950+
);
951+
$this->assertFalse(
952+
collect($data)->contains('element_name', 'OtherTask'),
953+
'Expected non-matching task to be excluded from filtered results'
954+
);
955+
}
956+
957+
/**
958+
* Build a query that joins process_request_tokens to itself via a
959+
* subquery aliased `cte`, matching the pattern used by the Saved Search
960+
* package. Any WHERE clauses added afterwards must use qualified column
961+
* names or they will hit an "ambiguous column" error.
962+
*/
963+
private function buildJoinedSubquery()
964+
{
965+
$query = ProcessRequestToken::query()->where('element_type', 'task');
966+
967+
$subQuery = clone $query;
968+
$subQuery->select('id', 'updated_at')->latest('updated_at')->take(25);
969+
970+
$query->select('process_request_tokens.*')
971+
->joinSub($subQuery, 'cte', function ($join) {
972+
$join->on('process_request_tokens.id', '=', 'cte.id');
973+
});
974+
975+
return $query;
976+
}
977+
978+
/**
979+
* Test that scopeFilter's non-indexed branch works when the query has
980+
* a join that introduces duplicate column names (Saved Search pattern).
981+
*/
982+
public function testScopeFilterWorksWithJoinedSubquery()
983+
{
984+
$request = ProcessRequest::factory()->create();
985+
986+
ProcessRequestToken::factory()->create([
987+
'element_type' => 'task',
988+
'element_name' => 'JoinTestTask',
989+
'status' => 'ACTIVE',
990+
'process_request_id' => $request->id,
991+
]);
992+
993+
$query = $this->buildJoinedSubquery();
994+
$query->filter('JoinTestTask');
995+
996+
$results = $query->get();
997+
998+
$this->assertNotEmpty($results);
999+
$this->assertTrue($results->contains('element_name', 'JoinTestTask'));
1000+
}
1001+
1002+
/**
1003+
* Test that scopeFilter's indexed-search branch (numeric filter path)
1004+
* uses a qualified `id` column so it works with the Saved Search join.
1005+
*/
1006+
public function testScopeFilterIndexedSearchNumericWorksWithJoinedSubquery()
1007+
{
1008+
Setting::updateOrCreate(
1009+
['key' => 'indexed-search'],
1010+
['config' => ['enabled' => true]]
1011+
);
1012+
1013+
$request = ProcessRequest::factory()->create();
1014+
1015+
$task = ProcessRequestToken::factory()->create([
1016+
'element_type' => 'task',
1017+
'status' => 'ACTIVE',
1018+
'process_request_id' => $request->id,
1019+
]);
1020+
1021+
$query = $this->buildJoinedSubquery();
1022+
$query->filter((string) $task->id);
1023+
1024+
$results = $query->get();
1025+
1026+
$this->assertTrue($results->contains('id', $task->id));
1027+
}
9161028
}

0 commit comments

Comments
 (0)