Skip to content

Commit

Permalink
reverted most of CActiveFinder::asArray
Browse files Browse the repository at this point in the history
replaced with a better solution which might have a bit more memory
overhead but will integrate more easily and does not blow code
  • Loading branch information
cebe committed Jun 18, 2012
1 parent fc8d737 commit 4980804
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 82 deletions.
221 changes: 148 additions & 73 deletions framework/db/ar/CActiveFinder.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class CActiveFinder extends CComponent
*/
public $joinAll=false;
/**
* @param boolean whether result of find query should be array of attributes
* @var boolean whether result of find query should be array of attributes
* instead of active records.
* This property is internally used.
* @since 1.1.11
Expand Down Expand Up @@ -85,7 +85,7 @@ public function query($criteria,$all=false)
$index=$criteria->index;
$array=array();
foreach($result as $object)
$array[$object[$index]]=$object; // CModel implements ArrayAccess
$array[$object->$index]=$object;
$result=$array;
}
}
Expand All @@ -109,12 +109,15 @@ public function findBySql($sql,$params=array())
Yii::trace(get_class($this->_joinTree->model).'.findBySql() eagerly'.($this->asArray?' as array':''),'system.db.ar.CActiveRecord');
if(($row=$this->_builder->createSqlCommand($sql,$params)->queryRow())!==false)
{
$baseRecord=$this->_joinTree->model->populateRecord($row,false);
if($this->asArray)
$baseRecord=new CActiveRecordArray($row,$this->_joinTree->model);
else
$baseRecord=$this->_joinTree->model->populateRecord($row,false);
$this->_joinTree->beforeFind(false);
$this->_joinTree->findWithBase($baseRecord);
$this->_joinTree->afterFind();
$this->destroyJoinTree();
return $baseRecord;
return $this->asArray?$baseRecord->toArray():$baseRecord;
}
else
$this->destroyJoinTree();
Expand All @@ -131,11 +134,23 @@ public function findAllBySql($sql,$params=array())
Yii::trace(get_class($this->_joinTree->model).'.findAllBySql() eagerly'.($this->asArray?' as array':''),'system.db.ar.CActiveRecord');
if(($rows=$this->_builder->createSqlCommand($sql,$params)->queryAll())!==array())
{
$baseRecords=$this->_joinTree->model->populateRecords($rows,false);
if($this->asArray)
{
$baseRecords=array();
foreach($rows as $row)
$baseRecords[]=new CActiveRecordArray($row,$this->_joinTree->model);
}
else
$baseRecords=$this->_joinTree->model->populateRecords($rows,false);
$this->_joinTree->beforeFind(false);
$this->_joinTree->findWithBase($baseRecords);
$this->_joinTree->afterFind();
$this->destroyJoinTree();
if($this->asArray)
{
foreach($baseRecords as $index=>$record)
$baseRecords[$index]=$record->toArray();
}
return $baseRecords;
}
else
Expand Down Expand Up @@ -359,7 +374,7 @@ class CJoinElement
public $model;
/**
* @var array list of active records found by the queries. They are indexed by primary key values.
* since version 1.1.11 these can be arrays of attributes
* since version 1.1.11 these will be instances of CActiveRecordArray when {@link CActiveFinder::$asArray} is true.
*/
public $records=array();
/**
Expand Down Expand Up @@ -799,11 +814,10 @@ public function afterFind()
{
foreach($this->records as $record)
$record->afterFindInternal();
foreach($this->children as $child)
$child->afterFind();
}

foreach($this->children as $child)
$child->afterFind();

$this->children = null;
}

Expand Down Expand Up @@ -842,8 +856,8 @@ public function runQuery($query)
* Populates the active records with the query data.
* @param CJoinQuery $query the query executed
* @param array $row a row of data
* @return CActiveRecord|array the populated record.
* since version 1.1.11 this will be an array of attributes when {@link CActiveFinder::$asArray} is true.
* @return CActiveRecord|CActiveRecordArray the populated record.
* since version 1.1.11 this will be an instance of {@link CActiveRecordArray} when {@link CActiveFinder::$asArray} is true.
*/
private function populateRecord($query,$row)
{
Expand Down Expand Up @@ -881,22 +895,13 @@ private function populateRecord($query,$row)
$attributes[$aliases[$alias]]=$value;
}
if($this->_finder->asArray)
{
$record=$attributes;
foreach($this->children as $child)
{
if(!empty($child->relation->select))
$record[$child->relation->name]=($child->relation instanceof CHasManyRelation)?array():null;
}
}
$record=new CActiveRecordArray($attributes,$this->model);
else
{
$record=$this->model->populateRecord($attributes,false);
foreach($this->children as $child)
{
if(!empty($child->relation->select))
$record->addRelatedRecord($child->relation->name,null,$child->relation instanceof CHasManyRelation);
}
foreach($this->children as $child)
{
if(!empty($child->relation->select))
$record->addRelatedRecord($child->relation->name,null,$child->relation instanceof CHasManyRelation);
}
$this->records[$pk]=$record;
}
Expand All @@ -908,48 +913,21 @@ private function populateRecord($query,$row)
continue;
$childRecord=$child->populateRecord($query,$row);
if($child->relation instanceof CHasOneRelation || $child->relation instanceof CBelongsToRelation)
{
if($this->_finder->asArray)
$record[$child->relation->name]=$childRecord;
else
$record->addRelatedRecord($child->relation->name,$childRecord,false);
}
$record->addRelatedRecord($child->relation->name,$childRecord,false);
else // has_many and many_many
{
// need to double check to avoid adding duplicated related objects
$fpk=0;
if($childRecord instanceof CActiveRecord)
if($childRecord instanceof CActiveRecord || $childRecord instanceof CActiveRecordArray)
$fpk=serialize($childRecord->getPrimaryKey());
elseif($this->_finder->asArray)
{
// determine the primary key value from array
$table=$child->model->getTableSchema();
if(is_string($table->primaryKey))
$fpk=$childRecord[$table->primaryKey];
else if(is_array($table->primaryKey))
{
$fpk=array();
foreach($table->primaryKey as $name)
$fpk[$name]=$childRecord[$name];
$fpk=serialize($fpk);
}
}
else
$fpk=0;
if(!isset($this->_related[$pk][$child->relation->name][$fpk]))
{
if($child->relation->index!==null)
$index=$childRecord[$child->relation->index]; // CModel implements ArrayAccess
if($childRecord instanceof CActiveRecord && $child->relation->index!==null)
$index=$childRecord->{$child->relation->index};
else
$index=true;

if($this->_finder->asArray)
{
if($index===true)
$record[$child->relation->name][]=$childRecord;
else
$record[$child->relation->name][$index]=$childRecord;
}
else
$record->addRelatedRecord($child->relation->name,$childRecord,$index);
$record->addRelatedRecord($child->relation->name,$childRecord,$index);
$this->_related[$pk][$child->relation->name][$fpk]=true;
}
}
Expand Down Expand Up @@ -1564,7 +1542,9 @@ private function queryOneMany()
}
}

