Completed
Push — dev-master ( e130f8...332492 )
by Vijay
03:20
created

ControllerMapper::checkFieldsExist()   B

Complexity

Conditions 6
Paths 8

Size

Total Lines 19
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 12
nc 8
nop 2
dl 0
loc 19
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
7
 */
8
trait ControllerMapper
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
    public static function checkOrderField(string $order = null, array $allFields = []): array
21
    {
22
        if (empty($order)) {
23
            return [];
24
        }
25
26
        $orderClauses = empty($order) ? [] : preg_split("/[,]/", $order);
27
        foreach ($orderClauses as $k => $field) {
28
            // split into field, asc/desc
29
            $field = preg_split("/[\s]+/", trim($field));
30
            if (!in_array($field[0], $allFields)) {
31
                // invalid field
32
                unset($orderClauses[$k]);
33
                continue;
34
            } elseif (count($field) == 1) {
35
                $field[1] = 'asc';
36
            } elseif (count($field) == 2) {
37
                if (!in_array($field[1], ['asc', 'desc'])) {
38
                    $field[1] = 'asc';
39
                }
40
            }
41
            $orderClauses[$k] = $field[0] . ' ' . $field[1];
42
        }
43
        $order = join(',', $orderClauses);
44
45
        return $order;
46
    }
47
48
49
    public static function checkFieldsExist(array $checkFieldsExist = [], array $fieldsList = [])
0 ignored issues
show
Unused Code introduced by
The parameter $fieldsList is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
50
    {
51
        $f3 = \Base::instance();
0 ignored issues
show
Unused Code introduced by
$f3 is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
52
        // fields to return and fields to search - validate
53
        $validFields = [];
54
        foreach ($checkFieldsExist as $fieldsList) {
55
            if (empty($fields)) {
56
                continue;
57
            }
58
            $fields = empty($fields) ? [] : preg_split("/[,]/", $fields);
59
            foreach ($fields as $k => $field) {
60
                if (!in_array($field, $allFields)) {
0 ignored issues
show
Bug introduced by
The variable $allFields does not exist. Did you mean $fields?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
61
                    unset($fields[$k]);
62
                }
63
            }
64
            $validFields[$fieldsList] = join(',', $fields);
65
        }
66
        return $validFields;
67
    }
68
69
70
   /**
71
     * list objects (list is a reserved keyword) of mapper
72
     *
73
     * @param \Base $f3
74
     * @param \FFCMS\Mappers\Mapper $m
75
     * @return array
76
     */
77
    protected function &getListingResults(\Base $f3, \FFCMS\Mappers\Mapper $m): array
