Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
27.59% |
8 / 29 |
|
0.00% |
0 / 2 |
CRAP | |
0.00% |
0 / 1 |
| SearchableTrait | |
27.59% |
8 / 29 |
|
0.00% |
0 / 2 |
47.97 | |
0.00% |
0 / 1 |
| handleSearch | |
47.06% |
8 / 17 |
|
0.00% |
0 / 1 |
11.34 | |||
| handleSearchWithStatus | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
20 | |||
| 1 | <?php |
| 2 | declare(strict_types=1); |
| 3 | |
| 4 | namespace App\Controller\Admin\Trait; |
| 5 | |
| 6 | use Cake\Http\Response; |
| 7 | use Cake\ORM\Query\SelectQuery; |
| 8 | |
| 9 | /** |
| 10 | * SearchableTrait |
| 11 | * |
| 12 | * Provides common search, pagination, and AJAX handling functionality |
| 13 | * for admin controllers. This trait standardizes the index action pattern |
| 14 | * used across admin controllers. |
| 15 | * |
| 16 | * Usage: |
| 17 | * class MyController extends AppController |
| 18 | * { |
| 19 | * use SearchableTrait; |
| 20 | * |
| 21 | * public function index(): ?Response |
| 22 | * { |
| 23 | * $query = $this->MyModel->find(); |
| 24 | * return $this->handleSearch( |
| 25 | * $query, |
| 26 | * 'myEntities', |
| 27 | * ['title', 'description'] // searchable fields |
| 28 | * ); |
| 29 | * } |
| 30 | * } |
| 31 | */ |
| 32 | trait SearchableTrait |
| 33 | { |
| 34 | /** |
| 35 | * Handle common search, pagination, and AJAX response pattern |
| 36 | * |
| 37 | * @param \Cake\ORM\Query\SelectQuery $query The base query |
| 38 | * @param string $variableName The variable name to set for the view (e.g., 'articles') |
| 39 | * @param array<string> $searchFields Fields to search in (e.g., ['title', 'body']) |
| 40 | * @param string|null $tableName Table alias for field prefixing (auto-detected if null) |
| 41 | * @param string $searchResultsTemplate Template name for AJAX results |
| 42 | * @return \Cake\Http\Response|null |
| 43 | */ |
| 44 | protected function handleSearch( |
| 45 | SelectQuery $query, |
| 46 | string $variableName, |
| 47 | array $searchFields, |
| 48 | ?string $tableName = null, |
| 49 | string $searchResultsTemplate = 'search_results', |
| 50 | ): ?Response { |
| 51 | // Auto-detect table name from controller name if not provided |
| 52 | if ($tableName === null) { |
| 53 | $tableName = $this->getName(); |
| 54 | } |
| 55 | |
| 56 | // Apply search filter |
| 57 | $search = $this->request->getQuery('search'); |
| 58 | if (!empty($search)) { |
| 59 | $orConditions = []; |
| 60 | foreach ($searchFields as $field) { |
| 61 | // If field already has table prefix, use as-is |
| 62 | if (str_contains($field, '.')) { |
| 63 | $orConditions[$field . ' LIKE'] = '%' . $search . '%'; |
| 64 | } else { |
| 65 | $orConditions[$tableName . '.' . $field . ' LIKE'] = '%' . $search . '%'; |
| 66 | } |
| 67 | } |
| 68 | $query->where(['OR' => $orConditions]); |
| 69 | } |
| 70 | |
| 71 | // Paginate results |
| 72 | $results = $this->paginate($query); |
| 73 | |
| 74 | // Handle AJAX request |
| 75 | if ($this->request->is('ajax')) { |
| 76 | $this->set([$variableName => $results, 'search' => $search]); |
| 77 | $this->viewBuilder()->setLayout('ajax'); |
| 78 | |
| 79 | return $this->render($searchResultsTemplate); |
| 80 | } |
| 81 | |
| 82 | // Normal request |
| 83 | $this->set([$variableName => $results]); |
| 84 | |
| 85 | return null; |
| 86 | } |
| 87 | |
| 88 | /** |
| 89 | * Handle common search with status filter pattern |
| 90 | * |
| 91 | * @param \Cake\ORM\Query\SelectQuery $query The base query |
| 92 | * @param string $variableName The variable name to set for the view |
| 93 | * @param array<string> $searchFields Fields to search in |
| 94 | * @param string $statusField The field name for status filtering |
| 95 | * @param string|null $tableName Table alias for field prefixing |
| 96 | * @param string $searchResultsTemplate Template name for AJAX results |
| 97 | * @return \Cake\Http\Response|null |
| 98 | */ |
| 99 | protected function handleSearchWithStatus( |
| 100 | SelectQuery $query, |
| 101 | string $variableName, |
| 102 | array $searchFields, |
| 103 | string $statusField = 'is_published', |
| 104 | ?string $tableName = null, |
| 105 | string $searchResultsTemplate = 'search_results', |
| 106 | ): ?Response { |
| 107 | // Auto-detect table name from controller name if not provided |
| 108 | if ($tableName === null) { |
| 109 | $tableName = $this->getName(); |
| 110 | } |
| 111 | |
| 112 | // Apply status filter |
| 113 | $statusFilter = $this->request->getQuery('status'); |
| 114 | if ($statusFilter !== null && $statusFilter !== '') { |
| 115 | $query->where([$tableName . '.' . $statusField => (int)$statusFilter]); |
| 116 | } |
| 117 | |
| 118 | return $this->handleSearch( |
| 119 | $query, |
| 120 | $variableName, |
| 121 | $searchFields, |
| 122 | $tableName, |
| 123 | $searchResultsTemplate, |
| 124 | ); |
| 125 | } |
| 126 | } |