$this->populateResults($stats);
// populate the results into existing records
foreach($records as $pk=>$record)
$record->addRelatedRecord($relation->name,isset($stats[$pk])?$stats[$pk]:$relation->defaultValue,false);
}

/**
Expand Down Expand Up @@ -1698,25 +1678,120 @@ private function queryManyMany($joinTableName,$keys)
$stats[$row['c0']]=$row['s'];
}

$this->populateResults($stats);
foreach($records as $pk=>$record)
$record->addRelatedRecord($relation->name,isset($stats[$pk])?$stats[$pk]:$relation->defaultValue,false);
}
}

/**
* CActiveRecordArray is a very lightweight version of active record used internally by {@link CActiveFinder}
* to provide asArray functionality.
*
* @author Carsten Brandt <mail@cebe.cc>
* @version $Id$
* @package system.db.ar
* @since 1.1.11
*/
class CActiveRecordArray
{
/**
* populate the results into existing records
*
* @param array $stats stats results indexed by pk
* @since 1.1.11
* @var array attributes of the active record
*/
private function populateResults($stats)
public $attributes;

private $_pk;
private $_related=array();

/**
* @return array|string|null the primary key of this active record
*/
public function getPrimaryKey()
{
$relation=$this->relation;
foreach($this->_parent->records as $pk=>$record)
return $this->_pk;
}

/**
* PHP getter magic method.
* This method is overridden so that AR attributes can be accessed like properties.
* @param string $name property name
* @return mixed property value
* @throws CException if the property is not defined
*/
public function __get($name)
{
if(isset($this->attributes[$name]))
return $this->attributes[$name];

throw new CException(Yii::t('yii','Property "{class}.{property}" is not defined.',
array('{class}'=>get_class($this), '{property}'=>$name)));
}

/**
* Do not call this method. This method is used internally by {@link CActiveFinder} to populate
* related objects. This method adds a related object to this record.
* @param string $name attribute name
* @param mixed $record the related record
* @param mixed $index the index value in the related object collection.
* If true, it means using zero-based integer index.
* If false, it means a HAS_ONE or BELONGS_TO object and no index is needed.
*/
public function addRelatedRecord($name,$record,$index)
{
if($index!==false)
{
if($this->_finder->asArray)
$this->_parent->records[$pk][$relation->name]=isset($stats[$pk])?$stats[$pk]:$relation->defaultValue;
else
$record->addRelatedRecord($relation->name,isset($stats[$pk])?$stats[$pk]:$relation->defaultValue,false);
if(!isset($this->_related[$name]))
$this->_related[$name]=array();
if($record instanceof CActiveRecordArray)
{
if($index===true)
$this->_related[$name][]=$record;
else
$this->_related[$name][$index]=$record;
}
}
else if(!isset($this->_related[$name]))
$this->_related[$name]=$record;
}

/**
* Constructor.
* @param array $attributes attributes of the new active record instance
* @param CActiveRecord $model
*/
public function __construct($attributes, $model)
{
$this->attributes=$attributes;
$table=$model->getMetaData()->tableSchema;
if(is_string($table->primaryKey))
$this->_pk=$this->{$table->primaryKey};
else if(is_array($table->primaryKey))
{
$this->_pk=array();
foreach($table->primaryKey as $name)
$this->_pk[$name]=$this->$name;
}
else
$this->_pk=null;
}

/**
* Turns this instance to an array which will hold attributes and relations
* @return array the array representation of this active record
*/
public function toArray()
{
$array = $this->attributes;
foreach($this->_related as $name=>$data)
{
if (is_array($data))
{
$array[$name]=array();
foreach($data as $index => $record)
$array[$name][$index]=$record->toArray();
}
else
$array[$name]=$data;
}
return $array;
}
}
}
4 changes: 0 additions & 4 deletions framework/db/ar/CActiveRecord.php
Original file line number Diff line number Diff line change
Expand Up @@ -1576,10 +1576,6 @@ public function findAllBySql($sql,$params=array())
}
else
return $command->queryAll();