78
    {
79
        // set up paging limits
80
        $minPerPage = 5;
81
        $maxPerPage = 1024;
82
        $perPage = (int) $f3->get('REQUEST.per_page');
83
        if ($perPage < $minPerPage) {
84
            $perPage = $minPerPage;
85
        }
86
        if ($perPage > $maxPerPage) {
87
            $perPage = $maxPerPage;
88
        }
89
90
        $page = $f3->get('REQUEST.page');
91
        if ($page < 1) {
92
            $page = 1;
93
        }
94
95
        // validate order field
96
        $order = $f3->get('REQUEST.order');
0 ignored issues
show
Unused Code introduced by
$order is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
97
98
        // fetch data (paging is 0 based)
99
        $allFields = $m->fields();
100
101
        // validate order field
102
        $order = self::checkOrderField($f3->get('REQUEST.order'), $allFields);
103
104
        // validated fields to return
105
        $validFields = self::checkFieldsExist([$f3->get('REQUEST.fields')], $allFields);
106
        $fields = empty($validFields['fields']) ? join(',', $allFields) : $validFields['fields'];
107
108
        // count rows
109
        $data = [];
110
        $rows = $m->count();
111
        if ($rows < 1) {
112
            return $data;
113
        }
114
115
        // if fewer results than per page, set per_page
116
        if ($page == 1 && $perPage > $rows) {
117
            $perPage = $rows;
118
        }
119
120
        $pagination = [];
121
        $pagination['count'] = ceil($rows / $perPage);
122
123
        // too high page number?
124
        if ($page > $pagination['count']) {
125
            $page = $pagination['count'];
126
        }
127
128
        // set up page URLs
129
        $url = $f3->get('PATH');
130
        $urlParams = [
131
            'per_page' => $perPage,
132
        ];
133
        if (!empty($order)) {
134
            $urlParams['order'] = $order;
135
        }
136
        if (!empty($fields)) {
137
            $urlParams['fields'] = $fields;
138
        }
139
        ksort($urlParams);
140
141
        // next/previous page url
142
        $prevPage = (1 > $page - 1 ) ? null : $page - 1;
143
        $nextPage = (1 + $page > $pagination['count']) ? null : $page + 1;
144
145
        $resultsFrom = round($page * ($rows / $pagination['count'])) - $perPage + 1;
146
        $resultsTo = $resultsFrom + $perPage - 1;
147
148
        // return data
149
        $data['pagination'] = [
150
            'url_base' => $this->url($url, $urlParams),
151
            'url_current' => $this->url($url, $urlParams + ['page' => $page]),
152
            'url_first' => $this->url($url, $urlParams + ['page' => 1]),
153
            'url_last' => $this->url($url, $urlParams + ['page' => $pagination['count']]),
154
            'url_next' => (null == $nextPage) ? null : $this->url($url, $urlParams + ['page' => $nextPage]),
155
            'url_previous' => (null == $prevPage) ? null : $this->url($url, $urlParams + ['page' => $prevPage]),
156
            'results' => $rows,
157
            'results_from' => $resultsFrom,
158
            'results_to' => $resultsTo,
159
            'per_page' => $perPage,
160
            'pages' => $pagination['count'],
161
            'page' => $page,
162
            'object' => $m->table(),
163
            'fields' => preg_split("/[,]/", $fields),
164
            'view' => $f3->get('REQUEST.view')
165
        ];
166
167
        // fetch results
168
        $m->load('', [
169
            'order' => $order,
170
            'offset' => (1 == $page) ? 0 : ($page - 1) * $perPage,
171
            'limit' => $perPage
172
        ]);
173
174
        do {
175
            $data['objects'][] = $m->castFields($fields);
176
        }
177
        while ($m->skip());
178
179
        return $data;
180
    }
181
182
    /**
183
     * search objects of given mapper
184
     *
185
     * @param \Base $f3
186
     * @param \FFCMS\Mappers\Mapper $m
187
     * @return array $results
188
     */
189
    protected function &getSearchResults(\Base $f3, \FFCMS\Mappers\Mapper $m): array
190
    {
191
        // set up paging limits
192
        $minPerPage = 10;
193
        $maxPerPage = 100;
194
        $perPage = (int) $f3->get('REQUEST.per_page');
195
        if ($perPage < $minPerPage) {
196
            $perPage = $minPerPage;
197
        }
198
        if ($perPage > $maxPerPage) {
199
            $perPage = $maxPerPage;
200
        }
201
202
        $page = $f3->get('REQUEST.page');
203
        if ($page < 1) {
204
            $page = 1;
205
        }
206
207
        // fetch data (paging is 0 based)
208
        $allFields = $m->fields();
209
210
        // validate order field
211
        $order = self::checkOrderField($f3->get('REQUEST.order'), $allFields);
212
        $validFields = self::checkFieldsExist([$f3->get('REQUEST.fields'), $f3->get('REQUEST.search_fields')], $allFields);
213
214
        // validated fields to return
215
        $fields = empty($validFields['fields']) ? join(',', $allFields) : $validFields['fields'];
216
217
        // validated fields to search in, use all if empty
218
        $searchFields = empty($validFields['search_fields']) ? join(',', $allFields) : $validFields['search_fields'];
219
220
        // get search type
221
        $search = $f3->get('REQUEST.search');
222
        if (!empty($search)) {
223
            $search = trim(strtolower($search));
224
        }
225
        $search_type = $f3->get('REQUEST.search_type');
226
        if (empty($search_type)) {
227
            $search_type = 'exact';
228
        } elseif ($search_type !== 'exact') {
229
            $search_type = 'fuzzy';
230
        }
231
232
        // construct search query
233
        $db = \Registry::get('db');
234
        $sqlClauses = [];
235
        $searchFieldsArray = preg_split("/[,]/", $searchFields);
236 View Code Duplication
        foreach ($searchFieldsArray as $field) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
237
            $sqlClauses[] = 'LOWER(' . $db->quotekey($field) . ') = ' . $db->quote($search);
238
            if ($search_type == 'fuzzy') {
239
                $sqlClauses[] = 'LOWER(' . $db->quotekey($field) . ') LIKE ' . $db->quote('%' . $search . '%');
240
            }
241
        }
