CJoinElement


system.db.ar
继承 class CJoinElement
源自 1.0
版本 $Id: CActiveFinder.php 3562 2012-02-13 01:27:06Z qiang.xue $
源码
CJoinElement表示一个在关联树中由CActiveFinder创建的子树节点。

公共属性

属性 类型 描述 定义在
children array 子关联元素列表 CJoinElement
id integer 树节点的唯一ID CJoinElement
master CActiveRelation 主关系 CJoinElement
model CActiveRecord 与树节点关联的模型 CJoinElement
rawTableAlias string 此元素的引用表的别名 CJoinElement
records array 此查询找到的活动记录列表,它们按主键值索引。 CJoinElement
relation CActiveRelation 此树节点表示的关系 CJoinElement
slave CActiveRelation 从关系 CJoinElement
stats array 元素列表 CJoinElement
tableAlias string 关联元素的表别名 CJoinElement

公共方法

方法 描述 定义在
__construct() 构造函数。 CJoinElement
afterFind() 调用所有记录的 CActiveRecord::afterFind . CJoinElement
beforeFind() 调用 CActiveRecord::beforeFind. CJoinElement
buildQuery() 建立关联查询并带有所有的依赖 HAS_ONE 和 BELONGS_TO 结点. CJoinElement
count() 返回关联语句查询出来的主记录的数目。 CJoinElement
destroy() 删除引用的子元素和查询,以避免循环引用。 CJoinElement
find() 根据查询实体执行循环轮询查找。 CJoinElement
findWithBase() 当基本记录可用的时候,执行预先加载。 CJoinElement
getColumnPrefix() 返回关联的字段前缀以用来消除歧义。 CJoinElement
getColumnSelect() 生成要查询的字段列表。 CJoinElement
getJoinCondition() 返回返回关联语句(这个结点与它的父结点连接) CJoinElement
getPrimaryKeyRange() 返回根据查询的主键值指定行的条件。 CJoinElement
getPrimaryKeySelect() 返回返回主键的选择 CJoinElement
getTableNameWithAlias() 返回返回表名和表别名(若有),这个可以无须转义直接在SQL查询中使用。 CJoinElement
lazyFind() 根据指定的基记录来执行延迟查找。 CJoinElement
runQuery() 执行关联查询并填充查询结果。 CJoinElement

属性详细

children 属性
public array $children;

子关联元素列表

id 属性
public integer $id;

树节点的唯一ID

master 属性
public CActiveRelation $master;

主关系

model 属性
public CActiveRecord $model;

与树节点关联的模型

rawTableAlias 属性
public string $rawTableAlias;

此元素的引用表的别名

records 属性
public array $records;

此查询找到的活动记录列表,它们按主键值索引。

relation 属性
public CActiveRelation $relation;

此树节点表示的关系

slave 属性
public CActiveRelation $slave;

从关系

stats 属性
public array $stats;

元素列表

tableAlias 属性
public string $tableAlias;

关联元素的表别名

方法详细

__construct() 方法
public void __construct(CActiveFinder $finder, mixed $relation, CJoinElement $parent=NULL, integer $id=0)
$finder CActiveFinder 查询对象
$relation mixed 关联到这个树节点的关系(如果第三个参数不为 null), 或者是模型(如果第三个参数为 null)。
$parent CJoinElement 父树节点
$id integer 此树结点的ID,在所有树结点中是唯一的
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=CActiveRecord::model($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;
        else if(
is_array($table->primaryKey) && in_array($name,$table->primaryKey))
            
$this->_pkAlias[$name]=$alias;
    }
}

构造函数。

afterFind() 方法
public void afterFind()
public function afterFind()
{
    foreach(
$this->records as $record)
        
$record->afterFindInternal();
    foreach(
$this->children as $child)
        
$child->afterFind();

    
$this->children null;
}

调用所有记录的 CActiveRecord::afterFind .

beforeFind() 方法
public void beforeFind(boolean $isChild=true)
$isChild boolean 是否作为一个子节点调用
public function beforeFind($isChild=true)
{
    if(
$isChild)
        
$this->model->beforeFindInternal();

    foreach(
$this->children as $child)
        
$child->beforeFind(true);
}

调用 CActiveRecord::beforeFind.

buildQuery() 方法
public void buildQuery(CJoinQuery $query)
$query CJoinQuery 此查询被建立
public function buildQuery($query)
{
    foreach(
$this->children as $child)
    {
        if(
$child->master!==null)
            
$child->_joined=true;
        else if(
$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);
        }
    }
}

建立关联查询并带有所有的依赖 HAS_ONE 和 BELONGS_TO 结点.

count() 方法
public string count(CDbCriteria $criteria=NULL)
$criteria CDbCriteria 查询条件
{return} string 返回主记录的数量。 注意: 类型为字符串保持最大精度。
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);

    
$select=is_array($criteria->select) ? implode(',',$criteria->select) : $criteria->select;
    if(
$select!=='*' && !strncasecmp($select,'count',5))
        
$query->selects=array($select);
    else if(
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();
    
$query->limit=$query->offset=-1;
    
$command=$query->createCommand($this->_builder);
    return 
$command->queryScalar();
}

返回关联语句查询出来的主记录的数目。

destroy() 方法
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);
}

