Deep relation with through operator

Hi, I have the following models in my application: Address, City, Zip, Locale(=province) and Country. Where a Country has many Locale’s, a Locale has many Zip’s,…

What i want to achieve now is this:




$address = Address::model()->with('country')->findByPk(1);

echo $address->country->name;



Of course i want Yii only to load the data from the Address and the Country model. The data from the City, Zip and Locale aren’t needed so shouldn’t be loaded.

I tried to achieve this by defining my relations method of the Address model like this:




	public function relations()

	{

		return array(

			'city' => array(self::BELONGS_TO, 'City', 'id_city'),

      			'zip' => array(self::HAS_ONE, 'Zip', 'id_zip', 'through'=>'city'),

      			'locale' => array(self::HAS_ONE, 'Locale', 'id_locale', 'through'=>'zip'),

      			'country' => array(self::HAS_ONE, 'Country', 'id_country', 'through'=>'locale'),

		);

	}



Now Yii throws the following error:




The relation "locale" in active record class "Address" is specified with an invalid foreign key "id_locale".

There is no such column in the table "locale".



So apparently it is not possible to use the ‘through’ operator on a relation that already uses it.

How should this be done correctly in Yii?

You can not go through BELONGS_TO relations. There is no this feature. You can go only through HAS_ONE and HAS_MANY relations.

Thanks for your response but actually I don’t now if that is true. The Yii guide says you can only use the ‘through’ operator on HAS_ONE and HAS_MANY relations. However, it doesn’t say you can’t go through a BELONGS_TO relation.

This being said, it is possible to get my Zip model through my ‘city’ relation. The problem occurs when i try to load my Locale model through my ‘zip’ relation.

It doesn’t say about sunspots too :) It is logic. But i think this note will be added soon.

Anyway you have wrong reltions() definition. Right is:




        public function relations()

        {

                return array(

                        'city' => array(self::BELONGS_TO, 'City', 'id_city'),

                        'zip' => array(self::BELONGS_TO, 'Zip', 'id_zip', 'through'=>'city'),

                        'locale' => array(self::BELONGS_TO, 'Locale', 'id_locale', 'through'=>'zip'),

                        'country' => array(self::BELONGS_TO, 'Country', 'id_country', 'through'=>'locale'),

                );

        }



But it’s not works now without BELONGS_TO with ‘through’ option.

Okay thanks, I hope this will be added soon.

You don’t need through option. You can solve problem another way.

In your Address model:




        public function relations()

        {

                return array(

                        'city' => array(self::BELONGS_TO, 'City', 'id_city'),

                );

        }



Setup other models same way.

Now:




$address = Address::model()->with(array(

    'city'=>array(

        'select'=>false,

        'with'=>array(

            'zip'=>array(

                'select'=>false,

                'with'=>array(

                    'locale'=>array(

                        'select'=>false,

                        'with'=>'country',

                    ),

                ),

            ),

        ),

    ),

))->findByPk(1);

echo $address->city->zip->locale->country->name;



Hey creocoder, thanks a lot for the idea. Hadn’t thought of it myself.