/** * 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); } }