relatedsearchbehavior

Behavior making searching relations in CGridView easier
22 followers

RelatedSearchBehavior Class File

Behavior making it easier to provide search functionality for relations in a grid view. It also makes using related fields easier.

Live demo

Uses the KeenActiveDataProvider extension to limit the number of requests to the database.

Requirements

Developped with Yii 1.1.12 - surely useable with earlier and later versions.

Forum

A Forum thread is available for this extension.

Usage

To use this extension you must do 5 steps:

  1. "Install" the RelatedSearchBehavior;
  2. Update the Model's search() method;
  3. Add/Update the 'behaviors()' method;
  4. Add the search fields to the Model rules as safe fields;
  5. Use the search field as any other field in the CGridView.

Here are the details for these steps:

1. "Install" the RelatedSearchBehavior;

Installation is as easy as adding the location to the 'import' configuration of the Yii configuration file (main.php):

return array(
         [...]
    'import'=>array(
            [...]
            'ext.KeenActiveDataProvider',
        'ext.RelatedSearchBehavior',
    ),

In the above the RelatedSearchBehavior file was placed in the extensions directory.

2. Update the Model's search() method;

Update the overload of ActiveRecord::search(). The following example shows how you can set the default search order. You can set any parameter accepted by CActiveDataProvider (such as pagination) in the second array. So, replace code like this:

return new CActiveDataProvider($this, array(
  'criteria'=>$criteria,
  'sort'=>array(
    'defaultOrder'=>'title ASC',
  )
));

by code like this (I prefer using intermediate variables like '$sort' to improve readability and increase flexibility):

$sort=array(
    'defaultOrder'=>'title ASC',
);
return $this->relatedSearch(
    $criteria,
    array('sort'=>$sort)
);

In the above example, 'title' can be one of the relations that you defined.

3. Add/Update the 'behaviors()' method

Attach the behavior to the CActiveRecord and specifies the related fields.

function behaviors() {
    return array(
        'relatedsearch'=>array(
             'class'=>'RelatedSearchBehavior',
             'relations'=>array(
                  'serial'=>'device.device_identifier',
                  'location'=>'device.location.description',
                   // Field where search value is different($this->deviceid)
                 'fieldwithdifferentsearchvalue'=>array(
                     'field'=>'device.displayname',
                     'searchvalue'=>'deviceid'
                  ),
                 // Next line describes a field we do not search,
                 // but we define it here for convienience
                 'mylocalreference'=>'field.very.far.away.in.the.relation.tree',
             ),
         ),
    );
}

4. Add the search fields to the Model rules as safe fields;

public function rules()
    {
        return array(
            [...]
            array('serial,location,deviceid','safe','on'=>'search'),
        );
    }

5. Use the search field as any other field in the CGridView.

For the CGridView column specification, you can then just put 'serial' for the column (no need to do 'name'=>..., 'filter'=>..., 'value'=>... .

Example:

$this->widget('zii.widgets.grid.CGridView', array(
  [...]
  'columns'=>array(
      [...]
      'serial',
      'location',
  )
));

6. To use "autoScope"

Autoscope allows you to search a field using a scope without declaring the scope yourself.

For instance, if your Model has a field 'username', you can use this:

MyModel::model()->username('me')->findAll();

Before, you have to: 1. Add RelatedSearchBehavior to the behaviors of your CActiveRecord (already done in the preceding steps, 2. Add the following code to your CActiveRecord Model.

public function __call($name,$parameters) {
    try {
        return parent::__call($name,$parameters);
    } catch (CException $e) {
        if(preg_match('/ and its behaviors do not have a method or closure named /',$e->getMessage())) {
            return $this->autoScope($name, $parameters);
        } else {
            throw $e;
        }
    }
}

You are allowed to provide all the regular compare parameters:

MyModel::model()->username($searchvalue,$partialMatch,$operator,$escape)->findAll();

defaults are the same as compare: autoscope(,false,"AND",false).

This is usefull in complex nested conditions, not so much for simple searches like the above.

7. Tip

If you like RelatedSearchBehavior, you can create or update your Gii template to generate it automatically.

History

 * 1.03  Quoting relations in database.
 * 1.04  Added autoScope.
 *       Added option 'partialMatch' for relation.
 * 1.05  Enable multiple attributes in default sort.
 * 1.06  Fix to autoScope - return owner (chaining) + correct example in comment.

Resources

KeenActiveDataProvider

Total 20 comments

#13205 report it
le_top at 2013/05/14 02:33am
Discussion continued on forum

Please check the forum for the continuation of the discussion.

#13204 report it
hbalkhi at 2013/05/14 01:06am
Pagination issue

@le_top Thanks for taking time to reply , actually that's what i was planning to do but i thought maybe somebody faced the same issue ..anyway i found out that the the three order by clauses in the query (the outer and inner data sets query)that pick up the data are missing and i have no idea why ...

#13202 report it
le_top at 2013/05/13 07:26pm
Pagination issue

Hi In the demo the pagination behavior looks normal to me (using sqlite) and also on a production site (using mysql). This may have something to do with your gridview wrapper. I suggest that you check out the demo and "migrate" it to your bootstrap wrapper (using a new view). I think that this will help discover where the issue may be. Also check the SQL searches/requests made (using tbdebug extension for instance).

#13200 report it
hbalkhi at 2013/05/13 03:40pm
Pagination issue

thanks for this great extension..

i wanted to know if anybody facing issues with pagination where the data rows stay the same if you search using one of the related fields or sort by one of them and i noticed also that the indicators of the current page are changing but the data staying the same

everything works normally if i search using the fields of the same model i'm using crisu bootstrap grid and

appreciate any help

#13052 report it
le_top at 2013/04/30 02:13pm
Multi-attribute support for defaultOrder

Follow a request on the forum, I added multi-attribute defaultOrder support. So now the default order can be like this:

'sort'=>array('defaultOrder'=>'Artist DESC,TrackName ASC'),

where Artist and TrackName correspond to virtual attributes referring to fields in related tables (Related Search Behavior updates the defaultOrder as needed).

#12930 report it
le_top at 2013/04/21 12:57pm
Updated with partialMatch & autoScope

@zvik2004: I see that you are developing a relation as a keen user ;-).

I have added the partialMatch code (slightly differently), and 'autoScope' a feature that I started using myself.

#12927 report it
zvik2004 at 2013/04/21 07:33am
Great extension! (2) :-) - add parameter partialMatch

Hi

In your search, you use partialMatch=true. I think it will be great to get it as a parameter. I modified my code, please let me know what you think:

The configuration can be something like:

'fieldxyz'  => array(
    'field'         => 'table.field',
    'partialMatch'  => false,
),

And in the RelatedSearchBehavior file (in the appropriate places):

if(is_array($relationvar)) {
    $relationfield=$relationvar['field'];
    $search_value = isset($relationvar['searchvalue']) ? $this->getOwner()->{$relationvar['searchvalue']} : $this->getOwner()->{$var};;
    $partialMatch = isset($relationvar['partialMatch']) ? $relationvar['partialMatch'] : true;
} else {
    $relationfield=$relationvar;
    $search_value=$this->getOwner()->{$var};
    $partialMatch = true;
}
 
//and the search part:
$criteria->compare($column,$search_value,$partialMatch);

Thanks!

Zvi.

#12803 report it
le_top at 2013/04/12 04:10pm
? Your feedback on potential extension of RelatedSearchBehavior

Please check the Forum regarding a potential new feature to automatically add scopes for attributes.

#12647 report it
le_top at 2013/04/03 08:45am
? Why just one field

Hi Just one field because of the work (writing/testing) involved. Ideally, the code would accept multifield ordering in a single string specification or in an array specification. I am happy to integrate any full code.

#12645 report it
gb5256 at 2013/04/03 08:30am
Sorting on related record

Hi, thanks for extending your extension to work also with the default sort. Perfect. Just for curiosity: Why only for one field? Is this getting too complex otherwise? Don't get me wrong, I am totally happy with this, but I can think of cases where I need to do this for more than one field.

gb

#12615 report it
le_top at 2013/04/01 07:45pm
Sorting on related record

Hi I extended the extension so that you can define 1 related field for the defaultOrder. I have updated the demo (Invoiceline.php) to sort on 'Artist DESC' which is a related field.

#12612 report it
gb5256 at 2013/04/01 05:06pm
Sorting by RelatedRecord?

Thanks for providing this extension. Works very well. I right now want to do a sort on one of the related records. So to follow your example, this does not work for me:

$sort=array(
    'defaultOrder'=>'serial ASC',
);
return $this->relatedSearch(
    $criteria,
    array('sort'=>$sort)
);

Is there any work around for this? Thanks again, gb

#12454 report it
le_top at 2013/03/21 04:03pm
Demo

Hi The demo has no drop-down filters - it has the default filter function for gridviews - you fill in text. Using drop-downs for filtering is a different feature - I'll see where this is suitable to add.

I probably modified the KeenActiveDataProvider to improve it. I think I submitted my improvements, but not sure of it.

#12452 report it
maxtorchel at 2013/03/21 02:32pm
Field where search value is different

can you show example, how to setup it? I can`t understand how it works. demo has no dropdown filter.

ps your behavior not work with KeenActiveDataProvider.php from github, only work from demo.

#12435 report it
le_top at 2013/03/20 10:26am
Demo

The demo is available on line under 'Live demo' and you can download the demo too. That is not ok for you?

#12433 report it
Nur Rochim at 2013/03/20 10:17am
demo

Hi...

demo please :)

#12040 report it
le_top at 2013/02/22 04:26pm
Demo updated - using Chinook Demo Database

I updated the demo to use the Chinook Demo Database which is an open source database with a schema and data that is easy to understand.

It is a better demonstration of how easy it is to get a fully searcheable and sortable table.

Searching for 'jane' and sorting by email results in the following SQL query using 3 joins with very limited effort to achieve it.

SELECT COUNT(DISTINCT 't'."InvoiceLineId") FROM 'invoiceline' 't'  LEFT OUTER JOIN 'track' 'track' ON ('track'."TrackId"='t'."TrackId")  LEFT OUTER JOIN 'invoice' 'invoice' ON ('invoice'."InvoiceId"='t'."InvoiceId")  LEFT OUTER JOIN 'customer' 'customer' ON ('customer'."CustomerId"='invoice'."CustomerId")  LEFT OUTER JOIN 'employee' 'support' ON ('support'."EmployeeId"='customer'."SupportRepId")  WHERE (support.Email LIKE :ycp0)

Also, by using the KeenDataProvider there is just one extra SQL request to get all the related data from the database.

#11835 report it
le_top at 2013/02/06 04:35pm
Correction of the instructions + update of the extension.

Thanks for the feedback. I was certain that I was going to have some good suggestions to further improve this.

  1. I forgot to update the documentation. After adding the getter/setter implementation to the behavior I realised that the behavior could not be added on the fly and that the relations had to be known also to the data model instances. Which is actually a good thing because this make adding aliases to data fields in related models quite easy.

  2. Good suggestion - which is why '$options' is an array. To make it as generic as possible, the extension now passes on all the keys provided in '$options' to the DataProvider (while updating the 'sort' value).

There is also a fix for the 'searchvalue' functionality (which needs an example).

[Updates are in 1.02]

#11831 report it
zvik2004 at 2013/02/06 03:01pm
Great extension!

I have 2 comments:

  • In the example in this page, you specify:
return 
  $this->relatedSearch(
    $criteria,
    $relationsearch,
    array('sort'=>$sort)
  );

But the parameter: $relationsearch shouldn't be there.

  • There is no pagination handling (both in the code and in the examples); So for example, it will be nice to mention it in the example:
return 
  $this->relatedSearch(
    $criteria,
    array('sort'=>$sort, 'pagination'=>array('pageSize'=>10))
  );

And in your class: RelatedSearchBehavior, the method should be changed (this is the code that I used, but it's only example):

return new KeenActiveDataProvider($this->getOwner(), array(
                'pagination'=>(isset($options['pagination']) ? $options['pagination'] : null),
                'criteria'=>$criteria,
                'sort'=>$sort,
        ));

Thanks!

Zvi.

#11802 report it
le_top at 2013/02/04 10:58pm
Creation of getter/setter for search fields no longer needed

I just updated this extension so that the behavior itself takes care of the getter and setters for the search field. This helped me reduce the code in my own project a lot.

I hope that you'll enjoy this extension as much as I do.

Leave a comment

Please to leave your comment.

Create extension