Archive for the ‘CakePHP’ Category

Set CakePHP layouts in app_controller

Monday, August 4th, 2008

Fed up of writing $this->layout = ‘admin’ in all the admin methods of your controllers? Me too.

I realised that instead you could automatically set them in the beforeFilter() of your app_controller instead.

1
2
3
4
5
6
7
8
9
10
11
12
13
function beforeFilter(){
 
  if(isset($this->params['prefix'])) {
    //read the admin prefix set in core.php
    $admin = Configure::read('Routing.admin'); 
 
    if($this->params['prefix'] == $admin){
 
      $this->layout = $admin;
 
    }
  }    
}

If you need to over-ride this, you can just do so as normal in individual methods.

At last the CakePHP books have arrived

Monday, August 4th, 2008

I’d like to say it never rains but it pours, but that would be entirely the wrong sentiment, I need something more like good things come to those who wait

Anyway after an epic wait a flurry of CakePHP books have arrived.

I haven’t read any of these books yet, but I from personal experience I would say the Apress books are bound to be good. I have no idea what the books from Packt are like. I have one Manning book which is good but is unfortunately a PDF (and I hate reading PDFs).

XAMPP and the PHP CLI

Sunday, July 27th, 2008

Like a lot of people I do most of my development work on windows PC and locally run a web a server and database. Like a a lot of people I just downloaded and installed a server bundle. For the last few years I have used Apache2triad and it has generally been pretty painless, but I’ve recently been forced to abandon it. Unfortunately the development seems to have been abandoned about 2 years ago.

The reason I abandoned Apache2triad was PHP versions - I wanted to do some work with Google Base using the Zend Google Data Library which required PHP 5.1.4+.

Looking around at the various bundles I decided to go with XAMPP - a lot of people seem to use it and it seems to get pretty good feedback. I won’t say the process was entirely painless (it took a couple of goes to get everything up and running) but eventually it all seemed OK.

The fun started when I decided to do a bit of baking (generating scaffolding code for my CakePHP app from a command line interface). Nothing worked.

Looking into it I discovered more than I wanted to know about the PHP Command Line Interface (CLI) which I had happily used for years with no idea it actually existed, having assumed it was just there as part of PHP.

After a lot googleing and a lot of trial and error I got it all working again and this is how I did it.

My environment is Windows XP Pro Service Pack 2 with XAMPP installed on E:/

  1. Downloaded the latest version of PHP and replaced the entire contents of E:\xampp\php\.
  2. Added to the E:\xampp\php to my environment PATH.

At this stage PHP CLI wwas now working but there were nasty errors e.g. I was uanble to connect to MySQL from scripts. I discovered that this is because PHP CLI uses a different php.ini file to regular PHP.

To fix this I copied my php.ini from E:\xampp\apache\bin\php.ini (the default XAMPP ini file) to E:\xampp\php.

I then checked the PHP CLI again using php -v at a command prompt. There were now about 6 errors displayed, concerned with modules that could not be loaded or found. I believe that these concern modules just that aren’t meaningful in CLI mode. So I went through my new php.ini in E:\xampp\php and commented out the directives that were causing the errors until PHP CLI ran without errors.

Displaying Auth Error Messages

Wednesday, July 2nd, 2008

I feel a bit daft posting this because in the end the answer was so easy to find - but maybe it will help somebody else.

I’ve been re-writing my users / groups permission system for CakePHP 1.2 and decided to incorporate as much built in functionality as possible so I’m using the Auth Component - which is great - but one thing kept bugging me, I couldn’t for the life of me figure out how to get the messages from Auth to display in my views - but by debugging I could see they were sitting there right in the Session.

I spent a long time Googling and looking at the API for clues, eventually in desperation I looked at the cookbook (I have no idea why I hadn’t already looked there - as I use it a lot…) and the answer was both very easy and staring me right in the face.

Just pop the following in a view or layout:

