Related Search Behavior

Wow that was fast :blink:

Thank you very much!

In reply to:

and

There is only one order by clause at a time because only one column is sorted at a time.

The sort order is determined by a get variable which is typically ‘sort’:

RESTOFURL&sort=start_date

The above example would sort by ‘start_date’. ‘start_date’ must correspond to the virtual attribute if it is a column in a relation for ‘RelatedSearchBehavior’ to work. And of course the column should be sortable according to the grid view (but I guess that this is the case).

Check the URL that is called when you try to do a sort - an easy way to do so is ‘click right’/‘open link in new window’ - the Url will appear in the navigator’s location bar and you can even manually play with it.

Example with the demo: http://relatedsearchbehavior.ynamics.com/index.php?r=site/index&Invoiceline_page=7&ajax=yw0&sort=SupportEmail.desc

How to setup a MANY MANY relation?

If set it up the same way, as with simple relations there is no data in the column and no error :(

I have not really thought of many-many relations and it is a topic the regularly pops up in the forum.

Could you setup a demo for that which could be integrated in the demo for this extension and which can at the same time be used as a test case to check what the solution is?

I am thinking about how to integrate it into your demo,

but i currently have no idea how to do it on a sensemaking basis.

In my application I have these 3 tables

User

PK - [color="#FF0000"]id[/color]

AuthAssignment

PK1,[color="#0000FF"]FK1[/color] - itemname

PK2,[color="#FF0000"]FK2[/color] - userid

AuthItem

PK - [color="#0000FF"]name[/color]

I want to display the name of the assigned item in my User gridview


'roles' => array(self::HAS_ONE,'AuthAssignment','userid'),

'items' => array(self::HAS_ONE,'AuthItem',array('itemname'=>'name'),through'=>'roles')


'role' => 'items.description',

It works, just a workaround for MANY MANY relation :P

Hi

To set up within the demo, you can see that a track can appear on more than one invoice and that an invoice can have more than one Track.

I added the following relations the Track (untested):




   'invoice_lines'=>array(self::HAS_MANY,'InvoiceLine',array('TrackId'=>'TrackId')),

   'invoices'=>array(self::HAS_MANY,'Invoice',array('InvoiceId'=>'InvoiceId'),'through'=>'invoice_lines'),



However, these relations do not seem usefull for your purpose.

The demo uses ‘InvoiceLines’ as the base model class (equivalent to AuthAssignment) and therefore any existing combination of Track/Invoice appears in the table.

Hence, in a way, the demo already demonstrates how you can use ‘many_many’.

Knowning this, you might be able to demonstrate through the demo the situation where you said that there is no data in the column and no error.

First of all, great extension, I love it :)

But I seems to have a problem with the autoscope.

Among other things I added the code below to my model, I also adjust to search function as mentioned in the install guide.




public function __call($name,$parameters) {

		try {

			return parent::__call($name,$parameters);

		} catch (CException $e) {

			if(preg_match('/ en zijn behaviors hebben geen method of closure met naam /',$e->getMessage())) {

				return $this->autoScope($name, $parameters);

			} else {

				throw $e;

			}

		}

	}

	

	function behaviors() 

	{

		return array(

			'relatedsearch'=>array(

				 'class'=>'RelatedSearchBehavior',

				 'relations'=>array(

					  'appProcesId'=>'applicationProces.proces_id',

				 ),

			 ),

		);

	}



In a gridview searching and displaying the ‘appProcesId’ works great. But when I want to do something like this:




ApplicationProcesActivity::model()->appProcesId(2)->findAll();



I get an error:




Call to a member function findAll() on a non-object



While:




ApplicationProcesActivity::model()->id(40)->findAll();



works fine. the autoscope does work, but only with properties from that model, not the ones defined in the behaviour. Am I doing something wrong?

Hi Johannn

Autoscope is not currently implemented for related fields.

It is possible to do so and would require either duplicating some of the code or refactoring it to avoid code duplication.

Basically, the code of the ‘relatedSearch’ method is needed without the ‘sort’ logic and dataProvider creation - we just need to update the criteria.

For performance, the analysis of the relations would better be cached, but to start we could ignore caching.

The autoScope method then has to call on this new method in case the field does not exist as a regular field of the table, but when it exists as a relation.

I do not have much time currently, so on my end this implementation has to wait.

You can still do something like indicated in the documentation (I haven’t checked the code precisely, but it gives an idea):




...->findAll(array(

'with'=>array('applicationProcess'=>array('scopes'=>array('id'=>2))))



I notice that there also seems to be an internationalisation issue which I hadn’t notices (even though my application language is in French).

Which is logical because there is no translation for french for this message.

To resolve that, we have to modify _call:


/**

     * Add automatic scopes for attributes (uses RelatedSearchBehavior).

     */

	public function __call($name,$parameters) {

    	try {

        	return parent::__call($name,$parameters);

    	} catch (CException $e) {

        	if(preg_match(

                	'/'.Yii::t(

                        	'yii',

                        	quotemeta(

                                	Yii::t(

                                        	'yii',

                                        	'{class} and its behaviors do not have a method or closure named "{name}".'

                                        	)

                                	),

                                	array('{class}'=>'.*','{name}'=>'.*')

                        	)

                	.'/',$e->getMessage())) {

            	return $this->autoScope($name, $parameters);

        	} else {

            	throw $e;

        	}

    	}



P.S: (in Dutch): nog veel plezier met de extensie!

Thank you for the quick reply.

I’ll use the solution provided in the documentation by using the scope in the findAll()

Again thanks for this great extension :)

Hello out there,

I do use this extension quite a lot, but I run right now into an incompatibility with another extension.

Perhaps someone solved this already…

I am using the "yii-remember-filters-gridview" component which stores the filter, the pagination and the sorting in a session. When combing back to the grid (for example after updating one record) all three above mentioned parameters are active again. Nice.

But… as there is quite some logic in RelatedSearchBehaviour about Sorting, it breaks the function of the other extension. Does anybody see a way to solve this?

The "yii-remember-filters-gridview" is a small component, so I pasted the function for the storage of sorting in the session below.

Any ideas how to solve this? Could we add a check for the session into RSB?


* http://www.yiiframework.com/extension/remember-filters-gridview

*/




    private function doReadSave() {

     if ($this->owner->scenario == 'search' || $this->owner->scenario == $this->rememberScenario ) {

        $this->owner->unsetAttributes();


        // store also sorting order

        $key = get_class($this->owner).'_sort';

        if(!empty($_GET[$key])){

          Yii::app()->user->setState($this->getStatePrefix() . 'sort', $_GET[$key]);

        }else {

          $val = Yii::app()->user->getState($this->getStatePrefix() . 'sort');

          if(!empty($val))

            $_GET[$key] = $val;

        }

.......

        

There is actually a function in Yii for it (http://www.yiiframework.com/doc/api/1.1/CGridView#enableHistory-detail ) with limitations.

HOwever, I am using the same[size=2] ‘ERememberFiltersBehavior’ which I add automatically in my models using gii. [/size]

To fix the other extension, we need to consider that the sort key is in ‘sortVar’ . You are correct: sorting does not work correctly. I don’t see it at first sight - I think that RSB is doing it the right way, and the way RFB works needs some investigation.

I had version 1.2 of ERememberFiltersBehavior, but apparently that did not have the sort functionnality which explains why I couldn’t see it. The latest (same) 1.2 version does have it.

Basically ERememberFiltersBehavior relies on the name of the sort variable as provided by CActiveDataProvider which relies on CDataProvider where it is a combination of the DataProvider’s id which by default is the model class name.

So ideally, ERemberFiltersBehavior should get the $_GET key in the same way, but I can’t figure a way to do so at this time.

So, I modified RelatedSearchBehavior to rely on CActiveDataProvider and hence provide the expected sortVar in most cases.

The update is on the extension page.

Wowwwwww!

Awesome. It works very nice now. Thanks so much for changing this.

gb5256

Thanks for sharing this extension which seems very awesome. However did someone try it with postgresql ?? cause I never succeeded to use it…

EDIT:

get working with postgresql finally, However still in trouble with MANY_MANY relationships.

It doesn’t work when I set in behavior function :

‘my_name’=>‘myMANY_MANYrelation.anyField’

it displays nothing.

Moreover, I got problem with sorting my column through HAS_MANY relations, I have an error ’ missing FROM-clause entry for table “myHAS_MANYrelationship” ’ with the request send :

SELECT "t"."id" AS "t0_c0" FROM "mainTable" "t" GROUP BY t.id ORDER BY "myHAS_MANYrelation"."field" LIMIT 10;

The extension is awesome ;-), and it should work with postgresql which you ended up doing.

Is ‘myHAS_MANYrelation’ an existing relation in the active record? That is, ‘myHAS_MANYrelation’ must be the name of the relation defined for ‘mainTable’.

You write ‘myHAS_MANYrelationship’ and ‘myHAS_MANYrelation’. The latter is without ‘ship’ for instance so the name is not the same.

thanks for reply, anyway I get it working however I have problem coming from KeenLoading I guess. In group by I have only t.id… but as I have belongs_to relation sql tell me it miss my_relation.id in group by.

Moreover, my many_many relation works well with filering but sorting it gives same error that it misses a my_relation.id in group by.

I tried to deal it without your extension and just with keenloading extension and I have the same problem. I guess there is something wrong when keenLoading extension has to get the primary keys of relations’s table. It takes only t.id… well I guess.

Hi

I do not think the identification of the issue is correct here.

Using ‘KeenLoading’ alone with relations and witout RelatedSearchBehavior will hav you run into mssing configurations for the relations. The functionnality that you seem to be looking for works well for me in MySql or Sqlite so I do not see a reason why that would be different in postgresql.

If you can manage to repeat your issue in the demo environment, it would be easier to share it and hence to study it. The demo demonstrates sorting on fields that are in a relation.

Let’s take an example :

I have four tables : author(id,name,nationality_id) / author_genre(id,author_id,genre_id) / genre(id,genre_name) / nationality(id,name);

relations :

in author model : "genres" => array(self::MANY_MANY, "genre", "author_genre(author_id,genre_id)")

"from" => array(self::BELONGS_TO, "nationality", "nationality_id")

in genre : "authors" => array(self::MANY_MANY, "author", "author_genre(genre_id,author_id)")

in author_genre : "genre" => array(self::BELONGS_TO, "genre", "genre_id")

"author" => array(self::BELONGS_TO, "author", "author_id")

Here is my method search, the best way I found to get my many_many relationship working with filter and pager. Unfortunately I never succeed to get sorting working with my hisGenre column because I have an error as it misses "genres.id" in the group by.




$criteria=new CDbCriteria;

    $criteria->with=array('genres'=>array('select'=>false));

    //$criteria->limit=-1;

    $criteria->together=true;


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

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

    $criteria->compare('genres.id',$this->hisGenre);


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

        'pagination'=>array(

          'pageSize'=> Yii::app()->user->getState('pageSize',Yii::app()->params['defaultPageSize']),

        ),

        'withKeenLoading' => array('genres'),

        'criteria'=>$criteria,

        'sort'=>array('defaultOrder'=>'t.id ASC','attributes'=>array('assignedRole'=>array('asc'=>'genres.id','desc'=>'genres.id DESC'),'*')),

    ));

        }



Then I had the necessary to display the nationality as :




$criteria=new CDbCriteria;

    $criteria->with=array('genres'=>array('select'=>false),'from');

    //$criteria->limit=-1;

    $criteria->together=true;


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

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

    $criteria->compare('genres.id',$this->hisGenre);

    $criteria->compare('from.name',$this->isFrom);


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

        'pagination'=>array(

          'pageSize'=> Yii::app()->user->getState('pageSize',Yii::app()->params['defaultPageSize']),

        ),

        'withKeenLoading' => array('genres'),

        'criteria'=>$criteria,

        'sort'=>array('defaultOrder'=>'t.id ASC','attributes'=>array('assignedRole'=>array('asc'=>'genres.id','desc'=>'genres.id DESC'),'isFrom'=>array('asc'=>'from.name', 'desc'=>'from.name desc'),'*')),

    ));

        }



