Archive for July, 2009

Excuse the mess

Posted in Personal on July 17th, 2009 by Jamie – 1 Comment

Reorganizing and switching to a much slicker theme. Everything should be nice and clean again soon.

Useful CakePHP Tutorial Roundup for July 17, 2009

Posted in CakePHP on July 17th, 2009 by Jamie – 1 Comment

Back on time. I think I’m going to downgrade from a firm 5 tutorials per week to just as many as I have time to review, because I don’t want to skim over material or choose sub-par stuff simply to make my quota. So, three this week. But as always, I’m constantly coming across useful tips, code, and tutorials that save me a lot of time. So I thought it might be useful to others to gather up some tutorials every Friday and just get them out there. Some are new; some are old chestnuts.

Here’s what I found useful this week:

  • Secrets of Admin Routing – Nate (July 14, 2009)
    I’ve been struggling with prefix routing – see my recent post about prefix routing and named arguments – and just when I came up with a decent (but admittedly hackish) solution to deal with the issue, along comes Nate fresh from CakeFest with a far more elegant solution. Although I still like the idea of using Router::connectNamed() to define your custom named arguments, Nate’s idea to use the AppHelper to persist custom prefixes is also a good one. Plus, you’re likely to have less custom prefixes than named arguments, so it’s more convenient just to define your prefixes in AppHelper as they come up, rather than accounting for every named argument in Router::connectNamed().
  • An Akismet Behavior – Tom O’Reilly (March 09, 2009)
    I use Akismet for this blog and it stops dozens of spam comments every day. Tom’s behavior is a great implementation of the spam-prevention service that’s easy to install. The only catch is that you need to get an API key, which you can only do by signing up for a wordpress.com account.
  • Search Engine Friendly URLs in CakePHP – Philip Klauzinski (February 03, 2009)
    The SEO experts these days claim that we should be using hyphens instead of underscores in our URLs. Easy enough on a ‘regular’ website or on a platform like Wordpress (which hyphenates by defualt), but this is actually a pretty tricky process in CakePHP. While you can use something like Mariano Iglesias’ Sluggable Behavior to generate hyphenated slugs for your database items, good luck trying to replace the dashes in controller and action names! The only real solution, which, as Philip says in his tutorial, isn’t a solution at all, is to convert hyphens to underscores in your <em>app/config/bootstrap.php</em> or <em>app/config/routes.php</em>. Philip’s code for that is nice. But, this means you can’t take advantage of CakePHP’s reverse routing to generate links via the HtmlHelper and other tools. So you’re stuck writing your links manually, unless you want to hack or extend the HtmlHelper. So, the best part of this article might be the attention it draws to the issue of Cake’s support for SEO standards.

Custom Prefixes and Named URL Arguments – CakePHP Gotcha

Posted in CakePHP on July 15th, 2009 by Jamie – 1 Comment

Apparently custom prefixes and custom named arguments in URLs don’t mix all that well in good ol’ CakePHP. I’ve got a StatisticsController with some custom_-prefixed actions. So, in routes.php I added this line to parse the URLs:

Router::connect('/statistics/custom/:action/*', array('prefix' => 'custom', 'controller' => 'statistics', 'custom' => true));

Straightforward enough, right? And to generate a link with the $html helper, you just need to do pass the prefix as true, like so:

$html->link('Go here', array('controller' => 'statistics', 'action' => 'view', 'custom' => true));

That’ll generate your /statistics/custom/view link. But if you try to pass a custom named argument, for example:

$html->link('Go here', array('controller' => 'statistics', 'action' => 'view', 'custom' => true, 'Paginate' => 25));

You might think (hope?) that the generated link would be /statistics/custom/view/Paginate:25… but no. Thanks to the presence of the named Paginate argument, the code also interprets ‘custom’ as a named argument, resulting in the URL /statistics/custom_view/custom:1/Paginate:25/. So, it spits out the full method and also takes on a ‘custom’ named argument. Not what we want…

To solve this issue, you’ll need to define each and every named argument you intend to pass in your application at the top of your app/config/routes.php, like this:

Router::connectNamed(array('Paginate', 'page'));

Throw that line in your route file before any Router::connect() rules and your custom prefixes and named arguments will get along swimmingly. CakePHP will know to separate those named arguments from your custom prefixes, so you’ll get the /statistics/custom/view/Paginate::25 URL.