删除引用的子元素和查询,以避免循环引用。 这个方法是内部使用。

find() 方法
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>=|| $criteria->limit>=0);
        
$this->buildQuery($query);
        
$this->_finder->baseLimited=false;
        
$this->runQuery($query);
    }
    else if(!
$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();
}

根据查询实体执行循环轮询查找。

findWithBase() 方法
public void findWithBase(mixed $baseRecords)
$baseRecords mixed 可用的基本记录。
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();
}

当基本记录可用的时候,执行预先加载。

getColumnPrefix() 方法
public string getColumnPrefix()
{return} string 关联的字段前缀以用来消除歧义。
public function getColumnPrefix()
{
    if(
$this->tableAlias!==null)
        return 
$this->rawTableAlias.'.';
    else
        return 
$this->_table->rawName.'.';
}

getColumnSelect() 方法
public string getColumnSelect(mixed $select='*')
$select mixed 要查询的字段,默认值为 '*', 指示所有的字段。
{return} string 返回字段选择
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;
            }
            else if(
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);
        else if(
is_array($this->_pkAlias))
        {
            foreach(
$this->_table->primaryKey as $name)
                if(!isset(
$selected[$name]))
                    
$columns[]=$prefix.$schema->quoteColumnName($name).' AS '.$schema->quoteColumnName($this->_pkAlias[$name]);
        }
    }

    return 
implode(', ',$columns);
}

生成要查询的字段列表。 字段会正确处理别名,如果没有指定主键,那么会自动将主键加到查询语句。

getJoinCondition() 方法
public string getJoinCondition()
{return} string 返回关联语句(这个结点与它的父结点连接)
public function getJoinCondition()
{
    
$parent=$this->_parent;
    if(
$this->relation instanceof CManyManyRelation)
    {
        if(!
preg_match('/^\s*(.*?)\((.*)\)\s*$/',$this->relation->foreignKey,$matches))
            throw new 
CDbException(Yii::t('yii','The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key. The format of the foreign key must be "joinTable(fk1,fk2,...)".',
                array(
'{class}'=>get_class($parent->model),'{relation}'=>$this->relation->name)));

        
$schema=$this->_builder->getSchema();
        if((
$joinTable=$schema->getTable($matches[1]))===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}'=>$matches[1])));
        
$fks=preg_split('/\s*,\s*/',$matches[2],-1,PREG_SPLIT_NO_EMPTY);

        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->relation instanceof CBelongsToRelation)
        {
            
$pke=$this;
            
$fke=$parent;
        }
        else if(
$this->slave===null)
        {
            
$pke=$parent;
            
$fke=$this;
        }
        else
        {
            
$pke=$this;
            
$fke=$this->slave;
        }
        return 
$this->joinOneMany($fke,$fks,$pke,$parent);
    }
}

getPrimaryKeyRange() 方法
public string getPrimaryKeyRange()
{return} string 根据查询的主键值指定行的条件。
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() 方法
public string getPrimaryKeySelect()
{return} string 返回主键的选择
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);
    else if(
is_array($this->_pkAlias))
    {
        foreach(
$this->_pkAlias as $name=>$alias)
            
$columns[]=$prefix.$schema->quoteColumnName($name).' AS '.$schema->quoteColumnName($alias);
    }
    return 
implode(', ',$columns);
}

getTableNameWithAlias() 方法
public string getTableNameWithAlias()
{return} string 返回表名和表别名(若有),这个可以无须转义直接在SQL查询中使用。
public function getTableNameWithAlias()
{
    if(
$this->tableAlias!==null)
        return 
$this->_table->rawName ' ' $this->rawTableAlias;
    else
        return 
$this->_table->rawName;
}

lazyFind() 方法
public void lazyFind(CActiveRecord $baseRecord)
$baseRecord CActiveRecord 要被获取活动记录的相关对象
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();

    switch(
count($this->children))
    {
        case 
0:
            return;
        break;
        case 
1:
            
$child=reset($this->children);
        break;
        default: 
// bridge(s) inside
            
$child=end($this->children);
        break;
    }

    
$query=new CJoinQuery($child);
    
$query->selects=array();
    
$query->selects[]=$child->getColumnSelect($child->relation->select);
    
$query->conditions=array();
    
$query->conditions[]=$child->relation->condition;
    
$query->conditions[]=$child->relation->on;
    
$query->groups[]=$child->relation->group;
    
$query->joins[]=$child->relation->join;
    
$query->havings[]=$child->relation->having;
    
$query->orders[]=$child->relation->order;
    if(
is_array($child->relation->params))
        
$query->params=$child->relation->params;
    
$query->elements[$child->id]=true;
    if(
$child->relation instanceof CHasManyRelation)
    {
        
$query->limit=$child->relation->limit;
        
$query->offset=$child->relation->offset;
    }

    
$child->beforeFind();
    
$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);
        }
    }
}

根据指定的基记录来执行延迟查找。

runQuery() 方法
public void runQuery(CJoinQuery $query)
$query CJoinQuery 要被执行的查询。
public function runQuery($query)
{
    
$command=$query->createCommand($this->_builder);
    foreach(
$command->queryAll() as $row)
        
$this->populateRecord($query,$row);
}

执行关联查询并填充查询结果。