$this->_asArray=false;
return $command->queryAll();
}
else
return $this->populateRecords($command->queryAll(),true,$index);
Expand Down
12 changes: 7 additions & 5 deletions tests/framework/db/ar/CActiveRecordAsArrayTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
* Currently uncovered:
* - stat relations
* - composite pk
* - through relations
*/
class CActiveRecordAsArrayTest extends CTestCase
{
Expand Down Expand Up @@ -264,7 +265,7 @@ public function testFindByPk($condition, $params, $id)
{
$post=Post::model()->findByPk($id, $condition, $params);
$this->assertEquals($id,$post->id);
$posta=Post::model()->asArray()->findByPk(2, $condition, $params);
$posta=Post::model()->asArray()->findByPk($id, $condition, $params);

$this->assertArrayMatchesRecord($posta, $post);

Expand All @@ -277,17 +278,17 @@ public function testFindByPk($condition, $params, $id)
*/
public function testFindAllByPk($condition, $params)
{
$posts=Post::model()->findAllByPk(2, $condition, $params);
$postsa=Post::model()->asArray()->findAllByPk(2, $condition, $params);
$this->assertEquals(1,count($posts));
$posts=Post::model()->findAllByPk(4, $condition, $params);
$this->assertGreaterThan(0,count($posts));
$postsa=Post::model()->asArray()->findAllByPk(4, $condition, $params);

$this->assertAllArraysMatchRecords($posts, $postsa);

$this->assertFalse(Post::model()->getAsArray());

$posts=Post::model()->findAllByPk(array(4,3,2), $condition, $params);
$postsa=Post::model()->asArray()->findAllByPk(array(4,3,2), $condition, $params);
$this->assertGreaterThanOrEqual(2,count($posts));
$this->assertGreaterThanOrEqual(1,count($posts));

$this->assertAllArraysMatchRecords($posts, $postsa);

Expand Down Expand Up @@ -332,6 +333,7 @@ public function testFindBySql($condition, $params, $id)
if (is_array($condition) || $condition instanceof CDbCriteria)
Post::model()->getDbCriteria()->mergeWith($condition);

$this->assertFalse(Post::model()->getAsArray());
$post=Post::model()->findBySql('select * from posts where id=:id',array(':id'=>$id));

if (is_array($condition) || $condition instanceof CDbCriteria)
Expand Down

0 comments on commit 4980804

Please sign in to comment.