Useful CakePHP Tutorial Roundup for July 13, 2009

Posted in CakePHP on July 13th, 2009 by Jamie – Be the first to comment

OK, so maybe I’m a few days late. Plenty of useful CakePHP resources out there, just not enough time to write about it all! But, better late than never, right? Anyway, I’m constantly coming across useful tips, code, and tutorials that save me a lot of time. So I thought it might be useful to others to gather up some tutorials every Friday (this is Monday – oops!) and just get them out there. Some are new; some are old chestnuts.

Here’s what I found useful this week:

  • Auto Login – Miles Johnson (June 03, 2009)
    A useful little component that handles automatically logging in an Auth session if a user checks a “remember me” box during login. Miles just uses a cookie to save the Auth information; it’s not a horribly complex script, but a useful one. Plus, it’s the simplest thing in the world to install: just throw it into your $components array and add the form field to the login view, and bob’s yer uncle.
  • Using bindModel to get deep relations – Mark Story (November 14, 2008)
    I really like CakePHP, but I think the overuse of queries when fetching related data between models is a glaring weak point. Rather than using LEFT JOINs to retrieve associated data, Cake just executes secondary, tertiary, etc. queries, which is far from optimal in terms of speed, and also means that you can’t apply SQL conditions to the deep relations. So, Mark explains how to use bindModel() and unbindModel() to change model associations on the fly, basically adding custom find conditions in order to execute one big query instead of a bunch of small ones. This is something that really does need to go into the CakePHP core, but for now, solutions such as Mark’s do the job.
  • Sitemap Deluxe - Cristian Deluxe (June 15, 2009)
    OK, I admit that I haven’t tried this just yet, but Cristian’s sitemap generator looks pretty dang impressive. I’m just getting to the point in my CMS where I need to add a sitemap generator, and in the interests of time I’ll probably try Cristian’s solution. It’s new, so it even includes Bing!
  • 8 Ways to Speed Up the Performance of CakePHP Apps – Matt Curry (March 17, 2009)
    CakePHP can be a bit slow if you don’t know your way around enough to make the fine tunings necessary to give it a kick in the butt. Fortunately, Mat Curry does know his way around and presented a list of speed-up tips a few months ago. This list eventually made it into his book, which I featured a couple of weeks ago, but this topic is important enough to stand on its own. Not surprisingly, the name of the game here is caching: HTML, view, APC, etc. The one shady suggestion in the list is Matt’s recommendation to use persistant models, which is a buggy feature that tends to break when models have complex associations. Matt does identify this issue in the comments of the post.
  • CSV Helper – Adam (ifunk) (September 10, 2007)
    Now THIS is an oldie. But it still works, and it works well. Adam – or, should I say, “ifunk” – gave me a real time saver with this admittedly straightforward helper. I’m already using it on one of my side projects to generate CSV files from large sets of data, and it works like a charm with no modification or fixes needed. Usage is really simple, but Adam doesn’t mention that you need to set $this->layout to null and $this->autoLayout to false in the controller method that’ll be doing the exporting. Other than that, it’s a good tutorial. I might also follow the suggestion of a commenter who recommends swapping out the default MIME type of “application/vnd.ms-excel” in favour of the more generic “application/csv”.

And that’s it – see you next Friday.

Auto Paginate Component

Posted in CakePHP on July 13th, 2009 by Jamie – 1 Comment

Nothing like writing a tutorial on your code to realize how you could improve it! Just minutes after writing a tutorial on persisting pagination limits in CakePHP – a system I developed weeks ago, I wondered to myself why I just didn’t make it a component and boost its portability by a billion percent. So, I whipped it up and called it AutoPaginate. Usage is much simpler; no more AppController silliness, just setting up the view files and individual controllers for pagination. I’ll paste in those parts of the tutorial from version 1.0. Speaking of which:

If you find yourself paginating a lot of results, you may want to give your users the ability to change the number of results per page they see. Easy enough, I know, but the user’s preference should also persist through the session no matter what data the application is serving up. And, ideally, we don’t want to POST every time we want to change the pagination limit: I like having it right in the URL for easy reading.

So here’s a component that shows how easy it is to put in user-modifiable, session-persisting pagination limits.

Download the Component (zip)

