Part of the EllisLab Network
x
 
Create New Page
 View Previous Changes    ( Last updated by sophistry )

OOCalendar

An object oriented approach to building a monthly calendar.

This code assumes a database table with event dates specified in a datetime field. It also helps to have a CSS file that defines a few table classes. you can see my example below.

The controller:

function cal($y,$m)
    
{
        $this
->load->helper('url');
        
$this->load->model('Month_model');
        
$this->Month_model->set_event_table('Events');
        
$this->Month_model->set_event_field('datetime_start');

        
$p_arr = array('year','month','month_name_long','month_prev','month_next','year_in_month_prev','year_in_month_next');
        
$data['month_array'$this->Month_model->db_month_box($m,$y);
        foreach (
$p_arr as $p)
        
{
            $data[$p] 
$this->Month_model->$p;
        
}
        $this
->load->view('month_table',$data);            
    

The model:

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); 

class 
Month_model extends Model {

    
var $month;
    var 
$month_prev;
    var 
$month_next;
    
    var 
$month_name_long;
    var 
$month_name_short;
    
    var 
$year;
    var 
$year_in_month_prev;
    var 
$year_in_month_next;
    
    var 
$month_arr;
    var 
$db_table_with_dates;
    var 
$db_field_with_date;
    
    function 
Month_model()
    
{
        parent
::Model();
    
}
    
    
// set the db and field 
    // where events are
    
function set_event_table($tbl_name)
    
{
        $this
->db_table_with_dates $tbl_name;
    
}
    
    
function set_event_field($fld_name)
    
{
        $this
->db_field_with_date $fld_name;
    
}
    
    
// send month and year
    // build month box
    // and then hang the events
    // fetched from the specified db
    
function db_month_box($m,$y)
    
{
        
// get the raw month structure
        
$arr $this->month_box($m,$y);
        
// hang some objects onto the month
        
$arr $this->hang_events();
        
// chunk out weekly arrays, keep keys
        
$ca array_chunk($arr,7,TRUE);
        return 
$ca;
    
}
    
    
// return an array with Ymd format
    // dates as keys, calendar month with
    // padded entries to make a fully
    // rectangular month calendar
    
function month_box($m,$y)
    
{
        
if ($m 12$m 12;
        if (
$m 1$m 1;
        
        
$this->month $m;
        
$this->year $y;
        
$this->month_arr = array();
        
// get timestamp of the first 
        // day of requested month and year
        
$ts_start mktime(1200$this->month1$this->year);        
        
$days_in_month date('t',$ts_start);
        
        
// use noon so we don't get messed up
        
$ts_end mktime(1200$this->month$days_in_month$this->year);

        
$pms strtotime('-1 month',$ts_start);
        
$pme strtotime('-1 day',$ts_start);
        
$nms strtotime('+1 day',$ts_end);
        
// have to do extra to get prev/next month params
        
$this->month_next date('m',$nms);
        
$this->year_in_month_next date('Y',$nms);
        
$this->month_prev date('m',$pms);
        
$this->year_in_month_prev date('Y',$pms);
        
        
$days_in_month_nm date('t',$nms);
        
$nme mktime(1200$this->month_next$days_in_month_nm$this->year_in_month_next);

        
$prev_month_start date('Ymd'$pms);
        
$prev_month_end date('Ymd'$pme);
        
$next_month_start date('Ymd'$nms);
        
$next_month_end date('Ymd'$nme);
        
        
$start_day date('Ymd',$ts_start);
        
$end_day date('Ymd',$ts_end);
        
// generate array from the
        // number of days in month
        
$arr_tm range($start_day,$end_day);
        
$arr_pm range($prev_month_start,$prev_month_end);
        
$arr_nm range($next_month_start,$next_month_end);
        
        
// get the day num of the 
        // first/last day of the month
        
$day_of_week_first date('w'$ts_start);
        
$day_of_week_last date('w'$ts_end);
        
// pad the beginning of the array 
        // based on day_of_week, negative keys
        
$pad_in = ($day_of_week_first) ? array_slice($arr_pm,-$day_of_week_first) : array();
        
// now the lead-out days
        
$day_mod 6-$day_of_week_last;        
        
$pad_out = ($day_mod)?array_slice($arr_nm,0,$day_mod):array();
        
        
// put the lead-in/lead-out days on
        
$arr array_merge($pad_in,$arr_tm,$pad_out);
        
$arr array_flip($arr);
        
// normalize every value
        // do this to avoid making 
        // a day class right now
        // just use stdClass
        
$today_Ymd date('Ymd');
        
$this->month_name_long date('F',$ts_start);
        
$this->month_name_short date('M',$ts_start);
        foreach(
$arr as $k => $v
        
{    
            $o 
= new stdClass;
            
// remove the spaces in the sscanf() format string
            
list($year,$month,$day) = sscanf($k"% 4d% 2d% 2d");
            
// store the timestamp to enable
            // access to any date formatting 
            // via date() function in view
            
$o->ts strtotime($month.'/'.$day.'/'.$year);
            
$o->day_of_month $day;
            
$o->day_of_year date('z',$o->ts);

            
$o->day_ordinal date('jS',$o->ts);
            
$o->day_name_long date('D',$o->ts);
            
$o->day_name_short substr($o->day_name_long,0,3);
            
$o->day_name_initial substr($o->day_name_short,0,1);
            
            
$o->not_in_month = ($month!=$this->month);
            
$o->today = ($today_Ymd==$k);
            
// start with event FALSE, 
            // add events in other function
            
$o->has_event FALSE;
            
$o->data = array();
            
$this->month_arr[$k] $o;
        
}
        
return $this->month_arr;
    
}
    
    
function hang_events()
    
{
        $f 
$this->db_field_with_date;
        
// define some calculated fields in SQL
        
$this->db->select("*, DATE_FORMAT($f, '%m') as month, DATE_FORMAT($f, '%d') as day, DATE_FORMAT($f, '%Y%m%d') AS Ymd"FALSE);
        
// get everything in the month (any year)
        
$this->db->where("MONTH($f)",$this->month);
        
$this->db->order_by($f);
        
$q $this->db->get($this->db_table_with_dates);
        
$ev $q->result();
        foreach (
$ev as $e)
        
{
            $month_date 
$this->year.$e->month.$e->day;
            
// check for exact match first then annual
            // these should be separated to handle single and 
            // repeating dates differently
            
if ( array_key_exists($e->Ymd$this->month_arr) OR array_key_exists($month_date$this->month_arr) )
            
{
                $this
->month_arr[$month_date]->has_event TRUE;
                
$this->month_arr[$month_date]->data[] $e;
            
}
        }
        
return $this->month_arr;
    
}
    
}
// EOF 

and the view:

<table id="calendar" cellspacing="0" cellpadding="0" summary="This month's calendar">
<
caption>
<?php echo anchor('example/cal/'.$year_in_month_prev.'/'.$month_prev'&laquo;', array('title'=>"previous month"'class'=>"nav")); ?>
 <?php 
echo $month_name_long?> <?php echo $year?> 
<?php 
echo anchor('example/cal/'.$year_in_month_next.'/'.$month_next'&raquo;', array('title'=>"next month"'class'=>"nav")); ?>
</caption>
    <
tr>
    
<?php foreach($month_array[0] as $head): ?>
        
<th scope="col"><?php echo $head->day_name_initial?></th>
    
<?php endforeach; ?>
    
</tr>
    
<?php foreach($month_array as $week): ?>
        
<tr>
        
<?php foreach($week as $day): ?>
            
<td class=
                
<?php if($day->today){echo '"today"';}
                
elseif($day->not_in_month){echo '"not_in_month"';}
                else{echo 
'"day"';} ?>
            
>
                                    
                
<?php echo ($day->has_event) ? anchor('example/cal/'.$year.'/'.$month.'/'.$day->day_of_month$day->day_of_month) : $day->day_of_month?>
            
                
<!--
                <
ul
                
<?php foreach($day->data as $event): ?>
                        
<li><?php echo $event->id?><?php echo $event->event_name?></li>
                
<?php endforeach; ?>
                
</ul>
                -->
            
            </
td>
        
<?php endforeach; ?>
        
</tr>
    
<?php endforeach; ?>
    
</table

The CSS for the table (needs a gif file):

body {
    font
normal 12px/20px "Trebuchet MS"VerdanaArialHelveticasans-serif;
    
color#616B76;
}

a {
    color
#DF9496;
}

#calendar {
    
width141px;
    
padding0;
    
margin0;
    
border-left1px solid #A2ADBC;
    
fontnormal 12px/20px "Trebuchet MS"VerdanaArialHelveticasans-serif;
    
color#616B76;
    
text-aligncenter;
    
background-color#fff;
}

.nav, .nav a {
    font
bold 18px "Trebuchet MS"VerdanaArialHelveticasans-serif;
    
color#fff;    
    
text-aligncenter;
    
text-decorationnone;
}    


caption {
    margin
0;
    
padding0;
    
width141px;
    
background#A2ADBC;
    
color#fff;     
    
fontbold 12px "Trebuchet MS"VerdanaArialHelveticasans-serif;
    
text-aligncenter;
}

th {
    font
bold 11px/20px "Trebuchet MS"VerdanaArialHelveticasans-serif;
    
color#616B76;
    
background#D9E2E1;
    
border-right1px solid #A2ADBC;
    
border-bottom1px solid #A2ADBC;
    
border-top1px solid #A2ADBC;
}

.todaytd.today atd.today a:linktd.today a:visited {
    color
#F6F4DA;
    
font-weightbold;
    
background#DF9496;
}

.not_in_monthtd.not_in_month atd.not_in_month a:linktd.not_in_month a:visited {
    color
#CCCCCC;
    
font-weightnormal;
    
background#EEEEEE;
}

td
.day {
    border
-right1px solid #A2ADBC;
    
border-bottom1px solid #A2ADBC;
    
width20px;
    
height20px;
    
text-aligncenter;
    
backgroundurl(../img/bg_calendar.gifno-repeat right bottom;
}

td
.day a {
    text
-decorationnone;
    
font-weightbold;
    
displayblock;
}

td
.day a:linktd.day a:visited {    
    color
#B88546;
    
backgroundurl(../img/bg_calendar.gifno-repeat;
}

td
.day a:hovertd.day a:active {
    color
#6aa3ae;
    
backgroundurl(../img/bg_calendar.gifno-repeat right top;