Completed
Push — master ( c26f40...f7e861 )
by Nekrasov
01:21
created

ElementModel::load()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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