Completed
Push — master ( 889fac...47d4c3 )
by Nekrasov
01:32
created

ElementModel::sectionModel()   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\Models;
4
5
use Arrilot\BitrixModels\Exceptions\ExceptionFromBitrix;
6
use Arrilot\BitrixModels\Queries\ElementQuery;
7
use CIBlock;
8
use Illuminate\Support\Collection;
9
use LogicException;
10
11
/**
12
 * ElementQuery methods
13
 * @method static ElementQuery groupBy($value)
14
 * @method static ElementModel getByCode(string $code)
15
 * @method static ElementModel getByExternalId(string $id)
16
 *
17
 * Base Query methods
18
 * @method static Collection getList()
19
 * @method static ElementModel first()
20
 * @method static ElementModel getById(int $id)
21
 * @method static ElementQuery sort(string|array $by, string $order='ASC')
22
 * @method static ElementQuery order(string|array $by, string $order='ASC') // same as sort()
23
 * @method static ElementQuery filter(array $filter)
24
 * @method static ElementQuery addFilter(array $filters)
25
 * @method static ElementQuery resetFilter()
26
 * @method static ElementQuery navigation(array $filter)
27
 * @method static ElementQuery select($value)
28
 * @method static ElementQuery keyBy(string $value)
29
 * @method static ElementQuery limit(int $value)
30
 * @method static ElementQuery offset(int $value)
31
 * @method static ElementQuery page(int $num)
32
 * @method static ElementQuery take(int $value) // same as limit()
33
 * @method static ElementQuery forPage(int $page, int $perPage=15)
34
 * @method static \Illuminate\Pagination\LengthAwarePaginator paginate(int $perPage = 15, string $pageName = 'page')
35
 * @method static \Illuminate\Pagination\Paginator simplePaginate(int $perPage = 15, string $pageName = 'page')
36
 * @method static ElementQuery stopQuery()
37
 * @method static ElementQuery cache(float|int $minutes)
38
 *
39
 * Scopes
40
 * @method static ElementQuery active()
41
 * @method static ElementQuery sortByDate(string $sort = 'DESC')
42
 * @method static ElementQuery fromSectionWithId(int $id)
43
 * @method static ElementQuery fromSectionWithCode(string $code)
44
 */
