Completed
Push — master ( 4e1d85...893d4f )
by Nekrasov
02:14
created

ElementModel::create()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 8
Ratio 100 %

Importance

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