242
243
        // get total results
244
        $query = 'SELECT COUNT(*) AS results FROM ' . $db->quotekey($m->table()) . ' WHERE ' . join(' OR ', $sqlClauses);
245
        $data = [];
246
        $rows = $db->exec($query);
247
        $rows = (int) $rows[0]['results'];
248
        if ($rows < 1) {
249
            return $data;
250
        }
251
252
        // if fewer results than per page, set per_page
253
        if ($page == 1 && $perPage > $rows) {
254
            $perPage = $rows;
255
        }
256
257
        $pagination = [];
258
        $pagination['count'] = (int) ceil($rows / $perPage);
259
260
        // too high page number?
261
        if ($page > $pagination['count']) {
262
            $page = $pagination['count'];
263
        }
264
265
        // set up page URLs
266
        $url = $f3->get('PATH');
267
        $urlParams = [
268
            'per_page' => $perPage,
269
            'search' => $search,
270
            'search_type' => $search_type,
271
        ];
272
        if (!empty($order)) {
273
            $urlParams['order'] = $order;
274
        }
275
        if (!empty($fields)) {
276
            $urlParams['fields'] = $fields;
277
        }
278
        ksort($urlParams);
279
280
        // previous page url
281
        $prevPage = (1 > $page - 1 ) ? null : $page - 1;
282
        $nextPage = (1 + $page > $pagination['count']) ? null : $page + 1;
283
284
        $resultsFrom = 1 + ($page * $perPage) - $perPage;
285
        $resultsTo = $resultsFrom + $perPage - 1;
286
        if ($resultsTo > $rows) {
287
            $resultsTo = $rows;
288
        }
289
290
        // return data
291
        $data['pagination'] = [
292
            'url_base' => $this->url($url, $urlParams),
293
            'url_current' => $this->url($url, $urlParams + ['page' => $page]),
294
            'url_first' => $this->url($url, $urlParams + ['page' => 1]),
295
            'url_last' => $this->url($url, $urlParams + ['page' => $pagination['count']]),
296
            'url_next' => (null == $nextPage) ? null : $this->url($url, $urlParams + ['page' => $nextPage]),
297
            'url_previous' => (null == $prevPage) ? null : $this->url($url, $urlParams + ['page' => $prevPage]),
298
            'results' => $rows,
299
            'results_from' => $resultsFrom,
300
            'results_to' => $resultsTo,
301
            'per_page' => $perPage,
302
            'pages' => $pagination['count'],
303
            'page' => $page,
304
            'object' => $m->table(),
305
            'fields' => preg_split("/[,]/", $fields),
306
            'view' => $f3->get('REQUEST.view')
307
        ];
308
309
        // retrieve results
310
        $query = 'SELECT * FROM ' . $db->quotekey($m->table()) . ' WHERE ' . join(' OR ', $sqlClauses);
311
        $query .= sprintf(' LIMIT %d,%d', (1 == $page) ? 0 : ($page - 1) * $perPage, $perPage);
312
        $results = $db->exec($query);
313
        foreach ($results as $row) {
314
            $data['objects'][] = $m->castFields($fields, $row);
315
        }
316
317
        return $data;
318
    }
319
320
}
321