Completed
Push — master ( c6b2b3...07b59f )
by Nekrasov
02:34
created

BaseQuery::paginate()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 10
rs 9.4286
cc 2
eloc 7
nc 2
nop 3
1
<?php
2
3
namespace Arrilot\BitrixModels\Queries;
4
5
use BadMethodCallException;
6
use Illuminate\Support\Collection;
7
8
/**
9
 * @method ElementQuery active()
10
 */
11
abstract class BaseQuery
12
{
13
    /**
14
     * Bitrix object to be queried.
15
     *
16
     * @var object
17
     */
18
    protected $bxObject;
19
20
    /**
21
     * Name of the model that calls the query.
22
     *
23
     * @var string
24
     */
25
    protected $modelName;
26
27
    /**
28
     * Model that calls the query.
29
     *
30
     * @var object
31
     */
32
    protected $model;
33
34
    /**
35
     * Query sort.
36
     *
37
     * @var array
38
     */
39
    public $sort = [];
40
41
    /**
42
     * Query filter.
43
     *
44
     * @var array
45
     */
46
    public $filter = [];
47
48
    /**
49
     * Query navigation.
50
     *
51
     * @var array|bool
52
     */
53
    public $navigation = false;
54
55
    /**
56
     * Query select.
57
     *
58
     * @var array
59
     */
60
    public $select = ['FIELDS', 'PROPS'];
61
62
    /**
63
     * The key to list items in array of results.
64
     * Set to false to have auto incrementing integer.
65
     *
66
     * @var string|bool
67
     */
68
    public $keyBy = false;
69
70
    /**
71
     * Indicates that the query should be stopped instead of touching the DB.
72
     * Can be set in query scopes or manually.
73
     *
74
     * @var bool
75
     */
76
    protected $queryShouldBeStopped = false;
77
78
    /**
79
     * Get count of users that match $filter.
80
     *
81
     * @return int
82
     */
83
    abstract public function count();
84
85
    /**
86
     * Get list of items.
87
     *
88
     * @return Collection
89
     */
90
    abstract public function getList();
91
92
    /**
93
     * Constructor.
94
     *
95
     * @param object $bxObject
96
     * @param string $modelName
97
     */
98
    public function __construct($bxObject, $modelName)
99
    {
100
        $this->bxObject = $bxObject;
101
        $this->modelName = $modelName;
102
        $this->model = new $modelName();
103
    }
104
105
    /**
106
     * Paginate the given query into a paginator.
107
     *
108
     * @param  int  $perPage
109
     * @param  array  $columns
0 ignored issues
show
Bug introduced by
There is no parameter named $columns. 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...
110
     * @param  string  $pageName
111
     * @param  int|null  $page
112
     * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
113
     */
114
    public function paginate($perPage = 15, $pageName = 'page', $page = null)
115
    {
116
        $page = $page ?: Paginator::resolveCurrentPage($pageName);
117
        $total = $this->getCountForPagination($columns);
0 ignored issues
show
Bug introduced by
The variable $columns does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Documentation Bug introduced by
The method getCountForPagination does not exist on object<Arrilot\BitrixModels\Queries\BaseQuery>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
118
        $results = $this->forPage($page, $perPage)->get($columns);
0 ignored issues
show
Documentation Bug introduced by
The method forPage does not exist on object<Arrilot\BitrixModels\Queries\BaseQuery>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
119
        return new LengthAwarePaginator($results, $total, $perPage, $page, [
120
            'path' => Paginator::resolveCurrentPath(),
121
            'pageName' => $pageName,
122
        ]);
123
    }
124
125
    /**
126
     * Get the first item that matches query params.
127
     *
128
     * @return mixed
129
     */
130
    public function first()
131
    {
132
        return $this->limit(1)->getList()->first(null, false);
133
    }
134
135
    /**
136
     * Get item by its id.
137
     *
138
     * @param int $id
139
     *
140
     * @return mixed
141
     */
142
    public function getById($id)
143
    {
144
        if (!$id || $this->queryShouldBeStopped) {
145
            return false;
146
        }
147
148
        $this->sort = [];
149
        $this->keyBy = false;
150
        $this->filter['ID'] = $id;
151
152
        $items = $this->getList();
153
154
        return !empty($items) ? $items[0] : false;
155
    }
156
157
    /**
158
     * Setter for sort.
159
     *
160
     * @param mixed  $by
161
     * @param string $order
162
     *
163
     * @return $this
164
     */
165
    public function sort($by, $order = 'ASC')
166
    {
167
        $this->sort = is_array($by) ? $by : [$by => $order];
168
169
        return $this;
170
    }
171
172
    /**
173
     * Setter for filter.
174
     *
175
     * @param array $filter
176
     *
177
     * @return $this
178
     */
179
    public function filter($filter)
180
    {
181
        $this->filter = array_merge($this->filter, $filter);
182
183
        return $this;
184
    }
185
186
    /**
187
     * Reset filter.
188
     *
189
     * @return $this
190
     */
191
    public function resetFilter()
192
    {
193
        $this->filter = [];
194
195
        return $this;
196
    }
197
198
    /**
199
     * Add another filter to filters array.
200
     *
201
     * @param $filters
202
     *
203
     * @return $this
204
     */
205
    public function addFilter($filters)
206
    {
207
        foreach ($filters as $field => $value) {
208
            $this->filter[$field] = $value;
209
        }
210
211
        return $this;
212
    }
213
214
    /**
215
     * Setter for navigation.
216
     *
217
     * @param $value
218
     *
219
     * @return $this
220
     */
221
    public function navigation($value)
222
    {
223
        $this->navigation = $value;
224
225
        return $this;
226
    }
227
228
    /**
229
     * Setter for select.
230
     *
231
     * @param $value
232
     *
233
     * @return $this
234
     */
235
    public function select($value)
236
    {
237
        $this->select = is_array($value) ? $value : func_get_args();
238
239
        return $this;
240
    }
241
242
    /**
243
     * Setter for keyBy.
244
     *
245
     * @param $value
246
     *
247
     * @return $this
248
     */
249
    public function keyBy($value)
250
    {
251
        $this->keyBy = $value;
252
253
        return $this;
254
    }
255
256
    /**
257
     * Set the "limit" value of the query.
258
     *
259
     * @param int $value
260
     *
261
     * @return $this
262
     */
263
    public function limit($value)
264
    {
265
        $this->navigation['nPageSize'] = $value;
266
267
        return $this;
268
    }
269
270
    /**
271
     * Set the "page number" value of the query.
272
     *
273
     * @param int $num
274
     *
275
     * @return $this
276
     */
277
    public function page($num)
278
    {
279
        $this->navigation['iNumPage'] = $num;
280
281
        return $this;
282
    }
283
284
    /**
285
     * Alias for "limit".
286
     *
287
     * @param int $value
288
     *
289
     * @return $this
290
     */
291
    public function take($value)
292
    {
293
        return $this->limit($value);
294
    }
295
296
    /**
297
     * Stop the query from touching DB.
298
     *
299
     * @return $this
300
     */
301
    public function stopQuery()
302
    {
303
        $this->queryShouldBeStopped = true;
304
305
        return $this;
306
    }
307
308
    /**
309
     * Adds $item to $results using keyBy value.
310
     *
311
     * @param $results
312
     * @param $item
313
     *
314
     * @return array
315
     */
316
    protected function addItemToResultsUsingKeyBy(&$results, $item)
317
    {
318
        $keyByValue = ($this->keyBy && isset($item[$this->keyBy])) ? $item[$this->keyBy] : false;
319
320
        if ($keyByValue) {
321
            $results[$keyByValue] = $item;
322
        } else {
323
            $results[] = $item;
324
        }
325
    }
326
327
    /**
328
     * Determine if all fields must be selected.
329
     *
330
     * @return bool
331
     */
332
    protected function fieldsMustBeSelected()
333
    {
334
        return in_array('FIELDS', $this->select);
335
    }
336
337
    /**
338
     * Determine if all fields must be selected.
339
     *
340
     * @return bool
341
     */
342
    protected function propsMustBeSelected()
343
    {
344
        return in_array('PROPS', $this->select)
345
            || in_array('PROPERTIES', $this->select)
346
            || in_array('PROPERTY_VALUES', $this->select);
347
    }
348
349
    /**
350
     * Set $array[$new] as $array[$old] and delete $array[$old].
351
     *
352
     * @param array $array
353
     * @param $old
354
     * @param $new
355
     *
356
     * return null
357
     */
358
    protected function substituteField(&$array, $old, $new)
359
    {
360
        if (isset($array[$old]) && !isset($array[$new])) {
361
            $array[$new] = $array[$old];
362
        }
363
364
        unset($array[$old]);
365
    }
366
367
    /**
368
     * Clear select array from duplication and additional fields.
369
     *
370
     * @return array
371
     */
372
    protected function clearSelectArray()
373
    {
374
        $strip = ['FIELDS', 'PROPS', 'PROPERTIES', 'PROPERTY_VALUES', 'GROUPS', 'GROUP_ID', 'GROUPS_ID'];
375
376
        return array_values(array_diff(array_unique($this->select), $strip));
377
    }
378
379
    /**
380
     * Handle dynamic method calls into the method.
381
     *
382
     * @param string $method
383
     * @param array  $parameters
384
     *
385
     * @throws BadMethodCallException
386
     *
387
     * @return $this
388
     */
389
    public function __call($method, $parameters)
390
    {
391
        if (method_exists($this->model, 'scope'.$method)) {
392
            array_unshift($parameters, $this);
393
394
            $query = call_user_func_array([$this->model, 'scope'.$method], $parameters);
395
396
            if ($query === false) {
397
                $this->stopQuery();
398
            }
399
400
            return $query instanceof static ? $query : $this;
401
        }
402
403
        $className = get_class($this);
404
405
        throw new BadMethodCallException("Call to undefined method {$className}::{$method}()");
406
    }
407
}
408