And with that I always have on this page an error as it misses from.id in group by… but If I add in KeenLoading extension by hand line 200 : $pkNames[] .="from.id";

Then I get something working for displaying, sorting and filtering for the column “isFrom” but I didn’t succeed to get sorting working for hisGenre column. Because if I add “genres.id” in group by then pager doesn’t work anymore… for Gridview displays not all objects…

I created a topic here if you wand to read more about my problem and try to help me. I tried a lot of thing but never get all working correctly =) thanks

http://www.yiiframework.com/forum/index.php/topic/44838-filter-and-sort-a-column-fed-by-a-many-many-relation/page__gopid__213109#entry213109

Hi

I now see the issue: you are not (fully) using the extension !

You should define a ‘relation’ that you call ‘hisGenre’ (or whatever), us that as the column name/value, and then RelatedSearchbehavior does the magic.

In stead of doing:


   $criteria->compare('genres.id',$this->hisGenre); 




you should


public function rules()

	{

  return array(

 		// .. other rules


// Related field aliases allowed during search.

 		array('hisgenre', 'safe', 'on'=>'search'),

		);

	}


public function behaviors() {

      return array(

               // Add RelatedSearchBehavior

               'relatedsearch'=>array(

                       'class'=>'RelatedSearchBehavior',

                       'relations'=>array(

                                 'hisGenre'=>'genres.id',

                              ),

              ),

	    );

	}




Check out ‘protected/models/Invoiceline.php’ of the demo and ‘protected/views/site/index.php’ of the demo.

You can see that there are only the ‘compare’ statements for the native fields in ‘search()’. The other ‘compare’ statements are provided by RelatedSearchBehavior! When the ‘CGridView’ column parameter says ‘Composer’, this in fact looks for ‘track.Composer’, and when it says ‘SupportLastName’ it looks for ‘invoice.customer.support.FirstName’ which is several relations deep. And you can even do $model->SupportLastName to get the value of that relation.

I guess there is too much magic in RelatedSearchBehavior - too little to write for a lot of "hidden" functionnality.