[SNIPPET] Dropdown Date Picker

Hi,

Quite a few people have previously asked on the forum about having dropdown lists for day, month and year when entering dates.

I have come up with a rough solution which works for me. I want to improve my code so please give me your thoughts.

Extend the CHtml helper - I have saved this in /protected/components is this the best place?




class Html extends CHtml

{

        public static function activeDateField($model,$attribute,$htmlOptions=array())

	{

		// SET UP ARRAYS OF OPTIONS FOR DAY, MONTH, YEAR

                $x = 1;

		$dayOptions = array('0'=>' - ');

		while ($x < 31)

		{

			$dayOptions[$x] = $x;

			$x++;

		}


		$monthOptions = array(

			'0' => ' - ',

			'1'=>'January',

			'2'=>'February',

			'3'=>'March',

			'4'=>'April',

			'5'=>'May',

			'6'=>'June',

			'7'=>'July',

			'8'=>'August',

			'9'=>'September',

			'10'=>'October',

			'11'=>'November',

			'12'=>'December',

		);


		$yearOptions = array('0'=>' - ');

		$x = 1901;

		while ($x < 2030)

		{

			$yearOptions[$x] = $x;

			$x++;

		}




		parent::resolveNameID($model,$attribute,$htmlOptions);

		

		if ($model->$attribute != '0000-00-00' && isset($model->$attribute))

		{

			// intval removes leading zero

			$day = intval(date('j',strtotime($model->$attribute)));

			$month = intval(date('m',strtotime($model->$attribute)));

			$year = intval(date('Y',strtotime($model->$attribute)));

		} else

		{

			// DEFAULT TO 0 IF THERE IS NO DATE SET

                        $day = 0;

			$month = 0;

			$year = 0;

		}

		


		$return  = parent::dropDownList($htmlOptions['name'].'[day]', $day,$dayOptions);

		$return .= parent::dropDownList($htmlOptions['name'].'[month]', $month,$monthOptions);

		$return .= parent::dropDownList($htmlOptions['name'].'[year]', $year,$yearOptions);

		return $return;

}



I don’t like the code at the start of this function to get options for day, month and year.

This also lacks some functionality for active input fields - it doesn’t add an ‘error’ class etc.

Extend the CActiveRecord class - The SetAttributes function looks for all date fields (given in an array in the model) and merges the three separate input fields into a single MySQL date string.




class ActiveRecord extends CActiveRecord

{

	/**

	 * Extends setAttributes to handle active date fields

	 *

	 * @param $values

	 * @param $scenario

	 */

	public function setAttributes ($values,$scenario='')

	{

		foreach ($this->dateAttributes() as $dateAttribute)

		{

			if (is_array($values[$dateAttribute]))

			{

				$day = $values[$dateAttribute]['day'];

				$month = $values[$dateAttribute]['month'];

				$year = $values[$dateAttribute]['year'];


				$values[$dateAttribute] = date('Y-m-d',mktime(0,0,0,$month,$day,$year));

			}

		}


		parent::setAttributes($values,$scenario);

	}

}



Usage:

In your model add the following function to specify which fields are dates:




class Passport extends ActiveRecord

{

    ....


        public function dateAttributes()

	{

		return array('expiryDate');

	}


    ....

}



And finally, in your view use Html::activeDateField($model,‘attributeName’) to get date dropdowns.




<div class="simple">

	<? echo CHtml::activeLabelEx($model, 'expiryDate'); ?>

	<? echo Html::activeDateField($model, 'expiryDate'); ?>

</div>



I think, that for the displaying purposes it’s better to create widget rather than extending CHtml.

And the presentation -> mysql solution is really great. Thanks for sharing. I implemented it in the beforeSave method which is not a good idea.

Hi Alex,

That’s great. I use your solution in my project. I added error-class support by adding




                if($model->hasErrors($attribute)){

                   parent::addErrorCss($htmlOptions);

                }


                $return  = parent::dropDownList($htmlOptions['name'].'[day]', $day,$dayOptions);

                $return .= parent::dropDownList($htmlOptions['name'].'[month]', $month,$monthOptions);

                $return .= parent::dropDownList($htmlOptions['name'].'[year]', $year,$yearOptions);

                return $return;



thanks,

~holly~

Can you explain in more details in your snippet usage?

I placed code in components dir named Html.php and made other changes to model and view, but it does not work…


- '); while ($x < 31) { $dayOptions[$x] = $x; $x++; } $monthOptions = array( '0' => ' - ', '1'=>'January', '2'=>'February', '3'=>'March', '4'=>'April', '5'=>'May', '6'=>'June', '7'=>'July', '8'=>'August', '9'=>'September', '10'=>'October', '11'=>'November', '12'=>'December', ); $yearOptions = array('0'=>' - '); $x = 1901; while ($x < 2030) { $yearOptions[$x] = $x; $x++; } parent::resolveNameID($model,$attribute,$htmlOptions); if ($model->$attribute != '0000-00-00' && isset($model->$attribute)) { // intval removes leading zero $day = intval(date('j',strtotime($model->$attribute))); $month = intval(date('m',strtotime($model->$attribute))); $year = intval(date('Y',strtotime($model->$attribute))); } else { // DEFAULT TO 0 IF THERE IS NO DATE SET $day = 0; $month = 0; $year = 0; } $return = parent::dropDownList($htmlOptions['name'].'[day]', $day,$dayOptions); $return .= parent::dropDownList($htmlOptions['name'].'[month]', $month,$monthOptions); $return .= parent::dropDownList($htmlOptions['name'].'[year]', $year,$yearOptions); return $return; }

Fatal error: Class 'Html' not found in C:\wamp\www\ssis\protected\views\klientas\_form.php on line 23

Any news?:slight_smile:

I tried to use the snippet for date of birth field. I have name, username, password, password repeat, email fields before date of birth field. When submitting the form the password repeat field error comes up even when I enter correct values.

My guess is it has got to do something with setattributes function.

Any idea?

What is the error it comes up with? Is your confirmPassword field set as safe. Is it defined in the model?

I have the same problem. Everything with the password field is fine. When I remove snippet, it works again.