Completed
Push — master ( a4a1ca...4311a9 )
by Nekrasov
01:52
created

ElementModel::normalizePropertyFormat()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

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