I don’t know about you, but I’m tired of clients treating web developers like brainless code monkeys. After so many years as a part of a legitimate and growing industry, why are web developers not yet treated like experts? Why do clients demand the final say on the presentation and mechanics of a website? Web developers are the experts. We know what works best in user interfaces. We know what attracts the eye. We know how to word page copy and navigation items. We can distinguish between a good colour scheme and a horrible one. So why do most clients insist on getting their own way, all the time?
In my few years as a web professional I’ve been the primary developer of dozens of sites, and I’ve had my hands in over a hundred. I’ve written two complete content management systems which power most of those aforementioned sites. I’ve also extensively studied the best design, usability, and accessibility practices and take pride in being able to tell a client what “works” with the utmost confidence. Yet all too often, the response is “well we want it this way”, or “we think this text should be bigger”, or “we think this should go over here”, or, even better, “my friends think this doesn’t work, so change it to something else”. Yet these same clients usually have little to no knowledge of how to make a website or what design elements work best on the web. If they do have design knowledge, it’s usually very, very rudimentary, almost always outdated, and most likely relating to print, which is (of course) a vastly different medium and should be treated as such. In other words, most clients don’t know what works.
Let’s say that you need to get your car repaired and you know absolutely nothing about cars. Would you squabble with the mechanic over the inner workings of the engine? I don’t think so. Would you ask your friend for his opinion, even though he’s equally clueless? Probably not. So why are web professionals not treated with the same respect? It astounds me to no end that many people still view a website as something that is easy to make and maintain. Websites are easy to make and relatively simple to maintain, but it is very difficult to make an effective website and requires expertise not unlike that of a trained mechanic.
Just as anyone can pop the hood of their car and start playing around with the parts, anyone can throw a website on the Internet. But it takes special skill to craft a website that will attract visitors, generate business, and generally serve as a respectable online presence for an individual or company. So please, clients, keep that in mind before you start assuming that you are the expert, simply because the website is for you. No. We are the experts.
If you develop in PHP5 and you use Security::cipher in your CakePHP application – which is unlikely, since it’s an obscure function really only used by CookieComponent – and you also use PHP functions like rand() and array_rand(), you might notice an annoying bug: you’re getting the same random numbers each time. This is due to a bug of sorts in the Security class.
Line 183 of Security::cipher() reads:
srand(CIPHER_SEED);
This call to srand(), which seeds PHP’s random number generator, is only necessary in PHP4. In PHP5, the seeding process is done automatically and you’ll find that calling srand() in this way will persist for all subsequent calls of rand() and the like. So, all we need to do is wrap srand() in an if check:
if (!PHP5) {
srand(CIPHER_SEED);
}
And that’s it! I’ve submitted a ticket at code.cakephp.org, so hopefully a fix comes into the core.
… barely. Busy. Still working on migrating to a new theme. Oh, and thank my hosting company for screwing up and rendering all of my MySQL databases useless for almost 24 hours!
Reorganizing and switching to a much slicker theme. Everything should be nice and clean again soon.
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:
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.
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:
And that’s it – see you next Friday.
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.
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!
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!
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:
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.
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!
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!