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:

var oldLink = null;
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/:

<?php
/**
* 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->setFormTag($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->setFormTag($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:

<html>
  <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:

<?php
class TestController extends AppController {
    var $helpers = array(..., 'DatePicker');
    ....
}

6. Use the Helper in the view files with:
for Popup

<?php echo $datePicker->picker('datefield');?>

or Flat

<?php echo $datePicker->flat('datefield');?>

*** 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!

Tags: , , , , ,


Add to: del.icio.us:Advanced Datepicker helper for CakePHP digg:Advanced Datepicker helper for CakePHP spurl:Advanced Datepicker helper for CakePHP wists:Advanced Datepicker helper for CakePHP simpy:Advanced Datepicker helper for CakePHP newsvine:Advanced Datepicker helper for CakePHP blinklist:Advanced Datepicker helper for CakePHP furl:Advanced Datepicker helper for CakePHP reddit:Advanced Datepicker helper for CakePHP fark:Advanced Datepicker helper for CakePHP blogmarks:Advanced Datepicker helper for CakePHP Y!:Advanced Datepicker helper for CakePHP smarking:Advanced Datepicker helper for CakePHP magnolia:Advanced Datepicker helper for CakePHP segnalo:Advanced Datepicker helper for CakePHP gifttagging:Advanced Datepicker helper for CakePHP

40 Comments to “Advanced Datepicker helper for CakePHP”

  1. PierreNo Gravatar | September 24th, 2007 at 4:52 pm

    Hello,

    There is a small bug in 3. wrt picker(): if there is no $options specified, domId(null) generates a string whereas an associative array is expected.

    A quick fix: replace the default value
    function picker($fieldName, $options = null) {
    by
    function picker($fieldName, $options = array()) {

  2. Nik ChankovNo Gravatar | September 24th, 2007 at 6:13 pm

    Hi Pierre,

    Thanks for your comment.

    You are right! It’s good to stick to Cake’s Coding Style.
    Changing immediately.

  3. CarinaNo Gravatar | October 4th, 2007 at 1:19 pm

    Hi!

    I’m trying out the jscalendar with cakephp. Your manual is very good, and I have the datepicker up and running. But I would really like to have the calendar “flat” underneath text field.(instead of a popup). Any idea how to do this.
    Thanks in advance!!

    Carina

  4. Nik ChankovNo Gravatar | October 4th, 2007 at 9:23 pm

    @Carina - Thanks for your comments!

    About the Flat Calendar option - I just made some amendments which will allow you to make Flat Calendar field. The field which hold the data is hidden, so users wont mess it up. They can only pick from the Calendar and that’s it. Calendar feed the date value from this hidden field so every time it will be selected properly.

    Hope that helps!

  5. DGCNo Gravatar | October 10th, 2007 at 7:19 pm

    I did everything you said here, but it doesn`t work.

    Fatal error: Call to undefined method DatePickerHelper::setFormTag() in cakePHP/app/views/helpers/date_picker.php on line 25

    Thanks…

  6. Nik ChankovNo Gravatar | October 11th, 2007 at 12:48 am

    @DGC Thanks for visiting my page.

    This code is tested and developed on CakePHP verison 1.2, I just check the 1.1 Api and I didnt see such function setFormTag(). Probably this is the problem.

    I would like to hear if this is the case, and thanks, I will add a note in the article that this is applied for v.1.2

    Regards

  7. links for 2007-10-13 « Richard@Home | October 13th, 2007 at 8:18 am

    [...] Advanced Datepicker helper for CakePHP | Dev weblog A plug in date/time picker (tags: cakephp calendar) [...]

  8. silidNo Gravatar | October 22nd, 2007 at 4:57 pm

    I just found this and it is great. Could do with writing up for the bakery.

    How do I pass variables through to the calendar? For example:

    showsTime = ‘true’

    or any of the other variables as seen at:
    http://www.dynarch.com/demos/jscalendar/doc/html/reference.html#node_sec_2.3

  9. Nik ChankovNo Gravatar | October 22nd, 2007 at 5:10 pm

    Thanks silid,
    I am considering to write an article there. BUT I wrote an article based on my Helper for Autocomplete, and so far it’s not publiched and it’s quite a while since then :(
    I will try to publish it anyway.

    About the parameters - you could modify the webroot/js/common.js mentioned in my article. There is the Calendar “setup”. As you can see there are some options already used there. Unfortunately, once they are set there will be available for all callendar instances. So far there is no solution to make this for each instance of the calendar.

    Have a good day!

  10. Alberto D'EsteNo Gravatar | November 14th, 2007 at 12:34 pm

    Hi, I’ve just modified date_picker.php to works with CakePHP version 1.1.x.
    In case you are interested of:
    function picker($fieldName, $options = array()) {
    $this->Html->setFormTag($fieldName);
    $htmlAttributes = array(’class’=>’date’,'type’=>’text’,’size’ => ‘10′, ‘id’=>(isset($options['id'])?$options['id']:$fieldName)); if(isset($options['value'])){ $htmlAttributes['value'] = $options['value'];
    }
    $output = $this->Html->input($fieldName, $htmlAttributes);
    $output .= $this->Html->link($this->Html->image(’../js/jscalendar/img.gif’), ‘#’, array(’class’=>’calendar’, ‘onClick’=>”return showCalendar(’”.$htmlAttributes['id'].”‘, ‘”.((isset($options['format'])?$options['format']:$this->format)).”‘); return false;”), null, false);
    return $output;
    }

    Thanks,
    Alberto

  11. SulNo Gravatar | November 17th, 2007 at 8:28 pm

    Hi Nik,
    I have a question, I probe the code on Firefox and works fine, but when it open the calendar on IE 7, appears in another position respect than the calendar icon, why?
    Thanks!

  12. DawnNo Gravatar | November 28th, 2007 at 12:21 am

    Hello-

    I am trying to locate the .js files: calendar-en.js and calendar.js.

    Thank you.

  13. Nik ChankovNo Gravatar | November 28th, 2007 at 1:41 pm

    @Alberto D’Este - Thanks, this code would help somebody.

    @Sul - I am using such helper in a project and I checked it with IE as well and there is no problem like you mention. I think that this is a problem of CSS rather than Calendar problem. But this is just a guess.

    @Dawn - Calendar JS is not my code :) I wish to be but it is not. so you can download the calendar from it’s official web site http://www.dynarch.com/projects/calendar/
    The download link is in the right column of the page. Hope that helps.

  14. JoeNo Gravatar | December 27th, 2007 at 10:16 pm

    How do you change the default position of the calendar from below the field to the right of the field? I’ve tried to change it in common.js, from “Br” to “R”, which should align it to the right of the field according to the jscalendar reference manual, but nothing happens. Thanks for your taking the time to do this. It is a great help and works great otherwise!

  15. Nik ChankovNo Gravatar | December 28th, 2007 at 1:16 pm

    @Joe - I am glad that helper helps somebody :)
    About the positioning I made a test it’s working you should remember that there are 2 letters defining the position:
    first one is the vertical positioning and the possible variants are: T=Top,C=Center,B=Bottom
    The second one is the horizontal pozition and the possible variants are: L=Left,C=Center,R=Right
    so if you want to align the calendar popup to center right you should replace BR with CR and this will do the job.

    Hope this make sense!

  16. BuzzNo Gravatar | January 21st, 2008 at 5:16 am

    Hi,
    Thanks for the code.
    When I use it from an Add View, the calendar displays as expected. However on an Edit View where a date value exists (and is displayed in the input box) The calendar displays without any of the graphics, just the numbers ‘floating’ transparently where the finished calender should be.
    Any thoughts?

  17. Nik ChankovNo Gravatar | January 21st, 2008 at 10:23 am

    Hi Buzz, for me this sounds like missing CSS. Another reason is that other CSS overrule the calendar one.

  18. fUGLYNo Gravatar | January 24th, 2008 at 3:30 pm

    Thanks! Works like a charm!

  19. ZeEightNo Gravatar | January 31st, 2008 at 8:18 pm

    Thanks! Saved me a good bit of time on my current project.

  20. Abdullah Zainul AbidinNo Gravatar | February 13th, 2008 at 10:24 am

    Thank you. I find it very useful. I’ve modified the helper a bit so that you wouldn’t need to add the links in every view that uses this helper. You can check it out on my website. Thank you once again for the great work.

  21. Nik ChankovNo Gravatar | February 13th, 2008 at 11:33 am

    Hi Abdullah,
    I am glad that you found this helpful. :)
    Adding the lines for JavaScript includes doesn’t need to be included in every action which you had.

    The idea behind is that you can add the JS code in your layout, so once you do this the only code which you need to add in your helpers is the $datePicker->picker() function.

    I think your addition is good but only if you have one date field per screen, otherwise you will load the JavaScript as many times as you have Date Field, which should work, but is not the best practice ;)

  22. Abdullah Zainul AbidinNo Gravatar | February 16th, 2008 at 1:28 am

    I’m sorry but I’m a bit confused now. When I look at the source code of the page rendered using my modified helper, the javascript include lines are rendered only once but way on top of the page even when I have two or three date fields which use the datepicker. In fact, so far on top of the page that it actually renders even before the doctype (Which is probably not good either). I’ll see what I can do to fix it. But when you said load the javascript many times you meant there will be many times the javascript include lines are rendered right? or does cakephp actually loads them many times on the server? Thanks for the heads up though.

  23. Nik ChankovNo Gravatar | February 16th, 2008 at 8:55 pm

    Abdullah,
    It is my fault, I didn’t understood your idea. :) I didn’t saw that you using beforeRender(). I thought you just print the script part just right before printing the input tag.

    About printing the code above the doctype - I also think it’s not the best way.

    Anyway, important is that this helper do what it supposed to do :)

  24. Abdullah Zainul AbidinNo Gravatar | February 17th, 2008 at 11:15 am

    Hi Nik,

    Just like to mention that I solved the output before docman problem. Now it’s output is in the header part proper. Just in case you’re interested you can check out the link. Thank you again for this great helper.

  25. jarrettNo Gravatar | February 18th, 2008 at 12:40 pm

    Really a brilliant piece of code integration, thanx alot..You made my lunch time longer….lol…:-D
    one small Q, Im trying to add the date and time, i pass the helper the params $datePicker->picker(’Sitepage/publish_date’, array(’format’=>’%Y-%m-%d [%W] %H:%M’)…but it doesnt show the calendar with the time picker:(

  26. jarrettNo Gravatar | February 18th, 2008 at 1:06 pm

    Answer to my own Q, here we go for cakephp1.1:

    function picker($fieldName, $options = null, $showsTime = null) {
    $this->Html->setFormTag($fieldName);
    $htmlAttributes = array(’class’=>’date’,'type’=>’text’,’size’ => ‘10′, ‘id’=>(isset($options['id'])?$options['id']:$fieldName));
    if(isset($options['value'])){
    $htmlAttributes['value'] = $options['value'];
    }
    $output = $this->Html->input($fieldName, $htmlAttributes);
    //var_dump($options);
    $output .= $this->Html->link($this->Html->image(’../js/jscalendar-1.0/img.gif’), ‘#’, array(’class’=>’calendar’, ‘onClick’=>”return showCalendar(’”.$htmlAttributes['id'].”‘, ‘”.((isset($options['format'])?$options['format']:$this->format)).”‘, ‘”.$showsTime.”‘); return false;”), null, false);
    return $output;
    }
    Then U simple call it with:
    echo $datePicker->picker(’Sitepage/publish_date’, array(’format’=>’%Y-%m-%d %H:%M’), ‘yes’);

  27. SteveHNo Gravatar | March 13th, 2008 at 7:25 am

    Hi Nik,

    Got you very nice code working in my CakePHP 1.1 test app, using jarret’s fix. The only problem I’m having is that the popup calendar is being drawn for the full width of the web page! Do you have any ideas what the problem might be?

    Thanks very much,
    Steve

  28. Nik ChankovNo Gravatar | March 13th, 2008 at 11:31 am

    Hi SteveH,

    Just to clarify the issue with Alberto’s approach:
    the “problem” is that he miss to add the function _setup(); which actually extract the format from the Configure class. I take a look and in CakePHP1.1.x.x there is Configure class and I think the following code should work as a dream:

    function picker($fieldName, $options = array()) {
       $this->_setup();
       $this->Html->setFormTag($fieldName);
       $htmlAttributes =   array(class’=>’date’,'type’=>’text’,’size’ => ‘10?, ‘id’=>(isset($options[’id’])?$options[’id’]:$fieldName)); if(isset($options[’value’])){ $htmlAttributes[’value’] = $options[’value’];
    }
       $output = $this->Html->input($fieldName, $htmlAttributes);
    $output .= $this->Html->link($this->Html->image(’../js/jscalendar/img.gif’), ‘#’, array(’class’=>’calendar’, ‘onClick’=>”return showCalendar(’”.$htmlAttributes[’id’].”‘, ‘”.((isset($options[’format’])?$options[’format’]:$this->format)).”‘); return false;”), null, false);
    return $output;
    }

    About calendar being drawn full width of the page: it’s a css issue. CakePHP define table to be with width:100% which automatically make table of the popup 100%. The solution is to add following css in your css files:

    .calendar table {width:auto;}

    this will fix the issue. ;)

  29. SteveHNo Gravatar | March 13th, 2008 at 3:16 pm

    Nik,

    Your suggestions worked a treat!

    Blagodarya,

    Steve

  30. JenRNo Gravatar | April 4th, 2008 at 9:46 pm

    Thanks for contributing this code to the community Nik! I love calendar date pickers!

    I’m a cake noobie and need some help. I used the code you provided for CakePHP1.1.x.x, but I get an error:
    Notice: Undefined offset: 1 in /cake/libs/view/helpers/html.php on line 947

    Aside from that error showing up, the calendar picker appears to be working. Should I still be using this code in my edit page:
    picker(’datefield’);?>

    Any ideas? Your help is greatly appreciated! Thanks!
    Jen

  31. Nik ChankovNo Gravatar | April 5th, 2008 at 8:55 pm

    @JenR - can you send me some code, because as far I can see the function which causing that Notice is setFormTag() and there only the model and field was set.

    I think the problem comes, because you using field which is not exist in database.

  32. JenRNo Gravatar | April 7th, 2008 at 5:50 pm

    Oh duh! I didn’t put my own model name and db field in, it works now.

    picker(’Calendar/start_date’);?>

    Thanks for pointing me in the right direction!!

  33. hashemNo Gravatar | April 22nd, 2008 at 11:26 pm

    im getting an error saying that calender is not defined in my error console.
    after creating everything as described here, i get the input field, but at the end of it theres just a little blue square and when i click it, nothing happens.

  34. Nik ChankovNo Gravatar | April 23rd, 2008 at 10:25 am

    hashem, I think you didn’t put the code for calendar js in the right place, or you didn’t get the origilan calendar script from http://www.dynarch.com/projects/calendar/. the js code shown here is just an extension how to connect field and js, but the core js is different. About blue square - probably the icon is missing in your wwwroot/js/jscalendar/ folder.

    Hope this helps

  35. hashemNo Gravatar | April 23rd, 2008 at 5:02 pm

    well i did get my code from that site and i put it in the webroot/js folder as mentioned above, but it still didnt work.
    and what do you mean by “probably the icon is missing in your wwwroot/js/jscalendar/ folder.”

    just so you know, im just a co-op student working on the portal for the company that hired me, as part of my co-op, so im no programming genious or anything.

  36. Nik ChankovNo Gravatar | April 24th, 2008 at 10:26 am

    Hashem, if you be able to show me the site where you experienced this problem probably I could give you a hint. Other than that - I cannot help much.

  37. hashemNo Gravatar | April 24th, 2008 at 10:58 pm

    well i would but i cant, sorry. thanks anyway, ill let you know if i can figure out something specific

  38. NikoNo Gravatar | May 13th, 2008 at 7:42 pm

    Hi Nik !
    It’s really a great work,
    but I need your help
    th epicker works great saving in my databese correctly, but cause a problem to the debugger so that it couldn’t do a redirect
    it says:
    Warning: implode() [function.implode]: Bad arguments. in /var/cake/libs/debugger.php on line 497
    where can it be the problem?

  39. MarcNo Gravatar | June 19th, 2008 at 11:32 am

    Hi,

    Great work ! Works fine under 1.2. A bit hard to adapt to 1.1 for a noobie like me.
    Just a question : does somebody notice that the icon beside the date field is on a new line ? I looked at the html source page, and I think it must come from a CSS attribute. But, which one ? I use the cake.generic.css default file.

  40. GiorgioNo Gravatar | July 13th, 2008 at 4:22 am

    i want to thanks who posted the cakephp 1.1.x version, without i would spend a lot of time trying to change then 1.2 version. Thanks a lot to everybody and a special thanks to Nick for his tip

Leave a Comment

*
To prove that you're not a bot, enter this code
Anti-Spam Image