relational query: only fetch objects with certain attribute on joint table

Hi all,

I have 3 tables:

  • user

  • company

  • company_has_user (joint table): user_id, company_id, role_id (company can have different roles)

I can load all companies associated with the user with the following code:

model User




'companies' => array(self::MANY_MANY, 'Company', 'company_has_user(user_id, company_id)'),



view/controller




foreach ($model->companies as $company) {

echo $company->name; }



How can I only load the companies that have role_id == 1 in the joint table?

Thanks, any help would be much appreciated…

You can do that with scopes (http://www.yiiframework.com/doc/guide/1.1/en/database.ar#named-scopes). Check the new features with version 1.1.7, you can pass parameters: http://www.yiiframework.com/doc/guide/1.1/en/database.arr#relational-query-with-named-scopes

Thx, good hint. I had a look and tried to implement but have failed. Could you have a quick look at the code?




// main model User


public function relations()

	{

		return array(

                        'jointUserCompany'=>array(self::HAS_MANY,'CompanyHasUser','user_id'),

                        'companies'=>array(self::HAS_MANY,'Company','company_id','through'=>'jointUserCompany'),

                 );

	}




// model Company


	public function relations()

	{	

		return array(

                        'jointCompanyUser'=>array(self::HAS_MANY,'CompanyHasUser','company_id'),

                        'users'=>array(self::HAS_MANY,'User','user_id','through'=>'jointCompanyUser'),

		);

	}

        

// model joint table


	public function relations()

	{

		return array(

			'company' => array(self::BELONGS_TO, 'Company', 'company_id'),

			'user' => array(self::BELONGS_TO, 'User', 'user_id'),

		);

	}


        public function scopes()

        {

            return array(

                'seller'=>array(

                    'condition'=>'company_has_user_role_id=1',

                ),

            );

        }




// view main model user


$companiesSeller=Company::model()->findAll(array(

    'with'=>array(

        'jointCompanyUser'=>array(

            'scopes'=>array('seller')

        ),

    ),

));




// error message


Invalid argument supplied for foreach()


/Applications/MAMP/htdocs/yii/framework/db/ar/CActiveFinder.php(840)


828     {

829         // determine the primary key value

830         if(is_string($this->_pkAlias))  // single key

831         {

832             if(isset($row[$this->_pkAlias]))

833                 $pk=$row[$this->_pkAlias];

834             else    // no matching related objects

835                 return null;

836         }

837         else // is_array, composite key

838         {

839             $pk=array();

840             foreach($this->_pkAlias as $name=>$alias)

841             {

842                 if(isset($row[$alias]))

843                     $pk[$name]=$row[$alias];

844                 else    // no matching related objects

845                     return null;

846             }

847             $pk=serialize($pk);

848         }

849 

850         // retrieve or populate the record according to the primary key value

851         if(isset($this->records[$pk]))

852             $record=$this->records[$pk];




Also, if I want to output $companiesSeller, what do I have to do? I guess this is wrong:




foreach ($model->companiesSellers as $companiesSeller) { echo $companiesSeller->name; }



Thanks a lot…

The middle Table requires an ID… include it on the table, add the new attribute to your model CompanyHasUser and you are done

Thanks, that worked.

Mike,

What if, i want to see dynamic results of company_has_user_role_id.

ie., from the GUI i will pass a value that i want to see all the companies who has role id $_POST[‘role_id’]

I assume company_has_user has MANY_MANY relations. If not let say you have MANY_MANY relation in the table.

Can someone explain this scenario

I started using Yii just a few weeks ago and I’m a complete beginner. But let me try to answer your question.

  1. Define the relationship in the model User. In my particular case it looks like this but it can also be different.



public function relations()

	{

		return array(

                        'jointUserCompany'=>array(self::HAS_MANY,'CompanyHasUser','user_id'),

                        'companies'=>array(self::HAS_MANY,'Company','company_id','through'=>'jointUserCompany'),

                        //instead you can use: 'companies' => array(self::MANY_MANY, 'Company', 'company_has_user(user_id, company_id)'),

            

                 );

        }



  1. Create a view associated with the controller User with a form and an input field

  2. Now in a view associated with the controller user, the following outputs all companies associated with the user by using the name of the relation declared above




// loads all companies associated with the model User through the relation 'companies'

$companies=$model->companies;


// loops all companies by using the class $company declared in the model Company

foreach ($companies as $company)

            {              

                      // outputs db field name

                      echo $company->name;

            }



We can restrict the companies to those who have company_has_user_role_id=1




'companies'=>$model->companies(array('condition'=>'company_has_user_role_id=1'))



Now let’s implement the whole thing with the form and with separating logic (controller) and display (view).

UserController:




	public function actionShowCompanies()

	{


		// load all attributes and methods from db / model User

		// by using the ID set in UserIdentity.php

		$model = $this->loadModel(Yii::app()->user->id);


                // when form is posted

                if(isset($_POST['Company']))

				{

					// fetch posted value

 					$role = strip_tags($_POST['CompanyHasUser']['company_has_user_role_id']);

					

					// display companies according to $role

					

					if ( $role = 1 ) {

					

						$this->render('sell',array(

								'model'=>$model,

								// only get companies that have role X and pass these on to the view

								'companies'=>$model->companies(array('condition'=>'company_has_user_role_id=1'))

						));

					

					} else if ($role = 2) {

						

						$this->render('sell',array(

								'model'=>$model,

								// only get companies that have role X and pass these on to the view

								'companies'=>$model->companies(array('condition'=>'company_has_user_role_id=2'))

						

					}


               // if form has not been posted, display all companies associated with this user

			   } else {

 


               $this->render('sell',array(

                        'model'=>$model,

						'companies'=>$model->companies

						

                ));


	}



In the view you can then use:




// loops all companies by using the class $company declared in the model Company

foreach ($companies as $company)

            {              

                      // outputs db field name

                      echo $company->name;

            }



As I said, I’m just a beginner and this code has not been tested. So there are probably better ways to do this but hopefully it will give you a starting point.