This is a really old post. I’ve wrote a new one which you can find it here
I am using this part of code from long time ago, but it wasn’t in a separate helper. It was part from one not very good written Auth component/helper mash up which I already not using. I fact the default Date input of CakePHP Framework is ugly and not user friendly at all. The drop-downs are good because the format is clear and users cannot mess it, but if you thinking for the user convenience, then you should know that it’s 3 times easier just to type the date in a field or to pick it from a Calendar pop up than selecting from 3 drop-downs.
In that helper I am using JSCalendar. The benefits of it are:
- Flexible Date and Time formats
- Full Theme support
- Translated in many languages
- Fast and very easy to setup
For additional information: Full Demo and Calendar Documentation Pages.
What you should do in order to run it in you CakePHP project.
Update 04 Oct 2007 – From now on the Helper includes 2 options
- Regular Po-pup Calendar – simple text field with link which will pop-up the calendar helper
- Flat Calendar – It’s using hidden field to store date more convenient if you don’t want to allow your users to mess up with data input in that field
Update 11 Oct 2007 – The code is tested and working with CakePHP v1.2! Previous versions of the Framework throws an error.
1. Download a copy of the calendar and unzip it in your {cakeproject}/app/webroot/js directory. It’s possible to strip down the unnecessary files like examples, docs etc.
2. Create a file with name common.js with the following content:
function setActiveStyleSheet(link, title) {
var i, a, main;
for(i=0; (a = document.getElementsByTagName("link")[i]); i++) {
if(a.getAttribute("rel").indexOf("style") != -1 && a.getAttribute("title")) {
a.disabled = true;
if(a.getAttribute("title") == title) a.disabled = false;
}
}
if (oldLink) oldLink.style.fontWeight = 'normal';
oldLink = link;
link.style.fontWeight = 'bold';
return false;
}
// This function gets called when the end-user clicks on some date.
function selected(cal, date) {
cal.sel.value = date; // just update the date in the input field.
if (cal.dateClicked && (cal.sel.id == "sel1" || cal.sel.id == "sel3"))
// if we add this call we close the calendar on single-click.
// just to exemplify both cases, we are using this only for the 1st
// and the 3rd field, while 2nd and 4th will still require double-click.
cal.callCloseHandler();
}
// And this gets called when the end-user clicks on the _selected_ date,
// or clicks on the "Close" button. It just hides the calendar without
// destroying it.
function closeHandler(cal) {
cal.hide(); // hide the calendar
// cal.destroy();
_dynarch_popupCalendar = null;
}
// This function shows the calendar under the element having the given id.
// It takes care of catching "mousedown" signals on document and hiding the
// calendar if the click was outside.
function showCalendar(id, format, showsTime, showsOtherMonths) {
var el = document.getElementById(id);
if (_dynarch_popupCalendar != null) {
// we already have some calendar created
_dynarch_popupCalendar.hide(); // so we hide it first.
} else {
// first-time call, create the calendar.
var cal = new Calendar(1, null, selected, closeHandler);
// uncomment the following line to hide the week numbers
// cal.weekNumbers = false;
if (typeof showsTime == "string") {
cal.showsTime = true;
cal.time24 = (showsTime == "24");
}
if (showsOtherMonths) {
cal.showsOtherMonths = true;
}
_dynarch_popupCalendar = cal; // remember it in the global var
cal.setRange(1900, 2070); // min/max year allowed.
cal.create();
}
_dynarch_popupCalendar.setDateFormat(format); // set the specified date format
_dynarch_popupCalendar.parseDate(el.value); // try to parse the text in field
_dynarch_popupCalendar.sel = el; // inform it what input field we use
// the reference element that we pass to showAtElement is the button that
// triggers the calendar. In this example we align the calendar bottom-right
// to the button.
_dynarch_popupCalendar.showAtElement(el.nextSibling, "Br"); // show the calendar
return false;
}
var MINUTE = 60 * 1000;
var HOUR = 60 * MINUTE;
var DAY = 24 * HOUR;
var WEEK = 7 * DAY;
// If this handler returns true then the "date" given as
// parameter will be disabled. In this example we enable
// only days within a range of 10 days from the current
// date.
// You can use the functions date.getFullYear() -- returns the year
// as 4 digit number, date.getMonth() -- returns the month as 0..11,
// and date.getDate() -- returns the date of the month as 1..31, to
// make heavy calculations here. However, beware that this function
// should be very fast, as it is called for each day in a month when
// the calendar is (re)constructed.
function isDisabled(date) {
var today = new Date();
return (Math.abs(date.getTime() - today.getTime()) / DAY) > 10;
}
function showFlatCalendar(id, holder, format, selectedHandler) {
var prnt = document.getElementById(holder);
var element = document.getElementById(id);
// construct a calendar giving only the "selected" handler.
var cal = new Calendar(0, null, selectedHandler);
// hide week numbers
cal.setDateFormat(format);
cal.weekNumbers = false;
cal.create(prnt);
cal.parseDate(element.value);
cal.show();
}
Put this file in {cakeproject}/app/webroot/js/. Basicaly this is a code snipet which I found in Calendar documentation.
3. Get this Helper and put it into date_picker.php file under your Helpers directory – {cakeproject}/app/view/helpers/:
/**
* Autocomplete Helper
*
* @author Nik Chankov
* @website http://nik.chankov.net
* @version 1.0.0
*/
uses('view/helpers/Form');
class DatePickerHelper extends FormHelper {
var $format = '%Y-%m-%d';
/**
*Setup the format if exist in Configure class
*/
function _setup(){
$format = Configure::read('DatePicker.format');
if($format != null){
$this->format = $format;
}
}
/**
* The Main Function - picker
*
* @param string $field Name of the database field. Possible usage with Model.
* @param array $options Optional Array. Options are the same as in the usual text input field.
*/
function picker($fieldName, $options = array()) {
$this->_setup();
$this->setEntity($fieldName);
$htmlAttributes = $this->domId($options);
$divOptions['class'] = 'date';
$options['type'] = 'text';
$options['div']['class'] = 'date';
$options['after'] = $this->Html->link($this->Html->image('../js/jscalendar/img.gif'), '#', array('onClick'=>"return showCalendar('".$htmlAttributes['id']."', '".$this->format."'); return false;"), null, false);
$output = $this->input($fieldName, $options);
return $output;
}
function flat($fieldName, $options = array()){
$this->_setup();
$this->setEntity($fieldName);
$htmlAttributes = $this->domId($options);
$divOptions['class'] = 'date';
$options['type'] = 'hidden';
$options['div']['class'] = 'date';
$hoder = '<div id="'.$htmlAttributes['id'].'_cal'.'"></div><script type="text/javascript">showFlatCalendar("'.$htmlAttributes['id'].'", "'.$htmlAttributes['id'].'_cal'.'", "'.$this->format.'", function(cal, date){document.getElementById(\''.$htmlAttributes['id'].''.'\').value = date});</script>';
$output = $this->input($fieldName, $options).$hoder;
return $output;
}
}
?>
In fact as you can see it’s usinf a textbox from form helper with added at the end calendar image for date picker.
4. Add the folowing code in your layout file between head tags:
<head>
...
<!-- Javascript includes-->
<?php echo $javascript->link('jscalendar/calendar.js'); ?>
<?php echo $javascript->link('jscalendar/lang/calendar-en.js'); ?>
<?php echo $javascript->link('common.js'); ?>
<!-- CSS Theme-->
<?php echo $html->css('../js/jscalendar/skins/aqua/theme');?>
...
</head>
....
</html>
or in every view which using that helper. Depends on your taste.
5. Add this helper in $helpers variable under the controller:
6. Use the Helper in the view files with:
for Popup
or Flat
*** Update *** Abdullah Zainul Abidin extended this helper so now it’s not required to touch layout by adding the scripts and css required. Now they are called automatically in the header. Check it out
Happy Baking!
Hi, I found the helper very useful.Thanks for such a nice bunch of code.In my project I need to restrict user to choose date between a “higher date” and a “lower” date.How can I achieve that using this helper
Hi Anand,
I haven’t used this functionality, but I would suggest you to take a look on JSCalendar site which is mentioned in the post.
Thanks for the article. I seem to be having a similar problem to someone else however I am using cakephp 1.2 (not 1.1). Comes up with error “Warning (512): Method DatePickerHelper::setFormTag does not exist [CORE/cake/libs/view/helper.php, line 148]”
Chris, see the comment of Johnny Hughes above, it shows the right approach, I will change in the code of the post too.
Thanks a lot Nik, great article. Everything working fine in CakePHP 1.2.
@Anand
I doubt this is possible as a setting. Will get back to you if I find it.
I’m getting a JS error about _dynarch_popupCalendar, so loading the calendar is failing.
What is it anyway? and what can I do?
By the way, I’m using JSCal2-1.7
This helper still works great even with CakePHP 1.3. Just need to pay attention to two very important things: You MUST use JSCalendar version1 NOT 2. Otherwise you will just get a lot of errors. Secondly, if using CakePHP1.3 you must disable escaping of the image in the date_picker.php helper by changing this line: $options[‘after’] = $this->Html->link($this->Html->image(‘../js/jscalendar/cal.gif’), ‘#’, array(‘escape’ => false,’onClick’=>”return showCalendar(‘”.$htmlAttributes[‘id’].”‘, ‘”.$this->format.”‘); return false;”), null, false);
I adding : Advanced Datepicker helper for CakePHP. . Error:
[php]
Debugger::handleError() – CORE\cake\libs\debugger.php, line 306
Helper::call__() – CORE\cake\libs\view\helper.php, line 154
Overloadable::__call() – CORE\cake\libs\overloadable_php5.php, line 50
DatePickerHelper::setFormTag() – [internal], line ??
DatePickerHelper::picker() – APP\views\helpers\date_picker.php, line 51
include – APP\views\blogs\add_posts.ctp, line 211
View::_render() – CORE\cake\libs\view\view.php, line 723
DebugView::_render() – APP\plugins\debug_kit\views\debug.php, line 43
View::render() – CORE\cake\libs\view\view.php, line 419
DebugView::render() – APP\plugins\debug_kit\views\debug.php, line 85
Controller::render() – CORE\cake\libs\controller\controller.php, line 913
Dispatcher::_invoke() – CORE\cake\dispatcher.php, line 207
Dispatcher::dispatch() – CORE\cake\dispatcher.php, line 171
[main] – APP\webroot\index.php, line 83
[/php]
———-
Can you send to me demo : Advanced Datepicker helper for CakePHP.
-Email: leeseawuyhs@yahoo.com
Thanks.
Thanks Yves Jaques for the tip, that was a nice cure for my current headache….
If u need time
pls replace
$options[‘after’] = $this->Html->image(‘calendar.png’, array(‘id’ => $fieldName.’-trigger’)).’ Calendar.setup({trigger: “‘.$fieldName.’-trigger”, inputField: “‘.$htmlAttributes[‘id’].'”, dateFormat: “‘. $this->format.'”, showTime: 12 }); ‘;
I can’t save db with this format ( %y-%m-%d %H:%M) into mysql. pls tell me! Thanks
Hi, it’s really old post, I haven’t used this method for ages 🙂 Try with jQuery
Thank you Nik Chankov, i found jQuery calender