Making Relational Column In Gridview Searchable/sortable (Lost!)

Hello,

I am currently struggling to try to make my relational columns in the CGridView searchable. I have tried different tutorials (ex) but they’re not working for me. I get errors such as $target not defined, or target.target_id not defined.

The columns which are currently NOT searchable are target_name and target_id (see image below), and the information from these columns comes from a 2nd table called target. Right now, I am using a partial view to obtain these in CGridView.

I was wondering if someone knows a way to make this work (maybe you know of a more simple tutorial that a newbie could follow?). I am new to Yii, and possibly going about things the wrong way :S

4024

Screenshot from 2013-03-26 09:48:28.png

Thank you :)

Hi Tanya,

Have you tried this wiki?

Searching and sorting by related model in CGridView

Hi Softark,

I had tried this, it’s similar to the tutorial I followed.

Following the tutorial, I modified the Drug.php file (below). I don’t get an error message, but a blank screen :S




//Drug.php


<?php


/**

 * This is the model class for table "drug".

 *

 * The followings are the available columns in table 'drug':

 * @property string $drug_name

 * @property string $drug_id

 *

 * The followings are the available model relations:

 * @property target[] $targets

 */

class Drug extends CActiveRecord


public $target_search;


{


    /**

     * Returns the static model of the specified AR class.

     * @param string $className active record class name.

     * @return Drug the static model class

     */

    public static function model($className=__CLASS__)

    {

        return parent::model($className);

    }


    /**

     * @return string the associated database table name

     */

    public function tableName()

    {

        return 'drug';

    }


    /**

     * @return array validation rules for model attributes.

     */

    public function rules()

    {

        // NOTE: you should only define rules for those attributes that

        // will receive user inputs.

        return array(

            array('drug_name, drug_indication, drug_synonym, drug_brand', 'required'),

            array('drug_name', 'length', 'max' => 100),

            array('drug_id', 'length', 'max' => 60),

            array('drug_indication', 'length', 'max' => 1700),

            array('drug_synonym', 'length', 'max' => 120),

            array('drug_brand', 'length', 'max' => 40),

            // The following rule is used by search().

            // Please remove those attributes that should not be searched.

            array('drug_name, drug_id, drug_indication, drug_synonym, drug_brand, target_search', 'safe', 'on' => 'search'),

        );

    }


    /**

     * @return array relational rules.

     */

    public function relations()

    {

        // NOTE: you may need to adjust the relation name and the related

        // class name for the relations automatically generated below.

        return array(

            'targets' => array(self::MANY_MANY, 'Target', 'Drug_has_target(drug_id, target_id)')

        );

    }


    /*

      public function targetsToString()

      {

      $targets = $this->targets;

      if ($targets)

      {

      $string = '';

      foreach($targets as $target) {

      $string .= $targets->target_id . ', ';

      }

      return substr($string,0,strlen($string)-1);

      }

      return null;

      }


      /**

     * @return array customized attribute labels (name=>label)

     */


    public function attributeLabels()

    {

        return array(

            'drug_name' => 'Drug Name',

            'drug_id' => 'Drug ID',

            'drug_indication' => 'Drug Indication',

            'drug_synonym' => 'Drug Synonym',

            'drug_brand' => 'Drug Brand',

            'target_id' => 'Target ID',

        );

    }


    /*

      function behaviors() {

      return array(

      'relatedsearch'=>array(

      'class'=>'RelatedSearchBehavior',

      'relations'=>array(

      'drug_id'=>'drug.drug_id',

      'drug_name'=>'drug.drug_name',

      'target_id'=>'target.target_id',

      // Field where search value is different($this->target_id)

      /*'targets'=>array(

      'field'=>'target.target_id',

      'searchvalue'=>'target_id'

      ),

      // Next line describes a field we do not search,

      // but we define it here for convienience


      ),

      ),

      );

      }

     */


    /**

     * Retrieves a list of models based on the current search/filter conditions.

     * @return CActiveDataProvider the data provider that can return the models based on the search/filter conditions.

     */

    public function search()

    {

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

        // should not be searched.


        $criteria = new CDbCriteria;

	

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

	$criteria->with = 'targets';

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

	$criteria->compare('targets.target_id', $this->target_search,true);

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

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

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

	

	




        return new CActiveDataProvider($this, array(

                'criteria' => $criteria,

               'sort'=>array(

        'attributes'=>array(

            'target_search'=>array(

                'asc'=>'targets.target_id',

                'desc'=>'targets.target_id DESC',

            ),

            '*',

        ),

    ),


 ));

    }


}



