Completed
Push — master ( eda6ec...438e8b )
by Nekrasov
03:00
created

BaseQuery::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 6
rs 9.4285
cc 1
eloc 4
nc 1
nop 2
1
<?php
2
3
namespace Arrilot\BitrixModels\Queries;
4
5
use Arrilot\BitrixModels\Models\BaseModel;
6
use BadMethodCallException;
7
use Illuminate\Pagination\Paginator;
8
use Illuminate\Pagination\LengthAwarePaginator;
9
use Illuminate\Support\Collection;
10
use LogicException;
11
12
/**
13
 * @method ElementQuery active()
14
 */
15
abstract class BaseQuery
16
{
17
    /**
18
     * Bitrix object to be queried.
19
     *
20
     * @var object
21
     */
22
    protected $bxObject;
23
24
    /**
25
     * Name of the model that calls the query.
26
     *
27
     * @var string
28
     */
29
    protected $modelName;
30
31
    /**
32
     * Model that calls the query.
33
     *
34
     * @var object
35
     */
36
    protected $model;
37
38
    /**
39
     * Query sort.
40
     *
41
     * @var array
42
     */
43
    public $sort = [];
44
45
    /**
46
     * Query filter.
47
     *
48
     * @var array
49
     */
50
    public $filter = [];
51
52
    /**
53
     * Query navigation.
54
     *
55
     * @var array|bool
56
     */
57
    public $navigation = false;
58
59
    /**
60
     * Query select.
61
     *
62
     * @var array
63
     */
64
    public $select = ['FIELDS', 'PROPS'];
65
66
    /**
67
     * The key to list items in array of results.
68
     * Set to false to have auto incrementing integer.
69
     *
70
     * @var string|bool
71
     */
72
    public $keyBy = 'ID';
73
74
    /**
75
     * Indicates that the query should be stopped instead of touching the DB.
76
     * Can be set in query scopes or manually.
77
     *
78
     * @var bool
79
     */
80
    protected $queryShouldBeStopped = false;
81
82
    /**
83
     * Get count of users that match $filter.
84
     *
85
     * @return int
86
     */
87
    abstract public function count();
88
89
    /**
90
     * Get list of items.
91
     *
92
     * @return Collection
93
     */
94
    abstract public function getList();
95
96
    /**
97
     * Constructor.
98
     *
99
     * @param object $bxObject
100
     * @param string $modelName
101
     */
102
    public function __construct($bxObject, $modelName)
103
    {
104
        $this->bxObject = $bxObject;
105
        $this->modelName = $modelName;
106
        $this->model = new $modelName();
107
    }
108
109
    /**
110
     * Get the first item that matches query params.
111
     *
112
     * @return mixed
113
     */
114
    public function first()
115
    {
116
        return $this->limit(1)->getList()->first(null, false);
117
    }
118
119
    /**
120
     * Get item by its id.
121
     *
122
     * @param int $id
123
     *
124
     * @return mixed
125
     */
126
    public function getById($id)
127
    {
128
        if (!$id || $this->queryShouldBeStopped) {
129
            return false;
130
        }
131
132
        $this->sort = [];
133
        $this->filter['ID'] = $id;
134
135
        return $this->getList()->first(null, false);
136
    }
137
138
    /**
139
     * Setter for sort.
140
     *
141
     * @param mixed  $by
142
     * @param string $order
143
     *
144
     * @return $this
145
     */
146
    public function sort($by, $order = 'ASC')
147
    {
148
        $this->sort = is_array($by) ? $by : [$by => $order];
149
150
        return $this;
151
    }
152
153
    /**
154
     * Setter for filter.
155
     *
156
     * @param array $filter
157
     *
158
     * @return $this
159
     */
160
    public function filter($filter)
161
    {
162
        $this->filter = array_merge($this->filter, $filter);
163
164
        return $this;
165
    }
166
167
    /**
168
     * Reset filter.
169
     *
170
     * @return $this
171
     */
172
    public function resetFilter()
173
    {
174
        $this->filter = [];
175
176
        return $this;
177
    }
178
179
    /**
180
     * Add another filter to filters array.
181
     *
182
     * @param $filters
183
     *
184
     * @return $this
185
     */
186
    public function addFilter($filters)
187
    {
188
        foreach ($filters as $field => $value) {
189
            $this->filter[$field] = $value;
190
        }
191
192
        return $this;
193
    }
194
195
    /**
196
     * Setter for navigation.
197
     *
198
     * @param $value
199
     *
200
     * @return $this
201
     */
202
    public function navigation($value)
203
    {
204
        $this->navigation = $value;
205
206
        return $this;
207
    }
208
209
    /**
210
     * Setter for select.
211
     *
212
     * @param $value
213
     *
214
     * @return $this
215
     */
216
    public function select($value)
217
    {
218
        $this->select = is_array($value) ? $value : func_get_args();
219
220
        return $this;
221
    }
222
223
    /**
224
     * Setter for keyBy.
225
     *
226
     * @param $value
227
     *
228
     * @return $this
229
     */
230
    public function keyBy($value)
231
    {
232
        $this->keyBy = $value;
233
234
        return $this;
235
    }
236
237
    /**
238
     * Set the "limit" value of the query.
239
     *
240
     * @param int $value
241
     *
242
     * @return $this
243
     */
244
    public function limit($value)
245
    {
246
        $this->navigation['nPageSize'] = $value;
247
248
        return $this;
249
    }
250
251
    /**
252
     * Set the "page number" value of the query.
253
     *
254
     * @param int $num
255
     *
256
     * @return $this
257
     */
258
    public function page($num)
259
    {
260
        $this->navigation['iNumPage'] = $num;
261
262
        return $this;
263
    }
264
265
    /**
266
     * Alias for "limit".
267
     *
268
     * @param int $value
269
     *
270
     * @return $this
271
     */
272
    public function take($value)
273
    {
274
        return $this->limit($value);
275
    }
276
277
    /**
278
     * Set the limit and offset for a given page.
279
     *
280
     * @param  int  $page
281
     * @param  int  $perPage
282
     * @return $this
283
     */
284
    public function forPage($page, $perPage = 15)
285
    {
286
        return $this->page($page)->take($perPage);
287
    }
288
289
    /**
290
     * Paginate the given query into a paginator.
291
     *
292
     * @param  int  $perPage
293
     * @param  string  $pageName
294
     *
295
     * @return \Illuminate\Pagination\LengthAwarePaginator
296
     */
297 View Code Duplication
    public function paginate($perPage = 15, $pageName = 'page')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
298
    {
299
        $page = Paginator::resolveCurrentPage($pageName);
300
        $total = $this->count();
301
        $results = $this->forPage($page, $perPage)->getList();
302
303
        return new LengthAwarePaginator($results, $total, $perPage, $page, [
304
            'path' => Paginator::resolveCurrentPath(),
305
            'pageName' => $pageName,
306
        ]);
307
    }
308
309
    /**
310
     * Get a paginator only supporting simple next and previous links.
311
     *
312
     * This is more efficient on larger data-sets, etc.
313
     *
314
     * @param  int  $perPage
315
     * @param  string  $pageName
316
     *
317
     * @return \Illuminate\Pagination\Paginator
318
     */
319 View Code Duplication
    public function simplePaginate($perPage = 15, $pageName = 'page')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
320
    {
321
        $page = Paginator::resolveCurrentPage($pageName);
322
        $results = $this->forPage($page, $perPage + 1)->getList();
323
324
        return new Paginator($results, $perPage, $page, [
325
            'path' => Paginator::resolveCurrentPath(),
326
            'pageName' => $pageName,
327
        ]);
328
    }
329
330
    /**
331
     * Stop the query from touching DB.
332
     *
333
     * @return $this
334
     */
335
    public function stopQuery()
336
    {
337
        $this->queryShouldBeStopped = true;
338
339
        return $this;
340
    }
341
342
    /**
343
     * Adds $item to $results using keyBy value.
344
     *
345
     * @param $results
346
     * @param BaseModel $object
347
     *
348
     * @return void
349
     */
350
    protected function addItemToResultsUsingKeyBy(&$results, BaseModel $object)
351
    {
352
        $item = $object->fields;
353
        if (!isset($item[$this->keyBy])) {
354
            throw new LogicException("Field {$this->keyBy} is not found in object");
355
        }
356
357
        $keyByValue = $item[$this->keyBy];
358
359
        if (!isset($results[$keyByValue])) {
360
            $results[$keyByValue] = $object;
361
        } else {
362
            $oldFields = $results[$keyByValue]->fields;
363
            foreach ($oldFields as $field => $oldValue) {
364
                // пропускаем служебные поля.
365
                if (in_array($field, ['_was_multiplied', 'PROPERTIES'])) {
366
                    continue;
367
                }
368
369
                // мультиплицируем только несовпадающие значения полей
370
                $newValue = $item[$field];
371
                if ($oldValue !== $newValue) {
372
                    // если еще не мультиплицироваи объект то все поля надо превратить в массивы
373
                    if (empty($oldFields['_was_multiplied'])) {
374
                        $oldFields[$field] = [
375
                            $oldFields[$field]
376
                        ];
377
                    }
378
379
                    // в любом случае добавляем новое значение полю
380
                    $oldFields[$field][] = $newValue;
381
                }
382
            }
383
384
            $oldFields['_was_multiplied'] = true;
385
            $results[$keyByValue]->fields = $oldFields;
386
        }
387
    }
388
389
    /**
390
     * Determine if all fields must be selected.
391
     *
392
     * @return bool
393
     */
394
    protected function fieldsMustBeSelected()
395
    {
396
        return in_array('FIELDS', $this->select);
397
    }
398
399
    /**
400
     * Determine if all fields must be selected.
401
     *
402
     * @return bool
403
     */
404
    protected function propsMustBeSelected()
405
    {
406
        return in_array('PROPS', $this->select)
407
            || in_array('PROPERTIES', $this->select)
408
            || in_array('PROPERTY_VALUES', $this->select);
409
    }
410
411
    /**
412
     * Set $array[$new] as $array[$old] and delete $array[$old].
413
     *
414
     * @param array $array
415
     * @param $old
416
     * @param $new
417
     *
418
     * return null
419
     */
420
    protected function substituteField(&$array, $old, $new)
421
    {
422
        if (isset($array[$old]) && !isset($array[$new])) {
423
            $array[$new] = $array[$old];
424
        }
425
426
        unset($array[$old]);
427
    }
428
429
    /**
430
     * Clear select array from duplication and additional fields.
431
     *
432
     * @return array
433
     */
434
    protected function clearSelectArray()
435
    {
436
        $strip = ['FIELDS', 'PROPS', 'PROPERTIES', 'PROPERTY_VALUES', 'GROUPS', 'GROUP_ID', 'GROUPS_ID'];
437
438
        return array_values(array_diff(array_unique($this->select), $strip));
439
    }
440
441
    /**
442
     * Handle dynamic method calls into the method.
443
     *
444
     * @param string $method
445
     * @param array  $parameters
446
     *
447
     * @throws BadMethodCallException
448
     *
449
     * @return $this
450
     */
451
    public function __call($method, $parameters)
452
    {
453
        if (method_exists($this->model, 'scope'.$method)) {
454
            array_unshift($parameters, $this);
455
456
            $query = call_user_func_array([$this->model, 'scope'.$method], $parameters);
457
458
            if ($query === false) {
459
                $this->stopQuery();
460
            }
461
462
            return $query instanceof static ? $query : $this;
463
        }
464
465
        $className = get_class($this);
466
467
        throw new BadMethodCallException("Call to undefined method {$className}::{$method}()");
468
    }
469
}
470