Bitrix-D7  20.0.0
main/lib/orm/query/query.php
См. документацию.
1 <?php
2 
3 namespace Bitrix\Main\ORM\Query;
4 
5 use Bitrix\Main;
18 
19 /**
20  * Query builder for Entities.
21  *
22  * Virtual WHERE methods (proxy to Filter):
23  *
24  * @method $this where(...$filter)
25  * @see Filter::where()
26  *
27  * @method $this whereNot(...$filter)
28  * @see Filter::whereNot()
29  *
30  * @method $this whereColumn(...$filter)
31  * @see Filter::whereColumn()
32  *
33  * @method $this whereNull($column)
34  * @see Filter::whereNull()
35  *
36  * @method $this whereNotNull($column)
37  * @see Filter::whereNotNull()
38  *
39  * @method $this whereIn($column, $values)
40  * @see Filter::whereIn()
41  *
42  * @method $this whereNotIn($column, $values)
43  * @see Filter::whereNotIn()
44  *
45  * @method $this whereBetween($column, $valueMin, $valueMax)
46  * @see Filter::whereBetween()
47  *
48  * @method $this whereNotBetween($column, $valueMin, $valueMax)
49  * @see Filter::whereNotBetween()
50  *
51  * @method $this whereLike($column, $value)
52  * @see Filter::whereLike()
53  *
54  * @method $this whereNotLike($column, $value)
55  * @see Filter::whereNotLike()
56  *
57  * @method $this whereExists($query)
58  * @see Filter::whereExists()
59  *
60  * @method $this whereNotExists($query)
61  * @see Filter::whereNotExists()
62  *
63  * @method $this whereMatch($column, $value)
64  * @see Filter::whereMatch()
65  *
66  * @method $this whereNotMatch($column, $value)
67  * @see Filter::whereNotMatch()
68  *
69  * Virtual HAVING methods (proxy to Filter):
70  *
71  * @method $this having(...$filter)
72  * @see Filter::where()
73  *
74  * @method $this havingNot(...$filter)
75  * @see Filter::whereNot()
76  *
77  * @method $this havingColumn(...$filter)
78  * @see Filter::whereColumn()
79  *
80  * @method $this havingNull($column)
81  * @see Filter::whereNull()
82  *
83  * @method $this havingNotNull($column)
84  * @see Filter::whereNotNull()
85  *
86  * @method $this havingIn($column, $values)
87  * @see Filter::whereIn()
88  *
89  * @method $this havingNotIn($column, $values)
90  * @see Filter::whereNotIn()
91  *
92  * @method $this havingBetween($column, $valueMin, $valueMax)
93  * @see Filter::whereBetween()
94  *
95  * @method $this havingNotBetween($column, $valueMin, $valueMax)
96  * @see Filter::whereNotBetween()
97  *
98  * @method $this havingLike($column, $value)
99  * @see Filter::whereLike()
100  *
101  * @method $this havingNotLike($column, $value)
102  * @see Filter::whereNotLike()
103  *
104  * @method $this havingExists($query)
105  * @see Filter::whereExists()
106  *
107  * @method $this havingNotExists($query)
108  * @see Filter::whereNotExists()
109  *
110  * @package Bitrix\Main\ORM
111  */
112 class Query
113 {
114  /** @var Entity */
115  protected $entity;
116 
117  protected
118  $select = array(),
119  $group = array(),
120  $order = array(),
121  $limit = null,
122  $offset = null,
123  $countTotal = null;
124 
125  // deprecated array filter format
126  protected
127  $filter = array(),
128  $where = array(),
129  $having = array();
130 
131  /** @var Filter */
132  protected $filterHandler;
133 
134  /** @var Filter */
135  protected $whereHandler;
136 
137  /** @var Filter */
138  protected $havingHandler;
139 
140  /**
141  * @var Chain[]
142  */
143  protected // all chain storages keying by alias
144  $select_chains = array(),
145  $group_chains = array(),
146  $order_chains = array();
147 
148  /**
149  * @var Chain[]
150  */
151  protected
152  $filter_chains = array(),
153  $where_chains = array(),
154  $having_chains = array();
155 
156  /**
157  * @var Chain[]
158  */
159  protected
160  $select_expr_chains = array(), // from select expr "build_from"
161  $having_expr_chains = array(), // from having expr "build_from"
162  $hidden_chains = array(); // all expr "build_from" elements;
163 
164  /** @var Chain[] */
165  protected $runtime_chains;
166 
167  /** @var Chain[] */
168  protected $global_chains = array(); // keying by both def and alias
169 
170  /** @var string[] */
172 
173  /** @var Expression */
174  protected static $expressionHelper;
175 
176  /**
177  * Enable or Disable data doubling for 1:N relations in query filter
178  * If disabled, 1:N entity fields in filter will be transformed to exists() subquery
179  * @var bool
180  */
181  protected $data_doubling_off = false;
182 
183  /** @var string */
184  protected $table_alias_postfix = '';
185 
186  /** @var string Custom alias for the table of the init entity */
187  protected $custom_base_table_alias = null;
188 
189  /** @var array */
190  protected $join_map = array();
191 
192  /** @var array list of used joins */
193  protected $join_registry;
194 
195  /** @var Union */
196  protected $unionHandler;
197 
198  /** @var bool */
199  protected $is_executing = false;
200 
201  /** @var string Last executed SQL query */
202  protected static $last_query;
203 
204  /** @var array Replaced field aliases */
205  protected $replaced_aliases;
206 
207  /** @var array Replaced table aliases */
209 
210  /** @var callable[] */
211  protected $selectFetchModifiers = array();
212 
213  protected
215  $cacheJoins = false;
216 
217  /**
218  * @param Entity|Query|string $source
219  *
220  * @throws Main\ArgumentException
221  * @throws Main\SystemException
222  */
223  public function __construct($source)
224  {
225  if ($source instanceof $this)
226  {
227  $this->entity = Entity::getInstanceByQuery($source);
228  }
229  elseif ($source instanceof Entity)
230  {
231  $this->entity = clone $source;
232  }
233  elseif (is_string($source))
234  {
235  $this->entity = clone Entity::getInstance($source);
236  }
237  else
238  {
239  throw new Main\ArgumentException(sprintf(
240  'Unknown source type "%s" for new %s', gettype($source), __CLASS__
241  ));
242  }
243 
244  $this->filterHandler = static::filter();
245  $this->whereHandler = static::filter();
246  $this->havingHandler = static::filter();
247  }
248 
249  /**
250  * @param $method
251  * @param $arguments
252  *
253  * @return $this
254  * @throws Main\SystemException
255  */
256  public function __call($method, $arguments)
257  {
258  // where and having proxies
259  if (substr($method, 0, 6) === 'having')
260  {
261  $method = str_replace('having', 'where', $method);
262  }
263 
264  if (substr($method, 0, 5) === 'where')
265  {
266  if (method_exists($this->filterHandler, $method))
267  {
268  call_user_func_array(
269  array($this->filterHandler, $method),
270  $arguments
271  );
272 
273  return $this;
274  }
275  }
276 
277  throw new Main\SystemException("Unknown method `{$method}`");
278  }
279 
280  /**
281  * Returns an array of fields for SELECT clause
282  *
283  * @return array
284  */
285  public function getSelect()
286  {
287  return $this->select;
288  }
289 
290  /**
291  * Sets a list of fields for SELECT clause
292  *
293  * @param array $select
294  * @return Query
295  */
296  public function setSelect(array $select)
297  {
298  $this->select = $select;
299  return $this;
300  }
301 
302  /**
303  * Adds a field for SELECT clause
304  *
305  * @param mixed $definition Field
306  * @param string $alias Field alias like SELECT field AS alias
307  * @return $this
308  */
309  public function addSelect($definition, $alias = '')
310  {
311  if (strlen($alias))
312  {
313  $this->select[$alias] = $definition;
314  }
315  else
316  {
317  $this->select[] = $definition;
318  }
319 
320  return $this;
321  }
322 
323  /**
324  * Returns an array of filters for WHERE clause
325  *
326  * @return array
327  */
328  public function getFilter()
329  {
330  return $this->filter;
331  }
332 
333  /**
334  * Sets a list of filters for WHERE clause
335  *
336  * @param array $filter
337  * @return $this
338  */
339  public function setFilter(array $filter)
340  {
341  $this->filter = $filter;
342  return $this;
343  }
344 
345  /**
346  * Adds a filter for WHERE clause
347  *
348  * @param string $key
349  * @param mixed $value
350  * @return $this
351  */
352  public function addFilter($key, $value)
353  {
354  if (is_null($key) && is_array($value))
355  {
356  $this->filter[] = $value;
357  }
358  else
359  {
360  $this->filter[$key] = $value;
361  }
362 
363  return $this;
364  }
365 
366  /**
367  * Returns an array of fields for GROUP BY clause
368  *
369  * @return array
370  */
371  public function getGroup()
372  {
373  return $this->group;
374  }
375 
376  /**
377  * Sets a list of fields in GROUP BY clause
378  *
379  * @param mixed $group
380  * @return $this
381  */
382  public function setGroup($group)
383  {
384  $group = !is_array($group) ? array($group) : $group;
385  $this->group = $group;
386 
387  return $this;
388  }
389 
390  /**
391  * Adds a field to the list of fields for GROUP BY clause
392  *
393  * @param $group
394  * @return $this
395  */
396  public function addGroup($group)
397  {
398  $this->group[] = $group;
399  return $this;
400  }
401 
402  /**
403  * Returns an array of fields for ORDER BY clause
404  *
405  * @return array
406  */
407  public function getOrder()
408  {
409  return $this->order;
410  }
411 
412  /**
413  * Sets a list of fields for ORDER BY clause.
414  * Format:
415  * setOrder('ID') -- ORDER BY `ID` ASC
416  * setOrder(['ID' => 'DESC', 'NAME' => 'ASC]) -- ORDER BY `ID` DESC, `NAME` ASC
417  *
418  * @param mixed $order
419  *
420  * @return $this
421  * @throws Main\ArgumentException
422  * @throws Main\SystemException
423  */
424  public function setOrder($order)
425  {
426  $this->order = array();
427 
428  if (!is_array($order))
429  {
430  $order = array($order);
431  }
432 
433  foreach ($order as $k => $v)
434  {
435  if (is_numeric($k))
436  {
437  $this->addOrder($v);
438  }
439  else
440  {
441  $this->addOrder($k, $v);
442  }
443  }
444 
445  return $this;
446  }
447 
448  /**
449  * Adds a filed to the list of fields for ORDER BY clause
450  *
451  * @param string $definition
452  * @param string $order
453  *
454  * @return $this
455  * @throws Main\ArgumentException
456  * @throws Main\SystemException
457  */
458  public function addOrder($definition, $order = 'ASC')
459  {
460  $order = strtoupper($order);
461 
462  if (!in_array($order, array('ASC', 'DESC'), true))
463  {
464  throw new Main\ArgumentException(sprintf('Invalid order "%s"', $order));
465  }
466 
467  $connection = $this->entity->getConnection();
468  $helper = $connection->getSqlHelper();
469 
470  if ($order == 'ASC')
471  {
472  $order = $helper->getAscendingOrder();
473  }
474  else
475  {
476  $order = $helper->getDescendingOrder();
477  }
478 
479  $this->order[$definition] = $order;
480 
481  return $this;
482  }
483 
484  /**
485  * Returns a limit
486  *
487  * @return null|int
488  */
489  public function getLimit()
490  {
491  return $this->limit;
492  }
493 
494  /**
495  * Sets a limit for LIMIT n clause
496  *
497  * @param int $limit
498  * @return $this
499  */
500  public function setLimit($limit)
501  {
502  $this->limit = $limit;
503  return $this;
504  }
505 
506  /**
507  * Returns an offset
508  *
509  * @return null|int
510  */
511  public function getOffset()
512  {
513  return $this->offset;
514  }
515 
516  /**
517  * Sets an offset for LIMIT n, m clause
518 
519  * @param int $offset
520  * @return $this
521  */
522  public function setOffset($offset)
523  {
524  $this->offset = $offset;
525  return $this;
526  }
527 
528  public function countTotal($count = null)
529  {
530  if ($count === null)
531  {
532  return $this->countTotal;
533  }
534  else
535  {
536  $this->countTotal = (bool) $count;
537  return $this;
538  }
539  }
540 
541  /**
542  * Puts additional query to union with current.
543  * Accepts one ore more Query / SqlExpression.
544  *
545  * @return $this
546  * @throws Main\ArgumentException
547  * @throws Main\SystemException
548  */
549  public function union()
550  {
551  foreach (func_get_args() as $arg)
552  {
553  $this->getUnionHandler()->addQuery(new UnionCondition($arg, false));
554  }
555 
556  return $this;
557  }
558 
559  /**
560  * Puts additional query to union (all) with current.
561  * Accepts one ore more Query / SqlExpression.
562  *
563  * @return $this
564  * @throws Main\ArgumentException
565  * @throws Main\SystemException
566  */
567  public function unionAll()
568  {
569  foreach (func_get_args() as $arg)
570  {
571  $this->getUnionHandler()->addQuery(new UnionCondition($arg, true));
572  }
573 
574  return $this;
575  }
576 
577  /**
578  * General order for all the union queries.
579  * Has the same format as Query::setOrder().
580  * @see Query::setOrder()
581  *
582  * @param $order
583  *
584  * @return $this
585  * @throws Main\SystemException
586  */
587  public function setUnionOrder($order)
588  {
589  $this->getUnionHandler()->setOrder($order);
590  return $this;
591  }
592 
593  /**
594  * General order for all the union queries.
595  * Has the same format as Query::addOrder().
596  * @see Query::addOrder()
597  *
598  * @param string $definition
599  * @param string $order
600  *
601  * @return $this
602  * @throws Main\ArgumentException
603  * @throws Main\SystemException
604  */
605  public function addUnionOrder($definition, $order = 'ASC')
606  {
607  $this->getUnionHandler()->addOrder($definition, $order);
608  return $this;
609  }
610 
611  /**
612  * General limit for all the union queries.
613  *
614  * @param $limit
615  *
616  * @return $this
617  * @throws Main\SystemException
618  */
619  public function setUnionLimit($limit)
620  {
621  $this->getUnionHandler()->setLimit($limit);
622  return $this;
623  }
624 
625  /**
626  * General offset for all the union queries.
627  *
628  * @param $offset
629  *
630  * @return $this
631  * @throws Main\SystemException
632  */
633  public function setUnionOffset($offset)
634  {
635  $this->getUnionHandler()->setOffset($offset);
636  return $this;
637  }
638 
639  /**
640  * @see disableDataDoubling
641  *
642  * @return $this
643  */
644  public function enableDataDoubling()
645  {
646  $this->data_doubling_off = false;
647 
648  return $this;
649  }
650 
651  /**
652  * Replaces all 1:N relations in filter to ID IN (subquery SELECT ID FROM <1:N relation>)
653  * Available for Entities with 1 primary field only
654  *
655  * @return $this
656  */
657  public function disableDataDoubling()
658  {
659  if (count($this->entity->getPrimaryArray()) !== 1)
660  {
661  // mssql doesn't support constructions WHERE (col1, col2) IN (SELECT col1, col2 FROM SomeOtherTable)
662  /* @see http://connect.microsoft.com/SQLServer/feedback/details/299231/add-support-for-ansi-standard-row-value-constructors */
663  trigger_error(sprintf(
664  'Disabling data doubling available for Entities with 1 primary field only. Number of primaries of your entity `%s` is %d.',
665  $this->entity->getFullName(), count($this->entity->getPrimaryArray())
666  ), E_USER_WARNING);
667  }
668  else
669  {
670  $this->data_doubling_off = true;
671  }
672 
673  return $this;
674  }
675 
676  /**
677  * Adds a runtime field (being created dynamically, opposite to being described statically in the entity map)
678  *
679  * @param string|null $name
680  * @param array|Field $fieldInfo
681  *
682  * @return $this
683  * @throws Main\ArgumentException
684  * @throws Main\SystemException
685  */
686  public function registerRuntimeField($name, $fieldInfo = null)
687  {
688  if ($name instanceof Field && $fieldInfo === null)
689  {
690  // short call for Field objects
691  $fieldInfo = $name;
692  $name = $fieldInfo->getName();
693  }
694  elseif ((empty($name) || is_numeric($name)) && $fieldInfo instanceof Field)
695  {
696  $name = $fieldInfo->getName();
697  }
698 
699  // clone field as long as Field object could be initialized only once
700  // there is no need to initialize original object
701  if ($fieldInfo instanceof Field)
702  {
703  $fieldInfo = clone $fieldInfo;
704  }
705 
706  // attach field to the entity
707  $this->entity->addField($fieldInfo, $name);
708 
709  // force chain creation for further needs
710  $chain = $this->getRegisteredChain($name, true);
711  $this->registerChain('runtime', $chain);
712 
713  if ($chain->getLastElement()->getValue() instanceof ExpressionField)
714  {
715  $this->collectExprChains($chain, array('hidden'));
716  }
717 
718  return $this;
719  }
720 
721  public function setTableAliasPostfix($postfix)
722  {
723  $this->table_alias_postfix = $postfix;
724  return $this;
725  }
726 
727  public function getTableAliasPostfix()
728  {
730  }
731 
732  /**
733  * Sets a custom alias for the table of the init entity
734  *
735  * @param string $alias
736  *
737  * @return $this
738  */
739  public function setCustomBaseTableAlias($alias)
740  {
741  $this->custom_base_table_alias = $alias;
742  return $this;
743  }
744 
745  /**
746  * Returns new instance of Filter.
747  *
748  * Usage:
749  * Query::filter()->where(...)
750  *
751  * Alternatively short calls Query::where* can be used.
752  * @see Query::where()
753  *
754  * @return Filter
755  */
756  public static function filter()
757  {
758  return new Filter;
759  }
760 
761  /**
762  * Used to create ExpressionField in a short way.
763  * @see Filter::where()
764  *
765  * @return Expression
766  */
767  public static function expr()
768  {
769  if (static::$expressionHelper === null)
770  {
771  static::$expressionHelper = new Expression;
772  }
773 
774  return static::$expressionHelper;
775  }
776 
777  /**
778  * Builds and executes the query and returns the result
779  *
780  * @return Result
781  * @throws Main\ObjectPropertyException
782  * @throws Main\SystemException
783  */
784  public function exec()
785  {
786  $this->is_executing = true;
787 
788  $query = $this->buildQuery();
789 
790  $cacheId = "";
791  $ttl = 0;
792  $result = null;
793 
794  if($this->cacheTtl > 0 && (empty($this->join_map) || $this->cacheJoins == true))
795  {
796  $ttl = $this->entity->getCacheTtl($this->cacheTtl);
797  }
798 
799  if($ttl > 0)
800  {
801  $cacheId = md5($query);
802  $result = $this->entity->readFromCache($ttl, $cacheId, $this->countTotal);
803  }
804 
805  if($result === null)
806  {
807  $result = $this->query($query);
808 
809  if($ttl > 0)
810  {
811  $result = $this->entity->writeToCache($result, $cacheId, $this->countTotal);
812  }
813  }
814 
815  $this->is_executing = false;
816 
817  return new Result($this, $result);
818  }
819 
820  /**
821  * Short alias for $result->fetch()
822  *
823  * @param Main\Text\Converter|null $converter
824  *
825  * @return array|false
826  * @throws Main\ObjectPropertyException
827  * @throws Main\SystemException
828  */
829  public function fetch(\Bitrix\Main\Text\Converter $converter = null)
830  {
831  return $this->exec()->fetch($converter);
832  }
833 
834  /**
835  * Short alias for $result->fetchAll()
836  *
837  * @param Main\Text\Converter|null $converter
838  *
839  * @return array
840  * @throws Main\ObjectPropertyException
841  * @throws Main\SystemException
842  */
843  public function fetchAll(\Bitrix\Main\Text\Converter $converter = null)
844  {
845  return $this->exec()->fetchAll($converter);
846  }
847 
848  /**
849  * Short alias for $result->fetchObject()
850  *
851  * @return null Actual type should be annotated by orm:annotate
852  * @throws Main\ArgumentException
853  * @throws Main\ObjectPropertyException
854  * @throws Main\SystemException
855  */
856  public function fetchObject()
857  {
858  return $this->exec()->fetchObject();
859  }
860 
861  /**
862  * Short alias for $result->fetchCollection()
863  *
864  * @return null Actual type should be annotated by orm:annotate
865  * @throws Main\ObjectPropertyException
866  * @throws Main\SystemException
867  */
868  public function fetchCollection()
869  {
870  return $this->exec()->fetchCollection();
871  }
872 
873  /**
874  * @param $definition
875  * @param null $alias
876  *
877  * @return $this
878  * @throws Main\ArgumentException
879  * @throws Main\SystemException
880  */
881  protected function addToSelectChain($definition, $alias = null)
882  {
883  if ($definition instanceof ExpressionField)
884  {
885  if (empty($alias))
886  {
887  $alias = $definition->getName();
888  }
889 
890  $this->registerRuntimeField($alias, $definition);
891  $chain = $this->getRegisteredChain($alias);
892 
893  // add
894  $this->registerChain('select', $chain);
895 
896  // recursively collect all "build_from" fields
897  if ($chain->getLastElement()->getValue() instanceof ExpressionField)
898  {
899  $this->collectExprChains($chain, array('hidden', 'select_expr'));
900  }
901  }
902  elseif (is_array($definition))
903  {
904  // it is runtime field
905  // now they are @deprecated in here
906  throw new Main\ArgumentException(
907  'Expression as an array in `select` section is no more supported due to security reason.'
908  .' Please use `runtime` parameter, or Query->registerRuntimeField method, or pass ExpressionField object instead of array.'
909  );
910  }
911  else
912  {
913  // localize definition (get last field segment e.g. NAME from REF1.REF2.NAME)
914  $localDefinitionPos = strrpos($definition, '.');
915 
916  if ($localDefinitionPos !== false)
917  {
918  $localDefinition = substr($definition, $localDefinitionPos+1);
919  $localEntityDef = substr($definition, 0, $localDefinitionPos);
920  $localChain = Chain::getChainByDefinition($this->entity, $localEntityDef.'.*');
921  $lastElemValue = $localChain->getLastElement()->getValue();
922 
923  if ($lastElemValue instanceof Reference)
924  {
925  $localEntity = $lastElemValue->getRefEntity();
926  }
927  elseif (is_array($lastElemValue))
928  {
929  list($localEntity, ) = $lastElemValue;
930  }
931  else
932  {
933  $localEntity = $lastElemValue;
934  }
935  }
936  else
937  {
938  $localDefinition = $definition;
939  $localEntityDef = "";
940  $dataClass = $this->entity->getDataClass();
941  $localEntity = $dataClass::getEntity();
942  }
943 
944  // if there is a shell pattern in final segment, run recursively
945  if ((strlen($localDefinition) > 1 && strpos($localDefinition, '*') !== false)
946  || strpos($localDefinition, '?') !== false
947  )
948  {
949  // get fields by pattern
950  foreach ($localEntity->getFields() as $field)
951  {
952  if (
953  ($field instanceof ScalarField || $field instanceof ExpressionField)
954  && fnmatch($localDefinition, $field->getName())
955  )
956  {
957  // build alias
958  $customAlias = null;
959 
960  if ($alias !== null)
961  {
962  // put alias as a prefix
963  $customAlias = $alias.$field->getName();
964  }
965 
966  // build definition
967  $fieldDefinition = $field->getName();
968 
969  if (!empty($localEntityDef))
970  {
971  $fieldDefinition = $localEntityDef.'.'.$fieldDefinition;
972  }
973 
974  $this->addToSelectChain($fieldDefinition, $customAlias);
975  }
976  }
977 
978  return $this;
979  }
980 
981  // there is normal scalar field, or Reference, or Entity (all fields of)
982  $chain = $this->getRegisteredChain($definition, true);
983 
984  if ($alias !== null)
985  {
986  // custom alias
987  $chain = clone $chain;
988  $chain->setCustomAlias($alias);
989  }
990 
991  $last_elem = $chain->getLastElement();
992 
993  // fill if element is not scalar
994  /** @var null|Entity $expand_entity */
995  $expand_entity = null;
996 
997  if ($last_elem->getValue() instanceof Reference)
998  {
999  $expand_entity = $last_elem->getValue()->getRefEntity();
1000  }
1001  elseif (is_array($last_elem->getValue()))
1002  {
1003  list($expand_entity, ) = $last_elem->getValue();
1004  }
1005  elseif ($last_elem->getValue() instanceof Entity)
1006  {
1007  $expand_entity = $last_elem->getValue();
1008  }
1009  elseif ($last_elem->getValue() instanceof OneToMany)
1010  {
1011  $expand_entity = $last_elem->getValue()->getRefEntity();
1012  }
1013  elseif ($last_elem->getValue() instanceof ManyToMany)
1014  {
1015  $expand_entity = $last_elem->getValue()->getRefEntity();
1016  }
1017 
1018  if (!$expand_entity && $alias !== null)
1019  {
1020  // we have a single field, let's check its custom alias
1021  if (
1022  $this->entity->hasField($alias)
1023  && (
1024  // if it's not the same field
1025  $this->entity->getFullName() !== $last_elem->getValue()->getEntity()->getFullName()
1026  ||
1027  $last_elem->getValue()->getName() !== $alias
1028  )
1029  )
1030  {
1031  // deny aliases eq. existing fields
1032  throw new Main\ArgumentException(sprintf(
1033  'Alias "%s" matches already existing field "%s" of initial entity "%s". '.
1034  'Please choose another name for alias.',
1035  $alias, $alias, $this->entity->getFullName()
1036  ));
1037  }
1038  }
1039 
1040  if ($expand_entity)
1041  {
1042  // add all fields of entity
1043  foreach ($expand_entity->getFields() as $exp_field)
1044  {
1045  // except for references and expressions
1046  if ($exp_field instanceof ScalarField)
1047  {
1048  $exp_chain = clone $chain;
1049  $exp_chain->addElement(new ChainElement(
1050  $exp_field
1051  ));
1052 
1053  // custom alias
1054  if ($alias !== null)
1055  {
1056  $fieldAlias = $alias . $exp_field->getName();
1057 
1058  // deny aliases eq. existing fields
1059  if ($this->entity->hasField($fieldAlias))
1060  {
1061  throw new Main\ArgumentException(sprintf(
1062  'Alias "%s" + field "%s" match already existing field "%s" of initial entity "%s". '.
1063  'Please choose another name for alias.',
1064  $alias, $exp_field->getName(), $fieldAlias, $this->entity->getFullName()
1065  ));
1066  }
1067 
1068  $exp_chain->setCustomAlias($fieldAlias);
1069  }
1070 
1071  // add
1072  $this->registerChain('select', $exp_chain);
1073  }
1074  }
1075  }
1076  else
1077  {
1078  // scalar field that defined in entity
1079  $this->registerChain('select', $chain);
1080 
1081  // it would be nice here to register field as a runtime when it has custom alias
1082  // it will make possible to use aliased fields as a native init entity fields
1083  // e.g. in expressions or in data_doubling=off filter
1084 
1085  // collect buildFrom fields (recursively)
1086  if ($chain->getLastElement()->getValue() instanceof ExpressionField)
1087  {
1088  $this->collectExprChains($chain, array('hidden', 'select_expr'));
1089  }
1090  }
1091  }
1092 
1093  return $this;
1094  }
1095 
1096  /**
1097  * @param $filter
1098  * @param string $section
1099  *
1100  * @throws Main\ArgumentException
1101  * @throws Main\SystemException
1102  */
1103  public function setFilterChains(&$filter, $section = 'filter')
1104  {
1105  foreach ($filter as $filter_def => &$filter_match)
1106  {
1107  if ($filter_def === 'LOGIC')
1108  {
1109  continue;
1110  }
1111 
1112  if (!is_numeric($filter_def))
1113  {
1114  $sqlWhere = new \CSQLWhere();
1115  $csw_result = $sqlWhere->makeOperation($filter_def);
1116  list($definition, ) = array_values($csw_result);
1117 
1118  // do not register it in global chain registry - get it in a smuggled way
1119  // - we will do the registration later after UF rewriting and data doubling checking
1120  $chain = $this->getRegisteredChain($definition);
1121 
1122  if (!$chain)
1123  {
1124  // try to find it in filter chains if it is 2nd call of method (when dividing filter for where/having)
1125  // and chain is still not registered in global (e.g. when forcesDataDoublingOff)
1126  if (isset($this->filter_chains[$definition]))
1127  {
1128  $chain = $this->filter_chains[$definition];
1129  }
1130  else
1131  {
1132  $chain = Chain::getChainByDefinition($this->entity, $definition);
1133  }
1134  }
1135 
1136  // dirty hack for UF multiple fields: replace text UF_SMTH by UF_SMTH_SINGLE
1137  $dstField = $chain->getLastElement()->getValue();
1138  $dstEntity = $dstField->getEntity();
1139 
1140  if ($dstField instanceof ExpressionField && count($dstField->getBuildFromChains()) == 1)
1141  {
1142  // hold entity, but get real closing field
1143  $dstBuildFromChains = $dstField->getBuildFromChains();
1144 
1145  /** @var Chain $firstChain */
1146  $firstChain = $dstBuildFromChains[0];
1147  $dstField = $firstChain->getLastElement()->getValue();
1148  }
1149 
1150  // check for base linking
1151  if ($dstField instanceof TextField && $dstEntity->hasField($dstField->getName().'_SINGLE'))
1152  {
1153  $utmLinkField = $dstEntity->getField($dstField->getName().'_SINGLE');
1154 
1155  if ($utmLinkField instanceof ExpressionField)
1156  {
1157  $buildFromChains = $utmLinkField->getBuildFromChains();
1158 
1159  // check for back-reference
1160  if (count($buildFromChains) == 1 && $buildFromChains[0]->hasBackReference())
1161  {
1162  $endField = $buildFromChains[0]->getLastElement()->getValue();
1163 
1164  // and final check for entity name
1165  if (strpos($endField->getEntity()->getName(), 'Utm'))
1166  {
1167  $expressionChain = clone $chain;
1168  $expressionChain->removeLastElement();
1169  $expressionChain->addElement(new ChainElement(clone $utmLinkField));
1170  $expressionChain->forceDataDoublingOff();
1171 
1172  $chain = $expressionChain;
1173 
1174  // rewrite filter definition
1175  unset($filter[$filter_def]);
1176  $filter[$filter_def.'_SINGLE'] = $filter_match;
1177  $definition .= '_SINGLE';
1178  }
1179  }
1180  }
1181  }
1182 
1183  // continue
1184  $registerChain = true;
1185 
1186  // if data doubling disabled and it is back-reference - do not register, it will be overwritten
1187  if ($chain->forcesDataDoublingOff() || ($this->data_doubling_off && $chain->hasBackReference()))
1188  {
1189  $registerChain = false;
1190  }
1191 
1192  if ($registerChain)
1193  {
1194  $this->registerChain($section, $chain, $definition);
1195 
1196  // fill hidden select
1197  if ($chain->getLastElement()->getValue() instanceof ExpressionField)
1198  {
1199  $this->collectExprChains($chain);
1200  }
1201  }
1202  else
1203  {
1204  // hide from global registry to avoid "join table"
1205  // but we still need it in filter chains
1206  $this->filter_chains[$chain->getAlias()] = $chain;
1207  $this->filter_chains[$definition] = $chain;
1208 
1209  // and we will need primary chain in filter later when overwriting data-doubling
1210  $this->getRegisteredChain($this->entity->getPrimary(), true);
1211  }
1212  }
1213  elseif (is_array($filter_match))
1214  {
1215  $this->setFilterChains($filter_match, $section);
1216  }
1217  }
1218  }
1219 
1220  /**
1221  * @param Filter $where
1222  * @param string $section
1223  *
1224  * @throws Main\ArgumentException
1225  * @throws Main\SystemException
1226  */
1227  public function setFilterHandlerChains(Filter $where, $section = 'filter')
1228  {
1229  foreach ($where->getConditions() as $condition)
1230  {
1231  if ($condition instanceof Filter)
1232  {
1233  // subfilter
1234  $this->setFilterHandlerChains($condition, $section);
1235  }
1236  else
1237  {
1238  $definition = $condition->getDefinition();
1239 
1240  // check for runtime fields
1241  if ($definition instanceof Field)
1242  {
1243  // register runtime field
1244  $this->registerRuntimeField($definition);
1245 
1246  // rewrite definition in filter - replace field with its name
1247  $definition = $definition->getName();
1248  $condition->setDefinition($definition);
1249  }
1250 
1251  // check if it's a regular condition, not kind of boolean/exists expression
1252  if ($definition !== null)
1253  {
1254  // regular condition
1255  $chain = $this->getRegisteredChain($definition);
1256 
1257  if (!$chain)
1258  {
1259  // try to find it in filter chains if it is 2nd call of method (when dividing filter for where/having)
1260  // and chain is still not registered in global (e.g. when forcesDataDoublingOff)
1261  if (isset($this->filter_chains[$definition]))
1262  {
1263  $chain = $this->filter_chains[$definition];
1264  }
1265  else
1266  {
1267  $chain = Chain::getChainByDefinition($this->entity, $definition);
1268  }
1269  }
1270 
1271  // dirty hack for UF multiple fields: replace text UF_SMTH by UF_SMTH_SINGLE
1272  $dstField = $chain->getLastElement()->getValue();
1273  $dstEntity = $dstField->getEntity();
1274 
1275  if ($dstField instanceof ExpressionField && count($dstField->getBuildFromChains()) == 1)
1276  {
1277  // hold entity, but get real closing field
1278  $dstBuildFromChains = $dstField->getBuildFromChains();
1279 
1280  /** @var Chain $firstChain */
1281  $firstChain = $dstBuildFromChains[0];
1282  $dstField = $firstChain->getLastElement()->getValue();
1283  }
1284 
1285  // check for base linking
1286  if ($dstField instanceof TextField && $dstEntity->hasField($dstField->getName().'_SINGLE'))
1287  {
1288  $utmLinkField = $dstEntity->getField($dstField->getName().'_SINGLE');
1289 
1290  if ($utmLinkField instanceof ExpressionField)
1291  {
1292  $buildFromChains = $utmLinkField->getBuildFromChains();
1293 
1294  // check for back-reference
1295  if (count($buildFromChains) == 1 && $buildFromChains[0]->hasBackReference())
1296  {
1297  $endField = $buildFromChains[0]->getLastElement()->getValue();
1298 
1299  // and final check for entity name
1300  if (strpos($endField->getEntity()->getName(), 'Utm'))
1301  {
1302  $expressionChain = clone $chain;
1303  $expressionChain->removeLastElement();
1304  $expressionChain->addElement(new ChainElement(clone $utmLinkField));
1305  $expressionChain->forceDataDoublingOff();
1306 
1307  $chain = $expressionChain;
1308 
1309  // rewrite filter definition
1310  $definition .= '_SINGLE';
1311  $condition->setDefinition($definition);
1312  }
1313  }
1314  }
1315  }
1316 
1317  // continue
1318  $registerChain = true;
1319 
1320  // if data doubling disabled and it is back-reference - do not register, it will be overwritten
1321  if ($chain->forcesDataDoublingOff() || ($this->data_doubling_off && $chain->hasBackReference()))
1322  {
1323  $registerChain = false;
1324  }
1325 
1326  if ($registerChain)
1327  {
1328  $this->registerChain($section, $chain, $definition);
1329 
1330  // fill hidden select
1331  if ($chain->getLastElement()->getValue() instanceof ExpressionField)
1332  {
1333  $this->collectExprChains($chain);
1334  }
1335  }
1336  else
1337  {
1338  // hide from global registry to avoid "join table"
1339  // but we still need it in filter chains
1340  $this->filter_chains[$chain->getAlias()] = $chain;
1341  $this->filter_chains[$definition] = $chain;
1342 
1343  // and we will need primary chain in filter later when overwriting data-doubling
1344  $this->getRegisteredChain($this->entity->getPrimary(), true);
1345  }
1346  }
1347 
1348  // when compare with column, put it in the chains too
1349  foreach ($condition->getAtomicValues() as $value)
1350  {
1351  if ($value instanceof ColumnExpression)
1352  {
1353  $valueDefinition = $value->getDefinition();
1354 
1355  if (isset($this->filter_chains[$valueDefinition]))
1356  {
1357  $chain = $this->filter_chains[$valueDefinition];
1358  }
1359  else
1360  {
1361  $chain = Chain::getChainByDefinition($this->entity, $valueDefinition);
1362  }
1363 
1364  $this->registerChain($section, $chain, $valueDefinition);
1365  }
1366 
1367  // set connection to correct escaping in expressions
1368  if ($value instanceof Main\DB\SqlExpression)
1369  {
1370  $value->setConnection($this->entity->getConnection());
1371  }
1372  }
1373  }
1374  }
1375  }
1376 
1377  /**
1378  * @throws Main\ArgumentException
1379  * @throws Main\SystemException
1380  */
1381  protected function divideFilter()
1382  {
1383  // divide filter to where and having
1384 
1385  $logic = isset($this->filter['LOGIC']) ? $this->filter['LOGIC'] : 'AND';
1386 
1387  if ($logic == 'OR')
1388  {
1389  // if has aggr then move all to having
1390  if ($this->checkFilterAggregation($this->filter))
1391  {
1392  $this->where = array();
1393  $this->where_chains = array();
1394 
1395  $this->having = $this->filter;
1396  $this->having_chains = $this->filter_chains;
1397  }
1398  else
1399  {
1400  $this->where = $this->filter;
1401  $this->where_chains = $this->filter_chains;
1402 
1403  $this->having = array();
1404  $this->having_chains = array();
1405  }
1406  }
1407  elseif ($logic == 'AND')
1408  {
1409  // we can separate root filters
1410  foreach ($this->filter as $k => $sub_filter)
1411  {
1412  if ($k === 'LOGIC')
1413  {
1414  $this->where[$k] = $sub_filter;
1415  $this->having[$k] = $sub_filter;
1416 
1417  continue;
1418  }
1419 
1420  $tmp_filter = array($k => $sub_filter);
1421 
1422  if ($this->checkFilterAggregation($tmp_filter))
1423  {
1424  $this->having[$k] = $sub_filter;
1425  $this->setFilterChains($tmp_filter, 'having');
1426  }
1427  else
1428  {
1429  $this->where[$k] = $sub_filter;
1430  $this->setFilterChains($tmp_filter, 'where');
1431  }
1432  }
1433  }
1434 
1435  // collect "build_from" fields from having
1436  foreach ($this->having_chains as $chain)
1437  {
1438  if ($chain->getLastElement()->getValue() instanceof ExpressionField)
1439  {
1440  $this->collectExprChains($chain, array('hidden', 'having_expr'));
1441  }
1442  }
1443  }
1444 
1445  /**
1446  * @throws Main\ArgumentException
1447  * @throws Main\SystemException
1448  */
1449  protected function divideFilterHandler()
1450  {
1451  $logic = $this->filterHandler->logic();
1452 
1453  if ($logic == 'or')
1454  {
1455  // if has aggr then move all to having
1456  if ($this->checkFilterHandlerAggregation($this->filterHandler))
1457  {
1458  $this->havingHandler = $this->filterHandler;
1459  $this->having_chains = $this->filter_chains;
1460  }
1461  else
1462  {
1463  $this->whereHandler = $this->filterHandler;
1464  $this->where_chains = $this->filter_chains;
1465  }
1466  }
1467  elseif ($logic == 'and')
1468  {
1469  // we can separate root filters
1470  foreach ($this->filterHandler->getConditions() as $condition)
1471  {
1472  $tmpFilter = static::filter()->addCondition($condition);
1473 
1474  if ($this->checkFilterHandlerAggregation($tmpFilter))
1475  {
1476  $this->havingHandler->addCondition($tmpFilter);
1477  $this->setFilterHandlerChains($tmpFilter, 'having');
1478  }
1479  else
1480  {
1481  $this->whereHandler->addCondition($condition);
1482  $this->setFilterHandlerChains($tmpFilter, 'where');
1483  }
1484  }
1485  }
1486 
1487  // collect "build_from" fields from having
1488  foreach ($this->having_chains as $chain)
1489  {
1490  if ($chain->getLastElement()->getValue() instanceof ExpressionField)
1491  {
1492  $this->collectExprChains($chain, array('hidden', 'having_expr'));
1493  }
1494  }
1495  }
1496 
1497  /**
1498  * @param $filter
1499  *
1500  * @return bool
1501  * @throws Main\SystemException
1502  */
1503  protected function checkFilterAggregation($filter)
1504  {
1505  foreach ($filter as $filter_def => $filter_match)
1506  {
1507  if ($filter_def === 'LOGIC')
1508  {
1509  continue;
1510  }
1511 
1512  $is_having = false;
1513  if (!is_numeric($filter_def))
1514  {
1515  $sqlWhere = new \CSQLWhere();
1516  $csw_result = $sqlWhere->makeOperation($filter_def);
1517  list($definition, ) = array_values($csw_result);
1518 
1519  $chain = $this->filter_chains[$definition];
1520  $last = $chain->getLastElement();
1521 
1522  $is_having = $last->getValue() instanceof ExpressionField && $last->getValue()->isAggregated();
1523  }
1524  elseif (is_array($filter_match))
1525  {
1526  $is_having = $this->checkFilterAggregation($filter_match);
1527  }
1528 
1529  if ($is_having)
1530  {
1531  return true;
1532  }
1533  }
1534 
1535  return false;
1536  }
1537 
1538  /**
1539  * @param Filter $filter
1540  *
1541  * @return bool
1542  * @throws Main\SystemException
1543  */
1545  {
1546  foreach ($filter->getConditions() as $condition)
1547  {
1548  $is_having = false;
1549 
1550  if ($condition instanceof Filter)
1551  {
1552  // subfilter
1553  $is_having = $this->checkFilterHandlerAggregation($condition);
1554  }
1555  else
1556  {
1557  // check if it is not a boolean/exists condition
1558  if ($condition->getDefinition() !== null)
1559  {
1560  // regular condition
1561  $chain = $this->filter_chains[$condition->getDefinition()];
1562  $last = $chain->getLastElement();
1563 
1564  $is_having = $last->getValue() instanceof ExpressionField && $last->getValue()->isAggregated();
1565 
1566  // check if value is a field and has aggregation
1567  if (!$is_having && $condition->getValue() instanceof ColumnExpression)
1568  {
1569  $chain = $this->filter_chains[$condition->getValue()->getDefinition()];
1570  $last = $chain->getLastElement();
1571 
1572  $is_having = $last->getValue() instanceof ExpressionField && $last->getValue()->isAggregated();
1573 
1574  // actually if it has happened, we need to add group by the first column
1575  }
1576  }
1577  }
1578 
1579  if ($is_having)
1580  {
1581  return true;
1582  }
1583  }
1584 
1585  return false;
1586  }
1587 
1588  /**
1589  * @param Filter $filter
1590  * @param $section
1591  *
1592  * @throws Main\ArgumentException
1593  * @throws Main\SystemException
1594  */
1595  protected function rewriteDataDoubling(Filter $filter, $section)
1596  {
1597  foreach ($filter->getConditions() as $condition)
1598  {
1599  if ($condition instanceof Filter)
1600  {
1601  //subfilter
1602  $this->rewriteDataDoubling($condition, $section);
1603  }
1604  elseif ($condition->getDefinition() !== null)
1605  {
1606  // regular condition
1607  $chain = $this->filter_chains[$condition->getDefinition()];
1608 
1609  if ($chain->forcesDataDoublingOff() || ($this->data_doubling_off && $chain->hasBackReference()))
1610  {
1611  $primaryName = $this->entity->getPrimary();
1612  $uniquePostfix = '_TMP'.rand();
1613 
1614  // build subquery
1615  $dataClass = $this->entity->getDataClass();
1616 
1617  $subQuery = $dataClass::query()
1618  ->addSelect($primaryName)
1619  ->where(clone $condition)
1620  ->setTableAliasPostfix(strtolower($uniquePostfix));
1621 
1622  // change condition
1623  $condition->setColumn($primaryName);
1624  $condition->setOperator('in');
1625  $condition->setValue($subQuery);
1626 
1627  // register primary's chain
1628  $idChain = $this->getRegisteredChain($primaryName);
1629  $this->registerChain($section, $idChain);
1630  }
1631  }
1632  }
1633  }
1634 
1635  /**
1636  * @param $definition
1637  *
1638  * @throws Main\SystemException
1639  */
1640  protected function addToGroupChain($definition)
1641  {
1642  $chain = $this->getRegisteredChain($definition, true);
1643  $this->registerChain('group', $chain);
1644 
1645  if ($chain->getLastElement()->getValue() instanceof ExpressionField)
1646  {
1647  $this->collectExprChains($chain);
1648  }
1649  }
1650 
1651  /**
1652  * @param $definition
1653  *
1654  * @throws Main\SystemException
1655  */
1656  protected function addToOrderChain($definition)
1657  {
1658  $chain = $this->getRegisteredChain($definition, true);
1659  $this->registerChain('order', $chain);
1660 
1661  if ($chain->getLastElement()->getValue() instanceof ExpressionField)
1662  {
1663  $this->collectExprChains($chain);
1664  }
1665  }
1666 
1667  /**
1668  * @param null $chains
1669  *
1670  * @throws Main\ArgumentException
1671  * @throws Main\SystemException
1672  */
1673  protected function buildJoinMap($chains = null)
1674  {
1675  $connection = $this->entity->getConnection();
1676  $helper = $connection->getSqlHelper();
1677 
1678  $aliasLength = $helper->getAliasLength();
1679 
1680  if (empty($chains))
1681  {
1682  $chains = $this->global_chains;
1683  }
1684 
1685  foreach ($chains as $chain)
1686  {
1687  if ($chain->getLastElement()->getParameter('talias'))
1688  {
1689  // already been here
1690  continue;
1691  }
1692 
1693  // in NO_DOUBLING mode skip 1:N relations that presented in filter only
1694  if ($chain->forcesDataDoublingOff() || ($this->data_doubling_off && $chain->hasBackReference()))
1695  {
1696  $alias = $chain->getAlias();
1697 
1698  if (isset($this->filter_chains[$alias])
1699  && !isset($this->select_chains[$alias]) && !isset($this->select_expr_chains[$alias])
1700  && !isset($this->group_chains[$alias]) && !isset($this->order_chains[$alias])
1701  )
1702  {
1703  continue;
1704  }
1705  }
1706 
1707  $prev_alias = $this->getInitAlias(false);
1708 
1709  $map_key = '';
1710 
1711  /**
1712  * elements after init entity
1713  * @var $elements ChainElement[]
1714  * */
1715  $elements = array_slice($chain->getAllElements(), 1);
1716 
1717  $currentDefinition = array();
1718 
1719  foreach ($elements as $element)
1720  {
1721  $table_alias = null;
1722 
1723  /**
1724  * define main objects
1725  * @var $ref_field Reference
1726  * @var $dst_entity Entity
1727  */
1728  if ($element->getValue() instanceof Reference)
1729  {
1730  // ref to another entity
1731  $ref_field = $element->getValue();
1732  $dst_entity = $ref_field->getRefEntity();
1733  $joinType = $ref_field->getJoinType();
1734  }
1735  elseif (is_array($element->getValue()))
1736  {
1737  // link from another entity to this
1738  list($dst_entity, $ref_field) = $element->getValue();
1739  $joinType = $ref_field->getJoinType();
1740  }
1741  elseif ($element->getValue() instanceof OneToMany)
1742  {
1743  // the same as back reference
1744  $dst_entity = $element->getValue()->getRefEntity();
1745  $ref_field = $element->getValue()->getRefField();
1746  $joinType = $element->getValue()->getJoinType() ?: $ref_field->getJoinType();
1747  }
1748  elseif ($element->getValue() instanceof ManyToMany)
1749  {
1750  $mtm = $element->getValue();
1751 
1752  // join mediator and remote entities in hidden mode
1753  // first, make new chain, remove everything after this mtm and remove mtm itself
1754  $tmpChain = clone $chain;
1755  $mtmDefinition = join('.', $currentDefinition);
1756 
1757  while ($tmpChain->getDefinition() != $mtmDefinition)
1758  {
1759  $tmpChain->removeLastElement();
1760  }
1761 
1762  // then add backReference to mediator - mediator entity and local reference
1763  $tmpChain->addElement(new ChainElement([
1764  $mtm->getMediatorEntity(), $mtm->getLocalReference()
1765  ]));
1766 
1767  // then add reference from mediator to remote entity
1768  $tmpChain->addElement(new ChainElement($mtm->getRemoteReference()));
1769 
1770  // now join this chain
1771  $this->registerChain('global', $tmpChain);
1772  $this->buildJoinMap([$tmpChain]);
1773 
1774  // and finally remember table alias for mtm element
1775  $prev_alias = $tmpChain->getLastElement()->getParameter('talias');
1776  $element->setParameter('talias', $prev_alias);
1777 
1778  // skip any standard actions, continue with next element
1779  continue;
1780  }
1781  else
1782  {
1783  // scalar field
1784  // if it's a field of the init entity, use getInitAlias to use 'base' alias
1785  if ($prev_alias === $this->getInitAlias(false))
1786  {
1787  $element->setParameter('talias', $this->getInitAlias());
1788  }
1789  else
1790  {
1791  $element->setParameter('talias', $prev_alias.$this->table_alias_postfix);
1792  }
1793 
1794  continue;
1795  }
1796 
1797  // mapping
1798  if (empty($map_key))
1799  {
1800  $map_key = join('.', $currentDefinition);
1801  }
1802 
1803  $map_key .= '/' . $ref_field->getName() . '/' . $dst_entity->getName();
1804 
1805  $currentDefinition[] = $element->getDefinitionFragment();
1806 
1807  if (isset($this->join_registry[$map_key]))
1808  {
1809  // already connected
1810  $table_alias = $this->join_registry[$map_key];
1811  }
1812  else
1813  {
1814  // prepare reference
1815  $reference = $ref_field->getReference();
1816 
1817  if ($element->getValue() instanceof Reference)
1818  {
1819  // ref to another entity
1820  if (is_null($table_alias))
1821  {
1822  $table_alias = $prev_alias.'_'.strtolower($ref_field->getName());
1823 
1824  if (strlen($table_alias.$this->table_alias_postfix) > $aliasLength)
1825  {
1826  $old_table_alias = $table_alias;
1827  $table_alias = 'TALIAS_' . (count($this->replaced_taliases) + 1);
1828  $this->replaced_taliases[$table_alias] = $old_table_alias;
1829  }
1830  }
1831 
1832  $alias_this = $prev_alias;
1833  $alias_ref = $table_alias;
1834 
1835  $isBackReference = false;
1836 
1837  $definition_this = join('.', array_slice($currentDefinition, 0, -1));
1838  $definition_ref = join('.', $currentDefinition);
1839  }
1840  elseif (is_array($element->getValue()) || $element->getValue() instanceof OneToMany)
1841  {
1842  if (is_null($table_alias))
1843  {
1844  $table_alias = StringHelper::camel2snake($dst_entity->getName()) . '_' . strtolower($ref_field->getName());
1845  $table_alias = $prev_alias.'_'.$table_alias;
1846 
1847  if (strlen($table_alias.$this->table_alias_postfix) > $aliasLength)
1848  {
1849  $old_table_alias = $table_alias;
1850  $table_alias = 'TALIAS_' . (count($this->replaced_taliases) + 1);
1851  $this->replaced_taliases[$table_alias] = $old_table_alias;
1852  }
1853  }
1854 
1855  $alias_this = $table_alias;
1856  $alias_ref = $prev_alias;
1857 
1858  $isBackReference = true;
1859 
1860  $definition_this = join('.', $currentDefinition);
1861  $definition_ref = join('.', array_slice($currentDefinition, 0, -1));
1862  }
1863  else
1864  {
1865  throw new Main\SystemException(sprintf('Unknown reference element `%s`', $element->getValue()));
1866  }
1867 
1868  // replace this. and ref. to real definition
1869  if ($reference instanceof Filter)
1870  {
1871  $csw_reference = $this->prepareJoinFilterReference(
1872  $reference,
1873  $alias_this.$this->table_alias_postfix,
1874  $alias_ref.$this->table_alias_postfix,
1875  $definition_this,
1876  $definition_ref,
1877  $isBackReference
1878  );
1879  }
1880  else
1881  {
1882  $csw_reference = $this->prepareJoinReference(
1883  $reference,
1884  $alias_this.$this->table_alias_postfix,
1885  $alias_ref.$this->table_alias_postfix,
1886  $definition_this,
1887  $definition_ref,
1888  $isBackReference
1889  );
1890  }
1891 
1892  // double check after recursive call in prepareJoinReference
1893  if (!isset($this->join_registry[$map_key]))
1894  {
1895  $join = array(
1896  'type' => $joinType,
1897  'table' => $dst_entity->getDBTableName(),
1898  'alias' => $table_alias.$this->table_alias_postfix,
1899  'reference' => $csw_reference,
1900  'map_key' => $map_key
1901  );
1902 
1903  $this->join_map[] = $join;
1904  $this->join_registry[$map_key] = $table_alias;
1905  }
1906  }
1907 
1908  // set alias for each element
1909  $element->setParameter('talias', $table_alias.$this->table_alias_postfix);
1910 
1911  $prev_alias = $table_alias;
1912  }
1913  }
1914  }
1915 
1916  protected function buildSelect()
1917  {
1918  $sql = array();
1919 
1920  foreach ($this->select_chains as $chain)
1921  {
1922  $sql[] = $chain->getSqlDefinition(true);
1923  }
1924 
1925  if (empty($sql))
1926  {
1927  $sql[] = 1;
1928  }
1929 
1930  return "\n\t".join(",\n\t", $sql);
1931  }
1932 
1933  /**
1934  * @return string
1935  * @throws Main\SystemException
1936  */
1937  protected function buildJoin()
1938  {
1939  $sql = array();
1940  $csw = new \CSQLWhere;
1941 
1942  $connection = $this->entity->getConnection();
1943  $helper = $connection->getSqlHelper();
1944 
1945  foreach ($this->join_map as $join)
1946  {
1947  // prepare csw fields
1948  $csw_fields = $this->getJoinCswFields($join['reference']);
1949  $csw->setFields($csw_fields);
1950 
1951  if ($join['reference'] instanceof Filter)
1952  {
1953  $joinConditionSql = $join['reference']->getSql($this->global_chains);
1954  }
1955  else
1956  {
1957  $joinConditionSql = trim($csw->getQuery($join['reference']));
1958  }
1959 
1960  // final sql
1961  $sql[] = sprintf('%s JOIN %s %s ON %s',
1962  $join['type'],
1963  $this->quoteTableSource($join['table']),
1964  $helper->quote($join['alias']),
1965  $joinConditionSql
1966  );
1967  }
1968 
1969  return "\n".join("\n", $sql);
1970  }
1971 
1972  /**
1973  * @return string
1974  * @throws Main\ArgumentException
1975  * @throws Main\SystemException
1976  */
1977  protected function buildWhere()
1978  {
1979  $sql = array();
1980 
1981  // old array filter
1982  if (!empty($this->where))
1983  {
1984  $csw = new \CSQLWhere;
1985 
1986  $csw_fields = $this->getFilterCswFields($this->where);
1987  $csw->setFields($csw_fields);
1988 
1989  $sql[] = trim($csw->getQuery($this->where));
1990  }
1991 
1992  // new QueryFilter
1993  if ($this->whereHandler && $this->whereHandler->hasConditions())
1994  {
1995  // rewrite data doubling
1996  $this->rewriteDataDoubling($this->whereHandler, 'where');
1997 
1998  $sql[] = $this->whereHandler->getSql($this->where_chains);
1999  }
2000 
2001  return join(' AND ', array_filter($sql));
2002  }
2003 
2004  /**
2005  * @return string
2006  * @throws Main\SystemException
2007  */
2008  protected function buildGroup()
2009  {
2010  $sql = array();
2011 
2012  if (!empty($this->group_chains) || !empty($this->having_chains)
2013  || $this->checkChainsAggregation($this->select_chains)
2014  || $this->checkChainsAggregation($this->order_chains)
2015  )
2016  {
2017  // add non-aggr fields to group
2018  foreach ($this->global_chains as $chain)
2019  {
2020  $alias = $chain->getAlias();
2021 
2022  // skip constants
2023  if ($chain->isConstant())
2024  {
2025  continue;
2026  }
2027 
2028  if (isset($this->select_chains[$alias]) || isset($this->order_chains[$alias]) || isset($this->having_chains[$alias]))
2029  {
2030  if (isset($this->group_chains[$alias]))
2031  {
2032  // skip already grouped
2033  continue;
2034  }
2035  elseif (!$chain->hasAggregation() && !$chain->hasSubquery())
2036  {
2037  // skip subqueries and already aggregated
2038  $this->registerChain('group', $chain);
2039  }
2040  elseif (!$chain->hasAggregation() && $chain->hasSubquery() && $chain->getLastElement()->getValue() instanceof ExpressionField)
2041  {
2042  // but include build_from of subqueries
2043  $sub_chains = $chain->getLastElement()->getValue()->getBuildFromChains();
2044 
2045  foreach ($sub_chains as $sub_chain)
2046  {
2047  // build real subchain starting from init entity
2048  $real_sub_chain = clone $chain;
2049 
2050  foreach (array_slice($sub_chain->getAllElements(), 1) as $sub_chain_elem)
2051  {
2052  $real_sub_chain->addElement($sub_chain_elem);
2053  }
2054 
2055  // add to query
2056  $this->registerChain('group', $this->global_chains[$real_sub_chain->getAlias()]);
2057  }
2058  }
2059  }
2060  elseif (isset($this->having_expr_chains[$alias]))
2061  {
2062  if (!$chain->hasAggregation() && $chain->hasSubquery())
2063  {
2064  $this->registerChain('group', $chain);
2065  }
2066  }
2067  }
2068  }
2069 
2070  foreach ($this->group_chains as $chain)
2071  {
2072  $connection = $this->entity->getConnection();
2073  $sqlDefinition = $chain->getSqlDefinition();
2074  $valueField = $chain->getLastElement()->getValue();
2075 
2076  if ($valueField instanceof ExpressionField)
2077  {
2078  $valueField = $valueField->getValueField();
2079  }
2080 
2081  if (($connection instanceof Main\DB\OracleConnection || $connection instanceof Main\DB\MssqlConnection)
2082  && $valueField instanceof TextField)
2083  {
2084  // softTextCast
2085  $sqlDefinition = $connection->getSqlHelper()->softCastTextToChar($sqlDefinition);
2086  }
2087 
2088  $sql[] = $sqlDefinition;
2089  }
2090 
2091  return join(', ', $sql);
2092  }
2093 
2094  /**
2095  * @return string
2096  * @throws Main\ArgumentException
2097  * @throws Main\SystemException
2098  */
2099  protected function buildHaving()
2100  {
2101  $sql = array();
2102 
2103  // old array filter
2104  if (!empty($this->having))
2105  {
2106  $csw = new \CSQLWhere;
2107 
2108  $csw_fields = $this->getFilterCswFields($this->having);
2109  $csw->setFields($csw_fields);
2110 
2111  $sql[] = trim($csw->getQuery($this->having));
2112  }
2113 
2114  // new QueryFilter
2115  if ($this->havingHandler && $this->havingHandler->hasConditions())
2116  {
2117  // rewrite data doubling
2118  $this->rewriteDataDoubling($this->havingHandler, 'having');
2119 
2120  $sql[] = $this->havingHandler->getSql($this->having_chains);
2121  }
2122 
2123  return join(' AND ', array_filter($sql));
2124  }
2125 
2126  /**
2127  * @return string
2128  * @throws Main\SystemException
2129  */
2130  protected function buildOrder()
2131  {
2132  $sql = array();
2133 
2134  foreach ($this->order_chains as $chain)
2135  {
2136  $sort = isset($this->order[$chain->getDefinition()])
2137  ? $this->order[$chain->getDefinition()]
2138  : $this->order[$chain->getAlias()];
2139 
2140  $connection = $this->entity->getConnection();
2141 
2142  // define value field
2143  $valueField = $chain->getLastElement()->getValue();
2144  if ($valueField instanceof ExpressionField)
2145  {
2146  $valueField = $valueField->getValueField();
2147  }
2148 
2149  // get final sql definition
2150  if (isset($this->select_chains[$chain->getAlias()]))
2151  {
2152  // optimization for fields that are in select already
2153  $sqlDefinition = $connection->getSqlHelper()->quote($chain->getAlias());
2154  }
2155  else
2156  {
2157  $sqlDefinition = $chain->getSqlDefinition();
2158  }
2159 
2160  if (($connection instanceof Main\DB\OracleConnection || $connection instanceof Main\DB\MssqlConnection)
2161  && $valueField instanceof TextField)
2162  {
2163  // softTextCast
2164  $sqlDefinition = $connection->getSqlHelper()->softCastTextToChar($sqlDefinition);
2165  }
2166 
2167  $sql[] = $sqlDefinition. ' ' . $sort;
2168  }
2169 
2170  return join(', ', $sql);
2171  }
2172 
2173  /**
2174  * @return mixed|string
2175  * @throws Main\ArgumentException
2176  * @throws Main\SystemException
2177  */
2178  protected function buildQuery()
2179  {
2180  $connection = $this->entity->getConnection();
2181  $helper = $connection->getSqlHelper();
2182 
2183  if ($this->query_build_parts === null)
2184  {
2185 
2186  foreach ($this->select as $key => $value)
2187  {
2188  $this->addToSelectChain($value, is_numeric($key) ? null : $key);
2189  }
2190 
2191  $this->setFilterChains($this->filter);
2192  $this->divideFilter();
2193 
2194  // unconditional entity scope
2195  $this->entity->setDefaultScope($this);
2196 
2197  $this->setFilterHandlerChains($this->filterHandler);
2198  $this->divideFilterHandler();
2199 
2200  foreach ($this->group as $value)
2201  {
2202  $this->addToGroupChain($value);
2203  }
2204 
2205  foreach ($this->order as $key => $value)
2206  {
2207  $this->addToOrderChain($key);
2208  }
2209 
2210  $this->buildJoinMap();
2211 
2212  $sqlJoin = $this->buildJoin();
2213 
2214  $sqlSelect = $this->buildSelect();
2215  $sqlWhere = $this->buildWhere();
2216  $sqlGroup = $this->buildGroup();
2217  $sqlHaving = $this->buildHaving();
2218  $sqlOrder = $this->buildOrder();
2219 
2220  $sqlFrom = $this->quoteTableSource($this->entity->getDBTableName());
2221 
2222  $sqlFrom .= ' '.$helper->quote($this->getInitAlias());
2223  $sqlFrom .= ' '.$sqlJoin;
2224 
2225  $this->query_build_parts = array_filter(array(
2226  'SELECT' => $sqlSelect,
2227  'FROM' => $sqlFrom,
2228  'WHERE' => $sqlWhere,
2229  'GROUP BY' => $sqlGroup,
2230  'HAVING' => $sqlHaving,
2231  'ORDER BY' => $sqlOrder
2232  ));
2233  }
2234 
2235  $build_parts = $this->query_build_parts;
2236 
2237  foreach ($build_parts as $k => &$v)
2238  {
2239  $v = $k . ' ' . $v;
2240  }
2241 
2242  $query = join("\n", $build_parts);
2243 
2244  list($query, $replaced) = $this->replaceSelectAliases($query);
2245  $this->replaced_aliases = $replaced;
2246 
2247  if ($this->limit > 0)
2248  {
2249  $query = $helper->getTopSql($query, $this->limit, $this->offset);
2250  }
2251 
2252  // union
2253  if (!empty($this->unionHandler))
2254  {
2255  $query = "({$query})";
2256 
2257  foreach ($this->unionHandler->getQueries() as $union)
2258  {
2259  $query .= " ".$union->getSql();
2260  }
2261 
2262  // union sort
2263  if ($this->unionHandler->getOrder())
2264  {
2265  $sqlUnionOrder = array();
2266  foreach ($this->unionHandler->getOrder() as $definition => $sort)
2267  {
2268  $sqlDefinition = $connection->getSqlHelper()->quote(
2269  $this->global_chains[$definition]->getAlias()
2270  );
2271 
2272  $sqlUnionOrder[] = $sqlDefinition . ' ' . $sort;
2273  }
2274 
2275  $query .= ' ORDER BY ' . join(', ', $sqlUnionOrder);
2276  }
2277 
2278  // union limit
2279  if ($this->unionHandler->getLimit())
2280  {
2281  $query = $helper->getTopSql($query, $this->unionHandler->getLimit(), $this->unionHandler->getOffset());
2282  }
2283  }
2284 
2285  return $query;
2286  }
2287 
2288  /**
2289  * @param $filter
2290  *
2291  * @return array
2292  * @throws Main\ArgumentException
2293  * @throws Main\SystemException
2294  */
2295  protected function getFilterCswFields(&$filter)
2296  {
2297  $fields = array();
2298 
2299  foreach ($filter as $filter_def => &$filter_match)
2300  {
2301  if ($filter_def === 'LOGIC')
2302  {
2303  continue;
2304  }
2305 
2306  if (!is_numeric($filter_def))
2307  {
2308  $sqlWhere = new \CSQLWhere();
2309  $csw_result = $sqlWhere->makeOperation($filter_def);
2310  list($definition, $operation) = array_values($csw_result);
2311 
2312  $chain = $this->filter_chains[$definition];
2313  $last = $chain->getLastElement();
2314 
2315  // need to create an alternative of CSQLWhere in D7.Entity
2316  $field_type = $last->getValue()->getDataType();
2317  $callback = null;
2318 
2319  // rewrite type & value for CSQLWhere
2320  if (in_array($operation, array('SE', 'SN'), true)
2321  && in_array($filter_match, array(null, true, false), true)
2322  )
2323  {
2324  $field_type = 'callback';
2325 
2326  if ($filter_match === null)
2327  {
2328  $callback = array($this, 'nullEqualityCallback');
2329  }
2330  else
2331  {
2332  // just boolean expression, without operator
2333  // e.g. WHERE EXISTS(...)
2334  $callback = array($this, 'booleanStrongEqualityCallback');
2335  }
2336  }
2337  elseif ($field_type == 'integer')
2338  {
2339  $field_type = 'int';
2340  }
2341  elseif ($field_type == 'boolean')
2342  {
2343  $field_type = 'string';
2344 
2345  /** @var BooleanField $field */
2346  $field = $last->getValue();
2347  $values = $field->getValues();
2348 
2349  if (is_numeric($values[0]) && is_numeric($values[1]))
2350  {
2351  $field_type = 'int';
2352  }
2353 
2354  if (is_scalar($filter_match))
2355  {
2356  $filter_match = $field->normalizeValue($filter_match);
2357  }
2358  }
2359  elseif ($field_type == 'float')
2360  {
2361  $field_type = 'double';
2362  }
2363  elseif ($field_type == 'enum' || $field_type == 'text')
2364  {
2365  $field_type = 'string';
2366  }
2367 
2368  $sqlDefinition = $chain->getSqlDefinition();
2369 
2370  // data-doubling-off mode
2371  /** @see disableDataDoubling */
2372  if ($chain->forcesDataDoublingOff() || ($this->data_doubling_off && $chain->hasBackReference()))
2373  {
2374  $primaryName = $this->entity->getPrimary();
2375  $uniquePostfix = '_TMP'.rand();
2376 
2377  // build subquery
2378  $subQuery = new Query($this->entity);
2379  $subQuery->addSelect($primaryName);
2380  $subQuery->addFilter($filter_def, $filter_match);
2381  $subQuery->setTableAliasPostfix(strtolower($uniquePostfix));
2382  $subQuerySql = $subQuery->getQuery();
2383 
2384  // proxying subquery as value to callback
2385  $filter_match = $subQuerySql;
2386  $callback = array($this, 'dataDoublingCallback');
2387 
2388  $field_type = 'callback';
2389 
2390  // change sql definition
2391  $idChain = $this->getRegisteredChain($primaryName);
2392  $sqlDefinition = $idChain->getSqlDefinition();
2393  }
2394 
2395  // set entity connection to the sql expressions
2396  if ($filter_match instanceof Main\DB\SqlExpression)
2397  {
2398  $filter_match->setConnection($this->entity->getConnection());
2399  }
2400 
2401  //$is_having = $last->getValue() instanceof ExpressionField && $last->getValue()->isAggregated();
2402 
2403  // if back-reference found (Entity:REF)
2404  // if NO_DOUBLING mode enabled, then change getSQLDefinition to subquery exists(...)
2405  // and those chains should not be in joins if it is possible
2406 
2407  /*if (!$this->data_doubling && $chain->hasBackReference())
2408  {
2409  $field_type = 'callback';
2410  $init_query = $this;
2411 
2412  $callback = function ($field, $operation, $value) use ($init_query, $chain)
2413  {
2414  $init_entity = $init_query->getEntity();
2415  $init_table_alias = CBaseEntity::camel2snake($init_entity->getName()).$init_query->getTableAliasPostfix();
2416 
2417  $filter = array();
2418 
2419  // add primary linking with main query
2420  foreach ($init_entity->getPrimaryArray() as $primary)
2421  {
2422  $filter['='.$primary] = new CSQLWhereExpression('?#', $init_table_alias.'.'.$primary);
2423  }
2424 
2425  // add value filter
2426  $filter[CSQLWhere::getOperationByCode($operation).$chain->getDefinition()] = $value;
2427 
2428  // build subquery
2429  $query_class = __CLASS__;
2430  $sub_query = new $query_class($init_entity);
2431  $sub_query->setFilter($filter);
2432  $sub_query->setTableAliasPostfix('_sub');
2433 
2434  return 'EXISTS(' . $sub_query->getQuery() . ')';
2435  };
2436  }*/
2437 
2438  $fields[$definition] = array(
2439  'TABLE_ALIAS' => 'table',
2440  'FIELD_NAME' => $sqlDefinition,
2441  'FIELD_TYPE' => $field_type,
2442  'MULTIPLE' => '',
2443  'JOIN' => '',
2444  'CALLBACK' => $callback
2445  );
2446  }
2447  elseif (is_array($filter_match))
2448  {
2449  $fields = array_merge($fields, $this->getFilterCswFields($filter_match));
2450  }
2451  }
2452 
2453  return $fields;
2454  }
2455 
2456  /**
2457  * @param $reference
2458  * @param $alias_this
2459  * @param $alias_ref
2460  * @param $baseDefinition
2461  * @param $refDefinition
2462  * @param $isBackReference
2463  *
2464  * @return array
2465  * @throws Main\ArgumentException
2466  * @throws Main\SystemException
2467  */
2468  protected function prepareJoinReference($reference, $alias_this, $alias_ref, $baseDefinition, $refDefinition, $isBackReference)
2469  {
2470  $new = array();
2471 
2472  foreach ($reference as $k => $v)
2473  {
2474  if ($k === 'LOGIC')
2475  {
2476  $new[$k] = $v;
2477  continue;
2478  }
2479 
2480  if (is_numeric($k))
2481  {
2482  // subfilter, recursive call
2483  $new[$k] = $this->prepareJoinReference($v, $alias_this, $alias_ref, $baseDefinition, $refDefinition, $isBackReference);
2484  }
2485  else
2486  {
2487  // key
2488  $sqlWhere = new \CSQLWhere();
2489  $csw_result = $sqlWhere->makeOperation($k);
2490  list($field, $operation) = array_values($csw_result);
2491 
2492  if (strpos($field, 'this.') === 0)
2493  {
2494  // parse the chain
2495  $definition = str_replace(\CSQLWhere::getOperationByCode($operation).'this.', '', $k);
2496  $absDefinition = strlen($baseDefinition) ? $baseDefinition . '.' . $definition : $definition;
2497 
2498  $chain = $this->getRegisteredChain($absDefinition, true);
2499 
2500  if (!$isBackReference)
2501  {
2502  // make sure these fields will be joined before the main join
2503  $this->buildJoinMap(array($chain));
2504  }
2505  else
2506  {
2507  $chain->getLastElement()->setParameter('talias', $alias_this);
2508  }
2509 
2510  // recursively collect all "build_from" fields
2511  if ($chain->getLastElement()->getValue() instanceof ExpressionField)
2512  {
2513  $this->collectExprChains($chain);
2514  $buildFrom = $chain->getLastElement()->getValue()->getBuildFromChains();
2515 
2516  foreach ($buildFrom as $bf)
2517  {
2518  // set base chain
2519  $baseChain = clone $chain;
2520 
2521  // remove the last one - expression itself
2522  $baseChain->removeLastElement();
2523 
2524  // remove parent entity for this child
2525  $bf->removeFirstElement();
2526 
2527  // set new parents
2528  $bf->prepend($baseChain);
2529  }
2530 
2531  $this->buildJoinMap($buildFrom);
2532  }
2533 
2534  $k = \CSQLWhere::getOperationByCode($operation).$chain->getSqlDefinition();
2535  }
2536  elseif (strpos($field, 'ref.') === 0)
2537  {
2538  $definition = str_replace(\CSQLWhere::getOperationByCode($operation).'ref.', '', $k);
2539 
2540  if (strpos($definition, '.') !== false)
2541  {
2542  throw new Main\ArgumentException(sprintf(
2543  'Reference chain `%s` is not allowed here. First-level definitions only.', $field
2544  ));
2545  }
2546 
2547  $absDefinition = strlen($refDefinition) ? $refDefinition . '.' . $definition : $definition;
2548  $chain = $this->getRegisteredChain($absDefinition, true);
2549 
2550  if ($isBackReference)
2551  {
2552  // make sure these fields will be joined before the main join
2553  $this->buildJoinMap(array($chain));
2554  }
2555  else
2556  {
2557  $chain->getLastElement()->setParameter('talias', $alias_ref);
2558  }
2559 
2560  // recursively collect all "build_from" fields
2561  if ($chain->getLastElement()->getValue() instanceof ExpressionField)
2562  {
2563  $this->collectExprChains($chain);
2564  $this->buildJoinMap($chain->getLastElement()->getValue()->getBuildFromChains());
2565  }
2566 
2567  $k = \CSQLWhere::getOperationByCode($operation).$chain->getSqlDefinition();
2568  }
2569  else
2570  {
2571  throw new Main\SystemException(sprintf('Unknown reference key `%s`, it should start with "this." or "ref."', $k));
2572  }
2573 
2574  // value
2575  if (is_array($v))
2576  {
2577  // field = expression
2578  $v = new \CSQLWhereExpression($v[0], array_slice($v, 1));
2579  }
2580  elseif ($v instanceof Main\DB\SqlExpression)
2581  {
2582  // set entity connection
2583  $v->setConnection($this->entity->getConnection());
2584  }
2585  elseif (!is_object($v))
2586  {
2587  if (strpos($v, 'this.') === 0)
2588  {
2589  $definition = str_replace('this.', '', $v);
2590  $absDefinition = strlen($baseDefinition) ? $baseDefinition . '.' . $definition : $definition;
2591 
2592  $chain = $this->getRegisteredChain($absDefinition, true);
2593 
2594  if (!$isBackReference)
2595  {
2596  // make sure these fields will be joined before the main join
2597  $this->buildJoinMap(array($chain));
2598  }
2599  else
2600  {
2601  $chain->getLastElement()->setParameter('talias', $alias_this);
2602  }
2603 
2604  // recursively collect all "build_from" fields
2605  if ($chain->getLastElement()->getValue() instanceof ExpressionField)
2606  {
2607  $this->collectExprChains($chain);
2608  $buildFrom = $chain->getLastElement()->getValue()->getBuildFromChains();
2609 
2610  foreach ($buildFrom as $bf)
2611  {
2612  // set base chain
2613  $baseChain = clone $chain;
2614 
2615  // remove the last one - expression itself
2616  $baseChain->removeLastElement();
2617 
2618  // remove parent entity for this child
2619  $bf->removeFirstElement();
2620 
2621  // set new parents
2622  $bf->prepend($baseChain);
2623  }
2624 
2625  $this->buildJoinMap($buildFrom);
2626  }
2627 
2628  $field_def = $chain->getSqlDefinition();
2629  }
2630  elseif (strpos($v, 'ref.') === 0)
2631  {
2632  $definition = str_replace('ref.', '', $v);
2633 
2634  if (strpos($definition, '.') !== false)
2635  {
2636  throw new Main\ArgumentException(sprintf(
2637  'Reference chain `%s` is not allowed here. First-level definitions only.', $v
2638  ));
2639  }
2640 
2641  $absDefinition = strlen($refDefinition) ? $refDefinition . '.' . $definition : $definition;
2642  $chain = $this->getRegisteredChain($absDefinition, true);
2643 
2644  if ($isBackReference)
2645  {
2646  // make sure these fields will be joined before the main join
2647  $this->buildJoinMap(array($chain));
2648  }
2649  else
2650  {
2651  $chain->getLastElement()->setParameter('talias', $alias_ref);
2652  }
2653 
2654  $this->buildJoinMap(array($chain));
2655 
2656  // recursively collect all "build_from" fields
2657  if ($chain->getLastElement()->getValue() instanceof ExpressionField)
2658  {
2659  $this->collectExprChains($chain);
2660  $this->buildJoinMap($chain->getLastElement()->getValue()->getBuildFromChains());
2661  }
2662 
2663  $field_def = $chain->getSqlDefinition();
2664  }
2665  else
2666  {
2667  throw new Main\SystemException(sprintf('Unknown reference value `%s`', $v));
2668  }
2669 
2670  $v = new \CSQLWhereExpression('?#', $field_def);
2671  }
2672  else
2673  {
2674  throw new Main\SystemException(sprintf('Unknown reference value `%s`, it should start with "this." or "ref."', $v));
2675  }
2676 
2677  $new[$k] = $v;
2678  }
2679  }
2680 
2681  return $new;
2682  }
2683 
2684  /**
2685  * @param Filter $reference
2686  * @param $alias_this
2687  * @param $alias_ref
2688  * @param $baseDefinition
2689  * @param $refDefinition
2690  * @param $isBackReference
2691  * @param $firstCall
2692  *
2693  * @return Filter
2694  * @throws Main\ArgumentException
2695  * @throws Main\SystemException
2696  */
2697  protected function prepareJoinFilterReference(Filter $reference, $alias_this, $alias_ref, $baseDefinition, $refDefinition, $isBackReference, $firstCall = true)
2698  {
2699  // do not make an impact on original reference object
2700  if ($firstCall)
2701  {
2702  $reference = clone $reference;
2703  }
2704 
2705  foreach ($reference->getConditions() as $condition)
2706  {
2707  if ($condition instanceof Filter)
2708  {
2709  // subfilter, recursive call
2711  $condition,
2712  $alias_this,
2713  $alias_ref,
2714  $baseDefinition,
2715  $refDefinition,
2716  $isBackReference,
2717  false
2718  );
2719  }
2720  else
2721  {
2722  // regular condition
2723  $field = $condition->getDefinition();
2724 
2725  if (strpos($field, 'this.') === 0)
2726  {
2727  // parse the chain
2728  $definition = str_replace('this.', '', $field);
2729  $absDefinition = strlen($baseDefinition) ? $baseDefinition . '.' . $definition : $definition;
2730 
2731  $chain = $this->getRegisteredChain($absDefinition, true);
2732 
2733  if (!$isBackReference)
2734  {
2735  // make sure these fields will be joined before the main join
2736  $this->buildJoinMap(array($chain));
2737  }
2738  else
2739  {
2740  $chain->getLastElement()->setParameter('talias', $alias_this);
2741  }
2742 
2743  // recursively collect all "build_from" fields
2744  if ($chain->getLastElement()->getValue() instanceof ExpressionField)
2745  {
2746  $this->collectExprChains($chain);
2747  $buildFrom = $chain->getLastElement()->getValue()->getBuildFromChains();
2748 
2749  foreach ($buildFrom as $bf)
2750  {
2751  // set base chain
2752  $baseChain = clone $chain;
2753 
2754  // remove the last one - expression itself
2755  $baseChain->removeLastElement();
2756 
2757  // remove parent entity for this child
2758  $bf->removeFirstElement();
2759 
2760  // set new parents
2761  $bf->prepend($baseChain);
2762  }
2763 
2764  $this->buildJoinMap($buildFrom);
2765  }
2766 
2767  $condition->setColumn($absDefinition);
2768  }
2769  elseif (strpos($field, 'ref.') === 0)
2770  {
2771  $definition = str_replace('ref.', '', $field);
2772 
2773  if (strpos($definition, '.') !== false)
2774  {
2775  throw new Main\ArgumentException(sprintf(
2776  'Reference chain `%s` is not allowed here. First-level definitions only.', $field
2777  ));
2778  }
2779 
2780  $absDefinition = strlen($refDefinition) ? $refDefinition . '.' . $definition : $definition;
2781  $chain = $this->getRegisteredChain($absDefinition, true);
2782 
2783  if ($isBackReference)
2784  {
2785  // make sure these fields will be joined before the main join
2786  $this->buildJoinMap(array($chain));
2787  }
2788  else
2789  {
2790  $chain->getLastElement()->setParameter('talias', $alias_ref);
2791  }
2792 
2793  // recursively collect all "build_from" fields
2794  if ($chain->getLastElement()->getValue() instanceof ExpressionField)
2795  {
2796  $this->collectExprChains($chain);
2797  $this->buildJoinMap($chain->getLastElement()->getValue()->getBuildFromChains());
2798  }
2799 
2800  $condition->setColumn($absDefinition);
2801  }
2802  else
2803  {
2804  throw new Main\SystemException(sprintf('Unknown reference key `%s`, it should start with "this." or "ref."', $field));
2805  }
2806 
2807  // value
2808  $v = $condition->getValue();
2809 
2810  if ($v instanceof Main\DB\SqlExpression)
2811  {
2812  // set entity connection
2813  $v->setConnection($this->entity->getConnection());
2814  }
2815  elseif ($v instanceof ColumnExpression)
2816  {
2817  if (strpos($v->getDefinition(), 'this.') === 0)
2818  {
2819  $definition = str_replace('this.', '', $v->getDefinition());
2820  $absDefinition = strlen($baseDefinition) ? $baseDefinition . '.' . $definition : $definition;
2821 
2822  $chain = $this->getRegisteredChain($absDefinition, true);
2823 
2824  if (!$isBackReference)
2825  {
2826  // make sure these fields will be joined before the main join
2827  $this->buildJoinMap(array($chain));
2828  }
2829  else
2830  {
2831  $chain->getLastElement()->setParameter('talias', $alias_this);
2832  }
2833 
2834  // recursively collect all "build_from" fields
2835  if ($chain->getLastElement()->getValue() instanceof ExpressionField)
2836  {
2837  $this->collectExprChains($chain);
2838  $buildFrom = $chain->getLastElement()->getValue()->getBuildFromChains();
2839 
2840  foreach ($buildFrom as $bf)
2841  {
2842  // set base chain
2843  $baseChain = clone $chain;
2844 
2845  // remove the last one - expression itself
2846  $baseChain->removeLastElement();
2847 
2848  // remove parent entity for this child
2849  $bf->removeFirstElement();
2850 
2851  // set new parents
2852  $bf->prepend($baseChain);
2853  }
2854 
2855  $this->buildJoinMap($buildFrom);
2856  }
2857 
2858  $v->setDefinition($absDefinition);
2859  }
2860  elseif (strpos($v->getDefinition(), 'ref.') === 0)
2861  {
2862  $definition = str_replace('ref.', '', $v->getDefinition());
2863 
2864  if (strpos($definition, '.') !== false)
2865  {
2866  throw new Main\ArgumentException(sprintf(
2867  'Reference chain `%s` is not allowed here. First-level definitions only.', $v->getDefinition()
2868  ));
2869  }
2870 
2871  $absDefinition = strlen($refDefinition) ? $refDefinition . '.' . $definition : $definition;
2872  $chain = $this->getRegisteredChain($absDefinition, true);
2873 
2874  if ($isBackReference)
2875  {
2876  // make sure these fields will be joined before the main join
2877  $this->buildJoinMap(array($chain));
2878  }
2879  else
2880  {
2881  $chain->getLastElement()->setParameter('talias', $alias_ref);
2882  }
2883 
2884  $this->buildJoinMap(array($chain));
2885 
2886  // recursively collect all "build_from" fields
2887  if ($chain->getLastElement()->getValue() instanceof ExpressionField)
2888  {
2889  $this->collectExprChains($chain);
2890  $this->buildJoinMap($chain->getLastElement()->getValue()->getBuildFromChains());
2891  }
2892 
2893  $v->setDefinition($absDefinition);
2894  }
2895  }
2896  }
2897  }
2898 
2899  return $reference;
2900  }
2901 
2902  protected function getJoinCswFields($reference)
2903  {
2904  $fields = array();
2905 
2906  foreach ($reference as $k => $v)
2907  {
2908  if ($k === 'LOGIC')
2909  {
2910  continue;
2911  }
2912 
2913  if (is_numeric($k))
2914  {
2915  $fields = array_merge($fields, $this->getJoinCswFields($v));
2916  }
2917  else
2918  {
2919  // key
2920  $sqlWhere = new \CSQLWhere();
2921  $csw_result = $sqlWhere->makeOperation($k);
2922  list($field, ) = array_values($csw_result);
2923 
2924  $fields[$field] = array(
2925  'TABLE_ALIAS' => 'alias',
2926  'FIELD_NAME' => $field,
2927  'FIELD_TYPE' => 'string',
2928  'MULTIPLE' => '',
2929  'JOIN' => ''
2930  );
2931 
2932  // no need to add values as csw fields
2933  }
2934  }
2935 
2936  return $fields;
2937  }
2938 
2939  /**
2940  * @param $chain
2941  *
2942  * @return bool
2943  * @throws Main\SystemException
2944  */
2945  protected function checkChainsAggregation($chain)
2946  {
2947  /** @var Chain[] $chains */
2948  $chains = is_array($chain) ? $chain : array($chain);
2949 
2950  foreach ($chains as $chain)
2951  {
2952  $last = $chain->getLastElement();
2953  $is_aggr = $last->getValue() instanceof ExpressionField && $last->getValue()->isAggregated();
2954 
2955  if ($is_aggr)
2956  {
2957  return true;
2958  }
2959  }
2960 
2961  return false;
2962  }
2963 
2964  /**
2965  * The most magic method. Do not edit without strong need, and for sure run tests after.
2966  *
2967  * @param Chain $chain
2968  * @param array $storages
2969  *
2970  * @throws Main\SystemException
2971  */
2972  protected function collectExprChains(Chain $chain, $storages = array('hidden'))
2973  {
2974  $last_elem = $chain->getLastElement();
2975  $bf_chains = $last_elem->getValue()->getBuildFromChains();
2976 
2977  $pre_chain = clone $chain;
2978  //$pre_chain->removeLastElement();
2979 
2980  foreach ($bf_chains as $bf_chain)
2981  {
2982  // collect hidden chain
2983  $tmp_chain = clone $pre_chain;
2984 
2985  // exclude init entity
2986  /** @var ChainElement[] $bf_elements */
2987  $bf_elements = array_slice($bf_chain->getAllElements(), 1);
2988 
2989  // add elements
2990  foreach ($bf_elements as $bf_element)
2991  {
2992  $tmp_chain->addElement($bf_element);
2993  }
2994 
2995  //if (!($bf_chain->getLastElement()->getValue() instanceof ExpressionField))
2996  {
2997  foreach ($storages as $storage)
2998  {
2999  $reg_chain = $this->registerChain($storage, $tmp_chain);
3000  }
3001 
3002  // replace "build_from" chain end by registered chain end
3003  // actually it's better and more correctly to replace the whole chain
3004  $bf_chain->removeLastElement();
3005  /** @var Chain $reg_chain */
3006  $bf_chain->addElement($reg_chain->getLastElement());
3007  }
3008 
3009  // check elements to recursive collect hidden chains
3010  foreach ($bf_elements as $bf_element)
3011  {
3012  if ($bf_element->getValue() instanceof ExpressionField)
3013  {
3014  $this->collectExprChains($tmp_chain);
3015  }
3016  }
3017  }
3018  }
3019 
3020  /**
3021  * @return Union
3022  * @throws Main\SystemException
3023  */
3024  protected function getUnionHandler()
3025  {
3026  if ($this->unionHandler === null)
3027  {
3028  $this->unionHandler = new Union($this->entity->getConnection());
3029  }
3030 
3031  return $this->unionHandler;
3032  }
3033 
3034  public function registerChain($section, Chain $chain, $opt_key = null)
3035  {
3036  $alias = $chain->getAlias();
3037 
3038  if (isset($this->global_chains[$alias]))
3039  {
3040  if ($this->global_chains[$alias]->getDefinition() == $chain->getDefinition())
3041  {
3042  $reg_chain = $this->global_chains[$alias];
3043  }
3044  else
3045  {
3046  // we have a collision
3047  // like book.author_id and book.author.id have the same aliases, but different definitions
3048  // in most of the cases it's not a problem, there would be the same expected data
3049  // but we need register this chain separately to be available for internal usage
3050  $reg_chain = $chain;
3051 
3052  $this->global_chains[$reg_chain->getDefinition()] = $chain;
3053  }
3054  }
3055  else
3056  {
3057  $reg_chain = $chain;
3058  $def = $reg_chain->getDefinition();
3059 
3060  $this->global_chains[$alias] = $chain;
3061  $this->global_chains[$def] = $chain;
3062  }
3063 
3064  $storage_name = $section . '_chains';
3065 
3066  // in case of collision do not rewrite by alias
3067  if (!isset($this->{$storage_name}[$alias]))
3068  {
3069  $this->{$storage_name}[$alias] = $reg_chain;
3070  }
3071 
3072  if (!is_null($opt_key))
3073  {
3074  $this->{$storage_name}[$opt_key] = $reg_chain;
3075  }
3076 
3077  return $reg_chain;
3078  }
3079 
3080  /**
3081  * @param $key
3082  * @param bool $force_create
3083  *
3084  * @return Chain|bool
3085  * @throws Main\ArgumentException
3086  * @throws Main\SystemException
3087  */
3088  public function getRegisteredChain($key, $force_create = false)
3089  {
3090  if (isset($this->global_chains[$key]))
3091  {
3092  return $this->global_chains[$key];
3093  }
3094 
3095  if ($force_create)
3096  {
3097  $chain = Chain::getChainByDefinition($this->entity, $key);
3098  $this->registerChain('global', $chain);
3099 
3100  return $chain;
3101  }
3102 
3103  return false;
3104  }
3105 
3106  public function booleanStrongEqualityCallback($field, $operation, $value)
3107  {
3108  $value = ($operation == 'SE') ? $value : !$value;
3109  return ($value ? '' : 'NOT ') . $field;
3110  }
3111 
3112  public function nullEqualityCallback($field, $operation, /** @noinspection PhpUnusedParameterInspection */ $value)
3113  {
3114  return $field.' IS '.($operation == 'SE' ? '' : 'NOT ') . 'NULL';
3115  }
3116 
3117  public function dataDoublingCallback($field, /** @noinspection PhpUnusedParameterInspection */ $operation, $value)
3118  {
3119  return $field.' IN ('.$value.')';
3120  }
3121 
3122  /**
3123  * @param $query
3124  *
3125  * @return Main\DB\ArrayResult|Main\DB\Result
3126  * @throws Main\ArgumentException
3127  * @throws Main\Db\SqlQueryException
3128  * @throws Main\SystemException
3129  */
3130  protected function query($query)
3131  {
3132  // check nosql configuration
3133  $connection = $this->entity->getConnection();
3134  $configuration = $connection->getConfiguration();
3135 
3136  /** @var Main\DB\Result $result */
3137  $result = null;
3138 
3139  if (isset($configuration['handlersocket']['read']))
3140  {
3141  // optimize through nosql
3142  $nosqlConnectionName = $configuration['handlersocket']['read'];
3143 
3144  $nosqlConnection = Main\Application::getInstance()->getConnectionPool()->getConnection($nosqlConnectionName);
3145  $isNosqlCapable = NosqlPrimarySelector::checkQuery($nosqlConnection, $this);
3146 
3147  if ($isNosqlCapable)
3148  {
3149  $nosqlResult = NosqlPrimarySelector::relayQuery($nosqlConnection, $this);
3150  $result = new Main\DB\ArrayResult($nosqlResult);
3151 
3152  // add data converters
3153  if (!empty($nosqlResult))
3154  {
3155  /** @var callable[] $converters */
3156  $converters = [];
3157 
3158  foreach ($this->getSelectChains() as $selectChain)
3159  {
3160  $field = $selectChain->getLastElement()->getValue();
3161 
3162  if ($field instanceof ScalarField)
3163  {
3164  $converter = $connection->getSqlHelper()->getConverter($field);
3165 
3166  if (is_callable($converter))
3167  {
3168  $converter[$selectChain->getAlias()] = $converter;
3169  }
3170  }
3171  }
3172 
3173  if (!empty($converters))
3174  {
3175  $result->setConverters($converters);
3176  }
3177  }
3178  }
3179  }
3180 
3181  if ($result === null)
3182  {
3183  // regular SQL query
3184  $cnt = null;
3185 
3186  if ($this->countTotal)
3187  {
3188  $buildParts = $this->query_build_parts;
3189 
3190  //remove order
3191  unset($buildParts['ORDER BY']);
3192 
3193  //remove select
3194  $buildParts['SELECT'] = "1 cntholder";
3195 
3196  foreach ($buildParts as $k => &$v)
3197  {
3198  $v = $k . ' ' . $v;
3199  }
3200 
3201  $cntQuery = join("\n", $buildParts);
3202 
3203  // select count
3204  $cntQuery = /** @lang text */
3205  "SELECT COUNT(cntholder) AS TMP_ROWS_CNT FROM ({$cntQuery}) xxx";
3206  $cnt = $connection->queryScalar($cntQuery);
3207  }
3208 
3209  $result = $connection->query($query);
3210  $result->setReplacedAliases($this->replaced_aliases);
3211 
3212  if($this->countTotal)
3213  {
3214  $result->setCount($cnt);
3215  }
3216 
3217  static::$last_query = $query;
3218  }
3219 
3220  if ($this->isFetchModificationRequired())
3221  {
3222  $result->addFetchDataModifier(array($this, 'fetchDataModificationCallback'));
3223  }
3224 
3225  return $result;
3226  }
3227 
3228  /**
3229  * Being called in Db\Result as a data fetch modifier
3230  * @param $data
3231  */
3232  public function fetchDataModificationCallback(&$data)
3233  {
3234  // entity-defined callbacks
3235  foreach ($this->selectFetchModifiers as $alias => $modifiers)
3236  {
3237  foreach ($modifiers as $modifier)
3238  {
3239  $data[$alias] = call_user_func_array($modifier, array($data[$alias], $this, $data, $alias));
3240  }
3241  }
3242  }
3243 
3244  /**
3245  * Check if fetch data modification required, also caches modifier-callbacks
3246  * @return bool
3247  * @throws Main\SystemException
3248  */
3250  {
3251  $this->selectFetchModifiers = array();
3252 
3253  foreach ($this->select_chains as $chain)
3254  {
3255  if ($chain->getLastElement()->getValue()->getFetchDataModifiers())
3256  {
3257  $this->selectFetchModifiers[$chain->getAlias()] = $chain->getLastElement()->getValue()->getFetchDataModifiers();
3258  }
3259  }
3260 
3261  return !empty($this->selectFetchModifiers) || !empty($this->files);
3262  }
3263 
3264  /**
3265  * @param $query
3266  *
3267  * @return array
3268  * @throws Main\SystemException
3269  */
3270  protected function replaceSelectAliases($query)
3271  {
3272  $connection = $this->entity->getConnection();
3273  $helper = $connection->getSqlHelper();
3274 
3275  $length = (int) $helper->getAliasLength();
3276  $leftQuote = $helper->getLeftQuote();
3277  $rightQuote = $helper->getRightQuote();
3278 
3279  $replaced = array();
3280 
3281  preg_match_all(
3282  '/ AS '.preg_quote($leftQuote).'([a-z0-9_]{'.($length+1).',})'.preg_quote($rightQuote).'/i',
3283  $query, $matches
3284  );
3285 
3286  if (!empty($matches[1]))
3287  {
3288  foreach ($matches[1] as $alias)
3289  {
3290  $newAlias = 'FALIAS_'.count($replaced);
3291  $replaced[$newAlias] = $alias;
3292 
3293  $query = str_replace(
3294  ' AS ' . $helper->quote($alias),
3295  ' AS ' . $helper->quote($newAlias) . '/* '.$alias.' */',
3296  $query
3297  );
3298  }
3299  }
3300 
3301  return array($query, $replaced);
3302  }
3303 
3304  /**
3305  * @param $source
3306  *
3307  * @return string
3308  * @throws Main\SystemException
3309  */
3310  public function quoteTableSource($source)
3311  {
3312  // don't quote subqueries
3313  if (!preg_match('/\s*\(\s*SELECT.*\)\s*/is', $source))
3314  {
3315  $source = $this->entity->getConnection()->getSqlHelper()->quote($source);
3316  }
3317 
3318  return $source;
3319  }
3320 
3321  public function __clone()
3322  {
3323  $this->entity = clone $this->entity;
3324 
3325  foreach ($this->select as $k => $v)
3326  {
3327  if ($v instanceof ExpressionField)
3328  {
3329  $this->select[$k] = clone $v;
3330  }
3331  }
3332  }
3333 
3334  /**
3335  * @return bool
3336  * @throws Main\SystemException
3337  */
3338  public function hasBackReference()
3339  {
3340  if (empty($this->global_chains))
3341  {
3342  throw new Main\SystemException('Query has not been executed or built');
3343  }
3344 
3345  foreach ($this->global_chains as $chain)
3346  {
3347  if ($chain->hasBackReference())
3348  {
3349  return true;
3350  }
3351  }
3352 
3353  return false;
3354  }
3355 
3356  /**
3357  * @return array|Chain[]
3358  */
3359  public function getChains()
3360  {
3361  return $this->global_chains;
3362  }
3363 
3364  /**
3365  * @return array|Chain[]
3366  */
3367  public function getGroupChains()
3368  {
3369  return $this->group_chains;
3370  }
3371 
3372  /**
3373  * @return array
3374  */
3375  public function getHiddenChains()
3376  {
3377  return $this->hidden_chains;
3378  }
3379 
3380  /**
3381  * @return array|Chain[]
3382  */
3383  public function getHavingChains()
3384  {
3385  return $this->having_chains;
3386  }
3387 
3388  /**
3389  * @return array|Chain[]
3390  */
3391  public function getFilterChains()
3392  {
3393  return $this->filter_chains;
3394  }
3395 
3396  /**
3397  * @return array|Chain[]
3398  */
3399  public function getOrderChains()
3400  {
3401  return $this->order_chains;
3402  }
3403 
3404  /**
3405  * @return array|Chain[]
3406  */
3407  public function getSelectChains()
3408  {
3409  return $this->select_chains;
3410  }
3411 
3412  /**
3413  * @return array|Chain[]
3414  */
3415  public function getWhereChains()
3416  {
3417  return $this->where_chains;
3418  }
3419 
3420  /**
3421  * @return Chain[]
3422  */
3423  public function getRuntimeChains()
3424  {
3425  return $this->runtime_chains;
3426  }
3427 
3428  public function getJoinMap()
3429  {
3430  return $this->join_map;
3431  }
3432 
3433  /**
3434  * Builds and returns SQL query string
3435  *
3436  * @return string
3437  * @throws Main\ArgumentException
3438  * @throws Main\SystemException
3439  */
3440  public function getQuery()
3441  {
3442  return $this->buildQuery();
3443  }
3444 
3445  /**
3446  * Returns last executed query string
3447  *
3448  * @return string
3449  */
3450  public static function getLastQuery()
3451  {
3452  return static::$last_query;
3453  }
3454 
3455  public function getEntity()
3456  {
3457  return $this->entity;
3458  }
3459 
3460  /**
3461  * Builds SQL filter conditions for WHERE.
3462  * Useful for external calls: building SQL for mass UPDATEs or DELETEs
3463  *
3464  * @param Entity $entity
3465  * @param array|Filter $filter the same format as for setFilter/where
3466  *
3467  * @return string
3468  * @throws Main\ArgumentException
3469  * @throws Main\SystemException
3470  */
3471  public static function buildFilterSql(Entity $entity, $filter)
3472  {
3473  $query = new static($entity);
3474 
3475  if ($filter instanceof Filter)
3476  {
3477  // new object filter
3478  $query->where($filter);
3479  }
3480  else
3481  {
3482  // old array filter
3483  $query->setFilter($filter);
3484  }
3485 
3486  $query->setCustomBaseTableAlias($entity->getDBTableName())->buildQuery();
3487 
3488  return $query->query_build_parts['WHERE'];
3489  }
3490 
3491  /**
3492  * @param bool $withPostfix
3493  *
3494  * @return string
3495  * @throws Main\SystemException
3496  */
3497  public function getInitAlias($withPostfix = true)
3498  {
3499  if ($this->custom_base_table_alias !== null)
3500  {
3502  }
3503 
3504  $init_alias = strtolower($this->entity->getCode());
3505 
3506  // add postfix
3507  if ($withPostfix)
3508  {
3509  $init_alias .= $this->table_alias_postfix;
3510  }
3511 
3512  // check length
3513  $connection = $this->entity->getConnection();
3514  $aliasLength = $connection->getSqlHelper()->getAliasLength();
3515 
3516  if (strlen($init_alias) > $aliasLength)
3517  {
3518  $init_alias = 'base';
3519 
3520  // add postfix
3521  if ($withPostfix)
3522  {
3523  $init_alias .= $this->table_alias_postfix;
3524  }
3525  }
3526 
3527  return $init_alias;
3528  }
3529 
3530  public function getReplacedAliases()
3531  {
3532  return $this->replaced_aliases;
3533  }
3534 
3535  /*
3536  * Sets cache TTL in seconds.
3537  * @param int $ttl
3538  * @return $this
3539  */
3540  public function setCacheTtl($ttl)
3541  {
3542  $this->cacheTtl = (int)$ttl;
3543  return $this;
3544  }
3545 
3546  /**
3547  * Enables or disables caching of queries with joins.
3548  * @param bool $mode
3549  * @return $this
3550  */
3551  public function cacheJoins($mode)
3552  {
3553  $this->cacheJoins = (bool)$mode;
3554  return $this;
3555  }
3556 
3557  public function dump()
3558  {
3559  echo '<pre>';
3560 
3561  echo 'last query: ';
3562  var_dump(static::$last_query);
3563  echo PHP_EOL;
3564 
3565  echo 'size of select_chains: '.count($this->select_chains);
3566  echo PHP_EOL;
3567  foreach ($this->select_chains as $num => $chain)
3568  {
3569  echo ' chain ['.$num.'] has '.$chain->getSize().' elements: '.PHP_EOL;
3570  $chain->dump();
3571  echo PHP_EOL;
3572  }
3573 
3574  echo PHP_EOL.PHP_EOL;
3575 
3576  echo 'size of where_chains: '.count($this->where_chains);
3577  echo PHP_EOL;
3578  foreach ($this->where_chains as $num => $chain)
3579  {
3580  echo ' chain ['.$num.'] has '.$chain->getSize().' elements: '.PHP_EOL;
3581  $chain->dump();
3582  echo PHP_EOL;
3583  }
3584 
3585  echo PHP_EOL.PHP_EOL;
3586 
3587  echo 'size of group_chains: '.count($this->group_chains);
3588  echo PHP_EOL;
3589  foreach ($this->group_chains as $num => $chain)
3590  {
3591  echo ' chain ['.$num.'] has '.$chain->getSize().' elements: '.PHP_EOL;
3592  $chain->dump();
3593  echo PHP_EOL;
3594  }
3595 
3596  echo PHP_EOL.PHP_EOL;
3597 
3598  echo 'size of having_chains: '.count($this->having_chains);
3599  echo PHP_EOL;
3600  foreach ($this->having_chains as $num => $chain)
3601  {
3602  echo ' chain ['.$num.'] has '.$chain->getSize().' elements: '.PHP_EOL;
3603  $chain->dump();
3604  echo PHP_EOL;
3605  }
3606 
3607  echo PHP_EOL.PHP_EOL;
3608 
3609  echo 'size of filter_chains: '.count($this->filter_chains);
3610  echo PHP_EOL;
3611  foreach ($this->filter_chains as $num => $chain)
3612  {
3613  echo ' chain ['.$num.'] has '.$chain->getSize().' elements: '.PHP_EOL;
3614  $chain->dump();
3615  echo PHP_EOL;
3616  }
3617 
3618  echo PHP_EOL.PHP_EOL;
3619 
3620  echo 'size of select_expr_chains: '.count($this->select_expr_chains);
3621  echo PHP_EOL;
3622  foreach ($this->select_expr_chains as $num => $chain)
3623  {
3624  echo ' chain ['.$num.'] has '.$chain->getSize().' elements: '.PHP_EOL;
3625  $chain->dump();
3626  echo PHP_EOL;
3627  }
3628 
3629  echo PHP_EOL.PHP_EOL;
3630 
3631  echo 'size of hidden_chains: '.count($this->hidden_chains);
3632  echo PHP_EOL;
3633  foreach ($this->hidden_chains as $num => $chain)
3634  {
3635  echo ' chain ['.$num.'] has '.$chain->getSize().' elements: '.PHP_EOL;
3636  $chain->dump();
3637  echo PHP_EOL;
3638  }
3639 
3640  echo PHP_EOL.PHP_EOL;
3641 
3642  echo 'size of global_chains: '.count($this->global_chains);
3643  echo PHP_EOL;
3644  foreach ($this->global_chains as $num => $chain)
3645  {
3646  echo ' chain ['.$num.'] has '.$chain->getSize().' elements: '.PHP_EOL;
3647  $chain->dump();
3648  echo PHP_EOL;
3649  }
3650 
3651  echo PHP_EOL.PHP_EOL;
3652 
3653  var_dump($this->join_map);
3654 
3655  echo '</pre>';
3656  }
3657 }
Bitrix\Main\ORM\Query\Query\fetch
fetch(\Bitrix\Main\Text\Converter $converter=null)
Short alias for $result->fetch()
Definition: main/lib/orm/query/query.php:829
Bitrix\Main\ORM\Query\Query\$order_chains
$order_chains
Definition: main/lib/orm/query/query.php:146
Bitrix\Main\ORM\Query\Query\rewriteDataDoubling
rewriteDataDoubling(Filter $filter, $section)
Definition: main/lib/orm/query/query.php:1595
Bitrix\Main\ORM\Query\Query\setGroup
setGroup($group)
Sets a list of fields in GROUP BY clause.
Definition: main/lib/orm/query/query.php:382
Bitrix\Main\ORM\Query\Query\getLimit
getLimit()
Returns a limit.
Definition: main/lib/orm/query/query.php:489
Bitrix\Main\ORM\Query\Query\isFetchModificationRequired
isFetchModificationRequired()
Check if fetch data modification required, also caches modifier-callbacks.
Definition: main/lib/orm/query/query.php:3249
Bitrix\Main\ORM\Query\Query\$query_build_parts
$query_build_parts
Definition: main/lib/orm/query/query.php:171
Bitrix\Main\ORM\Query\Query\getJoinCswFields
getJoinCswFields($reference)
Definition: main/lib/orm/query/query.php:2902
Bitrix\Main\ORM\Query\Query\fetchObject
fetchObject()
Short alias for $result->fetchObject()
Definition: main/lib/orm/query/query.php:856
Bitrix\Main\ORM\Query
Definition: main/lib/orm/query/chain.php:3
Bitrix\Main\ORM\Query\Query\addFilter
addFilter($key, $value)
Adds a filter for WHERE clause.
Definition: main/lib/orm/query/query.php:352
Bitrix\Main\ORM\Query\Query\getEntity
getEntity()
Definition: main/lib/orm/query/query.php:3455
Bitrix\Main\ORM\Query\Query\collectExprChains
collectExprChains(Chain $chain, $storages=array('hidden'))
The most magic method.
Definition: main/lib/orm/query/query.php:2972
Bitrix\Main\ORM\Query\Chain\dump
dump()
Definition: main/lib/orm/query/chain.php:505
Bitrix\Main\ORM\Query\Query\__construct
__construct($source)
Definition: main/lib/orm/query/query.php:223
Bitrix\Main\ORM\Query\Query\setUnionOffset
setUnionOffset($offset)
General offset for all the union queries.
Definition: main/lib/orm/query/query.php:633
Bitrix\Main\ORM\Query\Chain\getChainByDefinition
static getChainByDefinition(Entity $init_entity, $definition)
Definition: main/lib/orm/query/chain.php:208
Bitrix\Main\ORM\Fields\TextField
Definition: textfield.php:16
Bitrix\Main\ORM\Query\Query\setFilter
setFilter(array $filter)
Sets a list of filters for WHERE clause.
Definition: main/lib/orm/query/query.php:339
Bitrix\Main\ORM\Query\Query\getSelectChains
getSelectChains()
Definition: main/lib/orm/query/query.php:3407
Bitrix\Main\Text\StringHelper
Definition: stringhelper.php:16
Bitrix\Main\ORM\Query\Query\prepareJoinReference
prepareJoinReference($reference, $alias_this, $alias_ref, $baseDefinition, $refDefinition, $isBackReference)
Definition: main/lib/orm/query/query.php:2468
Bitrix\Main\ORM\Query\Query\$hidden_chains
$hidden_chains
Definition: main/lib/orm/query/query.php:162
Bitrix\Main\ORM\Query\Query\checkFilterHandlerAggregation
checkFilterHandlerAggregation(Filter $filter)
Definition: main/lib/orm/query/query.php:1544
Bitrix\Main\ORM\Query\Query\divideFilter
divideFilter()
Definition: main/lib/orm/query/query.php:1381
Bitrix\Main\ORM\Query\Query\$where_chains
$where_chains
Definition: main/lib/orm/query/query.php:153
Bitrix\Main\ORM\Query\NosqlPrimarySelector\checkQuery
static checkQuery(\Bitrix\Main\Data\Connection $connection, Query $query)
Definition: nosqlprimaryselector.php:25
Bitrix\Main\ORM\Fields\Relations\Reference
Definition: reference.php:25
Bitrix\Main\ORM\Query\Query\$order
$order
Definition: main/lib/orm/query/query.php:120
Bitrix\Main\ORM\Query\Query\$select_expr_chains
$select_expr_chains
Definition: main/lib/orm/query/query.php:160
Bitrix\Main\ORM\Query\Query\$unionHandler
$unionHandler
Definition: main/lib/orm/query/query.php:196
Bitrix\Main\ORM\Query\NosqlPrimarySelector\relayQuery
static relayQuery(\Bitrix\Main\Data\Connection $connection, Query $query)
Definition: nosqlprimaryselector.php:106
Bitrix\Main
Bitrix\Main\ORM\Query\Query\$table_alias_postfix
$table_alias_postfix
Definition: main/lib/orm/query/query.php:184
Bitrix\Main\ORM\Query\Query\setUnionOrder
setUnionOrder($order)
General order for all the union queries.
Definition: main/lib/orm/query/query.php:587
Bitrix\Main\ORM\Query\Query\getGroupChains
getGroupChains()
Definition: main/lib/orm/query/query.php:3367
Bitrix\Main\ORM\Query\Query\$cacheTtl
$cacheTtl
Definition: main/lib/orm/query/query.php:214
Bitrix\Main\ORM\Query\Query\$filter
$filter
Definition: main/lib/orm/query/query.php:127
Bitrix\Main\ORM\Query\Query\$group
$group
Definition: main/lib/orm/query/query.php:119
Bitrix\Main\ORM\Query\Filter\Expressions\ColumnExpression
Definition: columnexpression.php:16
Bitrix\Main\ORM\Query\Query\getHavingChains
getHavingChains()
Definition: main/lib/orm/query/query.php:3383
Bitrix\Main\ORM\Query\Query\getLastQuery
static getLastQuery()
Returns last executed query string.
Definition: main/lib/orm/query/query.php:3450
Bitrix\Main\ORM\Query\Expression
Definition: expression.php:21
Bitrix\Main\ORM\Query\Query\query
query($query)
Definition: main/lib/orm/query/query.php:3130
Bitrix\Main\ORM\Query\Query\getRegisteredChain
getRegisteredChain($key, $force_create=false)
Definition: main/lib/orm/query/query.php:3088
Bitrix\Main\Text\Converter
Definition: main/lib/text/converter.php:4
Bitrix\Main\ORM\Query\Query\$filterHandler
$filterHandler
Definition: main/lib/orm/query/query.php:132
Bitrix\Main\ORM\Query\Query\__call
__call($method, $arguments)
Definition: main/lib/orm/query/query.php:256
Bitrix\Main\ORM\Query\Query\buildGroup
buildGroup()
Definition: main/lib/orm/query/query.php:2008
Bitrix\Main\ORM\Query\Query\setOrder
setOrder($order)
Sets a list of fields for ORDER BY clause.
Definition: main/lib/orm/query/query.php:424
Bitrix\Main\ORM\Query\Query\setTableAliasPostfix
setTableAliasPostfix($postfix)
Definition: main/lib/orm/query/query.php:721
Bitrix\Main\ORM\Query\Chain\getSize
getSize()
Definition: main/lib/orm/query/chain.php:143
Bitrix\Main\ORM\Query\Query\buildJoin
buildJoin()
Definition: main/lib/orm/query/query.php:1937
Bitrix\Main\Text\StringHelper\camel2snake
static camel2snake($str)
Changes registry from CamelCase to snake_case.
Definition: stringhelper.php:44
Bitrix\Main\ORM\Query\Query\getOrder
getOrder()
Returns an array of fields for ORDER BY clause.
Definition: main/lib/orm/query/query.php:407
Bitrix\Main\ORM\Query\Query\setCustomBaseTableAlias
setCustomBaseTableAlias($alias)
Sets a custom alias for the table of the init entity.
Definition: main/lib/orm/query/query.php:739
Bitrix\Main\ORM\Query\Query\getSelect
getSelect()
Returns an array of fields for SELECT clause.
Definition: main/lib/orm/query/query.php:285
Bitrix\Main\ORM\Query\Query\$global_chains
$global_chains
Definition: main/lib/orm/query/query.php:168
Bitrix\Main\ORM\Query\Query\$havingHandler
$havingHandler
Definition: main/lib/orm/query/query.php:138
Bitrix\Main\ORM\Query\Query\$join_registry
$join_registry
Definition: main/lib/orm/query/query.php:193
Bitrix\Main\ORM\Entity\getInstance
static getInstance($entityName)
Definition: main/lib/orm/entity.php:100
Bitrix\Main\ORM\Query\Query\$last_query
static $last_query
Definition: main/lib/orm/query/query.php:202
Bitrix\Main\ORM\Query\Query\hasBackReference
hasBackReference()
Definition: main/lib/orm/query/query.php:3338
Bitrix\Main\ORM\Query\Query\getJoinMap
getJoinMap()
Definition: main/lib/orm/query/query.php:3428
Bitrix\Main\ORM\Query\Query\addOrder
addOrder($definition, $order='ASC')
Adds a filed to the list of fields for ORDER BY clause.
Definition: main/lib/orm/query/query.php:458
Bitrix\Main\ORM\Query\Chain\getAlias
getAlias()
Definition: main/lib/orm/query/chain.php:180
Bitrix\Main\ORM\Query\Query\$is_executing
$is_executing
Definition: main/lib/orm/query/query.php:199
Bitrix\Main\ORM\Query\Query\fetchAll
fetchAll(\Bitrix\Main\Text\Converter $converter=null)
Short alias for $result->fetchAll()
Definition: main/lib/orm/query/query.php:843
Bitrix\Main\ORM\Query\Query\$cacheJoins
$cacheJoins
Definition: main/lib/orm/query/query.php:215
Bitrix\Main\ORM\Query\Query\getHiddenChains
getHiddenChains()
Definition: main/lib/orm/query/query.php:3375
Bitrix\Main\ORM\Query\Query\cacheJoins
cacheJoins($mode)
Enables or disables caching of queries with joins.
Definition: main/lib/orm/query/query.php:3551
Bitrix\Main\ORM\Fields\ScalarField
Definition: scalarfield.php:18
Bitrix\Main\ORM\Query\Query\setOffset
setOffset($offset)
Sets an offset for LIMIT n, m clause.
Definition: main/lib/orm/query/query.php:522
Bitrix\Main\ORM\Query\Query\setFilterChains
setFilterChains(&$filter, $section='filter')
Definition: main/lib/orm/query/query.php:1103
Bitrix\Main\DB\SqlExpression
Definition: sqlexpression.php:18
Bitrix\Main\ORM\Query\Query\buildWhere
buildWhere()
Definition: main/lib/orm/query/query.php:1977
Bitrix\Main\ORM\Query\Query\buildOrder
buildOrder()
Definition: main/lib/orm/query/query.php:2130
Bitrix\Main\ORM\Query\Query\buildJoinMap
buildJoinMap($chains=null)
Definition: main/lib/orm/query/query.php:1673
Bitrix\Main\ORM\Query\Query\$select_chains
$select_chains
Definition: main/lib/orm/query/query.php:144
Bitrix\Main\ORM\Fields\ExpressionField
Definition: expressionfield.php:23
Bitrix\Main\ORM\Query\Query\exec
exec()
Builds and executes the query and returns the result.
Definition: main/lib/orm/query/query.php:784
Bitrix\Main\ORM\Query\Query\buildSelect
buildSelect()
Definition: main/lib/orm/query/query.php:1916
Bitrix\Main\ORM\Query\Query\$group_chains
$group_chains
Definition: main/lib/orm/query/query.php:145
Bitrix\Main\ORM\Fields\ExpressionField\isAggregated
isAggregated()
Definition: expressionfield.php:208
Bitrix\Main\ORM\Entity\getInstanceByQuery
static getInstanceByQuery(Query $query, &$entity_name=null)
Definition: main/lib/orm/entity.php:874
Bitrix\Main\ORM\Query\Query\getFilter
getFilter()
Returns an array of filters for WHERE clause.
Definition: main/lib/orm/query/query.php:328
Bitrix\Main\ORM\Entity
Base entity.
Definition: main/lib/orm/entity.php:25
Bitrix\Main\ORM\Query\Query\countTotal
countTotal($count=null)
Definition: main/lib/orm/query/query.php:528
Bitrix\Main\ORM\Query\Query\$limit
$limit
Definition: main/lib/orm/query/query.php:121
Bitrix\Main\ORM\Query\Query\filter
static filter()
Returns new instance of Filter.
Definition: main/lib/orm/query/query.php:756
Bitrix\Main\ORM\Query\Query\$whereHandler
$whereHandler
Definition: main/lib/orm/query/query.php:135
Bitrix\Main\ORM\Query\Query\setUnionLimit
setUnionLimit($limit)
General limit for all the union queries.
Definition: main/lib/orm/query/query.php:619
Bitrix\Main\ORM\Query\Query\buildQuery
buildQuery()
Definition: main/lib/orm/query/query.php:2178
Bitrix\Main\ORM\Query\Query\addToSelectChain
addToSelectChain($definition, $alias=null)
Definition: main/lib/orm/query/query.php:881
Bitrix\Main\ArgumentException
Exception is thrown when function argument is not valid.
Definition: main/lib/exception.php:33
Bitrix\Main\ORM\Fields\Relations\ManyToMany
Definition: manytomany.php:25
Bitrix\Main\ORM\Query\Filter\ConditionTree
Definition: conditiontree.php:23
Bitrix\Main\ORM\Query\Query\$custom_base_table_alias
$custom_base_table_alias
Definition: main/lib/orm/query/query.php:187
Bitrix\Main\ORM\Query\Query\$where
$where
Definition: main/lib/orm/query/query.php:128
Bitrix\Main\ORM\Query\Query\addSelect
addSelect($definition, $alias='')
Adds a field for SELECT clause.
Definition: main/lib/orm/query/query.php:309
Bitrix\Main\ORM\Query\Query\fetchDataModificationCallback
fetchDataModificationCallback(&$data)
Being called in Db\Result as a data fetch modifier.
Definition: main/lib/orm/query/query.php:3232
Bitrix\Main\ORM\Query\Query
Definition: main/lib/orm/query/query.php:112
Bitrix\Main\SystemException
Base class for fatal exceptions.
Definition: main/lib/exception.php:7
Bitrix\Main\ORM\Query\Chain
Definition: main/lib/orm/query/chain.php:13
Bitrix\Main\ORM\Query\Chain\getDefinition
getDefinition($elementsSlice=0)
Definition: main/lib/orm/query/chain.php:153
Bitrix\Main\ORM\Query\Query\$join_map
$join_map
Definition: main/lib/orm/query/query.php:190
Bitrix\Main\ORM\Query\Query\nullEqualityCallback
nullEqualityCallback($field, $operation, $value)
Definition: main/lib/orm/query/query.php:3112
Bitrix\Main\ORM\Fields\BooleanField
Definition: booleanfield.php:16
Bitrix\Main\ORM\Query\Query\expr
static expr()
Used to create ExpressionField in a short way.
Definition: main/lib/orm/query/query.php:767
Bitrix\Main\ORM\Query\Query\divideFilterHandler
divideFilterHandler()
Definition: main/lib/orm/query/query.php:1449
Bitrix\Main\ORM\Query\Query\$replaced_taliases
$replaced_taliases
Definition: main/lib/orm/query/query.php:208
Bitrix\Main\ORM\Query\Query\setCacheTtl
setCacheTtl($ttl)
Definition: main/lib/orm/query/query.php:3540
Bitrix\Main\ORM\Query\Query\$selectFetchModifiers
$selectFetchModifiers
Definition: main/lib/orm/query/query.php:211
Bitrix\Main\ORM\Query\Query\addGroup
addGroup($group)
Adds a field to the list of fields for GROUP BY clause.
Definition: main/lib/orm/query/query.php:396
Bitrix\Main\ORM\Fields\Field
Definition: main/lib/orm/fields/field.php:24
Bitrix\Main\ORM\Query\Query\getOffset
getOffset()
Returns an offset.
Definition: main/lib/orm/query/query.php:511
Bitrix\Main\ORM\Query\Query\setLimit
setLimit($limit)
Sets a limit for LIMIT n clause.
Definition: main/lib/orm/query/query.php:500
Bitrix\Main\ORM\Query\Query\getFilterCswFields
getFilterCswFields(&$filter)
Definition: main/lib/orm/query/query.php:2295
Bitrix\Main\Application\getInstance
static getInstance()
Returns current instance of the Application.
Definition: main/lib/application.php:86
Bitrix\Main\ORM\Query\Query\$filter_chains
$filter_chains
Definition: main/lib/orm/query/query.php:152
Bitrix\Main\ORM\Query\ChainElement
Definition: chainelement.php:16
Bitrix\Main\ORM\Query\Query\disableDataDoubling
disableDataDoubling()
Replaces all 1:N relations in filter to ID IN (subquery SELECT ID FROM <1:N relation>) Available for ...
Definition: main/lib/orm/query/query.php:657
Bitrix\Main\ORM\Query\Query\addToOrderChain
addToOrderChain($definition)
Definition: main/lib/orm/query/query.php:1656
Bitrix\Main\ORM\Query\Query\$having_chains
$having_chains
Definition: main/lib/orm/query/query.php:154
Bitrix\Main\DB\ArrayResult
Definition: arrayresult.php:9
Bitrix\Main\ORM\Query\Query\getWhereChains
getWhereChains()
Definition: main/lib/orm/query/query.php:3415
Bitrix\Main\ORM\Query\Query\getReplacedAliases
getReplacedAliases()
Definition: main/lib/orm/query/query.php:3530
Bitrix\Main\ORM\Query\Query\fetchCollection
fetchCollection()
Short alias for $result->fetchCollection()
Definition: main/lib/orm/query/query.php:868
Bitrix\Main\ORM\Query\Query\$data_doubling_off
$data_doubling_off
Definition: main/lib/orm/query/query.php:181
Bitrix\Main\ORM\Query\Query\$offset
$offset
Definition: main/lib/orm/query/query.php:122
Bitrix\Main\ORM\Query\Query\getGroup
getGroup()
Returns an array of fields for GROUP BY clause.
Definition: main/lib/orm/query/query.php:371
Bitrix\Main\ORM\Query\Query\$expressionHelper
static $expressionHelper
Definition: main/lib/orm/query/query.php:174
Bitrix\Main\ORM\Query\Union
Definition: union.php:19
Bitrix\Main\ORM\Query\Query\addToGroupChain
addToGroupChain($definition)
Definition: main/lib/orm/query/query.php:1640
Bitrix\Main\ORM\Query\Query\$countTotal
$countTotal
Definition: main/lib/orm/query/query.php:123
Bitrix\Main\ORM\Query\Query\$having
$having
Definition: main/lib/orm/query/query.php:129
Bitrix\Main\ORM\Query\Query\buildFilterSql
static buildFilterSql(Entity $entity, $filter)
Builds SQL filter conditions for WHERE.
Definition: main/lib/orm/query/query.php:3471
Bitrix\Main\ORM\Query\Query\registerChain
registerChain($section, Chain $chain, $opt_key=null)
Definition: main/lib/orm/query/query.php:3034
Bitrix\Main\ORM\Query\Query\getFilterChains
getFilterChains()
Definition: main/lib/orm/query/query.php:3391
Bitrix\Main\ORM\Query\Query\setFilterHandlerChains
setFilterHandlerChains(Filter $where, $section='filter')
Definition: main/lib/orm/query/query.php:1227
Bitrix\Main\ORM\Query\Query\getChains
getChains()
Definition: main/lib/orm/query/query.php:3359
Bitrix\Main\ORM\Query\Query\unionAll
unionAll()
Puts additional query to union (all) with current.
Definition: main/lib/orm/query/query.php:567
Bitrix\Main\ORM\Query\Query\dataDoublingCallback
dataDoublingCallback($field, $operation, $value)
Definition: main/lib/orm/query/query.php:3117
Bitrix\Main\ORM\Query\UnionCondition
Definition: unioncondition.php:20
Bitrix\Main\ORM\Fields\Relations\OneToMany
Definition: onetomany.php:22
Bitrix\Sender\Connector\Filter
const Filter
Definition: resultview.php:22
Bitrix\Main\ORM\Query\Query\addUnionOrder
addUnionOrder($definition, $order='ASC')
General order for all the union queries.
Definition: main/lib/orm/query/query.php:605
Bitrix\Main\ORM\Query\Query\$select
$select
Definition: main/lib/orm/query/query.php:118
Bitrix\Main\ORM\Query\Query\registerRuntimeField
registerRuntimeField($name, $fieldInfo=null)
Adds a runtime field (being created dynamically, opposite to being described statically in the entity...
Definition: main/lib/orm/query/query.php:686
Bitrix\Main\ORM\Data\Result
Definition: main/lib/orm/data/result.php:15
Bitrix\Main\ORM\Query\Query\dump
dump()
Definition: main/lib/orm/query/query.php:3557
Bitrix\Main\ORM\Query\Query\getUnionHandler
getUnionHandler()
Definition: main/lib/orm/query/query.php:3024
Bitrix\Main\ORM\Query\Query\getOrderChains
getOrderChains()
Definition: main/lib/orm/query/query.php:3399
Bitrix\Main\ORM\Query\Query\getTableAliasPostfix
getTableAliasPostfix()
Definition: main/lib/orm/query/query.php:727
Bitrix\Main\ORM\Query\Query\checkChainsAggregation
checkChainsAggregation($chain)
Definition: main/lib/orm/query/query.php:2945
Bitrix\Main\ORM\Query\Query\$runtime_chains
$runtime_chains
Definition: main/lib/orm/query/query.php:165
Bitrix\Main\ORM\Query\Query\quoteTableSource
quoteTableSource($source)
Definition: main/lib/orm/query/query.php:3310
Bitrix\Main\ORM\Query\Query\buildHaving
buildHaving()
Definition: main/lib/orm/query/query.php:2099
Bitrix\Main\ORM\Query\Query\$entity
$entity
Definition: main/lib/orm/query/query.php:115
Bitrix\Main\ORM\Query\Chain\getLastElement
getLastElement()
Definition: main/lib/orm/query/chain.php:96
Bitrix\Main\ORM\Query\Query\enableDataDoubling
enableDataDoubling()
Definition: main/lib/orm/query/query.php:644
Bitrix
Class Button.
Bitrix\Main\ORM\Query\Query\checkFilterAggregation
checkFilterAggregation($filter)
Definition: main/lib/orm/query/query.php:1503
Bitrix\Main\ORM\Query\Query\prepareJoinFilterReference
prepareJoinFilterReference(Filter $reference, $alias_this, $alias_ref, $baseDefinition, $refDefinition, $isBackReference, $firstCall=true)
Definition: main/lib/orm/query/query.php:2697
Bitrix\Main\ORM\Query\Query\getRuntimeChains
getRuntimeChains()
Definition: main/lib/orm/query/query.php:3423
Bitrix\Main\ORM\Query\Query\setSelect
setSelect(array $select)
Sets a list of fields for SELECT clause.
Definition: main/lib/orm/query/query.php:296
Bitrix\Main\ORM\Query\Query\booleanStrongEqualityCallback
booleanStrongEqualityCallback($field, $operation, $value)
Definition: main/lib/orm/query/query.php:3106
Bitrix\Main\ORM\Query\Query\replaceSelectAliases
replaceSelectAliases($query)
Definition: main/lib/orm/query/query.php:3270
Bitrix\Main\ORM\Query\Query\__clone
__clone()
Definition: main/lib/orm/query/query.php:3321
Bitrix\Main\ORM\Query\Query\$having_expr_chains
$having_expr_chains
Definition: main/lib/orm/query/query.php:161
Bitrix\Main\ORM\Query\Query\$replaced_aliases
$replaced_aliases
Definition: main/lib/orm/query/query.php:205
Bitrix\Main\ORM\Query\Query\getQuery
getQuery()
Builds and returns SQL query string.
Definition: main/lib/orm/query/query.php:3440
Bitrix\Main\ORM\Query\Query\getInitAlias
getInitAlias($withPostfix=true)
Definition: main/lib/orm/query/query.php:3497