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