1
2
3
4
 
if ($session->check('Message.auth')) {
		$session->flash('auth');
	}

CakePHP Calendar Helper

Tuesday, April 8th, 2008

About six months ago I wrote a post about a Simple PHP calendar function I had written and how it was also really easy to use as a CakePHP helper.

Calendar Screenshot

I didn’t actually write the calendar specifically with Cake in mind, but I was working on a Cake site at the same time and I had a flash of inspiration. I was working on a project that needed a calendar, so I looked through all my old code but all the Calendars I had were all tied up in a terrible mess with bits of logic and SQL queries and layout all rolled into one. I looked on Google and still couldn’t find anything really easy to use - I wanted something I could just drop into place.

I suddenly realised that the way to do it was to stop trying to put any decision making into the calendar at all. The calendar only needs to display the right layout for the month and manage back and forward links. I decided to just dump the data in an array where the index corresponded to the day number (1 to 31) - the idea being that you can put anything in the array - plain text, html, javascript hooks for ajax etc.

It was in part influenced by some work I was doing using the The Yahoo! User Interface Library (YUI)
but I am very dubious about the whole notion of creating embedded calendars solely through Javascript when it could far more easily be done server side. (There is clearly a place for Javascript pop up calendars e.g. date pickers - I’m particularly fond of Marc Grabanski and Keith Woods’ version jQuery UI Datepicker)

At the time I had just moved over to using CakePHP as the main vehicle for my development work and it was clearly a great fit with the MVC setup of CakePHP - the calendar is just a shell that shows whatever you pour into it.

This is the first time I have had to go back and revisit the code, I have fixed the bugs, added some comments and set up a working example. At the moment all the logic is just sitting in the controller - but I am working on a component to tidy everything up and make it nice and portable (watch this space).

Thanks to everybody who commented or emailed me about the first version in September.

Calendar Helper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
 
<?php	
 
/**
* Calendar Helper for CakePHP
*
* Copyright 2007-2008 John Elliott
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
*
* @author John Elliott
* @copyright 2008 John Elliott
* @link http://www.flipflops.org More Information
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
*
*/
 
class CalendarHelper extends Helper
{
 