I’m really unsure of what I did to have the page not appear anymore :S

$target_search should be declared inside the class.




//Drug.php


<?php

class Drug extends CActiveRecord

{


    public $target_search;

...



Hi Softark,

Doing so, I get the following error : "include(target_search.php): failed to open stream: No such file or directory". Not sure what this means or how to fix it…

I tried changing $target_search to $target, which changed the error message to "Property "Target.0" is not defined. ".




//Drug.php

<?php


/**

 * This is the model class for table "drug".

 *

 * The followings are the available columns in table 'drug':

 * @property string $drug_name

 * @property string $drug_id

 *

 * The followings are the available model relations:

 * @property target[] $targets

 */

class Drug extends CActiveRecord


{

public $target_search;


    /**

     * Returns the static model of the specified AR class.

     * @param string $className active record class name.

     * @return Drug the static model class

     */

    public static function model($className=__CLASS__)

    {

        return parent::model($className);

    }


    /**

     * @return string the associated database table name

     */

    public function tableName()

    {

        return 'drug';

    }


    /**

     * @return array validation rules for model attributes.

     */

    public function rules()

    {

        // NOTE: you should only define rules for those attributes that

        // will receive user inputs.

        return array(

            array('drug_name, drug_indication, drug_synonym, drug_brand', 'required'),

            array('drug_name', 'length', 'max' => 100),

            array('drug_id', 'length', 'max' => 60),

            array('drug_indication', 'length', 'max' => 1700),

            array('drug_synonym', 'length', 'max' => 120),

            array('drug_brand', 'length', 'max' => 40),

            // The following rule is used by search().

            // Please remove those attributes that should not be searched.

            array('drug_name, drug_id, drug_indication, drug_synonym, drug_brand', 'target_search', 'safe', 'on' => 'search'),

        );

    }


    /**

     * @return array relational rules.

     */

    public function relations()

    {

        // NOTE: you may need to adjust the relation name and the related

        // class name for the relations automatically generated below.

        return array(

            'targets' => array(self::MANY_MANY, 'Target', 'Drug_has_target(drug_id, target_id)')

        );

    }


    /*

      public function targetsToString()

      {

      $targets = $this->targets;

      if ($targets)

      {

      $string = '';

      foreach($targets as $target) {

      $string .= $targets->target_id . ', ';

      }

      return substr($string,0,strlen($string)-1);

      }

      return null;

      }


      /**

     * @return array customized attribute labels (name=>label)

     */


    public function attributeLabels()

    {

        return array(

            'drug_name' => 'Drug Name',

            'drug_id' => 'Drug ID',

            'drug_indication' => 'Drug Indication',

            'drug_synonym' => 'Drug Synonym',

            'drug_brand' => 'Drug Brand',

            'targets.target_id' => 'Target ID',

        );

    }


    /*

      function behaviors() {

      return array(

      'relatedsearch'=>array(

      'class'=>'RelatedSearchBehavior',

      'relations'=>array(

      'drug_id'=>'drug.drug_id',

      'drug_name'=>'drug.drug_name',

      'target_id'=>'target.target_id',

      // Field where search value is different($this->deviceid)

      /*'targets'=>array(

      'field'=>'target.target_id',

      'searchvalue'=>'target_id'

      ),

      // Next line describes a field we do not search,

      // but we define it here for convienience


      ),

      ),

      );

      }

     */


    /**

     * Retrieves a list of models based on the current search/filter conditions.

     * @return CActiveDataProvider the data provider that can return the models based on the search/filter conditions.

     */

    public function search()

