Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
27.59% covered (danger)
27.59%
8 / 29
0.00% covered (danger)
0.00%
0 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
SearchableTrait
27.59% covered (danger)
27.59%
8 / 29
0.00% covered (danger)
0.00%
0 / 2
47.97
0.00% covered (danger)
0.00%
0 / 1
 handleSearch
47.06% covered (danger)
47.06%
8 / 17
0.00% covered (danger)
0.00%
0 / 1
11.34
 handleSearchWithStatus
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
20
1<?php
2declare(strict_types=1);
3
4namespace App\Controller\Admin\Trait;
5
6use Cake\Http\Response;
7use 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 */
32trait 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}