Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
DefaultElement.php
1<?php
10
14use Bitrix\Iblock\ORM\Fields\PropertyRelation;
31
37{
38 const DEFAULT_LIMIT = 10;
39
40 protected function getDefaultPreFilters()
41 {
42 return array_merge([new Scope(Scope::REST)], parent::getDefaultPreFilters());
43 }
44
45 public static function getAllowedList()
46 {
47 return [];
48 }
49
50 public static function getElementEntityAllowedList()
51 {
52 return [
53 'ID',
54 'NAME',
55 'IBLOCK_SECTION_ID',
56 ];
57 }
58
59 public static function getPropertyEntityAllowedList()
60 {
61 return [
62 'ID',
63 'VALUE',
64 'DESCRIPTION'
65 ];
66 }
67
78 public function getAction($iblock, $elementId, array $select = ['*'])
79 {
80 $listResult = $this->listAction($iblock, $select, [['ID', $elementId]]);
81 $element = !empty($listResult->getItems()[0]) ? $listResult->getItems()[0] : [];
82
83 return ['element' => $element];
84 }
85
98 public function listAction($iblock, $select = ['*'], $filter = [], $order = [], PageNavigation $pageNavigation = null)
99 {
100 $elementEntity = IblockTable::compileEntity($iblock);
101 $elementDataClass = $elementEntity->getDataClass();
102
103 if (!$elementEntity->getIblock()->fillRestOn())
104 {
105 throw new ArgumentException(sprintf('Restricted iblock ID:%s (%s)',
106 $elementEntity->getIblock()->getId(),
107 $elementEntity->getIblock()->getApiCode()
108 ));
109 }
110
111 $query = $elementDataClass::query();
112
113 // prepare id select
114 $query->addSelect(new ExpressionField('DISTINCT_ID', 'DISTINCT %s', 'ID'));
115
116 // prepare filter
117 if (!empty($filter))
118 {
119 $qFilter = static::prepareFilter($filter, $elementEntity);
120 $query->where($qFilter);
121 }
122
123 // prepare order
124 if (!empty($order))
125 {
126 $qOrder = static::prepareOrder($order, $elementEntity);
127 $query->setOrder($qOrder);
128 }
129
130 // prepare limit
131 $qLimit = $pageNavigation ? $pageNavigation->getLimit() : static::DEFAULT_LIMIT;
132 $qOffset = $pageNavigation ? $pageNavigation->getOffset() : 0;
133
134 // get elements
135 $result = $query
136 ->setLimit($qLimit)
137 ->setOffset($qOffset)
138 ->exec();
139
140 // count records
141 if ($result->getSelectedRowsCount() < $qLimit)
142 {
143 // optimization for first and last pages
144 $countTotal = $qOffset + $result->getSelectedRowsCount();
145 }
146 else
147 {
148 $countTotal = function () use ($query) {
149 return $query->queryCountTotal();
150 };
151 }
152
153 // get elements data
154 $elements = [];
155 $ids = [];
156
157 foreach ($result->fetchAll() as $row)
158 {
159 $ids[] = $row['DISTINCT_ID'];
160 }
161
162 if (!empty($ids))
163 {
164 $query = $elementDataClass::query();
165
166 $qSelect = static::prepareSelect($select, $elementEntity);
167 $query->setSelect($qSelect);
168
169 $query->whereIn('ID', $ids);
170
171 $resultElements = [];
172
173 foreach ($query->fetchCollection() as $elementObject)
174 {
176 $resultElements[$elementObject->getId()] = $elementObject->collectValues(Values::ALL, FieldTypeMask::ALL, true);
177 }
178
179 // original sort
180 foreach ($ids as $id)
181 {
182 $elements[] = $resultElements[$id];
183 }
184 }
185
186 return new Page('elements', $elements, $countTotal);
187 }
188
189 protected static function prepareSelect($fields, Entity $entity)
190 {
191 return static::checkFields($fields, $entity);
192 }
193
194
195 protected static function prepareFilter($filter, Entity $entity)
196 {
197 $filter = ConditionTree::createFromArray($filter);
198 $definitions = static::getFilterDefinitions($filter);
199
200 static::checkFields($definitions, $entity);
201
202 return $filter;
203 }
204
205 protected static function prepareOrder($order, Entity $entity)
206 {
207 static::checkFields(array_keys($order), $entity);
208
209 return $order;
210 }
211
212 protected static function checkFields($fields, Entity $entity)
213 {
214 $propertyEntityAllowedList = static::getPropertyEntityAllowedList();
215 $elementEntityAllowedList = static::getElementEntityAllowedList();
216 $allowedList = array_merge($elementEntityAllowedList, static::getAllowedList());
217
218 // replace REF.* for allowed fields REF.ALLOWED1, REF.ALLOWED2, etc.
219 $chainReplacement = [];
220
221 // analyze
222 foreach ($fields as $definition)
223 {
224 // check allowed list
225 if (in_array($definition, $allowedList, true))
226 {
227 continue;
228 }
229
230 // smart check for relations and property fields
231 $chain = Chain::getChainByDefinition($entity, $definition);
232 $currentDefinition = '';
233
234 $elements = $chain->getAllElements();
235 $lastElement = $chain->getLastElement();
236
237 foreach ($elements as $element)
238 {
239 $isLastElement = ($element === $lastElement);
240
241 // skip init entity
242 if ($element->getValue() instanceof ElementEntity && !$isLastElement)
243 {
244 continue;
245 }
246
247 // append definition
248 $currentDefinition = Chain::appendDefinition($currentDefinition, $element->getDefinitionFragment());
249
250 // handle wildcard
251 if ($currentDefinition === '*' && $isLastElement)
252 {
253 $chainReplacement[$definition] = [];
254
255 foreach ($elementEntityAllowedList as $allowedFieldName)
256 {
257 if ($entity->hasField($allowedFieldName))
258 {
259 $chainReplacement[$definition][] = $allowedFieldName;
260 }
261 }
262
263 continue;
264 }
265
266 // check access
267 if (!($element->getValue() instanceof Field))
268 {
269 throw new ArgumentException(
270 sprintf('Restricted field `%s`', $currentDefinition)
271 );
272 }
273
274 $currentField = $element->getValue();
275 $currentEntity = $currentField->getEntity();
276
277 // case 1. iblock
278 if ($currentEntity instanceof ElementEntity)
279 {
280 // case 1.1. iblock scalar
281 if (in_array($currentField->getName(), $elementEntityAllowedList, true))
282 {
283 continue;
284 }
285
286 // case 1.2. iblock property
287 if (!empty(class_uses($currentField)[PropertyRelation::class]))
288 {
289 if ($isLastElement)
290 {
291 // replace * with allowed fields
292 $propEntity = $currentField->getRefEntity();
293 $chainReplacement[$definition] = [];
294
295 foreach ($propertyEntityAllowedList as $allowedFieldName)
296 {
297 if ($propEntity->hasField($allowedFieldName))
298 {
299 $chainReplacement[$definition][] = Chain::appendDefinition($currentDefinition, $allowedFieldName);
300 }
301 }
302 }
303
304 continue;
305 }
306 }
307
308 // case 2. property entity
309 if ($currentEntity instanceof ValueStorageEntity)
310 {
311 // case 2.1. property scalar
312 if (in_array($currentField->getName(), $propertyEntityAllowedList, true))
313 {
314 continue;
315 }
316
317 // case 2.1. ref to another iblock
318 if ($currentField instanceof Reference)
319 {
320 $refEntity = $currentField->getRefEntity();
321
322 // check if remote iblock is readable
323 if ($refEntity instanceof ElementEntity && $refEntity->getIblock()->fillRestOn())
324 {
325 if ($isLastElement)
326 {
327 // replace * with allowed fields
328 $chainReplacement[$definition] = [];
329
330 foreach ($elementEntityAllowedList as $allowedFieldName)
331 {
332 if ($refEntity->hasField($allowedFieldName))
333 {
334 $chainReplacement[$definition][] = Chain::appendDefinition($currentDefinition, $allowedFieldName);
335 }
336 }
337 }
338
339 continue;
340 }
341 }
342 }
343
344 // restricted by default
345 throw new ArgumentException(
346 sprintf('Restricted field `%s`', $currentDefinition)
347 );
348 }
349 }
350
351 // time to replace *
352 foreach ($chainReplacement as $definition => $replacement)
353 {
354 // unset original
355 $key = array_search($definition, $fields);
356 unset($fields[$key]);
357
358 // add replacement
359 $fields = array_merge($fields, $replacement);
360 }
361
362 return $fields;
363 }
364
365 protected static function getFilterDefinitions(ConditionTree $filter)
366 {
367 $definitions = [];
368
369 foreach ($filter->getConditions() as $condition)
370 {
371 if ($condition instanceof ConditionTree)
372 {
373 // add subfilter recursively
374 $definitions = array_merge($definitions, static::getFilterDefinitions($condition));
375 }
376 else
377 {
378 // add column
379 if ($condition->getColumn() !== null)
380 {
381 $definitions[] = $condition->getColumn();
382 }
383
384 // add value
385 $values = $condition->getValue();
386 if (!is_array($values))
387 {
388 $values = [$values];
389 }
390
391 foreach ($values as $subValue)
392 {
393 if ($subValue instanceof ColumnExpression)
394 {
395 $definitions[] = $subValue->getDefinition();
396 }
397 }
398 }
399 }
400
401 return $definitions;
402 }
403}
static prepareOrder($order, Entity $entity)
static getFilterDefinitions(ConditionTree $filter)
static prepareSelect($fields, Entity $entity)
static checkFields($fields, Entity $entity)
getAction($iblock, $elementId, array $select=[' *'])
static prepareFilter($filter, Entity $entity)
static appendDefinition($currentDefinition, $newDefinitionPart)
Definition chain.php:383