58
55
* Uses the most aggressive join approach.
59
* By default, several join statements may be generated in order to avoid
60
* fetching duplicated data. By calling this method, all tables will be joined
61
* together all at once.
62
* @param boolean whether we should enforce join even when a limit option is placed on the primary table query.
63
* Defaults to true. If false, we would still use two queries when there is a HAS_MANY/MANY_MANY relation and
64
* the primary table has a LIMIT option. This parameter is available since version 1.0.3.
56
* By calling this method, even if there is LIMIT/OFFSET option set for
57
* the primary table query, we will still use a single SQL statement.
58
* By default (without calling this method), the primary table will be queried
59
* by itself so that LIMIT/OFFSET can be correctly applied.
65
60
* @return CActiveFinder the finder object
68
public function together($ignoreLimit=true)
63
public function together()
70
65
$this->joinAll=true;
72
$this->baseLimited=false;
76
69
private function query($criteria,$all=false)
78
if($this->_criteria!==null)
80
$this->_criteria->mergeWith($criteria);
81
$criteria=$this->_criteria;
71
$this->_joinTree->beforeFind();
72
$this->_joinTree->model->applyScopes($criteria);
84
73
$this->_joinTree->find($criteria);
85
74
$this->_joinTree->afterFind();
442
436
$child=reset($this->children);
443
$query=new CJoinQuery($this);
445
$child->_joined=true;
446
$query->join($child);
437
$query=new CJoinQuery($child);
438
$query->selects=array();
439
$query->selects[]=$child->getColumnSelect($child->relation->select);
440
$query->conditions=array();
441
$query->conditions[]=$child->relation->condition;
442
$query->conditions[]=$child->relation->on;
443
$query->groups[]=$child->relation->group;
444
$query->havings[]=$child->relation->having;
445
$query->orders[]=$child->relation->order;
446
if(is_array($child->relation->params))
447
$query->params=$child->relation->params;
448
$query->elements[$child->id]=true;
447
449
if($child->relation instanceof CHasManyRelation)
449
451
$query->limit=$child->relation->limit;
450
452
$query->offset=$child->relation->offset;
451
if($this->_finder->baseLimited===null)
452
$this->_finder->baseLimited=($query->offset>=0 || $query->limit>=0);
453
$query->groups[]=str_replace($child->relation->aliasToken.'.',$child->tableAlias.'.',$child->relation->group);
454
$query->havings[]=str_replace($child->relation->aliasToken.'.',$child->tableAlias.'.',$child->relation->having);
455
$child->applyLazyCondition($query,$baseRecord);
458
$child->_joined=true;
460
$this->_finder->baseLimited=false;
456
461
$child->buildQuery($query);
457
$this->runQuery($query);
462
$child->runQuery($query);
458
463
foreach($child->children as $c)
466
if(empty($child->records))
468
if($child->relation instanceof CHasOneRelation || $child->relation instanceof CBelongsToRelation)
469
$baseRecord->addRelatedRecord($child->relation->name,reset($child->records),false);
470
else // has_many and many_many
472
foreach($child->records as $record)
474
if($child->relation->index!==null)
475
$index=$record->{$child->relation->index};
478
$baseRecord->addRelatedRecord($child->relation->name,$record,$index);
483
private function applyLazyCondition($query,$record)
485
$schema=$this->_builder->getSchema();
486
$parent=$this->_parent;
487
if($this->relation instanceof CManyManyRelation)
489
if(!preg_match('/^\s*(.*?)\((.*)\)\s*$/',$this->relation->foreignKey,$matches))
490
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,...)".',
491
array('{class}'=>get_class($parent->model),'{relation}'=>$this->relation->name)));
493
if(($joinTable=$schema->getTable($matches[1]))===null)
494
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.',
495
array('{class}'=>get_class($parent->model), '{relation}'=>$this->relation->name, '{joinTable}'=>$matches[1])));
496
$fks=preg_split('/[\s,]+/',$matches[2],-1,PREG_SPLIT_NO_EMPTY);
499
$joinAlias=$schema->quoteTableName($this->relation->name.'_'.$this->tableAlias);
500
$parentCondition=array();
501
$childCondition=array();
504
foreach($fks as $i=>$fk)
506
if(isset($joinTable->foreignKeys[$fk])) // FK defined
508
list($tableName,$pk)=$joinTable->foreignKeys[$fk];
509
if(!isset($parentCondition[$pk]) && $schema->compareTableNames($parent->_table->rawName,$tableName))
511
$parentCondition[$pk]=$joinAlias.'.'.$schema->quoteColumnName($fk).'=:ypl'.$count;
512
$params[':ypl'.$count]=$record->$pk;
515
else if(!isset($childCondition[$pk]) && $schema->compareTableNames($this->_table->rawName,$tableName))
516
$childCondition[$pk]=$this->getColumnPrefix().$schema->quoteColumnName($pk).'='.$joinAlias.'.'.$schema->quoteColumnName($fk);
518
throw new CDbException(Yii::t('yii','The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key "{key}". The foreign key does not point to either joining table.',
519
array('{class}'=>get_class($parent->model), '{relation}'=>$this->relation->name, '{key}'=>$fk)));
521
else // FK constraints not defined
523
if($i<count($parent->_table->primaryKey))
525
$pk=is_array($parent->_table->primaryKey) ? $parent->_table->primaryKey[$i] : $parent->_table->primaryKey;
526
$parentCondition[$pk]=$joinAlias.'.'.$schema->quoteColumnName($fk).'=:ypl'.$count;
527
$params[':ypl'.$count]=$record->$pk;
532
$j=$i-count($parent->_table->primaryKey);
533
$pk=is_array($this->_table->primaryKey) ? $this->_table->primaryKey[$j] : $this->_table->primaryKey;
534
$childCondition[$pk]=$this->getColumnPrefix().$schema->quoteColumnName($pk).'='.$joinAlias.'.'.$schema->quoteColumnName($fk);
538
if($parentCondition!==array() && $childCondition!==array())
540
$join='INNER JOIN '.$joinTable->rawName.' '.$joinAlias.' ON ';
541
$join.='('.implode(') AND (',$parentCondition).') AND ('.implode(') AND (',$childCondition).')';
542
if(!empty($this->relation->on))
543
$join.=' AND ('.$this->relation->on.')';
544
$query->joins[]=$join;
545
foreach($params as $name=>$value)
546
$query->params[$name]=$value;
549
throw new CDbException(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.',
550
array('{class}'=>get_class($parent->model), '{relation}'=>$this->relation->name)));
554
$fks=preg_split('/[\s,]+/',$this->relation->foreignKey,-1,PREG_SPLIT_NO_EMPTY);
556
foreach($fks as $i=>$fk)
558
if($this->relation instanceof CBelongsToRelation)
560
if(isset($parent->_table->foreignKeys[$fk])) // FK defined
561
$pk=$parent->_table->foreignKeys[$fk][1];
562
else if(is_array($this->_table->primaryKey)) // composite PK
563
$pk=$this->_table->primaryKey[$i];
565
$pk=$this->_table->primaryKey;
566
$params[$pk]=$record->$fk;
570
if(isset($this->_table->foreignKeys[$fk])) // FK defined
571
$pk=$this->_table->foreignKeys[$fk][1];
572
else if(is_array($parent->_table->primaryKey)) // composite PK
573
$pk=$parent->_table->primaryKey[$i];
575
$pk=$parent->_table->primaryKey;
576
$params[$fk]=$record->$pk;
579
$prefix=$this->getColumnPrefix();
581
foreach($params as $name=>$value)
583
$query->conditions[]=$prefix.$schema->quoteColumnName($name).'=:ypl'.$count;
584
$query->params[':ypl'.$count]=$value;
759
* @return string the WHERE clause. Column references are properly disambiguated.
761
public function getCondition()
763
if($this->relation->condition!=='' && $this->tableAlias!==null)
764
return str_replace($this->relation->aliasToken.'.', $this->tableAlias.'.', $this->relation->condition);
766
return $this->relation->condition;
770
* @return string the ORDER BY clause. Column references are properly disambiguated.
772
public function getOrder()
774
if($this->relation->order!=='' && $this->tableAlias!==null)
775
return str_replace($this->relation->aliasToken.'.',$this->tableAlias.'.',$this->relation->order);
777
return $this->relation->order;
781
* @return string the GROUP BY clause. Column references are properly disambiguated.
784
public function getGroupBy()
786
if($this->relation->group!=='' && $this->tableAlias!==null)
787
return str_replace($this->relation->aliasToken.'.', $this->tableAlias.'.', $this->relation->group);
789
return $this->relation->group;
793
* @return string the HAVING clause. Column references are properly disambiguated.
796
public function getHaving()
798
if($this->relation->having!=='' && $this->tableAlias!==null)
799
return str_replace($this->relation->aliasToken.'.', $this->tableAlias.'.', $this->relation->having);
801
return $this->relation->having;
805
893
* @return string the column prefix for column reference disambiguation
807
895
public function getColumnPrefix()
809
897
if($this->tableAlias!==null)
810
return $this->tableAlias.'.';
898
return $this->rawTableAlias.'.';
812
900
return $this->_table->rawName.'.';