CactiveRecord find methods returns

Hello

I am trying to query the database with some agregated functions and I was looking for a way to get the findAll method from Cactiverecord to return only the resultset without creating all objects.

I mean

I have 3 tables

CadPeople HAS_MANY CadEXP

CadPeople HAS_MANY CadPOST

I did a query in CadPeople with Group By CadEXP.ID, CadPOST.id, that returns me how many POST ppl with a certain EXP have post.

The problem is that since CadPeople HAS_MANY of boths, when I query direct on DB I get the rigth result, but the objects are not, since 1 person ID can have a bunch of EXP and POST I get 1 person with 2 arrays, one for EXP and 1 for POST, with that I loose the correspondance between both EXP and POST.

If the findAll method would only return the result from DB I could look throught it without any problem.

I think there should be a param for all find methods in the ActiveRecord, something like ‘createObj’ = true witch I could override to get only the resultset.

I’ll do some research tomorrow, maybe I can extend CactiveRecord and override a couple of methods and post here.

TY for the time

Alf

OK,

after some tweaking I got it, I had to extend 2 classes, I was not in the mood to analyse all methods in CActiveFinder and CJoinElement, so I changed all to that was private to protected.

here are the code, it could be a lot better with some more help, but here it goes:

after changing the methods and variables derivate a class from CActiveFinder




class CActiveFinderAgregate extends CActiveFinder {	

	

	public function __construct($model,$with)

	{

		$this->_builder=$model->getCommandBuilder();

		$this->_joinTree=new CJoinElementAgregate($this,$model);

		$this->buildJoinTree($this->_joinTree,$with);

	}

	

	

	

	public function query($criteria,$all=false)

	{

		$this->joinAll=$criteria->together===true;

		$this->_joinTree->beforeFind(false);


		if($criteria->alias!='')

		{

			$this->_joinTree->tableAlias=$criteria->alias;

			$this->_joinTree->rawTableAlias=$this->_builder->getSchema()->quoteTableName($criteria->alias);

		}


		

		

		$this->_joinTree->find($criteria);

		$result = $this->_joinTree->records;

		

		$this->destroyJoinTree();

				

		return $result;

		

	}

	

	protected function buildJoinTree($parent,$with,$options=null)

	{

		if($parent instanceof CStatElement)

			throw new CDbException(Yii::t('yii','The STAT relation "{name}" cannot have child relations.',

				array('{name}'=>$parent->relation->name)));


		if(is_string($with))

		{

			if(($pos=strrpos($with,'.'))!==false)

			{

				$parent=$this->buildJoinTree($parent,substr($with,0,$pos));

				$with=substr($with,$pos+1);

			}


			// named scope

			$scopes=array();

			if(($pos=strpos($with,':'))!==false)

			{

				$scopes=explode(':',substr($with,$pos+1));

				$with=substr($with,0,$pos);

			}


			if(isset($parent->children[$with]) && $parent->children[$with]->master===null)

				return $parent->children[$with];


			if(($relation=$parent->model->getActiveRelation($with))===null)

				throw new CDbException(Yii::t('yii','Relation "{name}" is not defined in active record class "{class}".',

					array('{class}'=>get_class($parent->model), '{name}'=>$with)));


			$relation=clone $relation;

			$model=CActiveRecord::model($relation->className);

			if($relation instanceof CActiveRelation)

			{

				$oldAlias=$model->getTableAlias(false,false);

				$model->setTableAlias($relation->alias===null?$relation->name:$relation->alias);

			}


			if(($scope=$model->defaultScope())!==array())

				$relation->mergeWith($scope,true);


			if(!empty($options['scopes']))

				$scopes=array_merge($scopes,(array)$options['scopes']); // no need complex merging, $scopes always in simle format


			if($scopes!==array())

			{

				$scs=$model->scopes();

				foreach($scopes as $k=>$v)

				{

					if(is_integer($k))

					{

						if(is_string($v))

						{

							if(isset($scs[$v]))

							{

								$relation->mergeWith($scs[$v],true);

								continue;

							}

							$scope=$v;

							$params=array();

						}

						else if(is_array($v))

						{

							$scope=key($v);

							$params=current($v);

						}

					}

					else if(is_string($k))

					{

						$scope=$k;

						$params=$v;

					}

					if(method_exists($model,$scope))

					{

						$model->resetScope();

						call_user_func_array(array($model,$scope),(array)$params);

						$relation->mergeWith($model->getDbCriteria(),true);

					}

					else

						throw new CDbException(Yii::t('yii','Active record class "{class}" does not have a scope named "{scope}".',

							array('{class}'=>get_class($model), '{scope}'=>$scope)));

				}

			}


			// dynamic options

			if($options!==null)

				$relation->mergeWith($options);


			if($relation instanceof CActiveRelation)

				$model->setTableAlias($oldAlias);


			if($relation instanceof CStatRelation)

				return new CStatElement($this,$relation,$parent);

			else

			{

				if(isset($parent->children[$with]))

				{

					$element=$parent->children[$with];

					$element->relation=$relation;

				}

				else

					$element=new CJoinElementAgregate($this,$relation,$parent,++$this->_joinCount);

				if(!empty($relation->through))

				{

					$slave=$this->buildJoinTree($parent,$relation->through,array('select'=>false));

					$slave->master=$element;

					$element->slave=$slave;

				}

				$parent->children[$with]=$element;

				if(!empty($relation->with))

					$this->buildJoinTree($element,$relation->with);

				return $element;

			}

		}


		// $with is an array, keys are relation name, values are relation spec

		foreach($with as $key=>$value)

		{

			if(is_string($value))  // the value is a relation name

				$this->buildJoinTree($parent,$value);

			else if(is_string($key) && is_array($value))

				$this->buildJoinTree($parent,$key,$value);

		}

	}

	

		

}



Note that on the construct for this new agregate I had to chance CJoinElement to CJoinElementAgregate, same thing had to do with buildJoinTree since it was the method that create the childs.

query method in CActiveFinderAgregate alse had to be tweaked since after the find we don’t get objects and they don’t have an afterFind method to run.

after that derivate CJoinElement:




class CJoinElementAgregate extends CJoinElement {

	

	private $aliases = array();

	

	

	public function runQuery($query) {


		$command=$query->createCommand($this->_builder);

				

		$this->aliases = $this->getAllAliases();	

		foreach($command->queryAll() as $row)

			$this->populateRecord($query,$row);

		

	}

	

	

	public function getAllAliases() {

		$onde = array();

		$aliases = array_flip($this->_columnAliases);		

		$onde = array_merge($aliases, $onde);	

		foreach($this->children as $child) {

			$onde = array_merge($child->getAllAliases(), $onde);			

		}

		return $onde;		

	}

	


	

	protected function populateRecord($query,$row)

	{

		

		$attributes=array();

					

		foreach($row as $alias=>$value) {

				if(isset($this->aliases[$alias]))

					$attributes[$this->aliases[$alias]]=$value;

		}

		

		$this->records[] = $attributes;


	}

	

	

}



since the query won’t return objects it won’t have an hierarchy I had to get all column alias back from all child to create the retuning object, that way I’ll have an array with indexed values with the same name as the objects.

and that’s all of it, to use simply:




  $model = CActiveRecord::model('YourModel');

  $finder=new CActiveFinderAgregate($model,$criteria->with);

  $values = $finder->query($criteria,$all);



tyvm