Completed
Push — master ( 03a1cf...94b402 )
by Nekrasov
05:34
created

BaseQuery::first()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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