After a lot of research, everything I found on adding date range searching to a CGridView advanced search form seemed to involve adding two new public variables (e.g. $date_from, $date_to), 'safe' rules for the new variables, and a rather chunky if/elseif/else check in the search() method. This probably isn't a hassle for most, but because many of the tables in my database contain two or three and sometimes four date columns (e.g. date_created, date_modified, date_deleted etc.), it meant I was having to add up to eight public variables, the corresponding safe rules, and modifying the search() criteria for each date attribute. So, I set about creating a better way and I thought I'd share my work with the community.
To be able to add date range searching functionality to your advanced search forms (not grid view filters sorry, I'm still working on that!) without having to add public attributes, 'safe' rules, and chunky search conditions for each date attribute.
Create a file called EDateRangeSearchBehavior.php. Copy the following code in to the file and save it in to your components/behaviors folder.
/** * This model behavior builds a date range search condition. */ class EDateRangeSearchBehavior extends CActiveRecordBehavior { /** * @param the default 'from' date when nothing is entered. */ public $dateFromDefault = '1900-01-01'; /** * @param the default 'to' date when nothing is entered. */ public $dateToDefault = '2099-12-31'; /* * Date range search criteria * public $attribute name of the date attribute * public $value value of the date attribute * @return instance of CDbCriteria for the model's search() method */ public function dateRangeSearchCriteria($attribute, $value) { // Create a new db criteria instance $criteria = new CDbCriteria; // Check if attribute value is an array if (is_array($value)) { // Check if either key in the array has a value if (!empty($value[0]) || !empty($value[1])) { // Set the date 'from' variable to the first value in the array $dateFrom = $value[0]; if (empty($dateFrom)) { // Set the 'from' date to the default $dateFrom = $this->dateFromDefault; } // Set the date 'to' variable to the second value in the array $dateTo = $value[1]; if (empty($dateTo)) { // Set the 'to' date to the default $dateTo = $this->dateToDefault; } // Check if the 'from' date is greater than the 'to' date if ($dateFrom > $dateTo) { // Swap the dates around list($dateFrom, $dateTo) = array($dateTo, $dateFrom); } // Add a BETWEEN condition to the search criteria $criteria->addBetweenCondition($attribute, $dateFrom, $dateTo); } else { // The value array is empty so set it to an empty string $value = ''; // Add a compare condition to the search criteria $criteria->compare($attribute, $value, true); } } else { // Add a standard compare condition to the search criteria $criteria->compare($attribute, $value, true); } // Return the search criteria to merge with the model's search() method return $criteria; } }
Then, attach the behavior by adding it to the behaviors() array in your model
/** * Model behaviors */ public function behaviors() { return array( 'dateRangeSearch'=>array( 'class'=>'application.components.behaviors.EDateRangeSearchBehavior', ), ); }
Next, modify the search() method in the same model and replace the $criteria->compare() line for every date attribute you want to range search.
public function search() { $criteria = new CDbCriteria; $criteria->compare('id', $this->id); ... // Replace the standard date compare line... $criteria->compare('date_created', $this->date_created, true); // with the new mergeWith line... $criteria->mergeWith($this->dateRangeSearchCriteria('date_created', $this->date_created)); ... return new CActiveDataProvider($this, array( 'criteria'=>$criteria, )); }
And finally in your view, add the following code to your _search form to render your 'date from' and 'date to' inputs.
// Date range search inputs $attribute = 'date_created'; for ($i = 0; $i <= 1; $i++) { echo ($i == 0 ? Yii::t('main', 'From:') : Yii::t('main', 'To:')); $this->widget('zii.widgets.jui.CJuiDatePicker', array( 'id'=>CHtml::activeId($model, $attribute.'_'.$i), 'model'=>$model, 'attribute'=>$attribute."[$i]", )); }
I hope this works well for you. In my case, I actually use this for datetime/timestamp fields, and with some simple modifications (i.e. add the time to the $dateFromDefault and $dateToDefault values in the behavior, and change the view widget to a DateTimePicker) you can adapt it to your needs too. Also, if you have any enhancements, I'd love to hear.
Total 2 comments
Thanks for the post! Also just remember to set filter to false in gridview since it'll be an array not value, ( ie 'filter'=>false for the CGridView column)
I'm more interested in the grid "version" filter, but as you said you're working on that. I'll wait to check you solution :)
Leave a comment
Please login to leave your comment.