To use the component, just plug it into your AppController’s $components array (app/app_controller.php):

var $components = array('AutoPaginate');

Two config options: ‘options’, which determines the available results-per-page options for the user, and ‘defaultLimit’, which, predictably, determines the default pagination results limit. Using the options would look like this:

var $components = array('AutoPaginate' => array('options' => array(10, 25, 50, 100), 'defaultLimit' => 50));

Now we need to change the way we configure pagination in individual controllers. This is the easy part – just call _setPaginate() instead of explicitly defining $this->paginate when setting up your pagination. For example, in our PlayersController::admin_index() function we’re used to doing this:

$this->paginate = array(
 'limit' => 20,
 'contain' => array('Player, 'Position')
);
$this->set('players', $this->paginate());

But now we’ll just do this (app/controllers/players_controller.php):

function admin_index() {
 $this->_setPaginate(array(
 'contain' => array('Team', 'Position')
 ));

 $this->set('players', $this->paginate());
}

Finally, since our pagination will be standardized through the entire site, we’ll set up a couple of common view elements.

The top element will present the choices for the number of results per page, as well as the usual “display X out of X results” line. It looks like this (app/views/elements/pagination/top.ctp):

 Results per page:
 <?php
 $results = array();
 foreach ((array)$paginationOptions as $option) {
 if ($paginationLimit == $option) {
 $results[] = $option;
 } else {
 $args = $this->passedArgs;
 $args['Paginate'] = $option;
 $results[] = $htmla->link($option, $args);
 }
 }
 echo implode(" | ", $results);
 ?>

<?php
echo $paginator->counter(array(
'format' => __('Page %page% of %pages%, showing %current% records out of %count% total, starting on record %start%, ending on %end%.', true)
));
?>

That’ll produce a string of plain text links – not a form – that the user can use to change pagination limits. The output looks like this:

Results per page: 1 | 5 | 10 | 25 | 50 | 100

Page 1 of 105, showing 10 records out of 1042 total... (etc)

$paginationOptions and $paginationLimit are set in AutoPaginateComponent.

We’ll also make a new element for the bottom of paginated results too (app/views/elements/pagination/bottom.ctp):

<div class="paging">
 <?php echo $paginator->prev('<< '.__('previous', true), array('url' => $this->passedArgs), null, array('class'=>'disabled'));?>
 | 	<?php echo $paginator->numbers(array('url' => $this->passedArgs));?>
 <?php echo $paginator->next(__('next', true).' >>', array('url' => $this->passedArgs), null, array('class'=>'disabled'));?></div>

Now to insert the elements into a typical view with typical paginated results. I like to insert top.ctp as a table caption, but you can do anything you’d like. If we’re paginating in, say, PlayersController::admin_index, it might look something like this (app/views/players/admin_index.ctp):

<h2><?php __('Players');?></h2>
<table cellpadding="0" cellspacing="0" class="table_list">
 <caption>
 <?php echo $this->element('pagination/top'); ?>
 </caption>
 .... (data in table) ...</table>
<?php echo $this->element('pagination/bottom'); ?>

Pretty simple, eh? I prefer using helper functions to set variables anyway, so this kills two birds in one stone by automatically setting the pagination limit and getting rid of the “$this->paginate =” business in controllers.

As always, comments, suggestions, improvements and helpful criticisms welcome!

Auto Paginate Component for CakePHP (Persisting Pagination Limits 2.0)

Posted in CakePHP on July 13th, 2009 by Jamie – 7 Comments

Nothing like writing a tutorial on your code to realize how you could improve it! Just minutes after writing a tutorial on persisting pagination limits in CakePHP – a system I developed weeks ago, I wondered to myself why I just didn’t make it a component and boost its portability by a billion percent. So, I whipped it up and called it AutoPaginate. Usage is much simpler; no more AppController silliness, just setting up the view files and individual controllers for pagination. I’ll paste in those parts of the tutorial from version 1.0. Speaking of which:

If you find yourself paginating a lot of results, you may want to give your users the ability to change the number of results per page they see. Easy enough, I know, but the user’s preference should also persist through the session no matter what data the application is serving up. And, ideally, we don’t want to POST every time we want to change the pagination limit; I like having it right in the URL for easy reading.

So here’s a component that shows how easy it is to put in user-modifiable, session-persisting pagination limits.

The full component (app/controllers/components/auto_paginate.php):

<?php
/**
 * Auto Paginate Component class.
 *
 * A simple extension for paginating that helps with persisting user-defined pagination limits.
 *
 * @filesource
 * @author			Jamie Nay
 * @copyright       Jamie Nay
 * @license			http://www.opensource.org/licenses/mit-license.php The MIT License
 * @link            http://jamienay.com/code/auto-paginate-component
 */
class AutoPaginateComponent extends Object {

 /**
 * Other components needed by this component
 *
 * @access public
 * @var array
 */
 public $components = array('Session');

 /**
 * component settings
 *
 * @access public
 * @var array
 */
 public $settings = array();

 /**
 * Default values for settings.
 * - options: the results-per-page options to present to the user.
 *
 * @access private
 * @var array
 */
 private $__defaults = array(
 'options' => array(1, 5, 10, 25, 50, 100),
 'defaultLimit' => 25
 );

 /**
 * Configuration method.
 *
 * @access public
 * @param object $model
 * @param array $settings
 */
 public function initialize(&$controller, $settings = array()) {
 $this->settings = array_merge($this->__defaults, $settings);
 $this->controller =& $controller;
 }

 /**
 * beforeRender()
 *
 * Set the variables needed by the controller.
 *
 * @access public
 * @param $controller Controller object
 */
 public function beforeRender(&$controller) {
 $controller->set('paginationOptions', $this->settings['options']);
 $controller->set('paginationLimit', $this->paginationLimit());
 }

 /**
 *
 * Set the controller's $paginate variable.
 *
 * @access public
 * @param array $options
 */
 public function setPaginate($options = array()) {
 $defaults = array(
 'limit' => $this->paginationLimit()
 );

 $this->controller->paginate = array_merge($defaults, $options);
 }

 /**
 * Set the pagination limit based on user input and session variables.
 *
 * @access public
 */
 public function paginationLimit() {
 if (isset($this->controller->params['named']['Paginate'])) {
 $this->Session->write('Pagination.limit', $this->controller->params['named']['Paginate']);
 }

 return ($this->Session->check('Pagination.limit') ? $this->Session->read('Pagination.limit') :
 $this->settings['defaultLimit']);
 }

}

?>

Two config options: ‘options’, which determines the available results-per-page options for the user, and ‘defaultLimit’, which, predictably, determines the default pagination results limit.

To use the component, just plug it into your AppController’s $components array (app/app_controller.php):

var $components = array('AutoPaginate');

Now we need to change the way we configure pagination in individual controllers. This is the easy part – just call AutoPaginate::setPaginate() instead of explicitly defining $this->paginate when setting up your pagination. For example, in our PlayersController::admin_index() function we’re used to doing this:

$this->paginate = array(
 'limit' => 20,
 'contain' => array('Player, 'Position')
);
$this->set('players', $this->paginate());

But now we’ll just do this (app/controllers/players_controller.php):

function admin_index() {
 $this->AugoPaginate->setPaginate(array(
 'contain' => array('Team', 'Position')
 ));

 $this->set('players', $this->paginate());
}

Finally, since our pagination will be standardized through the entire site, we’ll set up a couple of common view elements.

The top element will present the choices for the number of results per page, as well as the usual “display X out of X results” line. It looks like this (app/views/elements/pagination/top.ctp):

 Results per page:
 <?php
 $results = array();
 foreach ((array)$paginationOptions as $option) {
 if ($paginationLimit == $option) {
 $results[] = $option;
 } else {
 $args = $this->passedArgs;
 $args['Paginate'] = $option;
 $results[] = $html->link($option, $args);
 }
 }
 echo implode(" | ", $results);
 ?>

<?php
echo $paginator->counter(array(
'format' => __('Page %page% of %pages%, showing %current% records out of %count% total, starting on record %start%, ending on %end%.', true)
));
?>

That’ll produce a string of plain text links – not a form – that the user can use to change pagination limits. The output looks like this:

Results per page: 1 | 5 | 10 | 25 | 50 | 100

Page 1 of 105, showing 10 records out of 1042 total... (etc)

$paginationOptions and $paginationLimit are set in AutoPaginateComponent.

We’ll also make a new element for the bottom of paginated results too (app/views/elements/pagination/bottom.ctp):

<div class="paging">
 <?php echo $paginator->prev('<< '.__('previous', true), array('url' => $this->passedArgs), null, array('class'=>'disabled'));?>
 | 	<?php echo $paginator->numbers(array('url' => $this->passedArgs));?>
 <?php echo $paginator->next(__('next', true).' >>', array('url' => $this->passedArgs), null, array('class'=>'disabled'));?></div>

Now to insert the elements into a typical view with typical paginated results. I like to insert top.ctp as a table caption, but you can do anything you’d like. If we’re paginating in, say, PlayersController::admin_index, it might look something like this (app/views/players/admin_index.ctp):

<h2><?php __('Players');?></h2>
<table cellpadding="0" cellspacing="0" class="table_list">
 <caption>
 <?php echo $this->element('pagination/top'); ?>
 </caption>
 .... (data in table) ...</table>
<?php echo $this->element('pagination/bottom'); ?>

Pretty simple, eh? I prefer using helper functions to set variables anyway, so this kills two birds in one stone by automatically setting the pagination limit and getting rid of the “$this->paginate =” business in controllers.

As always, comments, suggestions, improvements and helpful criticisms welcome!

Persisting Pagination Limits in CakePHP

Posted in CakePHP on July 12th, 2009 by Jamie – 3 Comments

If you find yourself paginating a lot of results, you may want to give your users the ability to change the number of results per page they see. Easy enough, I know, but the user’s preference should also persist through the session no matter what data the application is serving up. And, ideally, we don’t want to POST every time we want to change the pagination limit: I like having it right in the URL for easy reading.

So, I’m just going to do a little quickie here to show you how easy it is to put in user-modifiable pagination limits. We’ll write a common view element for pagination, then we’ll turn to AppController and write a couple of functions to handle setting the pagination limit and interpreting changes to that limit. Finally, we’ll slightly modify the way we set up pagination in individual controllers to handle the new code. It’ll go something like this:

  • Setting up Common View Elements
  • New Functions in AppController
  • Modifying Individual Controller Pagination Calls

Setting up a Common View Element

OK, so if all of our paginated results are going to look the same then we might as well have a common view element. This element will present the choices for the number of results per page, as well as the usual “display X out of X results” line. It looks like this (app/views/elements/pagination/top.ctp):

 Results per page:
 <?php
 $results = array();
 foreach ((array)$paginationOptions as $option) {
 if ($paginationLimit == $option) {
 $results[] = $option;
 } else {
 $args = $this->passedArgs;
 $args['Paginate'] = $option;
 $results[] = $htmla->link($option, $args);
 }
 }
 echo implode(" | ", $results);
 ?>

<?php
echo $paginator->counter(array(
'format' => __('Page %page% of %pages%, showing %current% records out of %count% total, starting on record %start%, ending on %end%.', true)
));
?>

That’ll produce a string of plain text links – not a form – that the user can use to change pagination limits. The output looks like this:

Results per page: 1 | 5 | 10 | 25 | 50 | 100

Page 1 of 105, showing 10 records out of 1042 total... (etc)

There are two variables in the pagination/top.ctp that we need to define, as well – $paginationOptions and $paginationLimit. You just need to set these somewhere in your AppController before the view is rendered, so in, say, beforeFilter() – I actually set mine in conjunction with my quick and dirty widget component (/end shameless plug ;) ):

// Pagination options
$this->set('paginationOptions', array(1, 5, 10, 25, 50, 100));
$this->set('paginationLimit', $this->_paginationLimit());

We’ll write the _paginationLimit() function in the next section.

We’ll also make a new element for the bottom of paginated results too (app/views/elements/pagination/bottom.ctp):

<div class="paging">
 <?php echo $paginator->prev('<< '.__('previous', true), array('url' => $this->passedArgs), null, array('class'=>'disabled'));?>
 | 	<?php echo $paginator->numbers(array('url' => $this->passedArgs));?>
 <?php echo $paginator->next(__('next', true).' >>', array('url' => $this->passedArgs), null, array('class'=>'disabled'));?></div>

Now to insert the elements into a typical view with typical paginated results. I like to insert top.ctp as a table caption, but you can do anything you’d like. If we’re paginating in, say, PlayersController::admin_index, it might look something like this (app/views/players/admin_index.ctp):

<h2><?php __('Players');?></h2>
<table cellpadding="0" cellspacing="0" class="table_list">
 <caption>
 <?php echo $this->element('pagination/top'); ?>
 </caption>
 .... (data in table) ...</table>
<?php echo $this->element('pagination/bottom'); ?>

The views are finished – now we just need the methods to make it all work.

New Functions in AppController

Two new functions here, one that we’ll use in the individual controllers in place of defining $this->paginate, and another one called by that function to set the pagination limit depending on possible user input and the existing session-written value.

The first method, _setPaginate(), is just a shortcut for setting $this->paginate in controllers, so we don’t need to set the limit every time. It’s short and sweet (app/app_controller.php):

protected function _setPaginate($options = array()) {
 $defaults = array(
 'limit' => $this->_paginationLimit()
 );

 $this->paginate = array_merge($defaults, $options);
}

Notice that _setPaginate() calls _paginationLimit(), which is the function that sets and gets the limit in the user’s session:

protected function _paginationLimit() {
 if (isset($this->params['named']['Paginate'])) {
 $this->Session->write('Pagination.limit', $this->params['named']['Paginate']);
 }

 // On two lines for blog readability.
 return ($this->Session->check('Pagination.limit') ? $this->Session->read('Pagination.limit') :
 Configure::read('SiteSettings.default_pagination_limit'));
}

The system is done – now we just have to use it!

Modifying Individual Controller Pagination Calls

This is the easy part – just call _setPaginate() instead of explicitly defining $this->paginate when setting up your pagination. For example, in our PlayersController::admin_index() function we’re used to doing this:

$this->paginate = array(
 'limit' => 20,
 'contain' => array('Player, 'Position')
);
$this->set('players', $this->paginate());

But now we’ll just do this (app/controllers/players_controller.php):

function admin_index() {
 $this->_setPaginate(array(
 'contain' => array('Team', 'Position')
 ));

 $this->set('players', $this->paginate());
}

Pretty simple, eh? I prefer using helper functions to set variables anyway, so this kills two birds in one stone by automatically setting the pagination limit and getting rid of the “$this->paginate =” business in controllers.

As always, comments, suggestions, improvements and helpful criticisms welcome!

Useful CakePHP Tutorial Roundup for July 03, 2009

Posted in CakePHP on July 3rd, 2009 by Jamie – 1 Comment

Week #2 of my “this stuff is useful” roundup, right on schedule! I wonder how long I can keep this up. Anyway, I’m constantly coming across useful tips, code, and tutorials that save me a lot of time. So I thought it might be useful to others to gather up some tutorials every Friday and just get them out there. Some are new; some are old chestnuts. I’ll try to post at least 5 each week.

Here’s what I found useful this week:

  • CakePHP RSS Feed Datasource (June 19, 2009)
    The guys at Loadsys have released a nice datasource for RSS feeds that allows models to use RSS feeds as a source for data instead of a database. Great for displaying, say, the latest articles from an affiliate site or the day’s top news stories, especially since you treat the feed as a standalone model rather than as part of, for example, a controller component. Its only downfall, which is really the downfall of CakePHP datasources in general, is that you need to declare a separate config array for each feed in config/database.php. I would love to be able to point to an RSS feed dynamically, but…
  • Simple tweak for model relations in CakePHP (June 30, 2009)
    A simple little trick for deleteAll() that speeds up performance (big time!) when deleting multiple records. By default, deleteAll() deletes items one by one – use the trick in this tutorial (written by the guy(s) who develop NeutrinoCMS) and deleteAll() will delete the records with one query.
  • Cake 1.2’s Set class eats nested arrays for breakfast! (February 27, 2007)
    In the “oldie but goodie” category, CakePHP stalwart Felix Geisendörfer weighs in on the Set class, which was pretty new at the time the article was written. Felix shows a few tips on how to manipulate multidimensional arrays with the various functions provided by Set. The class is very powerful, but some of the more complex functions tend to trip up newer programmers. Some of the post’s comments are also pretty useful.
  • jQuery image upload & crop (March 9, 2009)
    Thomas (’klagoggle’) has ported over a popular JQuery image cropping/manipulation tool to CakePHP, turning it into a component and helper. Anyone who’s used this JQuery tool knows that it’s really, really cool. And it’s even cooler to have a ready-made CakePHP version! I actually needed to put the same JQuery crop tool on a website for a client, and was dreading coding my own components/helpers – Thomas is a lifesaver.
  • Uploader (File Upload Plugin) (June 30, 2009)
    Hot off the presses, Miles Johnson’s generic file upload handler – packaged as a handy plugin – is a slick, quick, and simple upload processor. It supports different file types, renaming, resizing of images and thumbnail generation, validation, etc. I’ve already built another layer on top of it for storing upload records in a database, which Miles wisely left out (since DB implementations vary so much) of the base package. If you’ve been looking for a simple but powerful way to handle your file uploads, this is it.

Quick Gotcha with HTML Forms, Image Inputs, and Internet Explorer

Posted in HTML and CSS on July 2nd, 2009 by Jamie – Be the first to comment

Watch out when using ‘image’ input types, since the $_REQUEST variables you get vary depending on the user’s browser. Let’s say you’ve got this input field:

<input type="image" src="go.png" value="go" name="pagination_submit" />

In Firefox (and I assume most other logical browsers) you’ll get three $_REQUEST variables from this input: ‘pagination_submit’, ‘pagination_submit_x’, and ‘pagination_submit_y’, where the latter two correspond to the X and Y positions of the part of the image the user clicked.

In Internet Explorer, however – versions 6 through 8 – you’ll ONLY get ‘pagination_submit_x’ and ‘pagination_submit_y’, not the straight ‘pagination_submit’. That’s a real bitch if you ask me… so be careful when testing for the existence of $_REQUEST variables for image input types.

Simple Math Question Captcha Component for CakePHP

Posted in CakePHP on July 1st, 2009 by Jamie – 3 Comments

I don’t like CAPTCHAs. I don’t know anyone who does. But most forms need some sort of protection against spam, especially where heavyweight spam detection services (e.g. Akismet) aren’t suitable. The downfalls of CAPTCHAs are many – hard to read, annoying, impossible for those with vision difficulties – and the benefits are slim. So, a few months ago I wrote a little function (and I do mean little – like 10 lines of code) to generate a random math question to ask the user in plain text instead of a CAPTCHA. The idea was that a bot wouldn’t be able to answer it, since it requires some human logic. And you know what? It worked – spam went way down on our websites.

So, fast forward a few months and now I’m writing a CakePHP CMS, and I’ve decided to turn my ‘math captcha’ function into a Component. So here it is: my Math Captcha Component. This component generates a random equation and registers the answer as a session variable. The programmer can then check the form submitter’s answer against that registered answer using the validation function provided in the component.

Download the Component (zip)

The full component (app/controllers/components/math_captcha.php):

<?php
/**
 * Math Captcha Component class.
 *
 * Generates a simple, plain text math equation as an alternative to image-based CAPTCHAs.
 *
 * @filesource
 * @author			Jamie Nay
 * @copyright       Jamie Nay
 * @license			http://www.opensource.org/licenses/mit-license.php The MIT License
 * @link            http://jamienay.com/code/math-captcha-component
 */
class MathCaptchaComponent extends Object {

    /**
     * Other components needed by this component
     *
     * @access public
     * @var array
     */
    public $components = array('Session');

    /**
* component settings
*
* @access public
* @var array
*/
public $settings = array();

    /**
* Default values for settings.
* - operand: the operand used in the math equation
* - minNumber: the minimum number used to generate the random variables.
* - maxNumber: the corresponding maximum number.
* - numberOfVariables: the number of variables to include in the equation.
*
* @access private
* @var array
*/
    private $__defaults = array(
        'operand' => '+',
        'minNumber' => 1,
        'maxNumber' => 5,
        'numberOfVariables' => 2
    );

    /**
     * The variables used in the equation.
     *
     * @access public
     * @var array
     */
    public $variables = array();

    /*
     * The math equation.
     *
     * @access public
     * @var string
     */
    public $equation = null;

    /**
     * Configuration method.
     *
     * @access public
     * @param object $model
     * @param array $settings
     */
    public function initialize(&$controller, $settings = array()) {
        $this->settings = array_merge($this->__defaults, $settings);
    }

    /*
     * Method that generates a math equation based on the component settings. It also calls
     * a secondary function, registerAnswer(), which determines the answer to the equation
     * and sets it as a session variable.
     *
     * @access public
     * @return string
     *
     */
    public function generateEquation() {
        // Loop through our range of variables and set a random number for each one.
        foreach (range(1, $this->settings['numberOfVariables']) as $variable) {
            $this->variables[] = rand($this->settings['minNumber'], $this->settings['maxNumber']);
        }

       // debug($this->settings); debug($this->variables);
        $this->equation = implode(' ' . $this->settings['operand'] . ' ', $this->variables);
        //debug($this->equation);
        // This function determines the answer to the equation and stores it as a session variable.
        $this->registerAnswer();

        return $this->equation;
    }

    /*
     * Determines the answer to the math question from the variables set in generateEquation()
     * and registers it as a session variable.
     *
     * @access public
     * @return integer
     */
    public function registerAnswer() {
        // The eval() function gives us the $answer variable.
        eval("$answer = ".$this->equation.";");

        $this->Session->write('MathCaptcha.answer', $answer);

        return $answer;
    }

    /*
     * Compares the given data to the registered equation answer.
     *
     * @access public
     * @return boolean
     */
    public function validates($data) {
        return $data == $this->Session->read('MathCaptcha.answer');
    }

}

?>

Usage is really simple – I’ll run through putting it into an equally simple contact form, which looks a lot like the one done by Jonathan Snook. The Contact model looks like this (app/models/contact.php):

<?php
class Contact extends AppModel {
    var $name = 'Contact';
    var $useTable = false;

    var $_schema = array(
        'name'		=>array('type' => 'string', 'length' => 100),
        'email'		=>array('type' => 'string', 'length' => 255),
        'comments'	=>array('type' => 'text')
    );

    var $validate = array(
        'name' => array(
            'rule'=>array('minLength', 1),
            'message'=>'Please enter a name so the Geek know what to call you!' ),
        'email' => array(
            'rule'=>'email',
            'message'=>'Please enter an email address so the Geek knows how to reach you.' ),
        'details' => array(
            'rule'=>array('minLength', 1),
            'message'=> 'Don't forget to enter some comments.' )
    );
}
?>

No DB; manual schema; just a placeholder, really.

The Contact controller is set up like this (app/controllers/contact_controller.php):

<?php
class ContactController extends AppController {
    var $name = 'Contact';
    var $uses = 'Contact';
    var $components = array('RequestHandler', 'Email', 'Session', 'MathCaptcha');

    function index() {
        if ($this->RequestHandler->isPost()) {
            $this->Contact->set($this->data);
            if ($this->MathCaptcha->validates($this->data['Contact']['security_code'])) {
                if ($this->Contact->validates()) {
                    $this->Email->to = Configure::read('SiteSettings.email_form_address');
                    $this->Email->subject = 'Contact from message from ' . $this->data['Contact']['name'];
                    $this->Email->from = $this->data['Contact']['email'];

                    $this->Email->send($this->data['Contact']['comments']);
                }
            } else {
                $this->Session->setFlash(__('Please enter the correct answer to the math question.', true));
            }
        }

        $this->set('mathCaptcha', $this->MathCaptcha->generateEquation());
    }
}
?>

So, we’ve added MathCaptcha to our list of components. There are various configuration options which you can set when adding MathCaptcha to the $components array – the config array (with defaults) looks like this:

private $__defaults = array(
        'operand' => '+',
        'minNumber' => 1,
        'maxNumber' => 5,
        'numberOfVariables' => 2
    );

In the index() method, you can see the usage: if we’ve got a POST request, we call the component’s validates() method and pass to it the relevant data from the form – the user’s answer to the question. If it validates then we continue with the rest of the data validation, otherwise we give an error message. You’ll notice that the generateEquation() method is called regardless; we want a new question generated each time the page loads.

Finally, we just need one line in the view to grab the ’security_code’. Here’s the entire contact form (app/views/contact/index.ctp):

<?php
echo $form->create('Contact', array('url' => $this->here));
echo $form->input('name');
echo $form->input('email');
echo $form->input('comments');
echo $form->input('security_code', array('label' => 'Please Enter the Sum of ' . $mathCaptcha));
echo $form->end(array('name' => 'Send', 'class' => 'input_btn'));
?>

I’ve called the form field ’security_code’, but you can call it whatever you want.

And that’s it! A plain text math ‘captcha’ in almost no time.