/**
  * Constructor.
  * @param \Database\ActiveRecord\Finder $finder the finder
  * @param mixed $relation the relation (if the third parameter is not null)
  * or the model (if the third parameter is null) associated with this tree node.
  * @param \Database\ActiveRecord\Join\Element $parent the parent tree node
  * @param integer $id the ID of this tree node that is unique among all the tree nodes
  */
 public function __construct($finder, $relation, $parent = null, $id = 0)
 {
     $this->_finder = $finder;
     $this->id = $id;
     if ($parent !== null) {
         $this->relation = $relation;
         $this->_parent = $parent;
         $this->model = $this->_finder->getModel($relation->className);
         $this->_builder = $this->model->getCommandBuilder();
         $this->tableAlias = $relation->alias === null ? $relation->name : $relation->alias;
         $this->rawTableAlias = $this->_builder->getSchema()->quoteTableName($this->tableAlias);
         $this->_table = $this->model->getTableSchema();
     } else {
         $this->model = $relation;
         $this->_builder = $relation->getCommandBuilder();
         $this->_table = $relation->getTableSchema();
         $this->tableAlias = $this->model->getTableAlias();
         $this->rawTableAlias = $this->_builder->getSchema()->quoteTableName($this->tableAlias);
     }
     // set up column aliases, such as t1_c2
     $table = $this->_table;
     if ($this->model->getDbConnection()->getDriverName() === 'oci') {
         // Issue 482
         $prefix = 'T' . $id . '_C';
     } else {
         $prefix = $this->tableAlias . '_';
     }
     foreach ($table->getColumnNames() as $name) {
         $alias = $prefix . $name;
         $this->_columnAliases[$name] = $alias;
         if ($table->primaryKey === $name) {
             $this->_pkAlias = $alias;
         } elseif (is_array($table->primaryKey) && in_array($name, $table->primaryKey)) {
             $this->_pkAlias[$name] = $alias;
         }
     }
 }
 /**
  * @param $joinTableName
  * @param $keys
  * @throws Exception
  */
 private function queryManyMany($joinTableName, $keys)
 {
     $relation = $this->relation;
     $model = $this->_finder->getModel($relation->className);
     $table = $model->getTableSchema();
     $builder = $model->getCommandBuilder();
     $schema = $builder->getSchema();
     $pkTable = $this->_parent->model->getTableSchema();
     $tableAlias = $model->getTableAlias(true);
     if (($joinTable = $builder->getSchema()->getTable($joinTableName)) === null) {
         throw new Exception(Yii::t('yii', 'The relation "{relation}" in active record class "{class}" is not specified correctly: the join table "{joinTable}" given in the foreign key cannot be found in the database.', ['{class}' => get_class($this->_parent->model), '{relation}' => $relation->name, '{joinTable}' => $joinTableName]));
     }
     $fks = preg_split('/\\s*,\\s*/', $keys, -1, PREG_SPLIT_NO_EMPTY);
     if (count($fks) !== count($table->primaryKey) + count($pkTable->primaryKey)) {
         throw new Exception(Yii::t('yii', 'The relation "{relation}" in active record class "{class}" is specified with an incomplete foreign key. The foreign key must consist of columns referencing both joining tables.', ['{class}' => get_class($this->_parent->model), '{relation}' => $relation->name]));
     }
     $joinCondition = [];
     $map = [];
     $fkDefined = true;
     #foreach($fks as $i=>$fk)
     foreach ($fks as $fk) {
         if (!isset($joinTable->columns[$fk])) {
             throw new Exception(Yii::t('yii', 'The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key "{key}". There is no such column in the table "{table}".', ['{class}' => get_class($this->_parent->model), '{relation}' => $relation->name, '{key}' => $fk, '{table}' => $joinTable->name]));
         }
         if (isset($joinTable->foreignKeys[$fk])) {
             list($tableName, $pk) = $joinTable->foreignKeys[$fk];
             if (!isset($joinCondition[$pk]) && $schema->compareTableNames($table->rawName, $tableName)) {
                 $joinCondition[$pk] = $tableAlias . '.' . $schema->quoteColumnName($pk) . '=' . $joinTable->rawName . '.' . $schema->quoteColumnName($fk);
             } elseif (!isset($map[$pk]) && $schema->compareTableNames($pkTable->rawName, $tableName)) {
                 $map[$pk] = $fk;
             } else {
                 $fkDefined = false;
                 break;
             }
         } else {
             $fkDefined = false;
             break;
         }
     }
     if (!$fkDefined) {
         $joinCondition = [];
         $map = [];
         foreach ($fks as $i => $fk) {
             if ($i < count($pkTable->primaryKey)) {
                 $pk = is_array($pkTable->primaryKey) ? $pkTable->primaryKey[$i] : $pkTable->primaryKey;
                 $map[$pk] = $fk;
             } else {
                 $j = $i - count($pkTable->primaryKey);
                 $pk = is_array($table->primaryKey) ? $table->primaryKey[$j] : $table->primaryKey;
                 $joinCondition[$pk] = $tableAlias . '.' . $schema->quoteColumnName($pk) . '=' . $joinTable->rawName . '.' . $schema->quoteColumnName($fk);
             }
         }
     }
     if ($joinCondition === [] || $map === []) {
         throw new Exception(Yii::t('yii', 'The relation "{relation}" in active record class "{class}" is specified with an incomplete foreign key. The foreign key must consist of columns referencing both joining tables.', ['{class}' => get_class($this->_parent->model), '{relation}' => $relation->name]));
     }
     $records = $this->_parent->records;
     $cols = [];
     foreach (is_string($pkTable->primaryKey) ? [$pkTable->primaryKey] : $pkTable->primaryKey as $n => $pk) {
         $name = $joinTable->rawName . '.' . $schema->quoteColumnName($map[$pk]);
         $cols[$name] = $name . ' AS ' . $schema->quoteColumnName('c' . $n);
     }
     $keys = array_keys($records);
     if (is_array($pkTable->primaryKey)) {
         foreach ($keys as &$key) {
             $key2 = unserialize($key);
             $key = [];
             foreach ($pkTable->primaryKey as $pk) {
                 $key[$map[$pk]] = $key2[$pk];
             }
         }
     }
     $join = empty($relation->join) ? '' : ' ' . $relation->join;
     $where = empty($relation->condition) ? '' : ' WHERE (' . $relation->condition . ')';
     $group = empty($relation->group) ? '' : ', ' . $relation->group;
     $having = empty($relation->having) ? '' : ' AND (' . $relation->having . ')';
     $order = empty($relation->order) ? '' : ' ORDER BY ' . $relation->order;
     $sql = 'SELECT ' . $this->relation->select . ' AS ' . $schema->quoteColumnName('s') . ', ' . implode(', ', $cols) . ' FROM ' . $table->rawName . ' ' . $tableAlias . ' INNER JOIN ' . $joinTable->rawName . ' ON (' . implode(') AND (', $joinCondition) . ')' . $join . $where . ' GROUP BY ' . implode(', ', array_keys($cols)) . $group . ' HAVING (' . $builder->createInCondition($joinTable, $map, $keys) . ')' . $having . $order;
     $command = $builder->getDbConnection()->createCommand($sql);
     if (is_array($relation->params)) {
         $builder->bindValues($command, $relation->params);
     }
     $stats = [];
     foreach ($command->queryAll() as $row) {
         if (is_array($pkTable->primaryKey)) {
             $key = [];
             foreach ($pkTable->primaryKey as $n => $k) {
                 $key[$k] = $row['c' . $n];
             }
             $stats[serialize($key)] = $row['s'];
         } else {
             $stats[$row['c0']] = $row['s'];
         }
     }
     foreach ($records as $pk => $record) {
         $record->addRelatedRecord($relation->name, isset($stats[$pk]) ? $stats[$pk] : $this->relation->defaultValue, false);
     }
 }