You'll use the date and time functions you learned in Chapter 10, "Working with Strings, Dates, and Time," to build a calendar that will display the dates for any month between 1980 and 2010 (those are randomly selected years and have no significanceyou can make your calendar go from 1990 to 2005 if you want). The user will be able to select both month and year with pull-down menus, and the dates for the selected month will be organized according to the days of the week.
In this script, we will be working with two variablesone for month and one for yearwhich will be supplied by user input. These pieces of information will be used to build a time stamp based on the first day of the selected month. If user input is invalid or absent, the default value will be the first day of the current month.
Checking User Input
When the user accesses the calendar application for the first time, no information will have been submitted. Therefore, we must ensure that the script can handle the fact that the variables for month and year may not be defined. We could use the isset() function for this, which returns false if the variable passed to it has not been defined. However, let's use the checkdate() function instead, which not only will see whether the variable exists but will also do something meaningful with it, namely, validate that it is a date.
Listing 24.1 shows the fragment of code that checks for month and year variables coming from a form, and builds a time stamp based on them.
Listing 24.1. Checking User Input for the Calendar Script
Listing 24.1 is a fragment of a larger script, so it does not produce any output itself. But, it's an important fragment to understand, which is why it sits all alone here, ready for an explanation.
In the if statement on line 2, we test whether the month and year have been provided by a form. If the month and year have not been defined, the mktime() function used later in the fragment will not be able to make a valid date from undefined month and year arguments.
If the values are present, we use geTDate() on line 3 to create an associative array based on the current time. We then set values for $month and $year ourselves, using the array's mon and year elements (lines 4 and 5). If the variables have been set from the form, we put the data into $month and $year variables so as not to touch the values in the original $_POST superglobal.
Now that we are sure that we have valid data in $month and $year, we can use mktime() to create a time stamp for the first day of the month (line 10). We will need information about this time stamp later on, so on line 11, we create a variable called $firstDayArray that will store an associative array returned by getdate() and based on this time stamp.
Building the HTML Form
We now need to create an interface by which users can ask to see data for a month and year. For this, we will use SELECT elements. Although we could hard-code these in HTML, we must also ensure that the pull-downs default to the currently chosen month, so we will dynamically create these pull-downs, adding a SELECT attribute to the OPTION element where appropriate. The form is generated in Listing 24.2.
Listing 24.2. Building the HTML Form for the Calendar Script
Having created the $start time stamp and the $firstDayArray date array in lines 211, let's begin to write the HTML for the page. Notice that we use $firstDayArray to add the month and year to the TITLE element on lines 15 and 16.
Line 20 is the beginning of our form. To create the SELECT element for the month pull-down, we drop back into PHP mode on line 22, to write the individual OPTION tags. First, we create an array called $months in lines 23 and 24 that contains the names of the 12 months, for display purposes. We then loop through this array, creating an OPTION tag for each name (lines 2531).
This would be an overcomplicated way of writing a simple SELECT element, were it not for the fact that we are testing $x (the counter variable in the for statement) against the $month variable on line 27. If $x and $month are equivalent, we add the string SELECTED to the OPTION tag, ensuring that the correct month will be selected automatically when the page loads. We use a similar technique to write the year pull-down on lines 3642. Finally, back in HTML mode, we create a submit button on line 45.
We now have a form that can send the month and year parameters to itself and will default either to the current month and year, or the month and year previously chosen. If you save this listing as dateselector.php, place it in your web server document root, and access it with your web browser, you should see something like Figure 24.1 (your month and year may differ).
Figure 24.1. The calendar form.
Creating the Calendar Table
We now need to create a table and populate it with dates for the chosen month. We do this in Listing 24.3, which represents the complete calendar display script.
Listing 24.3. The Complete Calendar Display Script
Although line 2 is new, lines 347 come right from Listing 24.2. Line 2 simply defines a constant variable, in this case ADAY (for example, "a day") with a value of 86400. This value represents the number of seconds in a day, which will be used later in the script.
We pick up the new code at line 48. Because the table will be indexed by days of the week, we loop through an array of day names in lines 5255, printing each in its own table cell, on lines 5354. All the real magic of the script happens in the final for statement beginning on line 56.
In line 56, we initialize a variable called $count and ensure that the loop will end after 42 iterations. This is to make sure that we will have enough cells to populate with date information, taking into consideration that a four-week month may actually have partial weeks at the beginning and the end, thus the need for six seven-day weeks (rows).
Within this for loop, we transform the $start variable into a date array with getdate(), assigning the result to $dayArray (line 57). Although $start is the first day of the month during the loop's initial execution, we will increment this time stamp by the value of ADAY (24 hours) for every iteration (see line 69).
On line 58, we test the $count variable against the number 7, using the modulus operator. The block of code belonging to this if statement will therefore only be run when $count is either zero or a multiple of 7. This is our way of knowing whether we should end the loop altogether or start a new row, where rows represent weeks.
After we have established that we are in the first iteration or at the end of a row, we can go on to perform another test on line 59. If the mon (month number) element of the $dayArray is no longer equivalent to the $month variable, we are finished. Remember that $dayArray contains information about the $start time stamp, which is the current place in the month that we are displaying. When $start goes beyond the current month, $dayArray[ "mon" ] will hold a different figure than the $month number provided by user input. Our modulus test demonstrated that we are at the end of a row, and the fact that we are in a new month means that we can leave the loop altogether. Assuming, however, that we are still in the month that we are displaying, we end the row and start a new one on line 62.
In the next if statement, on line 65, we determine whether to write date information to a cell. Not every month begins on a Sunday, so it's likely that our rows will contain an empty cell or two. Similarly, few months will finish at the end of one of our rows, so it's also likely that we will have a few empty cells before we close the table.
We have stored information about the first day of the month in $firstDayArray; in particular, we can access the number of the day of the week in $firstDayArray[ "wday" ]. If the value of $count is smaller than this number, we know that we haven't yet reached the correct cell for writing. By the same token, if the value of the $month variable is no longer equal to $dayArray[ "mon" ], we know that we have reached the end of the month (but not the end of the row, as we determined in our earlier modulus test). In either case, we write an empty cell to the browser on line 66.
In the final else clause on line 67, we can do the fun stuff. We have already determined that we are within the month that we want to list, and that the current day column matches the day number stored in $firstDayArray[ "wday" ]. Now we must use the $dayArray associative array that we established early in the loop to write the day of the month and some blank space into a cell.
Finally, on line 69, we need to increment the $start variable, which contains our date stamp. We simply add the number of seconds in a day to it (we defined this value in line 2), and we're ready to begin the loop again with a new value in $start to be tested.
If you save this listing as showcalendar.php, place it in your web server document root, and access it with your web browser, you should see something like Figure 24.2 (your month and year may differ).
Figure 24.2. The calendar form and script.
Adding Events to the Calendar
Displaying the calendar is great, but in just a few extra lines of code, you can make it interactivethat is, you can add and view events on a given day. To begin, let's create a simple database table that holds event information. For purposes of simplicity, these events will only occur on a single day, and only their start date and time will be shown. Although you can make the event entries as complex as you want, this example is here just to show the basic process involved.
The calendar_events table will include fields for the start date and time, the event title, and an event short description:
CREATE TABLE calendar_events ( id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, event_title VARCHAR (25), event_shortdesc VARCHAR (255), event_start DATETIME );
Not only do we call the event.php file, but we also have to send along with it the date information for the particular link that is clicked. This is done via the query string, and you can see we're sending along three variables, what will become $_GET[ "m" ] for the month, $_GET[ "d" ] for the day, and $_GET[ "y" ] for the year.
Only one change remains for this particular script, before we tackle the event.php scriptadding an indicator to this particular view, if events do indeed exist. To do this, we have to connect to the database, so after the opening PHP tag at the original line 1, add the database connection code:
$mysqli = mysqli_connect("localhost", "joeuser", "somepass", "testDB");
The query that checks for existing events on a given day, it appears at the onset of the else statement that was originally found at line 7. An entirely new else statement is shown, where you can see that the query is issued, and, if results are found, text is printed within the table cell for that day:
In Listing 24.4, you can see the entirely new script, which we'll call showcalendar_withevent.php.
Listing 24.4. Calendar Display Script with Entry-Related Modifications
In Figure 24.3, you can see the new calendar, including the representation of the event title on a date that I prepopulated with an event.
Figure 24.3. Showing the calendar, with an event.
All that remains is adding the all-in-one event.php script, used in the pop-up window to display and also add an event to the calendar (on a particular day). Listing 24.5 contains all the necessary code; the fun part starts at lines 89, which connect to the MySQL database. Lines 1219 look to see whether the event entry form has been submitted; if it has, an INSERT statement is used to add the event to the calendar_events table before continuing.
Listing 24.5. Showing Events/Adding Events Via Pop-Up
Lines 2935 issue the query and retrieve all records that correspond to events on this given day. The text block used to display entries is created in lines 3751. However, users also need to see the form for adding an event, and this is built in lines 6187, effectively the end of the script.
In Figure 24.4, we see how a pop-up looks when a link is followed from the calendar and an entry is already present. In this example, we wanted to add another event on this day, so the form has been completed in preparation for adding the additional event.
Figure 24.4. Showing the day detail, ready to add another event.
In Figure 24.5, the second event has been added to this particular day.
Figure 24.5. A second event has been added.
Obviously, this is a simple example, but it shows that it is indeed easy to build a calendar type of system in just a few short scripts.