JavaScript Editor Javascript source editor     Web programming 

Team LiB
Previous Section Next Section

Creating a Calendar Library

Because dates are ubiquitous in Web interfaces, and because working with dates is often comparatively complicated, let's look at creating a class library to automate some of the work that dates can present. Along the way, we will revisit some of the techniques we have already covered.

The simple date_pulldown library created in this instance will consist of three separate select elements: one for day of the month, one for month, and one for year. When a user submits a page, the script will verify the form input. If there is a problem with the input, we will refresh the page with the user's input still in place. This is very easy to accomplish with text boxes, but is more of a chore with pull-down menus.

Pages that display information pulled from a database present a similar problem. Data can be entered straight into the value attributes of text type input elements. Dates will need to be split into month, day, and year values, and then the correct option elements selected.

The date_pulldown class aims to make date pull-downs sticky (so that they will remember settings from page to page) and easy to set. In order to create our class, we first need to declare it and create a constructor.

By the Way

A constructor is a function that exists within a class, and which is automatically called when a new instance of the class is created.

We can also declare some class properties. We will step through Listing 23.6; this first snippet shows the beginning of the class.

Listing 23.6. Creating a Calendar Library
 1: class date_pulldown {
 2:     var $name;
 3:     var $timestamp = -1;
 4:     var $months = array("Jan", "Feb", "Mar", "Apr", "May", "Jun",
 5:           "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
 6:     var $yearstart = -1;
 7:     var $yearend = -1;
 9:   function date_pulldown($name) {
10:      $this->name = $name;
11:  }

We first declare the $name property, on line 2. This will be used to name the HTML select elements. The $timestamp property, defined on line 3, will hold a Unix timestamp. The $months array property, defined on lines 45, contains the strings we will display in our month pull-down. The $yearstart and $yearend properties (lines 6 and 7) are both set to 1, pending initialization. They will eventually hold the first and last years of the range that will be presented in the year pull-down.

The constructor is very simple. It accepts a string, which we assign to the $name property. Now that we have the basis of our class, we need a set of methods by which the client code can set the date. Listing 23.6 continues as follows.

12:    function setDate_global( ) {
13:        if (!$this->setDate_array($GLOBALS[$this->name])) {
14:            return $this->setDate_timestamp(time());
15:        }
16:        return true;
17:    }
19:    function setDate_timestamp($time) {
20:        $this->timestamp = $time;
21:        return true;
22:    }
24:    function setDate_array($inputdate) {
25:        if (is_array($inputdate) &&
26:            isset($inputdate['mon']) &&
27:            isset($inputdate['mday']) &&
28:            isset($inputdate['year'])) {
30:            $this->timestamp = mktime(11, 59, 59,
31:                $inputdate['mon'], $inputdate['mday'], $inputdate['year']);
32:            return true;
33:        }
34:        return false;
35:    }

Of the methods shown here, setDate_timestamp() is the simplest (lines 1922). It requires a Unix timestamp, which it assigns to the $timestamp property. But let's not forget the others.

The setDate_array() method (lines 2435) expects an associative array with at least three keys: mon, mday, and year. These keys will have data values in the same format as in the array returned by getdate(). This means that setDate_array() will accept a hand-built array such as

array('mday'=> 20, 'mon'=>9, 'year' => 2003);

or the result of a call to geTDate():


It is no accident that the pull-downs we will build later will be constructed to produce an array containing mon, mday, and year keys. The method uses the mktime() function to construct a timestamp, which is then assigned to the $timestamp variable.

The setDate_global() method (lines 1217) is called by default. It attempts to find a global variable with the same name as the object's $name property. This is passed to setDate_array(). If this method discovers a global variable of the right structure, it uses that variable to create the $timestamp variable. Otherwise, the current date is used.

The ranges for days and months are fixed, but years are a different matter. As Listing 23.6 continues, we create a few methods to allow the client coder to set her own range of years (although we also provide default behavior).

36:    function setYearStart($year) {
37:        $this->yearstart = $year;
38:    }
40:    function setYearEnd($year) {
41:        $this->yearend = $year;
42:    }
44:    function getYearStart() {
45:        if ($this->yearstart < 0) {
46:            $nowarray = getdate(time());
47:            $this->yearstart = $nowarray[year]-5;
48:        }
50:        return $this->yearstart;
51:    }
53:    function getYearEnd() {
54:        if ($this->yearend < 0) {
55:            $nowarray = getdate(time());
56:            $this->yearend = $nowarray[year]+5;
57:        }
58:        return $this->yearend;
59:    }

The setYearStart() and setYearEnd() methods are straightforward (lines 3643), in that a year is directly assigned to the appropriate property. The getYearStart() method tests whether the $yearstart property has been set, and if it has not been set, getYearStart() assigns a $yearstart value five years before the current year. The getYearEnd() method performs a similar operation.

We're now ready to create the business end of the class.

60:    function output() {
61:        if ($this->timestamp < 0) {
62:            $this->setDate_global();
63:        }
64:        $datearray = getdate($this->timestamp);
65:        $out  = $this->day_select($this->name, $datearray);
66:        $out .= $this->month_select($this->name, $datearray);
67:        $out .= $this->year_select($this->name, $datearray);
68:        return $out;
69:    }
71:    function day_select($fieldname, $datearray) {
72:        $out = "<select name=\"$fieldname"."[mday]\">\n";
73:        for ($x=1; $x<=31; $x++) {
74:            $out .= "<option value=\"$x\"".($datearray['mday']==($x)
75:                ?" SELECTED":"").">".sprintf("%02d", $x ) ."\n";
76:        }
77:        $out .= "</select>\n";
78:        return $out;
79:    }
81:    function month_select($fieldname, $datearray) {
82:        $out = "<select name=\"$fieldname"."[mon]\">\n";
83:        for ($x = 1; $x <= 12; $x++) {
84:            $out .= "<option value=\"".($x)."\"".($datearray['mon']==($x)
85:                ?" SELECTED":"")."> ".$this->months[$x-1]."\n";
86:        }
87:        $out .= "</select>\n";
88:        return $out;
89:    }
91:    function year_select($fieldname, $datearray) {
92:        $out = "<select name=\"$fieldname"."[year]\">";
93:        $start = $this->getYearStart();
94:        $end = $this->getYearEnd();
95:        for ($x= $start; $x < $end; $x++) {
96:            $out .= "<option value=\"$x\"".($datearray['year']==($x)
97:                ?" SELECTED":"").">$x\n";
98:        }
99:        $out .= "</select>\n";
100:       return $out;
101:   }
102: }

The output() method orchestrates most of this code (lines 6069). It first checks the $timestamp property, and unless one of the setDate methods has been called, the value of $timestamp will be set to 1 and setDate_global() will be called by default. The timestamp is then passed to the getdate() function to construct a date array, and a method is called to produce each pull-down.

The day_select() method (lines 7179)simply constructs an HTML select element with an option element for each of the 31 possible days in a month. The object's current date is stored in the $datearray argument variable, which is used during the construction of the element to set the selected attribute of the relevant option element. The sprintf() function formats the day number, adding a leading zero to days 19. The month_select() and year_select() methods (lines 81101) use similar logic to construct the month and year pull-downs.

Why did we break down the output code into four methods, rather than simply creating one block of code? When we build a class, we have two kinds of programmer in mind: one who will want to instantiate a date_pulldown object, and the one who will want to subclass the date_pulldown class to refine its functionality.

For the former, we want to provide a simple and clear interface to the class's functionality. The programmer can then instantiate an object, set its date, and call the output() method. For the latter, we want to make it easy to change discrete elements of the class's functionality. By putting all the output code into one method, we would force a child class that needed to tweak output to reproduce a lot of code that is perfectly usable. By breaking this code into discrete methods, we allow for subclasses that can change limited aspects of functionality without disturbing the whole. If a child class needs to represent the year pull-down as two radio buttons, for example, the coder can simply override the year_select() method.

Listing 23.7 contains some code that calls the library class. Before you try to execute this code, take the code from Listing 23.6, surround it with PHP opening and closing tags, and save into a file called date_pulldown.class.php. Place this file in the document root of your Web server, because Listing 23.7 uses it and it had better be there!

Listing 23.7. Using the date_pulldown Class
1:  <html>
2:  <head>
3:  <title>Using the date_pulldown Class</title>
4:  </head>
5:  <?php
6:  include("date_pulldown.class.php");
7:  $date1 = new date_pulldown("fromdate");
8:  $date2 = new date_pulldown("todate");
9:  $date3 = new date_pulldown("foundingdate");
10: $date3->setYearStart("1972");
11: if (empty($foundingdate)) {
12:    $date3->setDate_array(array('mday'=>26, 'mon'=>4, 'year'=>1984));
13: }
14: ?>
15: <body>
16: <form>
17: <P><strong>From:</strong><br>
18: <?php echo $date1->output(); ?>
19: <P><strong>To:</strong><br>
20: <?php echo $date2->output(); ?>
21: <P><strong>Company Founded:</strong><br>
22: <?php echo $date3->output(); ?>
23: <P><input type="submit" name="submit" value="submit"></p>
24: </form>
25: </body>
26: </html>

On line 6, we include the date_pulldown.class.php; once we have included the class file, we can use all of its methods. We use the class's default behavior for all the pull-downs, apart from "foundingdate". For this particular object, we override the default year start, setting it to 1972 on line 10. On line 12, we assign this pull-down an arbitrary date that will be displayed until the form is submitted (see Figure 23.6).

Figure 23.6. The pull-downs generated by the date_pulldown class.

By the Way

This is only the front end of a form, with no action or method; you need to supply your own action or method in order for this to actually do something!

    Team LiB
    Previous Section Next Section

    JavaScript Editor Javascript source editor     Web programming