Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
chain.php
1<?php
2
4
12
13class Chain
14{
16 protected $chain;
17
18 protected $size = 0;
19
21 protected $definition;
22
25
26 protected $alias;
27
28 protected $custom_alias;
29
31 protected $forcesDataDoublingOff = false;
32
34 protected $last_element;
35
36 public function __construct()
37 {
38 $this->chain = array();
39 }
40
46 public function addElement(ChainElement $element)
47 {
48 if (empty($this->chain) && !($element->getValue() instanceof Entity))
49 {
50 throw new SystemException('The first element of chain should be Entity only.');
51 }
52
53 $this->chain[] = $element;
54 $this->definition = null;
55 $this->definitionParts = null;
56 $this->alias = null;
57
58 $this->last_element = $element;
59 $this->size++;
60 }
61
65 public function prependElement(ChainElement $element)
66 {
67 $this->chain = array_merge(array($element), $this->chain);
68 $this->definition = null;
69 $this->definitionParts = null;
70 $this->alias = null;
71
72 $this->size++;
73 }
74
78 public function prepend(Chain $chain)
79 {
80 $elements = $chain->getAllElements();
81
82 for ($i=count($elements)-1; $i>=0; $i--)
83 {
84 $this->prependElement($elements[$i]);
85 }
86 }
87
88 public function getFirstElement()
89 {
90 return $this->chain[0];
91 }
92
96 public function getLastElement()
97 {
99 }
100
104 public function getAllElements()
105 {
106 return $this->chain;
107 }
108
109 public function removeLastElement()
110 {
111 $this->chain = array_slice($this->chain, 0, -1);
112 $this->definition = null;
113 $this->definitionParts = null;
114 $this->alias = null;
115
116 $this->last_element = end($this->chain);
117 $this->size--;
118 }
119
120 public function removeFirstElement()
121 {
122 $this->chain = array_slice($this->chain, 1);
123 $this->definition = null;
124 $this->definitionParts = null;
125 $this->alias = null;
126
127 $this->size--;
128 }
129
130 public function hasBackReference()
131 {
132 foreach ($this->chain as $element)
133 {
134 if ($element->isBackReference())
135 {
136 return true;
137 }
138 }
139
140 return false;
141 }
142
143 public function getSize()
144 {
145 return $this->size;
146 }
147
153 public function getDefinition($elementsSlice = 0)
154 {
155 if (!isset($this->definition[$elementsSlice]))
156 {
157 $this->definition[$elementsSlice] = join('.',
158 $elementsSlice == 0
159 ? $this->getDefinitionParts()
160 : array_slice($this->getDefinitionParts(), 0, $elementsSlice)
161 );
162 }
163
164 return $this->definition[$elementsSlice];
165 }
166
170 public function getDefinitionParts()
171 {
172 if (is_null($this->definitionParts))
173 {
174 $this->definitionParts = static::getDefinitionPartsByChain($this);
175 }
176
178 }
179
180 public function getAlias()
181 {
182 if ($this->custom_alias !== null)
183 {
184 return $this->custom_alias;
185 }
186
187 if ($this->alias === null)
188 {
189 $this->alias = self::getAliasByChain($this);
190 }
191
192 return $this->alias;
193 }
194
195 public function setCustomAlias($alias)
196 {
197 $this->custom_alias = $alias;
198 }
199
208 public static function getChainByDefinition(Entity $init_entity, $definition)
209 {
210 if (!is_string($definition))
211 {
212 throw new Main\ArgumentException('String expected, but `'.gettype($definition).'` is given.');
213 }
214
215 $chain = new Chain;
216 $chain->addElement(new ChainElement($init_entity));
217
218 $def_elements = explode('.', $definition);
219 $def_elements_size = count($def_elements);
220
221 $prev_entity = $init_entity;
222
223 $i = 0;
224
225 foreach ($def_elements as &$def_element)
226 {
227 $is_last_elem = (++$i == $def_elements_size);
228
229 $not_found = false;
230
231 // all elements should be a Reference field or Entity
232 // normal (scalar) field can only be the last element
233
234 if ($prev_entity->hasField($def_element))
235 {
236 // field has been found at current entity
237 $field = $prev_entity->getField($def_element);
238
239 if ($field instanceof Reference)
240 {
241 $prev_entity = $field->getRefEntity();
242 }
243 elseif ($field instanceof ExpressionField)
244 {
245 // expr can be in the middle too
246 }
247 elseif ($field instanceof OneToMany)
248 {
249 $prev_entity = $field->getRefEntity();
250 }
251 elseif ($field instanceof ManyToMany)
252 {
253 $prev_entity = $field->getRefEntity();
254 }
255 elseif (!$is_last_elem)
256 {
257 throw new SystemException(sprintf(
258 'Normal fields can be only the last in chain, `%s` %s is not the last.',
259 $field->getName(), get_class($field)
260 ));
261 }
262
263 if ($is_last_elem && $field instanceof ExpressionField)
264 {
265 // we should have own copy of build_from_chains to set join aliases there
266 $field = clone $field;
267 }
268
269 $chain->addElement(new ChainElement($field));
270 }
271 elseif (Entity::isExists($def_element)
272 && Entity::getInstance($def_element)->getReferencesCountTo($prev_entity->getName()) == 1
273 )
274 {
275 // def_element is another entity with only 1 reference to current entity
276 // need to identify Reference field
277 $ref_entity = Entity::getInstance($def_element);
278 $field = end($ref_entity->getReferencesTo($prev_entity->getName()));
279
280 $prev_entity = $ref_entity;
281
282 $chain->addElement(new ChainElement(
283 array($ref_entity, $field)
284 ));
285 }
286 elseif ( ($pos_wh = strpos($def_element, ':')) > 0 )
287 {
288 $ref_entity_name = substr($def_element, 0, $pos_wh);
289
290 if (strpos($ref_entity_name, '\\') === false)
291 {
292 // if reference has no namespace, then it'is in the namespace of previous entity
293 $ref_entity_name = $prev_entity->getNamespace().$ref_entity_name;
294 }
295
296 if (
297 Entity::isExists($ref_entity_name)
298 && Entity::getInstance($ref_entity_name)->hasField($ref_field_name = substr($def_element, $pos_wh + 1))
299 && Entity::getInstance($ref_entity_name)->getField($ref_field_name) instanceof Reference
300 )
301 {
303 $reference = Entity::getInstance($ref_entity_name)->getField($ref_field_name);
304
305 if (
306 $reference->getRefEntity()->getFullName() == $prev_entity->getFullName() ||
307 is_subclass_of(
308 $prev_entity->getDataClass(),
309 $reference->getRefEntity()->getDataClass()
310 )
311 )
312 {
313 // chain element is another entity with >1 references to current entity
314 // def like NewsArticle:AUTHOR, NewsArticle:LAST_COMMENTER
315 // NewsArticle - entity, AUTHOR and LAST_COMMENTER - Reference fields
316 $chain->addElement(new ChainElement(array(
317 Entity::getInstance($ref_entity_name),
318 Entity::getInstance($ref_entity_name)->getField($ref_field_name)
319 )));
320
321 $prev_entity = Entity::getInstance($ref_entity_name);
322 }
323 else
324 {
325 $not_found = true;
326 }
327 }
328 else
329 {
330 $not_found = true;
331 }
332
333 }
334 elseif ($def_element == '*' && $is_last_elem)
335 {
336 continue;
337 }
338 else
339 {
340 // unknown chain
341 $not_found = true;
342 }
343
344 if ($not_found)
345 {
346 throw new SystemException(sprintf(
347 'Unknown field definition `%s` (%s) for %s Entity.',
348 $def_element, $definition, $prev_entity->getFullName()
349 ), 100);
350 }
351 }
352
353 return $chain;
354 }
355
356 public static function getDefinitionByChain(Chain $chain)
357 {
358 return join('.', static::getDefinitionPartsByChain($chain));
359 }
360
361 public static function getDefinitionPartsByChain(Chain $chain)
362 {
363 $def = array();
364
365 // add members of chain except of init entity
367 $elements = array_slice($chain->getAllElements(), 1);
368
369 foreach ($elements as $element)
370 {
371 //if ($element->getValue() instanceof ExpressionField && $element !== end($elements))
372 {
373 // skip non-last expressions
374 //continue;
375 }
376
377 $def[] = $element->getDefinitionFragment();
378 }
379
380 return $def;
381 }
382
383 public static function appendDefinition($currentDefinition, $newDefinitionPart)
384 {
385 if ($currentDefinition !== '')
386 {
387 $currentDefinition .= '.';
388 }
389
390 return $currentDefinition.$newDefinitionPart;
391 }
392
393 public static function getAliasByChain(Chain $chain)
394 {
395 $alias = array();
396
397 $elements = $chain->getAllElements();
398
399 // add prefix of init entity
400 if (count($elements) > 2)
401 {
402 $alias[] = $chain->getFirstElement()->getAliasFragment();
403 }
404
405 // add other members of chain
407 $elements = array_slice($elements, 1);
408
409 foreach ($elements as $element)
410 {
411 $fragment = $element->getAliasFragment();
412
413 if($fragment <> '')
414 {
415 $alias[] = $fragment;
416 }
417 }
418
419 return join('_', $alias);
420 }
421
430 public static function getAliasByDefinition(Entity $entity, $definition)
431 {
432 return self::getChainByDefinition($entity, $definition)->getAlias();
433 }
434
439 public function hasAggregation()
440 {
441 $elements = array_reverse($this->chain);
442
443 foreach ($elements as $element)
444 {
446 if ($element->getValue() instanceof ExpressionField && $element->getValue()->isAggregated())
447 {
448 return true;
449 }
450 }
451
452 return false;
453 }
454
459 public function hasSubquery()
460 {
461 $elements = array_reverse($this->chain);
462
463 foreach ($elements as $element)
464 {
466 if ($element->getValue() instanceof ExpressionField && $element->getValue()->hasSubquery())
467 {
468 return true;
469 }
470 }
471
472 return false;
473 }
474
475 public function isConstant()
476 {
477 return ($this->getLastElement()->getValue() instanceof ExpressionField
478 && $this->getLastElement()->getValue()->isConstant());
479 }
480
481 public function forceDataDoublingOff()
482 {
483 $this->forcesDataDoublingOff = true;
484 }
485
486 public function forcesDataDoublingOff()
487 {
489 }
490
497 public function getSqlDefinition($with_alias = false)
498 {
499 $sql_def = $this->getLastElement()->getSqlDefinition();
500
501 if ($with_alias)
502 {
503 $helper = $this->getLastElement()->getValue()->getEntity()->getConnection()->getSqlHelper();
504 $sql_def .= ' AS ' . $helper->quote($this->getAlias());
505 }
506
507 return $sql_def;
508 }
509
510 public function __clone()
511 {
512 $this->custom_alias = null;
513 }
514
515 public function dump()
516 {
517 echo ' '.' forcesDataDoublingOff: '.($this->forcesDataDoublingOff()?'true':'false');
518 echo PHP_EOL;
519
520 $i = 0;
521 foreach ($this->chain as $elem)
522 {
523 echo ' '.++$i.'. ';
524 $elem->dump();
525 echo PHP_EOL;
526 }
527 }
528}
static isExists($name)
Definition entity.php:759
static getInstance($entityName)
Definition entity.php:103
static appendDefinition($currentDefinition, $newDefinitionPart)
Definition chain.php:383
static getDefinitionByChain(Chain $chain)
Definition chain.php:356
prependElement(ChainElement $element)
Definition chain.php:65
addElement(ChainElement $element)
Definition chain.php:46
prepend(Chain $chain)
Definition chain.php:78
getSqlDefinition($with_alias=false)
Definition chain.php:497
static getAliasByDefinition(Entity $entity, $definition)
Definition chain.php:430
getDefinition($elementsSlice=0)
Definition chain.php:153