Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 101
0.00% covered (danger)
0.00%
0 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
Controller
0.00% covered (danger)
0.00%
0 / 101
0.00% covered (danger)
0.00%
0 / 6
1332
0.00% covered (danger)
0.00%
0 / 1
 searchQuery
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
30
 searchOnFields
0.00% covered (danger)
0.00%
0 / 31
0.00% covered (danger)
0.00%
0 / 1
90
 getSearchValue
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
20
 sortQuery
0.00% covered (danger)
0.00%
0 / 29
0.00% covered (danger)
0.00%
0 / 1
182
 paginate
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
6
 setTheme
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2
3namespace App\Http\Controllers;
4
5use App\Enums\Pagination\ItemsPerPaginationEnum;
6use App\Enums\Theme\BootstrapThemeEnum;
7use App\Lib\Helpers\ToolboxHelper;
8use Illuminate\Database\Eloquent\Builder;
9use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
10use Illuminate\Foundation\Validation\ValidatesRequests;
11use Illuminate\Http\Request;
12use Illuminate\Routing\Controller as BaseController;
13use Illuminate\Support\Facades\Cache;
14use Illuminate\Support\Facades\Schema;
15use Illuminate\Support\Facades\Session;
16use Illuminate\Support\Str;
17
18class Controller extends BaseController
19{
20    use AuthorizesRequests;
21    use ValidatesRequests;
22
23    /**
24     * Build Search query on specified fields
25     * splitting search into words or using whole sentence
26     *
27     * @param \Illuminate\Database\Eloquent\Builder $query       The eloquent query builder.
28     * @param string                                $search      The query string.
29     * @param callable                              $searchQuery The query string.
30     * @param string                                ...$fields   Either a simple array or a string
31     *                                                           in case of a string the two fields
32     *                                                           would be concatenate to make the search.
33     * @return \Illuminate\Database\Eloquent\Builder
34     */
35    protected function searchQuery(
36        Builder $query,
37        string $search,
38        callable $searchQuery = null,
39        string ...$fields
40    ): \Illuminate\Database\Eloquent\Builder {
41        if (count($fields)) {
42            // Search using words.
43            $words = explode(' ', $search);
44            $query->where(function ($query) use ($searchQuery, $words, $search, $fields) {
45                foreach ($words as $word) {
46                    if (!strlen($word)) {
47                        continue;
48                    }
49                    $this->searchOnFields($query, $word, $fields);
50                } //end foreach
51                // Search whole using whole sentence.
52                $this->searchOnFields($query, $search, $fields);
53                if ($searchQuery) {
54                    $searchQuery($query);
55                }
56            });
57        } //end if
58        return $query;
59    }
60
61    /**
62     * Build query search on all fields
63     *
64     * @param \Illuminate\Database\Eloquent\Builder $query
65     * @param string                                $search
66     * @param array                                 $fields
67     * @return \Illuminate\Database\Eloquent\Builder
68     */
69    private function searchOnFields(
70        Builder $query,
71        string $search,
72        array $fields
73    ): \Illuminate\Database\Eloquent\Builder {
74        $table = $query->getModel()->getTable();
75        foreach ($fields as $field) {
76            $field = str_replace('?', '\?', $field);
77            if (is_array($field)) {
78                $query->orWhereRaw(
79                    sprintf("UPPER(CONCAT(%s)) LIKE UPPER(?)", collect($field)->map(function ($q) use ($table) {
80                        return "`$table`.`$q`";
81                    })->implode(', \' \', ')),
82                    ['%' . htmlspecialchars($search) . '%']
83                );
84                return $query;
85            }
86            // Get models woth enum's label.
87            if (is_array($this->getSearchValue($query, $search, $field))) {
88                foreach ($this->getSearchValue($query, $search, $field) as $array) {
89                    $query->orWhereRaw(
90                        "UPPER(`$table`.`$field`) LIKE UPPER(?)",
91                        ['%' . htmlspecialchars($array->value) . '%']
92                    );
93                }
94                return $query;
95            }
96            // Get models with relation's name/label.
97            if (Str::endsWith($field, '_id') && $query->getModel()->has(Str::remove('_id', $field))) {
98                $query->orWhereHas(Str::remove('_id', $field), function ($queryRelation) use ($field, $search) {
99                    if (Schema::hasColumn(Str::remove('_id', $field) . 's', 'name')) {
100                        $queryRelation->where('name', 'like', '%' . $search . '%');
101                    } elseif (Schema::hasColumn(Str::remove('_id', $field) . 's', 'label')) {
102                        $queryRelation->where('label', 'like', '%' . $search . '%');
103                    }
104                });
105                return $query;
106            }
107            $query->orWhereRaw(
108                "UPPER(`$table`.`$field`) LIKE UPPER(?)",
109                ['%' . htmlspecialchars($this->getSearchValue($query, $search, $field)) . '%']
110            );
111        } //end foreach
112        return $query;
113    }
114
115    /**
116     * Get the search value (on string or Enum).
117     *
118     * @param \Illuminate\Database\Eloquent\Builder $query
119     * @param string                                $search
120     * @param string                                $field
121     * @return string|array
122     */
123    protected function getSearchValue(Builder $query, string $search, string $field): string|array
124    {
125        if (
126            count($query->getModel()->getCasts()) and // Check if the model has casts.
127            isset($query->getModel()->getCasts()[$field]) and // Check if there is cast about specific field.
128            enum_exists($query->getModel()->getCasts()[$field]) // Check if the cast is an enum.
129        ) {
130            $cases = collect($query->getModel()->getCasts()[$field]::toArray());
131            $enum  = $cases->filter(function ($item) use ($search) {
132                return preg_match('/' . Str::lower($search) . '/', Str::lower($item->label));
133            });
134            return $enum->toArray();
135        } else {
136            return $search;
137        }
138    }
139
140    /**
141     * Sort columns with a query.
142     *
143     * @param \Illuminate\Database\Eloquent\Builder $query
144     * @param array                                 $ignore
145     * @return \Illuminate\Database\Eloquent\Builder
146     */
147    protected function sortQuery(Builder $query, array $ignore = []): \Illuminate\Database\Eloquent\Builder
148    {
149        $rName = request()->route()->getName();
150        $table = $query->getModel()->getTable();
151        if (request()->sort_col) {
152            Session::put("$rName.sort_col", request()->sort_col);
153            Session::put("$rName.sorted", true);
154        }
155        if (request()->sort_way) {
156            Session::put("$rName.sort_way", request()->sort_way);
157            Session::put("$rName.sorted", true);
158        }
159        if (request()->rst) {
160            Session::remove("$rName.sorted");
161            Session::remove("$rName.sort_col");
162            Session::remove("$rName.sort_way");
163        }
164        if (request()->sort_col and request()->sort_way and Schema::hasColumn($table, request()->sort_col)) {
165            if (
166                !isset($ignore) or
167                (isset($ignore) and !in_array(request()->sort_col, $ignore))
168            ) {
169                $query = $query->orderBy(\sprintf("$table.%s", request()->sort_col), request()->sort_way);
170            }
171        } elseif (Schema::hasColumn($table, 'order')) {
172            $query = $query->orderBy('order', 'ASC');
173            Session::put("$rName.sort_col", 'order');
174            Session::put("$rName.sort_way", 'asc');
175        } elseif (Schema::hasColumn($table, 'updated_at')) {
176            $query = $query->orderBy('updated_at', 'DESC');
177            Session::put("$rName.sort_col", 'updated_at');
178            Session::put("$rName.sort_way", 'desc');
179        } elseif (Schema::hasColumn($table, 'created_at')) {
180            $query = $query->orderBy('created_at', 'DESC');
181            Session::put("$rName.sort_col", 'created_at');
182            Session::put("$rName.sort_way", 'desc');
183        }
184        return $query;
185    }
186
187    /**
188     * Customize pagination with cache or config (default).
189     *
190     * @param \Illuminate\Database\Eloquent\Builder             $query
191     * @param \App\Enums\Pagination\ItemsPerPaginationEnum|null $pagination
192     * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
193     */
194    protected function paginate(
195        Builder $query,
196        ItemsPerPaginationEnum $pagination = null
197    ): \Illuminate\Contracts\Pagination\LengthAwarePaginator {
198        $currentRoutePath = Str::slug(request()->route()->getName());
199        $cacheKey         = "pagination.{$currentRoutePath}";
200        $pagination       = $pagination ?? ItemsPerPaginationEnum::twelve;
201        $pagination       = ToolboxHelper::getValidatedEnum(
202            Cache::get($cacheKey, $pagination->value()),
203            'pagination',
204            '\App\Enums\Pagination\ItemsPerPaginationEnum',
205        );
206        if (Cache::get($cacheKey) !== $pagination) {
207            Cache::put($cacheKey, $pagination);
208        }
209        $query = $query->paginate($pagination);
210        return $query;
211    }
212
213    /**
214     * Set the bootstrap theme or light (default).
215     *
216     * @param \Illuminate\Http\Request $request
217     * @return \Illuminate\Http\RedirectResponse
218     */
219    protected function setTheme(Request $request): \Illuminate\Http\RedirectResponse
220    {
221        $theme = ToolboxHelper::getValidatedEnum(
222            $request->theme ?: Cache::get('theme', BootstrapThemeEnum::light->value()),
223            'theme',
224            '\App\Http\Controllers\BootstrapThemeEnum',
225        );
226        if (Cache::get("theme") !== $theme) {
227            Cache::put("theme", $theme);
228        }
229        return redirect()->back()->with('success', __('crud.messages.theme_updated'));
230    }
231}