Skip to content

Categories:

Deep River Blues – Doc Watson

I first fell in love with Doc’s music back in the 70′s when I heard the triple album “Will The Circle Be Unbroken”. While I have spent quite a bit of time with Doc’s flat picking style, his finger picking is more to my own style. Doc Watson

When I was first learning guitar I had a weird frailing style of picking which I used to murder Leo Kottke tunes, not having a clue at all about the various picking styles out there. Thankfully I bumped into the music of Mississippi John Hurt and soon picked up his wonderful alternating bass style.

Deep River Blues is a funky alternating bass piece, with a couple of nice little runs to dress it up. I don’t play anybody’s music note for note and rarely play a piece exactly the same way twice, however you will definitly pick up Doc’s style in my own rendition.

Here is the Tuxguitar tab for Deep River Blues

Here is a link to Doc playing and singing Deep River Blues on Youtube

Deep River Blues – Doc Watson

Let it rain let it pour, let it rain a whole lot more
Cos I’ve got those deep river blues
Let the rain drive right on, let the waves sweep along
Cos I’ve got them deep river blues

My old gal’s a good old pal, she walks like a water fowl
When I’ve got those deep river blues
Ain’t no one to cry for me and the fish all go out on a spree
When I’ve got them deep river blues

Give me back my old boat, I’m gonna sail if she’ll float,
Cos I’ve got them deep river blues,
I’m goin’ back to Muscle Shoals, times are better there I’m told,
Cause I got them deep river blues.

If my boat sinks with me, I’ll go down, don’t you see,
Cos I’ve got them deep river blues,
Now I’m gonna say goodbye and if I sink, just let me die,
Cos I got them deep river blues.

Posted in My Music.


yii-language-behavior – Multiple Languages For Yii

This behavior allows you to quickly and easily add multiple language support to your Yii application. It has been developed as part of a new Yii powered eCommerce platform: Suocommerce – Next Generation eCommerce™.

Download:

You may download the extension from here: yii-language-behavior.

Installation:

To install the extension, create a directory named behaviors within your application /protected/ directory and copy the file LanguageBehavior.php to this directory.  That’s it!

Using the Extension:

Database Tables, Models and CRUD

The first thing that is required is a language table that will store information about the languages you wish to use in your application.  The download includes the file example.sql which contains an ideal structure for this table, plus an example of two tables to handle translatable data.  The structure of our language table is as follows:


CREATE TABLE IF NOT EXISTS `language` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(96) NOT NULL,
`code` varchar(12) NOT NULL,
`image` varchar(96) DEFAULT NULL,
`sort_order` tinyint(3) DEFAULT NULL,
`default` tinyint(1) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `idx_language_code` (`code`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

You should create a model and CRUD for the language table and populate it with your preferred languages. The column default allows you to set a default language for the front end of your application.

Next we need at least two tables for translatable data.  The example.sql file contains two tables: category and category_lang which are structured as follows:


CREATE TABLE IF NOT EXISTS `category` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`category_image` varchar(96) DEFAULT NULL,
`sort_order` int(3) unsigned DEFAULT NULL,
`status` tinyint(1) unsigned NOT NULL DEFAULT '1',
PRIMARY KEY (`id`),
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

CREATE TABLE IF NOT EXISTS `category_lang` (
`category_id` int(11) unsigned NOT NULL,
`language_id` int(11) unsigned NOT NULL,
`category_name` varchar(96) NOT NULL,
`category_description` text,
PRIMARY KEY (`category_id`,`language_id`),
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

We also need foreign keys, in particular to make sure that data is deleted from the category_lang table whenever a category record is deleted:

ALTER TABLE `category_lang`
ADD CONSTRAINT `key_category_description_category_id` FOREIGN KEY (`category_id`) REFERENCES `category` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION,
ADD CONSTRAINT `key_category_description_language_id` FOREIGN KEY (`language_id`) REFERENCES `language` (`id`) ON DELETE RESTRICT ON UPDATE NO ACTION;

The important things to note with the category_lang table, which stores our translation strings are:

  1. A composite primary key comprised of category_id, which references the id column of our category table and language_id which references the id column of our language table.  These two columns are essential and can be named as you please.
  2. The category_name and category_description columns, both of which contain translation strings. You can have as many of these as you require and name them as you please.

You should generate a model for each of these tables and generate CRUD for the primary table, which in this case is for the Category class.

Configuring The Behavior

The behavior must be configured for each model pair you wish to use it with.  In this example, we will use our Category class file. First we must declare as public properties each attribute of our CategoryLang class that contain translation strings, plus a property  for model validation errors from our CategoryLang class. This attribute must always be named translationError. Within our Category class:

public $categoryName;
public $categoryDescription;
public $translationError;

Next we configure the behavior itself. Again in our Category class, we place the following:

	public function behaviors() {
		return array(
			'LanguageBehavior' => array(
				'class' => 'application.behaviors.LanguageBehavior',
				'translationClass' => 'CategoryLang',
				'translationForeignKey' => 'category_id',
				'languageColumn' => 'language_id',
				'languageRelation' => 'categoryLang',
				'translationColumns' => array('category_name', 'category_description'),
				'languages' => Yii::app()->params['languages'],
			),
		);
	}

The above is relatively straight forward. The attribute class defines the path to the LanguageBehavior class we installed. The attribute translationClass defines our translation class, which in this case is CategoryLang.  The attribute translationForeignKey refers to the foreign key column in our category_lang table that points to the id column of our category table.  The attribute languageColumn refers to the foreign key that points to the id column of our language table.  The attribute languageRelation refers to whatever we wish to name our relation between the category and  CategoryLang tables. The attribute transationColumns refers to an array containing all of our category_lang table  columns which contain translation strings.

With that the behavior is ready to use, however we need to set languages within the behavior. For testing I have been running two languages, English and Spanish (see example.sql). Languages need to  be set in the application configuration using a suitable configuration behavior. I use the following code within beginRequest():

	if(!isset(Yii::app()->params['languages'])) {
        $languages = Language::model()->findAll();
        $language_array = array();
        foreach($languages as $val) {
            $language_array[] = array('id' => $val->id,
                                      'code' => $val->code,
                                      'name' => $val->name,
                                      'image' => $val->image);
        }
        Yii::app()->params['languages'] = $language_array;
    }

We also need to modify the file _form.php that was created when generating CRUD for our category table. Below is an example from this file modified for the category_name and category_description attributes:

<?php foreach (Yii::app()->params['languages'] as $val) :
    ?>
<fieldset>
    <legend><?php echo $val['name']; ?></legend>
    <div class="row">
	<?php $class=''; (isset($model->translationError['category_name'][$val['id']]) ? $class='error':''); ?>
    <?php echo $form->labelEx($model,'category_name_', array('class' => $class)); ?>
    <?php echo $form->textField($model,'category_name[' . $val['id'] . ']',array('size'=>60,'maxlength'=>255, 'class' => $class)); ?>
    <?php echo (isset($model->translationError['category_name'][$val['id']]) ? '<div class="errorMessage">' . $model->translationError['category_name'][$val['id']] . '</div>':''); ?>
    </div>
     <div class="row">
    <?php echo $form->labelEx($model,'category_description'); ?>
    <?php echo $form->textArea($model,'category_description[' . $val['id'] . ']'); ?>
    <?php echo $form->error($model,'category_description'); ?>
    </div>
</fieldset>
<?php endforeach; ?>

At this point you should be able to create and update records for our two classes. For the front end however we need to set a session variable for the default or selected language, so that only a single language will be displayed on the front end. For this we must set Yii::app()->session['language_id']. This must be unset whenever we are working in the back end, otherwise you will only see data for one language. Exactly how this is accomplished for the front end I will leave to the reader. In the front end, translation attributes can be accessed like this:

$category = Category::model()->findByPk($id);
echo $category->categoryLang->category_name;

You can see above that we use categoryLang which is what we set as the name for our relation.

How It Works:

The LanguageBehavior class contains only four methods, which to me is beautiful in its simplicity. I am a strong believer in the KISS principle and always look to do things in the simplest way possible.

At the top of the class file we see six public properties. We set these properties when configuring the behavior within our class. Using properties in this way allows the behavior to be used for any pair of classes, no matter how their attributes are named or how many translation columns there are in a table.

The first method that we see is attach(), which is a method of the IBehavior interface. We use this to conditionally invoke a HAS_ONE relation between our two classes, so that in our front end when we retrieve records for the primary model, the relation data is also returned for the selected language in a single query:

		if(isset(Yii::app()->session['language_id'])) {
			$class = CActiveRecord::HAS_ONE;
			$owner->getMetaData()->relations[$this->languageRelation] = new $class($this->languageRelation, $this->translationClass, $this->translationForeignKey, array('condition' => $this->language_column . ' = ' . Yii::app()->session['language_id']));
		}

Even though in reality the relation between the tables is HAS_MANY, we can use a HAS_ONE relation by limiting it with the language_id condition.

The second method afterFind() is is used to find all translation records and to populate our declared translationColumns property within our primary class. This is used for the back end and is invoked whenever Yii::app()->session['language_id'] is not set.

The third method afterValidate() is invoked whenever our primary model attributes are validated. It first populates the translationClass properties, then validates those properties against the rules of the class. If errors are found, it first populates the translationError property we declared, then adds those errors to the primary class errors.

The fourth method afterSave() saves the translationClass data after the primary class data is saved. The translationClass attributes are populated by looping through the translationColumns properties.

I hope that many will find this extension as useful as I do. A happy new year to all!

Posted in PHP, Yii Framework.


Customising CGridView – Using A Function To Display Related Data

It often happens with creating grid views that we have related data that we want to both display and filter. If your database is designed well, this relation is defined in your main table as an ID to reference the related table. For this example I will use a ‘user’ table, along with an ‘organisations’ table. The relation will be referenced in the ‘user’ table with the column ‘organisation_id’.

We don’t however just want to display the ‘organisation_id’ in our grid view, with a standard text input for filtering – we want the organisation name for each of our users, plus a drop down filter that lists the organisations.

The first thing we need is a standard GII generated style controller action for displaying our grid view:

	public function actionUsers()
	{
		$model=new User('search');
		$model->unsetAttributes();  // clear any default values
		if(isset($_GET['User']))
			$model->attributes=$_GET['User'];
		$this->render('users_grid',array(
			'model'=>$model,
		));
	}

Now the best way that I have found for displaying things like ‘organisation_name’ from a related table is to build an array to reference. First thing we will do is to pull all the organisations in our controller. With this we can build our array, plus we have the data available for our organisations drop down filter. We can add the following at the top of our controller action:

		$organisations = Organisations::model()->findAll(array('order' => 'organisation_name'));
		$organisations_array = array('0' => '');
		foreach($organisations as $val) {
			$organisations_array[$val->id] = $val->organisation_name;
		}

We then pass both to our view like this:

$this->render('users_grid',array(
			'model'=>$model, 'organisations' => $organisations, 'organisations_array' => $organisations_array,
		));

Next we need to add the column to our CGridView. To display our ‘organisation_name’ requires the use of a function within the ‘value’ attribute:

'value' => function($data,$row) use ($organisations_array){ return $organisations_array[$data->organisation_id];}

Now one may ask “Why don’t you just put a relation in your model and use ‘with’ in your ‘search()’ method? Then you can reference it like this”:

$data->organisations->organisation_name

Simply because this performs much better. Yii builds abominable ‘count()’ queries once you begin joining tables, which perform very badly (you can create your own count queries, but that’s another story). We need the organisations for our filter anyway, so why do a JOIN?

Our filter is built very simply:

'filter' => CHtml::listData($organisations, 'id', 'organisation_name')

Thus the full code for our new column is as follows:

array('name' => 'organisation_id', 'type' => 'raw', 'value' => function($data,$row) use ($organisations_array){ return $organisations_array[$data->organisation_id];}, 'filter' => CHtml::listData($organisations, 'id', 'organisation_name')),

Well, that was easy, now get coding!

Posted in PHP, Yii Framework.


Customising CGridView – CButtonColumn Ajax Update the Grid Using $data

In a previous post I talked about using CButtonColumn for an Ajax update for a ‘status’ value in the grid data: . That included a simple refresh of the grid using:$.fn.yiiGridView.update(“my-grid”).

This time I have been working on a ‘categories’ grid view. The data includes sub categories using a ‘parent_id’ column. The first thing we want is for the gridview to initially only display top level categories, those with a parent_id of 0 (zero). Then we want a button (folder icon) which when clicked will display the sub categories of a particular category. The first thing is to modify the model search() method to initially only display top level categories. Below the search criteria we can add the following:

    if(empty($this->parent_id)) {
        $criteria->addCondition('parent_id = 0');
    }

Thats all we need to modify in our model class.

Next we want to display a folder icon button that when clicked will display the sub categories for the selected category. We want this on the left of our CGridview, so we will add a new CButtonColumn before anything else on the grid:

array(
      'class' => 'CButtonColumn',
      'headerHtmlOptions'=>array('style'=>'width:40px;'),
      'htmlOptions' => array('style'=>'width:40px; text-align:center'),
      'template' => '{subcat}',
      'buttons'=>array
		      (
		       'subcat' => array
				       (
					'label'=>'sub categories',
					'imageUrl'=>'images/icn/foldericn.png',  // make sure you have an image
					'url'=>'Yii::app()->createUrl("admin/categories", array("Categories[parent_id]"=>$data->id))',
				)
			)
		),

The above creates a hard link – you should test this to be sure that it works as expected.

Next we just need the Ajaxify the above, so below the ‘url’ parameter we add:

'click' => "function (){ $.fn.yiiGridView.update('categories-grid', {type:'GET', url:$(this).attr('href')}); return false}"

And thats all there is to it, easy when you know how!

Posted in PHP, Yii Framework.


Customising CGridView – CButtonColumn Image Swap

If you haven’t already done so, please read my post Customising CGridView – Custom CButtonColumn Ajax Buttons/

Further to that post, we will now look at customising CButtonColumn to display a different status button image according to whether ‘status’ is set to 0 or 1. There are several ways to do this and I will outline my preferred method.

The first thing to note with CButtom column is that only the ‘url’ in the ‘button’ array is eval()ed, thus we cannot pass $data->status directly to ‘imageURL’ (would be easy!). The simplest way to get past this limitation is to extend the CButtonColumn class:

class CustomButtonColumn extends CButtonColumn
{
        protected function renderButton($id, $button, $row, $data)
        {
                if(isset($button['options']['data-status'])) {
                        $button['imageUrl'] = $this->evaluateExpression("($data->status == 1 ? Yii::app()->request->baseUrl.'/images/icn/status_on.png': Yii::app()->request->baseUrl.'/images/icn/status_off.png')", array('data' => $data, 'row' => $row));
                }
                parent::renderButton($id, $button, $row, $data);
        }
}

Here we can see that is our record ‘status’ is set to 1 we display status_on.png, otherwise we display status_off.png. Please make sure that these images exist! The class detects whether ‘data-status’ is set in the ‘options’ array and if so displays our custom buttons, otherwise our button will be displayed in the regular way by the parent class.

Save this file as CustomButtonColumn.php in your /protected/components/ directory

Next we need to change the CButtonColumn class in our CGridView to use our new extension:

Change:

'class'=>'CButtonColumn',

to:

'class'=>'CustomButtonColumn',

Next we need to modify our button array to take advantage of our new class extension:

				'status' => array
				(
					'label'=>'status',
					'url'=>'Yii::app()->createUrl("mycontroller/status", array("id"=>$data->id))',
					'options' => array('data-status' => 1, 'ajax' => array('type' => 'get', 'url'=>'js:$(this).attr("href")', 'success' => 'js:function(data) { $.fn.yiiGridView.update("my-grid")}')),
				),

You will notice that we no longer have ‘imageURL’ as this is handled completely by our class extension, which looks to for ‘data-status’ and if set will generate the URL for the appropriate image according to ‘status’.

Great stuff!

Posted in PHP, Yii Framework.


Customising CGridView – Custom CButtonColumn Ajax Buttons

Yii CGridView comes with standard view, edit and delete buttons for standard actions generated by Yii CRUD.  Often however we require more, eg. a button to set the status of a record.  In this post we look at how to quickly and simply add a custom button to cGridview, in this case we will name our new button as ‘status’. The purpose of this button will be to set the status of a record to either 0 or 1 (depending on what it currently is).

First we need to create the required action in our controller, we will call it simply ‘status’.  To begin we add it in our controller rules ‘actions’ array:

array('allow'
      'actions'=>array('view', 'update', 'delete', 'status')),
      'users'=>array('admin'),
),

Next we need to create an action for setting our record status:

public function actionStatus($id)
{
    $model = MyModel::model()->findByPk($id);  // use whatever the correct class name is
    $model->status = ($model->status ==1 ? 0 : 1);
    $model->save();

    return true;
}

Now we are ready to create our custom button.  If you look at a standard Yii generated cGridview, toward the bottom you will see the following:


array(
     'class'=>'CButtonColumn',
),

This we need to extend to include both our standard buttons plus our new custom button, making use of ‘ajax’ in the ‘options’ array:


array(
     'class'=>'CButtonColumn',
     'template' => '{view}{update}{status}{delete}',  //include the standard buttons plus the new status button
     'buttons'=>array
     (
         'status' => array
         (
             'label'=>'status',
             'imageUrl'=>'images/icn/status.png',  // make sure you have an image
             'url'=>'Yii::app()->createUrl("mycontroller/status", array("id"=>$data->id))',
             'options' => array( 'ajax' => array('type' => 'get', 'url'=>'js:$(this).attr("href")', 'success' => 'js:function(data) { $.fn.yiiGridView.update("my-grid")}')),
         ),
     ),
),

Important things to note above:

  1. You need an image for your new button, I have used ‘images/icn/status.png’
  2. For the yiiGridView update() function, make sure that you enter the correct ID for your grid

Simple, huh?

Next we look at displaying a different button image according to whether our record $data->status is 0 or 1:  Customising CGridView – CButtonColumn Image Swap

Posted in PHP, Yii Framework.


Using Class Properties To Minimise Database Queries And Code Execution

In my previous post Customising CGridView – Adding A Select Menu I used two public methods from my Orderstatus class to display a select menu for seaching the ‘Orders Status’ CGridView column and to display status name rather than ID in the column. In that post I used the following in OrdersController to grab a database object to pass to the CHtml listData() method:


$status_obj = Orderstatus::model()->getStatusObj();

This was passed to CHtml listData()to create a select menu like this:


CHtml::listData($status_obj, 'id', 'orders_status_name')

I could have easily used CActiveRecord‘s findAll() method:

CHtml::listData(Orderstatus::model()->findAll(), 'id', 'orders_status_name')

however there is a good reason to use my own method  >>>  To save database queries and prevent unnecessary code execution by using declared properties in the Orderstatus class.

The first step was to declare two properties (these can be public or private) in the Orderstatus class, to be used in the two methods:


    public $status_obj;

    public $status_array;

The first of these is used in the getStatusObj() method:


    public function getStatusObj()
    {
        if(empty($this->status_obj)) {
            $this->status_obj = self::model()->findAll();
        }
        return $this->status_obj;
    }

This method is called in my OrdersController:

public function actionAdmin()

{
    $model=new Orders('search');
    $model->unsetAttributes();  // clear any default values
    if(isset($_GET['Orders']))
        $model->attributes=$_GET['Orders'];

    $status_obj = Orderstatus::model()->getStatusObj(); //fetch our orderstatus object

    $this->render('admin',array(
        'model'=>$model, 'status_obj' => $status_obj, //pass orderstatus object to view
    ));
}

So what is the advantage of this?  Because $status_obj is a property getStatusObj() can be called multiple times when a page is loaded and the database query will be executed only once.  But haven’t we only called it once in the OrdersController?  Thus far, yes, however we will also call it in the next method, getStatusArrayVal() which is called for every row in our CGridView:

    public function getStatusArrayVal($id)
    {
        if(empty($this->status_array)) {
            foreach($this->getStatusObj() as $val) {
                $this->status_array[$val->id] = $val->orders_status_name;
            }
        }
        return $this->status_array[$id];
    }

These are used in CGridView like this:

array('name' => 'orders_status', 'value' => 'Orderstatus::model()->getStatusArrayVal($data->orders_status)', 'filter'=>CHtml::listData($status_obj, 'id', 'orders_status_name')),

So if we have twenty rows of data on each page of our CGridView the database query in the getStatusObj() method is called only once (from our controller), rather than for each row in which the getStatusArrayVal() method is called.  Because we also have $status_array as a declared property the foreach() loop to build the array is also only iterated once per page load rather than each time getStatusArrayVal() is called (every row).

Happy coding!

Posted in PHP, Yii Framework.

Tagged with , , , , , .


Customising CGridView – Adding A Select Menu

In my previous post we looked at adding the CJuiDatepicker to the CGridView column search. Next we will look at the 'orders_status' column, which by default displays a status ID for order status’ I have stored in my 'orderstatus' database table (eg. processing, payment accepted, delivered etc.).  Displaying and searching these by ID is not very meaningful, so we will change this to a select menu showing the name of each status, plus we will change our column to display the status name.  I have two methods in my Orderstatus class, one that grabs my status’ into a database object – this we will use for our select menu, plus another method which matches column ID’s to the appropriate status names.  I will explore these methods in detail in my next post, but for now we will assume that we have what we need by calling those methods.

The first thing we must do is to call the Orderstatus database object in the OrdersController file, then pass this to our view file:


public function actionAdmin()

{
    $model=new Orders('search');
    $model->unsetAttributes();  // clear any default values
    if(isset($_GET['Orders']))
        $model->attributes=$_GET['Orders'];

    $status_obj = Orderstatus::model()->getStatusObj(); //fetch our orderstatus object

    $this->render('admin',array(
        'model'=>$model, 'status_obj' => $status_obj, //pass orderstatus object to view
    ));
}

For our select menu we will use Yii Fameworks‘s CHtml listData() method with $status_obj and to display the status name for each row we will call getStatusArrayVal() which is a method I have created in my Orderstatus class and to which we can pass the ID as $data->orders_status:


array('name' => 'orders_status', 'value' => 'Orderstatus::model()->getStatusArrayVal($data->orders_status)', 'filter'=>CHtml::listData($status_obj, 'id', 'orders_status_name')),

Our completed code:


<?php $this->widget('zii.widgets.grid.CGridView', array(
    'id'=>'orders-grid',
    'dataProvider'=>$model->search(),
    'filter'=>$model,
    'afterAjaxUpdate'=>"function(){jQuery('#date_purchased_search').datepicker({'dateFormat': 'yy-mm-dd'})}",
    'columns'=>array(
        'id',
        'user_name',
        'user_email_address',
        array('name' => 'date_purchased', 'type' => 'raw', 'filter'=>$this->widget('zii.widgets.jui.CJuiDatepicker', array('model'=>$model, 'attribute'=>'date_purchased', 'htmlOptions' => array('id' => 'date_purchased_search'), 'options' => array('dateFormat' => 'yy-mm-dd')), true)),
        'payment_method',
        array('name' => 'orders_status', 'value' => 'Orderstatus::model()->getStatusArrayVal($data->orders_status)', 'filter'=>CHtml::listData($status_obj, 'id', 'orders_status_name')),
        array(
            'class'=>'CButtonColumn',
        ),
    ),
)); ?>

Here is how it looks:

select menu

My 'payment_method' column also displays and is searchable only by ID, so I will do the same for this column. In my next post I will explore the methods that I used above and the advantage of using declared properties to save on database queries and prevent unnecessary code execution.

Posted in Yii Framework.

Tagged with , , , , .


Customising CGridView – CJuiDatepicker

Yii Framework comes with a nice table widget named CGridView for displaying model data. When generating CRUD with Yii the view files for each model will have an admin.php file. This will display selected database table columns that are both searchable and sortable using Ajax.  Developers will quickly find however that the standard text inputs for searching are less than appropriate in many cases, one of these being date columns.

This example will show how to use one of Yii’s standard jQuery widgets, CJuiDatepicker, for searching date columns. The example is from an Orders class from an eCommerce system I developed and we will be looking at the 'date_purchased' column which is a standard MySQL datetime column.

The standard code generated by CRUD in admin.php will look something similar to the following:


<?php $this->widget('zii.widgets.grid.CGridView', array(
    'id'=>'orders-grid',
    'dataProvider'=>$model->search(),
    'filter'=>$model,
    'columns'=>array(
        'id',
        'user_name',
        'user_email_address',
        'date_purchased',
        'payment_method',
        'orders_status',
        array(
            'class'=>'CButtonColumn',
        ),
    ),
)); ?>

The first step is to call the datepicker widget in the 'columns' array by changing the  'date_purchased' column to explicitly set the 'filter' property: (use your mouse and arrow keys if you can’t see the scrollbar for the code):


array('name' => 'date_purchased', 'type' => 'raw', 'filter'=>$this->widget('zii.widgets.jui.CJuiDatepicker', array('model'=>$model, 'attribute'=>'date_purchased', 'htmlOptions' => array('id' => 'date_purchased_search'), 'options' => array('dateFormat' => 'yy-mm-dd')), true)),

It is important to include 'dateformat' in the 'options' array to match your column format.  While MySQL datetime columns include hours, minutes and seconds these are not required.

Also important is to assign an ID, otherwise Yii will auto generate an ID that will be a duplicate of the one used in Advanced Search.

If you test the above you will find that it works, but only for the first request.  Because the search content is loaded via Ajax, we need something more to load the datepicker again after the Ajax request.  CGridView has the 'afterAjaxUpdate' property just for this purpose, which we can use like this:

'afterAjaxUpdate'=>"function(){jQuery('#date_purchased_search').datepicker({'dateFormat': 'yy-mm-dd'})}",

Thus our completed code looks as follows:


<?php $this->widget('zii.widgets.grid.CGridView', array(
    'id'=>'orders-grid',
    'dataProvider'=>$model->search(),
    'filter'=>$model,
    'afterAjaxUpdate'=>"function(){jQuery('#date_purchased_search').datepicker({'dateFormat': 'yy-mm-dd'})}",
    'columns'=>array(
        'id',
        'user_name',
        'user_email_address',
        array('name' => 'date_purchased', 'type' => 'raw', 'filter'=>$this->widget('zii.widgets.jui.CJuiDatepicker', array('model'=>$model, 'attribute'=>'date_purchased', 'htmlOptions' => array('id' => 'date_purchased_search'), 'options' => array('dateFormat' => 'yy-mm-dd')), true)),
        'payment_method',
        'orders_status',
        array(
            'class'=>'CButtonColumn',
        ),
    ),
)); ?>

Here is how it looks:

datepicker

Happy coding!

Posted in Yii Framework.

Tagged with , , , , .