45
class ElementModel extends BitrixModel
46
{
47
    /**
48
     * Corresponding IBLOCK_ID
49
     *
50
     * @var int
51
     */
52
    const IBLOCK_ID = null;
53
54
    /**
55
     * IBLOCK version (1 or 2)
56
     *
57
     * @var int
58
     */
59
    const IBLOCK_VERSION = 2;
60
61
    /**
62
     * Bitrix entity object.
63
     *
64
     * @var object
65
     */
66
    public static $bxObject;
67
68
    /**
69
     * Corresponding object class name.
70
     *
71
     * @var string
72
     */
73
    protected static $objectClass = 'CIBlockElement';
74
75
    /**
76
     * Have sections been already fetched from DB?
77
     *
78
     * @var bool
79
     */
80
    protected $sectionsAreFetched = false;
81
82
    /**
83
     * Update search after each create or update.
84
     *
85
     * @var bool
86
     */
87
    protected static $updateSearch = true;
88
89
    /**
90
     * Getter for corresponding iblock id.
91
     *
92
     * @throws LogicException
93
     *
94
     * @return int
95
     */
96
    public static function iblockId()
97
    {
98
        $id = static::IBLOCK_ID;
99
        if (!$id) {
100
            throw new LogicException('You must set IBLOCK_ID constant inside a model or override iblockId() method');
101
        }
102
        
103
        return $id;
104
    }
105
106
    /**
107
     * Create new item in database.
108
     *
109
     * @param $fields
110
     *
111
     * @throws LogicException
112
     *
113
     * @return static|bool
114
     */
115 View Code Duplication
    public static function create($fields)
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...
116
    {
117
        if (!isset($fields['IBLOCK_ID'])) {
118
            $fields['IBLOCK_ID'] = static::iblockId();
119
        }
120
121
        return static::internalCreate($fields);
122
    }
123
124
    public static function internalDirectCreate($bxObject, $fields)
125
    {
126
        return $bxObject->add($fields, false, static::$updateSearch);
127
    }
128
129
    /**
130
     * Corresponding section model full qualified class name.
131
     * MUST be overridden if you are going to use section model for this iblock.
132
     *
133
     * @throws LogicException
134
     *
135
     * @return string
136
     */
137
    public static function sectionModel()
138
    {
139
        throw new LogicException('public static function sectionModel() MUST be overridden');
140
    }
141
142
    /**
143
     * Instantiate a query object for the model.
144
     *
145
     * @return ElementQuery
146
     */
147
    public static function query()
148
    {
149
        return new ElementQuery(static::instantiateObject(), get_called_class());
150
    }
151
152
    /**
153
     * Scope to sort by date.
154
     *
155
     * @param ElementQuery $query
156
     * @param string       $sort
157
     *
158
     * @return ElementQuery
159
     */
160
    public function scopeSortByDate($query, $sort = 'DESC')
161
    {
162
        return $query->sort(['ACTIVE_FROM' => $sort]);
163
    }
164
165
    /**
166
     * Scope to get only items from a given section.
167
     *
168
     * @param ElementQuery $query
169
     * @param mixed        $id
170
     *
171
     * @return ElementQuery
172
     */
173
    public function scopeFromSectionWithId($query, $id)
174
    {
175
        $query->filter['SECTION_ID'] = $id;
176
177
        return $query;
178
    }
179
180
    /**
181
     * Scope to get only items from a given section.
182
     *
183
     * @param ElementQuery $query
184
     * @param string       $code
185
     *
186
     * @return ElementQuery
187
     */
188
    public function scopeFromSectionWithCode($query, $code)
189
    {
190
        $query->filter['SECTION_CODE'] = $code;
191
192
        return $query;
193
    }
194
195
    /**
196
     * Fill extra fields when $this->field is called.
197
     *
198
     * @return null
199
     */
200
    protected function afterFill()
201
    {
202
        $this->normalizePropertyFormat();
203
    }
204
205
    /**
206
     * Load all model attributes from cache or database.
207
     *
208
     * @return $this
209
     */
210
    public function load()
211
    {
212
        $this->getFields();
213
214
        return $this;
215
    }
216
217
    /**
218
     * Get element's sections from cache or database.
219
     *
220
     * @return array
221
     */
222
    public function getSections()
223
    {
224
        if ($this->sectionsAreFetched) {
225
            return $this->fields['IBLOCK_SECTION'];
226
        }
227
228
        return $this->refreshSections();
229
    }
230
231
    /**
232
     * Refresh model from database and place data to $this->fields.
233
     *
234
     * @return array
235
     */
236
    public function refresh()
237
    {
238
        return $this->refreshFields();
239
    }
240
241
    /**
242
     * Refresh element's fields and save them to a class field.
243
     *
244
     * @return array
245
     */
246 View Code Duplication
    public function refreshFields()
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...
247
    {
248
        if ($this->id === null) {
249
            return  $this->fields = [];
250
        }
251
252
        $sectionsBackup = isset($this->fields['IBLOCK_SECTION']) ? $this->fields['IBLOCK_SECTION'] : null;
253
254
        $this->fields = static::query()->getById($this->id)->fields;
255
256
        if (!empty($sectionsBackup)) {
257
            $this->fields['IBLOCK_SECTION'] = $sectionsBackup;
258
        }
259
260
        $this->fieldsAreFetched = true;
261
262
        return $this->fields;
263
    }
264
265
    /**
266
     * Refresh element's sections and save them to a class field.
267
     *
268
     * @return array
269
     */
270
    public function refreshSections()
271
    {
272
        if ($this->id === null) {
273
            return [];
274
        }
275
276
        $this->fields['IBLOCK_SECTION'] = static::$bxObject->getElementGroups($this->id, true);
277
        $this->sectionsAreFetched = true;
278
279
        return $this->fields['IBLOCK_SECTION'];
280
    }
281
282
    /**
283
     * @deprecated in favour of `->section()`
284
     * Get element direct section as ID or array of fields.
285
     *
286
     * @param bool $load
287
     *
288
     * @return false|int|array
289
     */
290
    public function getSection($load = false)
291
    {
292
        $fields = $this->getFields();
293
        if (!$load) {
294
            return $fields['IBLOCK_SECTION_ID'];
295
        }
296
297
        /** @var SectionModel $sectionModel */
298
        $sectionModel = static::sectionModel();
299
        if (!$fields['IBLOCK_SECTION_ID']) {
300
            return false;
301
        }
302
303
        return $sectionModel::query()->getById($fields['IBLOCK_SECTION_ID'])->toArray();
304
    }
305
306
    /**
307
     * Get element direct section as model object.
308
     *
309
     * @param bool $load
310
     *
311
     * @return false|SectionModel
312
     */
313
    public function section($load = false)
314
    {
315
        $fields = $this->getFields();
316
317
        /** @var SectionModel $sectionModel */
318
        $sectionModel = static::sectionModel();
319
320
        return $load
321
            ? $sectionModel::query()->getById($fields['IBLOCK_SECTION_ID'])
322
            : new $sectionModel($fields['IBLOCK_SECTION_ID']);
323
    }
324
325
    /**
326
     * Proxy for GetPanelButtons
327
     *
328
     * @param array $options
329
     * @return array
330
     */
331
    public function getPanelButtons($options = [])
332
    {
333
        return CIBlock::GetPanelButtons(
334
            static::iblockId(),
335
            $this->id,
336
            0,
337
            $options
338
        );
339
    }
340
341
    /**
342
     * Save props to database.
343
     * If selected is not empty then only props from it are saved.
344
     *
345
     * @param array $selected
346
     *
347
     * @return bool
348
     */
349
    public function saveProps($selected = [])
350
    {
351
        $propertyValues = $this->constructPropertyValuesForSave($selected);
352
        if (empty($propertyValues)) {
353
            return false;
354
        }
355
356
        $bxMethod = empty($selected) ? 'setPropertyValues' : 'setPropertyValuesEx';
357
        static::$bxObject->$bxMethod(
358
            $this->id,
359
            static::iblockId(),
360
            $propertyValues
361
        );
362
363
        return true;
364
    }
365
366
    /**
367
     * Normalize properties's format converting it to 'PROPERTY_"CODE"_VALUE'.
368
     *
369
     * @return null
370
     */
371
    protected function normalizePropertyFormat()
372
    {
373
        if (empty($this->fields['PROPERTIES'])) {
374
            return;
375
        }
376
377
        foreach ($this->fields['PROPERTIES'] as $code => $prop) {
378
            $this->fields['PROPERTY_'.$code.'_VALUE'] = $prop['VALUE'];
379
            $this->fields['~PROPERTY_'.$code.'_VALUE'] = $prop['~VALUE'];
380
            $this->fields['PROPERTY_'.$code.'_DESCRIPTION'] = $prop['DESCRIPTION'];
381
            $this->fields['~PROPERTY_'.$code.'_DESCRIPTION'] = $prop['~DESCRIPTION'];
382
            $this->fields['PROPERTY_'.$code.'_VALUE_ID'] = $prop['PROPERTY_VALUE_ID'];
383
        }
384
    }
385
386
    /**
387
     * Construct 'PROPERTY_VALUES' => [...] from flat fields array.
388
     * This is used in save.
389
     * If $selectedFields are specified only those are saved.
390
     *
391
     * @param $selectedFields
392
     *
393
     * @return array
394
     */
395
    protected function constructPropertyValuesForSave($selectedFields = [])
396
    {
397
        $propertyValues = [];
398
        $saveOnlySelected = !empty($selectedFields);
399
        foreach ($this->fields as $code => $value) {
0 ignored issues
show
Bug introduced by
The expression $this->fields of type null|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
400
            if ($saveOnlySelected && !in_array($code, $selectedFields)) {
401
                continue;
402
            }
403
404
            if (preg_match('/^PROPERTY_(.*)_VALUE$/', $code, $matches) && !empty($matches[1])) {
405
                $propertyValues[$matches[1]] = $value;
406
            }
407
        }
408
409
        return $propertyValues;
410
    }
411
412
    /**
413
     * @param $fields
414
     * @param $fieldsSelectedForSave
415
     * @return bool
416
     */
417
    protected function internalUpdate($fields, $fieldsSelectedForSave)
418
    {
419
        $result = !empty($fields) ? static::$bxObject->update($this->id, $fields, false, static::$updateSearch) : false;
420
        $savePropsResult = $this->saveProps($fieldsSelectedForSave);
421
        $result = $result || $savePropsResult;
422
423
        return $result;
424
    }
425
426
    /**
427
     * @param $value
428
     */
429
    public static function setUpdateSearch($value)
430
    {
431
        static::$updateSearch = $value;
432
    }
433
}
434