    {

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

        // should not be searched.


        $criteria = new CDbCriteria;

	

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

	$criteria->with = 'targets';

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


	$criteria->compare('targets.target_id', $this->target_search,true);


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

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

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

	

	




        return new CActiveDataProvider($this, array(

                'criteria' => $criteria,

               'sort'=>array(

        'attributes'=>array(

            'target_search'=>array(

                'asc'=>'targets.target_id',

                'desc'=>'targets.target_id DESC',

            ),

            '*',

        ),

    ),


 ));

    }


}



Tanya

Made some modifications …figured my problem was the many_many relationships. Decided to use a function in my model to get my targets, and used it in the view.

Two positives;

  1. target info shows up (w/o partial view I was using earlier)

  2. search box shows up for target_id

The negative:

The search no longer works … I think the error is in the model file under CDbCriteria …but it can also be in my CGridView.

If someone could help me, this would be appreciated. I think that if I could somehow make the "value=>" that I am showing in the CGridView available to the model, I can make it searchable, and this could maybe work.

(I’m new, so what I am doing may not really make sense).




//Drug.php


public function targetsToString()

      {

      $targets = $this->targets;

      if ($targets)

      {

      $string = '';

      foreach($targets as $target) {

      $string .= $target->target_id . ', ';

      }

      return substr($string,0,strlen($string)-1);

      }

      return null;

      }


....


public function rules()

    {

        // NOTE: you should only define rules for those attributes that

        // will receive user inputs.

        return array(

            array('drug_name, drug_indication, drug_synonym, drug_brand', 'required'),

            array('drug_name', 'length', 'max' => 100),

            array('drug_id', 'length', 'max' => 60),

            array('drug_indication', 'length', 'max' => 1700),

            array('drug_synonym', 'length', 'max' => 120),

            array('drug_brand', 'length', 'max' => 40),

            // The following rule is used by search().

            // Please remove those attributes that should not be searched.

            array('drug_name, target, drug_id, drug_indication, drug_synonym, drug_brand', 'safe', 'on' => 'search'),

        );

    }


....


public function search()

    {

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

        // should not be searched.


        $criteria = new CDbCriteria;

	

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


	$criteria->with = 'targets';


	$criteria->together=true;


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


	$criteria->compare('targets.target_id', $this->drug_id,true);


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

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

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

	

	




        return new CActiveDataProvider($this, array(

                'criteria' => $criteria,


 ));

    }


}







//admin.php

<?php $this->widget('zii.widgets.grid.CGridView', array(

        'id'=>'drug-targets-grid',

        'dataProvider'=>$model->search(),

        'filter'=>$model,

	//'nullDisplay' => 'N/A',


        'columns'=>array(

                'drug_id',

                'drug_name',

		'drug_synonym',

		'drug_brand',

		 array(

                        'header' => 'Target Name',

                        'type' => 'raw',

                        'value'=>'$this->grid->getOwner()->renderPartial("_drug_target_name", array("data"=>$data), true);', 

                ),


array (

'header' => 'Target ID',

'name' => 'drug_id',

'value' => '$data->targetsToString()',

),

               

        )

)); ?>



Hi Tanya,

You have this method in Drug.php, but you’d better remove it. I think it is interfering with your “targets” MANY_MANY relation, because the method is assumed to be the getter function for “targets” attribute. A model can not share the same name both for a relation and an attribute.

Then, I think your search will start to function.

But, searching by MANY_MANY relation requires you a little effort.

The following wiki is for HAS_MANY, but the basic structure is the same with MANY_MANY.

Just try it, please.

http://www.yiiframework.com/wiki/428/drills-search-by-a-has_many-relation

Hello Softark,

I have removed the gettarget method, but the search still doesn’t work. I looked at the tutorial, and it creates a function which for me, would get targets and drug ID’s. I’m thinking that I only want the target id’s, so could skip echoing the drug_id, correct?

The use of $Searchword is confusing to me as well (is this just the text that will be typed in on the CGridView?).

Also, I am wondering if I am supposed to make use of this function in the search function, under CDbCriteria …this part is confusing to me.

Here’s what I understood (apply to my Drug model);




//Drug.php

....


public static function GetTargetsWithDrugsByTargetID($searchWord)