	var $helpers = array('Html', 'Form');
 
/**
* Generates a Calendar for the specified by the month and year params and populates it with the content of the data array
*
* @param $year string
* @param $month string
* @param $data array
* @param $base_url
* @return string - HTML code to display calendar in view
*
*/
 
function calendar($year = '', $month = '', $data = '', $base_url ='')
	{
	$str = '';
	$month_list = array('january', 'febuary', 'march', 'april', 'may', 'june', 'july', 'august', 'september', 'october', 'november', 'december');
	$day_list = array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun');
	$day = 1;
	$today = 0;
 
	if($year == '' || $month == '') { // just use current yeear & month
		$year = date('Y');
		$month = date('M');
	}
 
 
 
	$flag = 0;
 
	for($i = 0; $i < 12; $i++) {
		if(strtolower($month) == $month_list[$i]) {
			if(intval($year) != 0) {
				$flag = 1;
				$month_num = $i + 1;
				break;
			}
		}
	}
 
 
	if($flag == 0) {
		$year = date('Y');
		$month = date('F');
		$month_num = date('m');
	}
 
	$next_year = $year;
	$prev_year = $year;
 
	$next_month = intval($month_num) + 1;
	$prev_month = intval($month_num) - 1;
 
	if($next_month == 13) {
		$next_month = 'january';
		$next_year = intval($year) + 1;
	} else {
		$next_month = $month_list[$next_month -1];
	}
 
	if($prev_month == 0) {
		$prev_month = 'december';
		$prev_year = intval($year) - 1;
	} else {
		$prev_month = $month_list[$prev_month - 1];
	}
 
	if($year == date('Y') && strtolower($month) == strtolower(date('F'))) {	
	// set the flag that shows todays date but only in the current month - not past or future...
		$today = date('j');
	}
 
	$days_in_month = date("t", mktime(0, 0, 0, $month_num, 1, $year));
 
	$first_day_in_month = date('D', mktime(0,0,0, $month_num, 1, $year)); 
 
	$str .= '<table class="calendar">';
 
	$str .= '<thead>';
 
	$str .= '<tr><th class="cell-prev">';
 
	$str .= $this->Html->link(__('prev', true), 'calendar/' . $prev_year . '/' . $prev_month); 
 
	$str .= '</th><th colspan="5">' . ucfirst($month) . ' ' . $year . '</th><th class="cell-next">';
 
	$str .= $this->Html->link(__('next', true), 'calendar/' . $next_year . '/' . $next_month); 
 
	$str .= '</th></tr>';
 
	$str .= '<tr>';
 
		for($i = 0; $i < 7;$i++) {
				$str .= '<th class="cell-header">' . $day_list[$i] . '</th>';
		}
 
	$str .= '</tr>';
 
	$str .= '</thead>';
 
	$str .= '<tbody>';
 
	while($day <= $days_in_month) {
		$str .= '<tr>';
 
		for($i = 0; $i < 7; $i ++) {
 
			$cell = '&nbsp;';
 
			if(isset($data[$day])) {
				$cell = $data[$day];
			}
 
			$class = '';
 
			if($i > 4) {
				$class = ' class="cell-weekend" ';
			}
 
			if($day == $today) {
				$class = ' class="cell-today" ';
			}
 
			if(($first_day_in_month == $day_list[$i] || $day > 1) && ($day <= $days_in_month)) {
				$str .= '<td ' . $class . '><div class="cell-number">' . $day . '</div><div class="cell-data">' . $cell . '</div></td>';
				$day++;
			} else {
				$str .= '<td ' . $class . '>&nbsp;</td>';
			}
		}
		$str .= '</tr>';
		}
 
	$str .= '</tbody>';
 
	$str .= '</table>';
 
	return $str;
	}
}	
 
?>

Calendar Controller

The controller is responsible for creating the data array containing the calendar ‘events’ and then passing this on to the view from where the calendar is called.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
 
<?php
 
/**
* Example controller for the Calendar Helper
*
*	Copyright 2008 John Elliott
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
*
* @author John Elliott
* @copyright 2008 John Elliott
* @link http://www.flipflops.org More Information
* @license			http://www.opensource.org/licenses/mit-license.php The MIT License
*
*/
 
uses('sanitize');
 
class EventsController extends AppController {
 
	var $name = 'Events';
	var $helpers = array('Html', 'Form', 'Calendar');
 
	/**
	* the idea is that the calendar helper itself is purely a shell
	* the calendar will just display whatever is sent to it
	* anything you want to do to it is put togthere here in the controller or in a component when I get around to writing it
	*
	* @param $year string
	* @param $month string
	*
	**/
 
