Completed
Push — dev-master ( 332492...6f38f2 )
by Vijay
02:50
created

SearchController::checkFieldsExist()   B

Complexity

Conditions 6
Paths 8

Size

Total Lines 18
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 11
nc 8
nop 2
dl 0
loc 18
rs 8.8571
c 0
b 0
f 0
1
<?php
2
3
namespace FFCMS\Traits;
4
5
/**
6
 * Controller Methods which utilise a mapper to search and list results
7
 */
8
trait SearchController
9
{
10
    /**
11
     * Create an internal URL
12
     * Uses method from
13
     * @see \FFCMS\Helpers\UrlHelper
14
     * @param string $url
15
     * @param array $params
16
     */
17
    abstract public function url(string $url, array $params = []): string;
18
19
20
    /**
21
     * Check the order by field is valid and return it corrected
22
     *
23
     * @param string $order order field in form of: "field1 ASC/DESC,field 2 ASC/DESC.."
0 ignored issues
show
Documentation introduced by
Should the type for parameter $order not be null|string?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
24
     * @param array $allFields the list of fields which are valid to order by
25
     * @return string $validFields
26
     */
27
    public static function checkOrderField(string $order = null, array $allFields = []): string
28
    {
29
        if (empty($order)) {
30
            return join(',', $allFields);
31
        }
32
33
        $orderClauses = empty($order) ? [] : preg_split("/[,]/", $order);
34
        foreach ($orderClauses as $k => $field) {
35
            // split into field, asc/desc
36
            $field = preg_split("/[\s]+/", trim($field));
37
            if (!in_array($field[0], $allFields)) {
38
                // invalid field
39
                unset($orderClauses[$k]);
40
                continue;
41
            } elseif (count($field) == 1) {
42
                $field[1] = 'asc';
43
            } elseif (count($field) == 2) {
44
                if (!in_array($field[1], ['asc', 'desc'])) {
45
                    $field[1] = 'asc';
46
                }
47
            }
48
            $orderClauses[$k] = $field[0] . ' ' . $field[1];
49
        }
50
        ksort($orderClauses);
51
        $order = join(',', $orderClauses);
52
53
        return $order;
54
    }
55
56
57
    /**
58
     * Check the fields exist and return only the existing ones
59
     *
60
     * @param array $checkFieldsExist [id => 'field1,field2'...]
61
     * @param array $fieldsList the list of fields which are valid
62
     * @param string|null $users_uuid uuid of user to get results for
0 ignored issues
show
Bug introduced by
There is no parameter named $users_uuid. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
63
     * @return array $validFields
64
     */
65
    public static function checkFieldsExist(array $checkFieldsExist = [], array $fieldsList = []): array
66
    {
67
        // fields to return and fields to search - validate
68
        $validFields = [];
69
        foreach ($checkFieldsExist as $id => $fields) {
70
            if (empty($fields)) {
71
                continue;
72
            }
73
            $fields = empty($fields) ? [] : preg_split("/[,]/", $fields);
74
            foreach ($fields as $k => $field) {
75
                if (!in_array($field, $fieldsList)) {
76
                    unset($fields[$k]);
77
                }
78
            }
79
            $validFields[$id] = join(',', $fields);
80
        }
81
        return $validFields;
82
    }
83
84
85
   /**
86
     * list objects (list is a reserved keyword) of mapper
87
     *
88
     * @param \Base $f3
89
     * @param \FFCMS\Mappers\Mapper $m
90
     * @return array
91
     */
92
    protected function &getListingResults(\Base $f3, \FFCMS\Mappers\Mapper $m, string $users_uuid = null): array
93
    {
94
        // set up paging limits
95
        $minPerPage = 5;
96
        $maxPerPage = 1024;
97
        $perPage = (int) $f3->get('REQUEST.per_page');
98
        if ($perPage < $minPerPage) {
99
            $perPage = $minPerPage;
100
        }
101
        if ($perPage > $maxPerPage) {
102
            $perPage = $maxPerPage;
103
        }
104
105
        $page = $f3->get('REQUEST.page');
106
        if ($page < 1) {
107
            $page = 1;
108
        }
109
110
        // fetch data (paging is 0 based)
111
        $allFields = $m->fields();
112
113
        // validate order field
114
        $order = self::checkOrderField($f3->get('REQUEST.order'), $allFields);
115
116
        // validated fields to return
117
        $validFields = self::checkFieldsExist([$f3->get('REQUEST.fields')], $allFields);
118
        $fields = empty($validFields['fields']) ? join(',', $allFields) : $validFields['fields'];
119
120
        // count rows
121
        $data = [];
122
123
        // count rows
124
        $isAdmin = $f3->get('isAdmin');
125
        $rows = 0;
126
        if  (in_array('users_uuid', $allFields) && !empty($users_uuid)) {
127
            $rows = $m->count(['users_uuid = ?', $users_uuid]);
128
        } elseif ($isAdmin && empty($users_uuid)) {
129
            $rows = $m->count();
130
        }
131
        if ($rows < 1) {
132
            return $data;
133
        }
134
135
        // if fewer results than per page, set per_page
136
        if ($page == 1 && $perPage > $rows) {
137
            $perPage = $rows;
138
        }
139
140
        $pagination = [];
141
        $pagination['count'] = ceil($rows / $perPage);
142
143
        // too high page number?
144
        if ($page > $pagination['count']) {
145
            $page = $pagination['count'];
146
        }
147
148
        // set up page URLs
149
        $url = $f3->get('PATH');
150
        $urlParams = [
151
            'per_page' => $perPage,
152
        ];
153
        if (!empty($order)) {
154
            $urlParams['order'] = $order;
155
        }
156
        if (!empty($fields)) {
157
            $urlParams['fields'] = $fields;
158
        }
159
        ksort($urlParams);
160
161
        // next/previous page url
162
        $prevPage = (1 > $page - 1 ) ? null : $page - 1;
163
        $nextPage = (1 + $page > $pagination['count']) ? null : $page + 1;
164
165
        $resultsFrom = round($page * ($rows / $pagination['count'])) - $perPage + 1;
166
        $resultsTo = $resultsFrom + $perPage - 1;
167
168
        // return data
169
        $data['pagination'] = [
170
            'url_base' => $this->url($url, $urlParams),
171
            'url_current' => $this->url($url, $urlParams + ['page' => $page]),
172
            'url_first' => $this->url($url, $urlParams + ['page' => 1]),
173
            'url_last' => $this->url($url, $urlParams + ['page' => $pagination['count']]),
174
            'url_next' => (null == $nextPage) ? null : $this->url($url, $urlParams + ['page' => $nextPage]),
175
            'url_previous' => (null == $prevPage) ? null : $this->url($url, $urlParams + ['page' => $prevPage]),
176
            'results' => $rows,
177
            'results_from' => $resultsFrom,
178
            'results_to' => $resultsTo,
179
            'per_page' => $perPage,
180
            'pages' => $pagination['count'],
181
            'page' => $page,
182
            'object' => $m->table(),
183
            'fields' => preg_split("/[,]/", $fields),
184
            'view' => $f3->get('REQUEST.view')
185
        ];
186
187
188
        // fetch results
189
        if ($isAdmin && empty($users_uuid)) {
190
            $m->load('', [
191
                'order' => $order,
192
                'offset' => (1 == $page) ? 0 : ($page - 1) * $perPage,
193
                'limit' => $perPage
194
            ]);
195
        } else {
196
            $m->load(['users_uuid = ?', $users_uuid], [
197
                'order' => $order,
198
                'offset' => (1 == $page) ? 0 : ($page - 1) * $perPage,
199
                'limit' => $perPage
200
            ]);
201
        }
202
203
        $adminView = $isAdmin || ($isAdmin && 'admin' == $f3->get('REQUEST.view'));
204
        do {
205
            $data['objects'][] = $adminView ? $m->castFields($fields) : $m->exportArray($fields);
206
        }
207
        while ($m->skip());
208
209
        return $data;
210
    }
211
212
    /**
213
     * search objects of given mapper
214
     *
215
     * @param \Base $f3
216
     * @param \FFCMS\Mappers\Mapper $m
217
     * @param string|null $users_uuid uuid of user to get results for
218
     * @return array $results
219
     */
220
    protected function &getSearchResults(\Base $f3, \FFCMS\Mappers\Mapper $m, string $users_uuid = null): array
221
    {
222
        // set up paging limits
223
        $minPerPage = 10;
224
        $maxPerPage = 100;
225
        $perPage = (int) $f3->get('REQUEST.per_page');
226
        if ($perPage < $minPerPage) {
227
            $perPage = $minPerPage;
228
        }
229
        if ($perPage > $maxPerPage) {
230
            $perPage = $maxPerPage;
231
        }
232
233
        $page = $f3->get('REQUEST.page');
234
        if ($page < 1) {
235
            $page = 1;
236
        }
237
238
        // fetch data (paging is 0 based)
239
        $allFields = $m->fields();
240
241
        // validate order field
242
        $order = self::checkOrderField($f3->get('REQUEST.order'), $allFields);
243
        $validFields = self::checkFieldsExist([$f3->get('REQUEST.fields'), $f3->get('REQUEST.search_fields')], $allFields);
244
245
        // validated fields to return
246
        $fields = empty($validFields['fields']) ? join(',', $allFields) : $validFields['fields'];
247
248
        // validated fields to search in, use all if empty
249
        $searchFields = empty($validFields['search_fields']) ? join(',', $allFields) : $validFields['search_fields'];
250
251
        // get search type
252
        $search = $f3->get('REQUEST.search');
253
        if (!empty($search)) {
254
            $search = trim(strtolower($search));
255
        }
256
        $search_type = $f3->get('REQUEST.search_type');
257
        if (empty($search_type)) {
258
            $search_type = 'exact';
259
        } elseif ($search_type !== 'exact') {
260
            $search_type = 'fuzzy';
261
        }
262
263
        // construct search query
264
        $db = \Registry::get('db');
265
        $sqlClauses = [];
266
        $searchFieldsArray = preg_split("/[,]/", $searchFields);
267
        foreach ($searchFieldsArray as $field) {
268
            $sqlClauses[] = 'LOWER(' . $db->quotekey($field) . ') = ' . $db->quote($search);
269
            if ($search_type == 'fuzzy') {
270
                $sqlClauses[] = 'LOWER(' . $db->quotekey($field) . ') LIKE ' . $db->quote('%' . $search . '%');
271
            }
272
        }
273
274
275
        // get total results
276
        $isAdmin = $f3->get('isAdmin');
277
        $query = 'SELECT COUNT(*) AS results FROM ' . $db->quotekey($m->table()) . ' WHERE ';
278
        if  (in_array('users_uuid', $allFields) && !empty($users_uuid)) {
279
            $query .= ' users_uuid = ' . $db->quote($users_uuid)  . ' AND ('.  join(' OR ', $sqlClauses) . ')';
280
        } elseif ($isAdmin && empty($users_uuid)) {
281
            $query .= join(' OR ', $sqlClauses);
282
        }
283
284
        $data = [];
285
        $rows = $db->exec($query);
286
        $rows = (int) $rows[0]['results'];
287
        if ($rows < 1) {
288
            return $data;
289
        }
290
291
        // if fewer results than per page, set per_page
292
        if ($page == 1 && $perPage > $rows) {
293
            $perPage = $rows;
294
        }
295
296
        $pagination = [];
297
        $pagination['count'] = (int) ceil($rows / $perPage);
298
299
        // too high page number?
300
        if ($page > $pagination['count']) {
301
            $page = $pagination['count'];
302
        }
303
304
        // set up page URLs
305
        $url = $f3->get('PATH');
306
        $urlParams = [
307
            'per_page' => $perPage,
308
            'search' => $search,
309
            'search_type' => $search_type,
310
        ];
311
        if (!empty($order)) {
312
            $urlParams['order'] = $order;
313
        }
314
        if (!empty($fields)) {
315
            $urlParams['fields'] = $fields;
316
        }
317
        ksort($urlParams);
318
319
        // previous page url
320
        $prevPage = (1 > $page - 1 ) ? null : $page - 1;
321
        $nextPage = (1 + $page > $pagination['count']) ? null : $page + 1;
322
323
        $resultsFrom = 1 + ($page * $perPage) - $perPage;
324
        $resultsTo = $resultsFrom + $perPage - 1;
325
        if ($resultsTo > $rows) {
326
            $resultsTo = $rows;
327
        }
328
329
        // return data
330
        $data['pagination'] = [
331
            'url_base' => $this->url($url, $urlParams),
332
            'url_current' => $this->url($url, $urlParams + ['page' => $page]),
333
            'url_first' => $this->url($url, $urlParams + ['page' => 1]),
334
            'url_last' => $this->url($url, $urlParams + ['page' => $pagination['count']]),
335
            'url_next' => (null == $nextPage) ? null : $this->url($url, $urlParams + ['page' => $nextPage]),
336
            'url_previous' => (null == $prevPage) ? null : $this->url($url, $urlParams + ['page' => $prevPage]),
337
            'results' => $rows,
338
            'results_from' => $resultsFrom,
339
            'results_to' => $resultsTo,
340
            'per_page' => $perPage,
341
            'pages' => $pagination['count'],
342
            'page' => $page,
343
            'object' => $m->table(),
344
            'fields' => preg_split("/[,]/", $fields),
345
            'view' => $f3->get('REQUEST.view')
346
        ];
347
348
349
        // retrieve results
350
        $query = 'SELECT * FROM ' . $db->quotekey($m->table()) . ' WHERE ';
351
        if (empty($users_uuid)) {
352
             $query .= join(' OR ', $sqlClauses);
353
        } else {
354
             $query .= ' users_uuid = ' . $db->quote($users_uuid)  . ' AND ('.  join(' OR ', $sqlClauses) . ')';
355
        }
356
        $query .= sprintf(' LIMIT %d,%d', (1 == $page) ? 0 : ($page - 1) * $perPage, $perPage);
357
358
        $adminView = $isAdmin || ($isAdmin && 'admin' == $f3->get('REQUEST.view'));
359
        $results = $db->exec($query);
360
        foreach ($results as $row) {
361
            $data['objects'][] = $adminView ? $m->castFields($fields, $row) : $m->exportArray($fields, $row);
362
        }
363
364
        return $data;
365
    }
366
367
}
368