{

    // query criteria

    $criteria = new CDbCriteria();

    // join Target model (without selecting)

    $criteria->with = array(

        'target' => array(

            'select' => false,

        ),

    );

    // compare title

    $criteria->compare('drug.drug_id', $searchWord, true);

    // find all targets

    $targets = Target::model()->findAll($criteria);

    // show all targets and their drugs

    foreach($targets as $target)

    {

        echo "Target = " . $target->target_id . "\n";

        foreach($target->drugs as $drug)

        {

            echo "Drug = " . $drug->drug_id . "\n";

        }

    }

}



Then in the search;




public function search()

    {

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

        // should not be searched.


        $criteria = new CDbCriteria;

	

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


	$criteria->with = 'targets';


	$criteria->together=true;


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




	$criteria->compare('target.target_id', $this->targets,true);




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

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

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

));



Even after reading the tutorial, I still feel very confused.

Tanya

Well, please forget about the wiki and get back to the point where you have removed "gettargets" method.

And please try this:




class Drug extends CActiveRecord

{

	/*

	 * for searching target

	 */

	public $target;

	...

	public function relations()

	{

		return array(

			'targets' => array(self::MANY_MANY, 'Target', 'Drug_has_target(drug_id, target_id)')

		);

	}

	...

	public function search()

	{

		$criteria = new CDbCriteria;

		$criteria->with = 'targets';

		$criteria->together=true;


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

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

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

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

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


		$criteria->compare('targets.target_id', $this->target, true);


		return new CActiveDataProvider($this, array(

			'criteria' => $criteria,

		));

	}

	

}



And in the "admin" view:




<?php

...

<?php $this->widget('zii.widgets.grid.CGridView', array(

	'id'=>'drug-targets-grid',

	'dataProvider'=>$model->search(),

	'filter'=>$model,

	'columns'=>array(

		'drug_id',

		'drug_name',

		'drug_synonym',

		'drug_brand',

		array(

			'name' => 'target',

			'value' => '$data->targetsToString()',

		),

	)

)); ?>



By entering some target_id in the filter of the last column, the grid will show all the drugs that has the target_id.

Hello Softark,

I have made the changes, but am still having a problem. The search box shows up (no error messages either), but when I type-in a target, nothing happens (the results aren’t filtered). Could this be done to the many_many relationship?

Tanya

Would you please disable the ajax updating of the grid and see what’s happening?




<?php

...

<?php $this->widget('zii.widgets.grid.CGridView', array(

	'id'=>'drug-targets-grid',

	'dataProvider'=>$model->search(),

	'filter'=>$model,

        'ajaxUpdate'=>false, // this is it.

	'columns'=>array(

		'drug_id',

		'drug_name',

		'drug_synonym',

		'drug_brand',

		array(

			'name' => 'target',

			'value' => '$data->targetsToString()',

		),

	)

)); ?>



Hai All,Gone through the tutorial for searching/sorting in relational attributes.but i dint found how to sort if we use customized function to display data where we have three modules.module1 has the attribute clientname , module2 has the relation BELONGS TO- ‘client’ to the model1,and i have one more module named module3 with relation ‘detail’ BELONGS TO module2.

In module2 i have a customized function,

public function getClientname(){

return $this->client->name;

}

For displaying client details in gridview of module3 i am using $data->RelName->clientname where RelName is ‘detail’ and clientname is name of the customised function that returns the name of the client of model1.That works fine,

But i have a problem while sorting and searching.can any one help how can i sort by using CSORT with this customized function.

Hi softark,

Nothing happens, but my url changed;

site/drug/admin?Drug[drug_id]=&Drug[drug_name]=&Drug[drug_synonym]=&Drug[drug_brand]=&Drug[target]=target&Drug_page=1

Was this what I should be noticing?

Tanya

You have to input a number for “target”, because you are comparing ‘target_id’ …

I’ve typed-in an actual ID, and nothing happens. The number of results returned does not change, screen does not change.

Have you made the target attribute safe on search?





public function rules()

{

	return array(

		...

		array('..., target', 'safe', 'on' => 'search')

	);

}



Matt

Hello Matt,

target wasn’t safe on search, I had a typo and had “targets”. It works now!

Thank you!!

Tanya

Hi softark,

Just wanted to say it works now (error on my part not setting target to be searchable …had "targets" instead).

Thank you so much for your help!

Tanya

Thank you for your feedback, Tanya.