	function calendar($year = null, $month = null) {
 
		$this->Event->recursive = 0;
 
		$month_list = array('january', 'febuary', 'march', 'april', 'may', 'june', 'july', 'august', 'september', 'october', 'november', 'december');
		$day_list = array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun');
		$base_url = $this->webroot . 'events/calendar'; // NOT not used in the current helper version but used in the data array
		$view_base_url = $this->webroot. 'events';
 
		$data = null;
 
		if(!$year || !$month) {
			$year = date('Y');
			$month = date('M');
			$month_num = date('n');
			$item = null;
		}
 
		$flag = 0;
 
		for($i = 0; $i < 12; $i++) { // check the month is valid if set
			if(strtolower($month) == $month_list[$i]) {
				if(intval($year) != 0) {
					$flag = 1;
					$month_num = $i + 1;
					$month_name = $month_list[$i];
					break;
				}
			}
		}
 
		if($flag == 0) { // if no date set, then use the default values
			$year = date('Y');
			$month = date('M');
			$month_name = date('F');
			$month_num = date('m');
		}
 
		$fields = array('id', 'name', 'DAY(event_date) AS event_day');
 
		$var = $this->Event->findAll('MONTH(Event.event_date) = ' . $month_num . ' AND YEAR(Event.event_date) = ' . $year, $fields, 'Event.event_date ASC');
 
		/**
		* loop through the returned data and build an array of 'events' that is passes to the view
		* array key is the day of month 
		*
		*/
 
		foreach($var as $v) {
 
			if(isset($v[0]['event_day'])) {
 
				$day = $v[0]['event_day'];
 
				if(isset($data[$day])) {
					$data[$day] .= '<br /><a href="' . $view_base_url . '/view/' . $v['Event']['id'] . '">' . $v['Event']['name'] . '</a>';
				} else {
					$data[$day] = '<a href="' . $view_base_url . '/view/' . $v['Event']['id'] . '">' . $v['Event']['name'] . '</a>';
				}
			}
		}
 
		$this->set('year', $year);
		$this->set('month', $month);
		$this->set('base_url', $base_url);
		$this->set('data', $data);
 
	}
}
 
 
?>

Event Model

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 
<?php
class Event extends AppModel {
 
	var $name = 'Event';
	var $useTable = 'events';
	var $validate = array(
 
		'name' => array(
			'rule' => array('minLength', 2),
			'message' => 'Name must be at least 2 characters long',
			'required' => true 
										),
		'notes' => array(
			'rule' => array('minLength', 2),
			'message' => 'Please add some notes',
			'required' => true 
										),	
	);
 
}
?>

Calendar View

1
2
3
4
5
6
7
8
9
 
<h2><?php __('Events');?></h2>
 
 
<?php 
 
	echo $calendar->calendar($year, $month, $data, $base_url);
 
?>

events SQL

1
2
3
4
5
6
7
8
9
10
 
CREATE TABLE `events` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `event_date` datetime DEFAULT NULL,
  `notes` text,
  `created` datetime DEFAULT NULL,
  `modified` datetime DEFAULT NULL,
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=15 DEFAULT CHARSET=latin1;

CSS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 
/* calendar CSS */
 
table.calendar {width: auto; border: 1px solid #cccccc; border-collapse: collapse; margin: 0px; padding: 0px; background-color: #ffffff;}
table.calendar th {background-color: #eeeeee; text-transform: none; color: #444444; padding: 4px; text-align: center; border: 1px solid #eeeeee;}
 
table.calendar th.cell-prev {text-align: left;}
table.calendar th.cell-next {text-align: right;}
table.calendar th.cell-header {width: 70px; border-bottom: 1px solid #cccccc;}
table.calendar td.cell-today {background-color: #e2e8f6;} /* today in the current month */
table.calendar td.cell-weekend {background-color: #F3F5EB;}
table.calendar td {border: 1px solid #cccccc;}
 
table.calendar td div.cell-number {text-align: right; font-size: 8px; color: #444444; display: block;}
table.calendar td div {display: block; font-size: 10px; text-align: left;}
table.calendar thead th {border: 1px solid #cccccc;}

To Do List

I’m feeling a little less time poor at the moment and want to do a bit of work on this. If anybody has any comments or suggestions, please let me know. At the moment my plans are as follows (in no particular order).

  • add support for internationalisation
  • put the checking date checking (and possibly data array construction into a component)
  • add built in AJAX support (haven’t decided on jQuery or Prototype yet though)
  • look at using Cake 1.2 routes as part of date checking using regular expressions (how easy would it be to combine this with the internationalisation?)

Examples & Downloads

Check out the Working Calendar Helper Example

Download all the files calendar-helper.zip - this is a zipped copy of the app directory, just set up you database, edit /app/core/database.php and slot it into place. (Note Cake 1.2 only)