INNER JOIN with complex ON clause

I have a CActiveDataProvider object for pagination with a ON clause that adds conditions.

The pagination says ‘1 article found’ but $dataProvider->getData() returns all of them.

model Article.php


	/**

	 * @return array relational rules.

	 */

	public function relations()

	{

		return array(

			'categories'=>array(self::MANY_MANY, 'CategorieArticle', 'article_categorie(article_id, categorie_id)'),			


	}

model CategorieArticle.php


	/**

	 * @return array relational rules.

	 */

	public function relations()

	{


		return array(

			'articles'=>array(self::MANY_MANY, 'Article', 'article_categorie(categorie_id, article_id)'),

		);

	}

model ArticleCategorie.php


	public function relations()

	{

		return array(

			'article' => array(self::BELONGS_TO, 'Article', 'article_id'),

			'categorie' => array(self::BELONGS_TO, 'CategorieArticle', 'categorie_id'),

		);

	}




		$dataProvider=new CActiveDataProvider('Article', array(

		    'criteria'=>array(	

		        'condition'=>'t.is_active=1',	       

		        'with'=>array(

				'categories' => array(

				'on'=>"categories.is_active = 1  AND categories.id = ".$categories_id, 

				'joinType' => 'INNER JOIN', 

				'order' => 'categories.position ASC',

							)		        

				),

		    ),

		    'pagination'=>array(

		        'pageSize'=>10,

		    ),

		));



Yii log :




SELECT * FROM `article` `t` INNER JOIN

`article_categorie` `categories_categories` ON

(`t`.`id`=`categories_categories`.`article_id`) INNER JOIN

`categorie_article` `categories` ON

(`categories`.`id`=`categories_categories`.`categorie_id`) AND

(categories.is_active = 1  AND categories.id = 4)  WHERE (`t`.`id`=1)

ORDER BY categories.position ASC



returns 1 article




SELECT COUNT(DISTINCT `t`.`id`) FROM `article` `t`  INNER

JOIN `article_categorie` `categories_categories` ON

(`t`.`id`=`categories_categories`.`article_id`) INNER JOIN

`categorie_article` `categories` ON

(`categories`.`id`=`categories_categories`.`categorie_id`) AND

(categories.is_active = 1  AND categories.id = 4)  WHERE (t.is_active=1)



returns 1 article

Both sql requests are fine. So what’s going on with CActiveDataProvider. Why does it return all the articles ?

Does it hate the (categories.is_active = 1 AND categories.id = 4) ON clause ?




$data = $dataProvider->getData();  

echo count($data); // count = 2 (2 articles in database)

echo $dataProvider->getTotalItemCount() . ' article';  // '1 article'


<div id="products-inner">

<?php foreach($data as $article)				

	$this->renderPartial('/article/_preview', array('categorie'=>$categorie, 'article'=>$article, 'cat_breadcrumb'=>$cat_breadcrumb));

?>

</div>



Are these the real queries that get logged when you call your example code? This seems very strange to me. Your example should be fine. I could not find problems with complex join conditions so far. You could even do parameter replacement e.g. with ‘:categories’ in your ON clause.

Yes, these are copy/past from webroot logs

on: the ON clause. The condition specified here will be appended to the joining condition using the AND operator. Column names referenced in this option should be disambiguated. This option does not apply to MANY_MANY relations.

Is there any workarounds ?

But the ON clause does appear in your generated SQL. So this is obviously not your problem. Except you catched the wrong logging entries.

I have a very similar problem. I’m storing dogs(hounds) and owners. The owners can of course own many dogs, and the dogs can have many owners.

I have 3 tables, hound, hound_person, person.

In my Hound-model I have the following relation:


'owners' => array(self::MANY_MANY, 'Person', 'hound_person(hound_id, person_id)', 'together'=>true),

I’m trying to get a list-view of dogs with a certain owner.




	public function search()

	{

		// Warning: Please modify the following code to remove attributes that

		// should not be searched.


		$criteria=new CDbCriteria;


		$criteria->compare('id',$this->id,true);


		$criteria->compare('name',$this->name,true);

		

		//$criteria->compare('t.owners.fullName', $this->owners->fullName, true);

		

		$criteria->with = array(

			'owners'=>array(

				'order'=>'owners.name ASC, owners.surname ASC',

				'on'=>'owners.name = "John"',

				'joinType'=>'INNER JOIN',

			),

			'breed',

		);


		return new CActiveDataProvider(get_class($this), array(

			'criteria'=>$criteria,

		));

	}	



There is only one dog with the owner John, so it should only return one dog. But instead it returns 10 (which the default limit for list-view).

The paginator on the other hand says "Showing 1-10 of 1 hound(s)".

It seems that the SQL COUNT is correctly performed:




Querying SQL: SELECT COUNT(DISTINCT `t`.`id`) FROM `hound` `t`  INNER JOIN

`hound_person` `owners_owners` ON (`t`.`id`=`owners_owners`.`hound_id`)

INNER JOIN `person` `owners` ON (`owners`.`id`=`owners_owners`.`person_id`)

AND (owners.name = "John") LEFT OUTER JOIN `breed` `breed` ON

(`t`.`breed_id`=`breed`.`id`)



But the actual query for the list-view is separated into two queries:




Querying SQL: SELECT `t`.`id` AS `t0_c0`, `t`.`breed_id` AS `t0_c1`,

`t`.`regnr` AS `t0_c2`, `t`.`name` AS `t0_c3`, `t`.`sex` AS `t0_c4`,

`t`.`cryptorchid` AS `t0_c5`, `t`.`birthDate` AS `t0_c6`, `t`.`title` AS

`t0_c7`, `t`.`color` AS `t0_c8`, `t`.`registeredSKK` AS `t0_c9`,

`t`.`foreign` AS `t0_c10`, `t`.`maxHeight` AS `t0_c11`, `t`.`marking` AS

`t0_c12`, `t`.`idnr` AS `t0_c13`, `t`.`int2` AS `t0_c14`, `t`.`off2` AS

`t0_c15`, `t`.`fRegnr` AS `t0_c16`, `t`.`fName` AS `t0_c17`, `t`.`mRegnr`

AS `t0_c18`, `t`.`mName` AS `t0_c19`, `t`.`breederName` AS `t0_c20`,

`t`.`breederCity` AS `t0_c21`, `t`.`image` AS `t0_c22`, `breed`.`id` AS

`t2_c0`, `breed`.`breedGroup_id` AS `t2_c1`, `breed`.`name` AS `t2_c2`,

`breed`.`FCINumber` AS `t2_c3` FROM `hound` `t`  LEFT OUTER JOIN `breed`

`breed` ON (`t`.`breed_id`=`breed`.`id`) LIMIT 10



…and…




Querying SQL: SELECT `t`.`id` AS `t0_c0`, `owners`.`id` AS `t1_c0`,

`owners`.`name` AS `t1_c1`, `owners`.`surname` AS `t1_c2`,

`owners`.`streetAddress` AS `t1_c3`, `owners`.`postalCode` AS `t1_c4`,

`owners`.`city` AS `t1_c5`, `owners`.`email` AS `t1_c6`,

`owners`.`telephone` AS `t1_c7`, `owners`.`cellphone` AS `t1_c8`,

`owners`.`debt` AS `t1_c9` FROM `hound` `t` INNER JOIN `hound_person`

`owners_owners` ON (`t`.`id`=`owners_owners`.`hound_id`) INNER JOIN

`person` `owners` ON (`owners`.`id`=`owners_owners`.`person_id`) AND

(owners.name = "John") WHERE (`t`.`id` IN ('1', '2', '3', '5769',

'5770', '5771', '5772', '5773', '5774', '5775')) ORDER BY owners.name ASC,

owners.surname ASC



I can not seem to find a solution. If anyone with more experience could point me in the right direction here, I would be very glad!

Best Regards

Johan

I have at the moment created some kind of work around, not very pretty…




	public function search()

	{

		// Warning: Please modify the following code to remove attributes that

		// should not be searched.


		$criteria=new CDbCriteria;


		$criteria->compare('t.name',$this->name,true);

		

		$criteria->compare('breed.name',$this->breed_id,true);

		

		$criteria->compare('Concat(owners2.name, " ", owners2.surname)',$_GET['Hound']['owner_name'],true);

		

		$criteria->with = array('breed', 'owners');

		

		if( ( isset($_GET['Hound']['owner_name']) ? $_GET['Hound']['owner_name'] : null) )

		{

			$criteria->join ='

				INNER JOIN hound_person owners_owners2

				ON ( t.id = owners_owners2.hound_id)

				INNER JOIN person owners2

				ON ( owners2.id=owners_owners2.person_id)

			';

		}

		

		return new CActiveDataProvider(get_class($this), array(

			'criteria'=>$criteria,

		));

	}	



…and this is the code for the view…




	<div class="row">

		<label for="Hound_owner_name">Owner</label>

		<input size="20" maxlength="20" name="Hound[owner_name]" id="Hound_owner_name" type="text" value="" />

	</div>