CJoinElement
Package | system.db.ar |
---|---|
Inheritance | class CJoinElement |
Since | 1.0 |
Source Code | framework/db/ar/CActiveFinder.php |
Public Properties
Property | Type | Description | Defined By |
---|---|---|---|
children | array | list of child join elements | CJoinElement |
id | integer | the unique ID of this tree node | CJoinElement |
master | CActiveRelation | the master relation | CJoinElement |
model | CActiveRecord | the model associated with this tree node | CJoinElement |
rawTableAlias | string | the quoted table alias for this element | CJoinElement |
records | array | list of active records found by the queries. | CJoinElement |
relation | CActiveRelation | the relation represented by this tree node | CJoinElement |
slave | CActiveRelation | the slave relation | CJoinElement |
stats | array | list of stat elements | CJoinElement |
tableAlias | string | table alias for this join element | CJoinElement |
Public Methods
Method | Description | Defined By |
---|---|---|
__construct() | Constructor. | CJoinElement |
afterFind() | Calls CActiveRecord::afterFind of all the records. | CJoinElement |
buildQuery() | Builds the join query with all descendant HAS_ONE and BELONGS_TO nodes. | CJoinElement |
count() | Count the number of primary records returned by the join statement. | CJoinElement |
destroy() | Removes references to child elements and finder to avoid circular references. | CJoinElement |
find() | Performs the recursive finding with the criteria. | CJoinElement |
findWithBase() | Performs the eager loading with the base records ready. | CJoinElement |
getColumnPrefix() | Returns the column prefix for column reference disambiguation | CJoinElement |
getColumnSelect() | Generates the list of columns to be selected. | CJoinElement |
getJoinCondition() | Returns the join statement (this node joins with its parent) | CJoinElement |
getPrimaryKeyRange() | Returns the condition that specifies only the rows with the selected primary key values. | CJoinElement |
getPrimaryKeySelect() | Returns the primary key selection | CJoinElement |
getTableNameWithAlias() | Returns the table name and the table alias (if any). This can be used directly in SQL query without escaping. | CJoinElement |
lazyFind() | Performs lazy find with the specified base record. | CJoinElement |
runQuery() | Executes the join query and populates the query results. | CJoinElement |
Property Details
children property
public array $children;
list of child join elements
id property
public integer $id;
the unique ID of this tree node
master property
public CActiveRelation $master;
the master relation
model property
public CActiveRecord $model;
the model associated with this tree node
rawTableAlias property
public string $rawTableAlias;
the quoted table alias for this element
records property
public array $records;
list of active records found by the queries. They are indexed by primary key values.
relation property
public CActiveRelation $relation;
the relation represented by this tree node
slave property
public CActiveRelation $slave;
the slave relation
stats property
public array $stats;
list of stat elements
tableAlias property
public string $tableAlias;
table alias for this join element
Method Details
__construct() method
public void __construct(CActiveFinder $finder, mixed $relation, CJoinElement $parent=NULL, integer $id=0) | ||
$finder | CActiveFinder | the finder |
$relation | mixed | the relation (if the third parameter is not null) or the model (if the third parameter is null) associated with this tree node. |
$parent | CJoinElement | the parent tree node |
$id | integer | 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 // root element, the first parameter is the model.
{
$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='t'.$id.'_c';
foreach($table->getColumnNames() as $key=>$name)
{
$alias=$prefix.$key;
$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;
}
}
Constructor.
afterFind() method
public void afterFind() |
public function afterFind()
{
foreach($this->records as $record)
$record->afterFindInternal();
foreach($this->children as $child)
$child->afterFind();
$this->children = null;
}
Calls CActiveRecord::afterFind of all the records.
buildQuery() method
public void buildQuery(CJoinQuery $query) | ||
$query | CJoinQuery | the query being built up |
public function buildQuery($query)
{
foreach($this->children as $child)
{
if($child->master!==null)
$child->_joined=true;
elseif($child->relation instanceof CHasOneRelation || $child->relation instanceof CBelongsToRelation
|| $this->_finder->joinAll || $child->relation->together || (!$this->_finder->baseLimited && $child->relation->together===null))
{
$child->_joined=true;
$query->join($child);
$child->buildQuery($query);
}
}
}
Builds the join query with all descendant HAS_ONE and BELONGS_TO nodes.
count() method
public string count(CDbCriteria $criteria=NULL) | ||
$criteria | CDbCriteria | the query criteria |
{return} | string | number of primary records. Note: type is string to keep max. precision. |
public function count($criteria=null)
{
$query=new CJoinQuery($this,$criteria);
// ensure only one big join statement is used
$this->_finder->baseLimited=false;
$this->_finder->joinAll=true;
$this->buildQuery($query);
$query->limit=$query->offset=-1;
if(!empty($criteria->group) || !empty($criteria->having))
{
$query->orders = array();
$command=$query->createCommand($this->_builder);
$sql=$command->getText();
$sql="SELECT COUNT(*) FROM ({$sql}) sq";
$command->setText($sql);
$command->params=$query->params;
return $command->queryScalar();
}
else
{
$select=is_array($criteria->select) ? implode(',',$criteria->select) : $criteria->select;
if($select!=='*' && preg_match('/^count\s*\(/i',trim($select)))
$query->selects=array($select);
elseif(is_string($this->_table->primaryKey))
{
$prefix=$this->getColumnPrefix();
$schema=$this->_builder->getSchema();
$column=$prefix.$schema->quoteColumnName($this->_table->primaryKey);
$query->selects=array("COUNT(DISTINCT $column)");
}
else
$query->selects=array("COUNT(*)");
$query->orders=$query->groups=$query->havings=array();
$command=$query->createCommand($this->_builder);
return $command->queryScalar();
}
}
Count the number of primary records returned by the join statement.
destroy() method
public void destroy() |
public function destroy()
{
if(!empty($this->children))
{
foreach($this->children as $child)
$child->destroy();
}
unset($this->_finder, $this->_parent, $this->model, $this->relation, $this->master, $this->slave, $this->records, $this->children, $this->stats);
}
Removes references to child elements and finder to avoid circular references. This is internally used.
find() method
public void find(CDbCriteria $criteria=NULL) | ||
$criteria | CDbCriteria | the query criteria |
public function find($criteria=null)
{
if($this->_parent===null) // root element
{
$query=new CJoinQuery($this,$criteria);
$this->_finder->baseLimited=($criteria->offset>=0 || $criteria->limit>=0);
$this->buildQuery($query);
$this->_finder->baseLimited=false;
$this->runQuery($query);
}
elseif(!$this->_joined && !empty($this->_parent->records)) // not joined before
{
$query=new CJoinQuery($this->_parent);
$this->_joined=true;
$query->join($this);
$this->buildQuery($query);
$this->_parent->runQuery($query);
}
foreach($this->children as $child) // find recursively
$child->find();
foreach($this->stats as $stat)
$stat->query();
}
Performs the recursive finding with the criteria.
findWithBase() method
public void findWithBase(mixed $baseRecords) | ||
$baseRecords | mixed | the available base record(s). |
public function findWithBase($baseRecords)
{
if(!is_array($baseRecords))
$baseRecords=array($baseRecords);
if(is_string($this->_table->primaryKey))
{
foreach($baseRecords as $baseRecord)
$this->records[$baseRecord->{$this->_table->primaryKey}]=$baseRecord;
}
else
{
foreach($baseRecords as $baseRecord)
{
$pk=array();
foreach($this->_table->primaryKey as $name)
$pk[$name]=$baseRecord->$name;
$this->records[serialize($pk)]=$baseRecord;
}
}
$query=new CJoinQuery($this);
$this->buildQuery($query);
if(count($query->joins)>1)
$this->runQuery($query);
foreach($this->children as $child)
$child->find();
foreach($this->stats as $stat)
$stat->query();
}
Performs the eager loading with the base records ready.
getColumnPrefix() method
public string getColumnPrefix() | ||
{return} | string | the column prefix for column reference disambiguation |
public function getColumnPrefix()
{
if($this->tableAlias!==null)
return $this->rawTableAlias.'.';
else
return $this->_table->rawName.'.';
}
getColumnSelect() method
public string getColumnSelect(mixed $select='*') | ||
$select | mixed | columns to be selected. Defaults to '*', indicating all columns. |
{return} | string | the column selection |
public function getColumnSelect($select='*')
{
$schema=$this->_builder->getSchema();
$prefix=$this->getColumnPrefix();
$columns=array();
if($select==='*')
{
foreach($this->_table->getColumnNames() as $name)
$columns[]=$prefix.$schema->quoteColumnName($name).' AS '.$schema->quoteColumnName($this->_columnAliases[$name]);
}
else
{
if(is_string($select))
$select=explode(',',$select);
$selected=array();
foreach($select as $name)
{
$name=trim($name);
$matches=array();
if(($pos=strrpos($name,'.'))!==false)
$key=substr($name,$pos+1);
else
$key=$name;
$key=trim($key,'\'"`');
if($key==='*')
{
foreach($this->_table->columns as $name=>$column)
{
$alias=$this->_columnAliases[$name];
if(!isset($selected[$alias]))
{
$columns[]=$prefix.$column->rawName.' AS '.$schema->quoteColumnName($alias);
$selected[$alias]=1;
}
}
continue;
}
if(isset($this->_columnAliases[$key])) // simple column names
{
$columns[]=$prefix.$schema->quoteColumnName($key).' AS '.$schema->quoteColumnName($this->_columnAliases[$key]);
$selected[$this->_columnAliases[$key]]=1;
}
elseif(preg_match('/^(.*?)\s+AS\s+(\w+)$/im',$name,$matches)) // if the column is already aliased
{
$alias=$matches[2];
if(!isset($this->_columnAliases[$alias]) || $this->_columnAliases[$alias]!==$alias)
{
$this->_columnAliases[$alias]=$alias;
$columns[]=$name;
$selected[$alias]=1;
}
}
else
throw new CDbException(Yii::t('yii','Active record "{class}" is trying to select an invalid column "{column}". Note, the column must exist in the table or be an expression with alias.',
array('{class}'=>get_class($this->model), '{column}'=>$name)));
}
// add primary key selection if they are not selected
if(is_string($this->_pkAlias) && !isset($selected[$this->_pkAlias]))
$columns[]=$prefix.$schema->quoteColumnName($this->_table->primaryKey).' AS '.$schema->quoteColumnName($this->_pkAlias);
elseif(is_array($this->_pkAlias))
{
foreach($this->_pkAlias as $name=>$alias)
if(!isset($selected[$alias]))
$columns[]=$prefix.$schema->quoteColumnName($name).' AS '.$schema->quoteColumnName($alias);
}
}
return implode(', ',$columns);
}
Generates the list of columns to be selected. Columns will be properly aliased and primary keys will be added to selection if they are not specified.
getJoinCondition() method
public string getJoinCondition() | ||
{return} | string | the join statement (this node joins with its parent) |
public function getJoinCondition()
{
$parent=$this->_parent;
if($this->relation instanceof CManyManyRelation)
{
$schema=$this->_builder->getSchema();
$joinTableName=$this->relation->getJunctionTableName();
if(($joinTable=$schema->getTable($joinTableName))===null)
throw new CDbException(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.',
array('{class}'=>get_class($parent->model), '{relation}'=>$this->relation->name, '{joinTable}'=>$joinTableName)));
$fks=$this->relation->getJunctionForeignKeys();
return $this->joinManyMany($joinTable,$fks,$parent);
}
else
{
$fks=is_array($this->relation->foreignKey) ? $this->relation->foreignKey : preg_split('/\s*,\s*/',$this->relation->foreignKey,-1,PREG_SPLIT_NO_EMPTY);
if($this->slave!==null)
{
if($this->relation instanceof CBelongsToRelation)
{
$fks=array_flip($fks);
$pke=$this->slave;
$fke=$this;
}
else
{
$pke=$this;
$fke=$this->slave;
}
}
elseif($this->relation instanceof CBelongsToRelation)
{
$pke=$this;
$fke=$parent;
}
else
{
$pke=$parent;
$fke=$this;
}
return $this->joinOneMany($fke,$fks,$pke,$parent);
}
}
getPrimaryKeyRange() method
public string getPrimaryKeyRange() | ||
{return} | string | the condition that specifies only the rows with the selected primary key values. |
public function getPrimaryKeyRange()
{
if(empty($this->records))
return '';
$values=array_keys($this->records);
if(is_array($this->_table->primaryKey))
{
foreach($values as &$value)
$value=unserialize($value);
}
return $this->_builder->createInCondition($this->_table,$this->_table->primaryKey,$values,$this->getColumnPrefix());
}
getPrimaryKeySelect() method
public string getPrimaryKeySelect() | ||
{return} | string | the primary key selection |
public function getPrimaryKeySelect()
{
$schema=$this->_builder->getSchema();
$prefix=$this->getColumnPrefix();
$columns=array();
if(is_string($this->_pkAlias))
$columns[]=$prefix.$schema->quoteColumnName($this->_table->primaryKey).' AS '.$schema->quoteColumnName($this->_pkAlias);
elseif(is_array($this->_pkAlias))
{
foreach($this->_pkAlias as $name=>$alias)
$columns[]=$prefix.$schema->quoteColumnName($name).' AS '.$schema->quoteColumnName($alias);
}
return implode(', ',$columns);
}
getTableNameWithAlias() method
public string getTableNameWithAlias() | ||
{return} | string | the table name and the table alias (if any). This can be used directly in SQL query without escaping. |
public function getTableNameWithAlias()
{
if($this->tableAlias!==null)
return $this->_table->rawName . ' ' . $this->rawTableAlias;
else
return $this->_table->rawName;
}
lazyFind() method
public void lazyFind(CActiveRecord $baseRecord) | ||
$baseRecord | CActiveRecord | the active record whose related object is to be fetched. |
public function lazyFind($baseRecord)
{
if(is_string($this->_table->primaryKey))
$this->records[$baseRecord->{$this->_table->primaryKey}]=$baseRecord;
else
{
$pk=array();
foreach($this->_table->primaryKey as $name)
$pk[$name]=$baseRecord->$name;
$this->records[serialize($pk)]=$baseRecord;
}
foreach($this->stats as $stat)
$stat->query();
if(!$this->children)
return;
$params=array();
foreach($this->children as $child)
if(is_array($child->relation->params))
$params=array_merge($params,$child->relation->params);
$query=new CJoinQuery($child);
$query->selects=array($child->getColumnSelect($child->relation->select));
$query->conditions=array(
$child->relation->on,
);
$query->groups[]=$child->relation->group;
$query->joins[]=$child->relation->join;
$query->havings[]=$child->relation->having;
$query->orders[]=$child->relation->order;
$query->params=$params;
$query->elements[$child->id]=true;
if($child->relation instanceof CHasManyRelation)
{
$query->limit=$child->relation->limit;
$query->offset=$child->relation->offset;
}
$child->applyLazyCondition($query,$baseRecord);
$this->_joined=true;
$child->_joined=true;
$this->_finder->baseLimited=false;
$child->buildQuery($query);
$child->runQuery($query);
foreach($child->children as $c)
$c->find();
if(empty($child->records))
return;
if($child->relation instanceof CHasOneRelation || $child->relation instanceof CBelongsToRelation)
$baseRecord->addRelatedRecord($child->relation->name,reset($child->records),false);
else // has_many and many_many
{
foreach($child->records as $record)
{
if($child->relation->index!==null)
$index=$record->{$child->relation->index};
else
$index=true;
$baseRecord->addRelatedRecord($child->relation->name,$record,$index);
}
}
}
Performs lazy find with the specified base record.
runQuery() method
public void runQuery(CJoinQuery $query) | ||
$query | CJoinQuery | the query to be executed. |
public function runQuery($query)
{
$command=$query->createCommand($this->_builder);
foreach($command->queryAll() as $row)
$this->populateRecord($query,$row);
}
Executes the join query and populates the query results.
© 2008–2017 by Yii Software LLC
Licensed under the three clause BSD license.
http://www.yiiframework.com/doc/api/1.1/CJoinElement