How to have a sortable virtual attribute in a Grid


I have defined a virtual attribute in my Model which is combination of two other attributes (something like FullName = FirstName . LastName)… now, the virtual attribute column in my Grid isn’t sortable (the title is a text rather than a Hyperlink).

I think, I cannot find the relation between CSort and a Model.


I think the problem is that CSort is used with CActiveRecord which itself only returns attributes defined in the DATABASE when calling $model->getAttributes(), but no virtual attributes (defined in the model class as variabels).

But have you tried to sort by a combination of two attributes that actually exist in the database rather then trying to sort by the virtual attribute?

Something like this maybe?


'asc'=>'first_name, last_name',

'desc'=>'first_name DESC, last_name DESC',

'label'=>'fullname' )

Thanks Haensel,

Yes it did work, I just didn’t know where these sort attributes should be defined, which was under my DataProvider.


Solved, for me anyway…

Note: I am not an experienced PHP person and I am a newbie to yii as well so please forgive my lack of efficient or proper coding techniques in both.

Step 1) Add Virtual Attribute for model attributes in same model - I will get to related model sortable later when I have a chance and if it is possible.

In my model:

private $_full_name = NULL;

public function getfull_name()




            if (!isset($this->first_name) && !isset($this->last_name) && !isset($this->salutation))

                $this->_full_name = NULL;





                    $this->_full_name = $this->last_name;


                if (isset($this->first_name) || isset($this->last_name) )


                    $this->_full_name .= ', '; 




                    $this->_full_name .= $this->first_name;




                    $this->_full_name .= ' ' . $this->salutation;





        return $this->_full_name;


Step 2) In my model rules

public function rules()


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

	// will receive user inputs.

	return array(

                array('full_name', 'safe', 'on'=>'edit, post, search'),

Step 3) In my model attributes

public function attributeLabels()


	return array(

                    'full_name'=>'Full Name',

Step 4) In my model search function

            ... other criteria


            $criteria->compare('first_name',$this->_full_name, true, 'OR');

            $criteria->compare('last_name',$this->_full_name, true, 'OR');

            $criteria->compare('salutation',$this->_full_name, true, 'OR');


            $sort = new CSort();

            $sort->attributes = array(


                'asc'=>'last_name ASC, first_name ASC, salutation ASC',

                'desc'=>'last_name DESC, first_name DESC, salutation DESC',


            'id', ... other fields....,


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

		'criteria'=>$criteria, 'sort'=>$sort,


Step 5) In my view/admin.php





            'other columns',

Step 6) In my view/_search.php

I removed the first_name, last_name, and salutation entries as they are not needed

    <div class="row">

	<?php echo $form->label($model,'first_name'); ?>

	<?php echo $form->textField($model,'first_name',array('size'=>60,'maxlength'=>100)); ?>



These are the only changes I made I think - if I notice something else I will add to this.

Again, I am not that experienced (a couple weeks) so please take this with a grain of salt. Hope it helps.

A Newbie so sorry if this is not ok…

I am having a problem figuring out how to accomplish the sorting of the virtual attribute.

Currently I have the virtual attribute working as well with correct filtering; however, the CGridView has no link on the header for sorting.

I have been searching but no definitive answer so far. If you could pass on the how to I would be grateful.

note: I tried placing the noted ‘user’=>array(…), portion into the attributes but no dice…

Thanks in advance.

Would you mind giving an example of how you put the in DataProvider?



No reply?


Hi Oliver,

I think MtlMike’s post is a good example. Where do you get stuck?

Hello there,

Sorry but I have been out of the Yii loop for a while now and I was just searching for something and by chance noticed this… I know its late but I will be able to look at this in the upcoming weekend if you can wait for a couple days more; I need a bit of time to review but if my memory serves me correctly I made this post very carefully and thought I included everything. Of course I must have missed something and will definitely follow up.

Again, sorry for the lack of responce, nothing like being left in the wind.


Following up…

Again, its been a while but I think this answers your question:

The private variable of the model below stores the string concatenation which is constructed by the public function which removes spaces or can be any formatting/arrangement for sorting. So your model, not only represents the table, but has this virtual property/column as well.

This search method in the model located near the bottom, which is called by the controller, returns a CActiveDataProvider containing a collection of objects, and which is then passed to the view. Hence MVC Pattern :).

The view gets the CActiveDataProvider with this new property and which can now be included in a grid and sorted via the Sort attribute of the CActiveDataProvider (see below in bold).

private $_full_name = NULL;

public function getfull_name() { // how ever you want to manip table fields }

[b] I use a string and CSqlDataProvider In the search method here is the entire search method [/b]

  public function search()


            // calls a method to make temp table list : myClientList_1 and 2



            // using first index temp table for count - myClientList_1

            $sql = "";

            $sqlTotal = "select count(*) from myClientList_2 where 1=1 ";

            $sqlSelect = "select  id, company_name, last_name, first_name, salutation, full_name,

                                title, phone_1, phone_2, fax, email, address, address_2, 

                                city_town, region, country, postal_code,

                                canvasser_year, status_code_id, status_description, extinct 

                    from myClientList_2 where 1=1 ";


            If (isset($_GET['Client']) || isset(Yii::app()->session['GetClient']))


                $sql .= ($this->id > 0 ? " AND id = ".$this->id : "");

                $sql .= (strlen($this->company_name) > 0 ? " AND company_name LIKE '%{$this->company_name}%'" : "");

                $sql .= (strlen($this->title) > 0 ? " AND title LIKE %{$this->title}%'" : "");

                $sql .= (strlen($this->phone_1) > 0 ? " AND phone_1 LIKE '%{$this->phone_1}%'" : "");

                $sql .= (strlen($this->phone_2) > 0 ? " AND phone_2 LIKE '%{$this->phone_2}%'" : "");

                $sql .= (strlen($this->fax) > 0 ? " AND fax LIKE '%{$this->phone_2}%'" : "");

                $sql .= (strlen($this->email) > 0 ? " AND email LIKE '%{$this->email}%'" : "");

                $sql .= (strlen($this->address) > 0 ? " AND address LIKE '%{$this->address}%'" : "");

                $sql .= (strlen($this->address_2) > 0 ? " AND address_2 LIKE '%{$this->address_2}%'" : "");

                $sql .= (strlen($this->city_town) > 0 ? " AND city_town LIKE '%{$this->city_town}%'" : "");

                $sql .= (strlen($this->region) > 0 ? " AND region LIKE '%{$this->region}%'" : '');


                if (strlen($this->full_name)> 0)


                    $sql .= " AND (salutation LIKE '%{$this->full_name}%'";

                    $sql .= " OR last_name LIKE '%{$this->full_name}%'";

                    $sql .= " OR first_name LIKE '%{$this->full_name}%')";






            // have to get total count using the where condition

            $count=Yii::app()->db->createCommand($sqlTotal . $sql)->queryScalar();


            $sortColumn = 'company_name, last_name, first_name, salutation';

            $sort = new CSort();

            //$sort->modelClass = 'Client';

            $sort->defaultOrder = $sortColumn;

            $sort->attributes=array('id', 'full_name','company_name', 'title', 'phone_1', 'canvasser_year', 

                        'status_code_id', 'extinct',




           $clientArray = array(



                    // 'pagination'=>false,




           $dataProvider= new CSqlDataProvider($sqlSelect . $sql, 




           return $dataProvider;




And here is the grid widget using this data

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










                     'header'=>'Company Name',












                            //'viewButtonUrl'=>'Yii::app()->createUrl("/client/view", array("id" => $data["id"]))',

                            'deleteButtonUrl'=>'Yii::app()->createUrl("/client/delete", array("id" =>  $data["id"]))',

                            'updateButtonUrl'=>'Yii::app()->createUrl("/client/update", array("id" =>  $data["id"]))',





Hope this gives you some ideas and helps.