<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Jamie Nay &#187; usability</title>
	<atom:link href="http://jamienay.com/tag/usability/feed/" rel="self" type="application/rss+xml" />
	<link>http://jamienay.com</link>
	<description>A PHP web developer writing about the web.</description>
	<lastBuildDate>Sat, 21 Aug 2010 21:11:22 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>Adding Automatic Query Caching to Model::find() in CakePHP 1.2</title>
		<link>http://jamienay.com/2009/11/adding-automatic-caching-to-model-find-in-cakephp-1-2/</link>
		<comments>http://jamienay.com/2009/11/adding-automatic-caching-to-model-find-in-cakephp-1-2/#comments</comments>
		<pubDate>Tue, 24 Nov 2009 14:30:58 +0000</pubDate>
		<dc:creator>Jamie</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[usability]]></category>

		<guid isPermaLink="false">http://jamienay.com/?p=451</guid>
		<description><![CDATA[If you use CakePHP&#8217;s caching system but don&#8217;t like having to wrap you calls to Model::find() in if statements (&#8220;if cache result found&#8230; else&#8230;&#8221;) then this little quick tip is for you. Basically, we&#8217;re just going to put a slightly modified version of Model::find() in AppModel. Our new find() method will check for the existence of a &#8216;cache&#8217; argument in [...]]]></description>
			<content:encoded><![CDATA[<p>If you use CakePHP&#8217;s caching system but don&#8217;t like having to wrap you calls to Model::find() in if statements (&#8220;if cache result found&#8230; else&#8230;&#8221;) then this little quick tip is for you. Basically, we&#8217;re just going to put a slightly modified version of Model::find() in AppModel. Our new find() method will check for the existence of a &#8216;cache&#8217; argument in the options array. Then, the function will either grab the query results from the cache or run a DB query, depending on what&#8217;s passed in the &#8216;cache&#8217; argument.</p>
<p>A couple of notes:</p>
<ul>
<li>Automatic caching only occurs if debug is set to 0.</li>
<li>The updated Model::find() function also implements Matt Curry&#8217;s version of <a href="http://www.pseudocoder.com/archives/2008/10/24/cakephp-custom-find-types/">custom find methods</a>.</li>
<li>After writing this code a few months ago, I came across <a href="http://www.endyourif.com/caching-queries-in-cakephp/">another implementation</a> of the same idea on End Your If. The two methods are pretty similar, but we came to the results independently. Must mean it&#8217;s a pretty good system. <img src='http://jamienay.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </li>
</ul>
<p>We&#8217;re putting two methods into AppModel: find() and __getCachedResults(), a helper function. You can download the latest version of this code at <a href="http://github.com/jamienay/automatic_query_caching">my GitHub repository</a>, or grab it from the bottom of this post.</p>
<p>Usage is simple:</p>
<ul>
<li>Whenever you want to cache a Model::find() result, pass &#8216;cache&#8217; as one of the method&#8217;s arguments.  Pass it as either a string or an array.
<ul>
<li>string: the name used to generate the cache key, which takes the format: model_alias_cache_key</li>
<li>array: two valid arguments: &#8216;name&#8217;, used as above, and &#8216;config&#8217;, optionally used to pass the name of the cache config to use. &#8216;default&#8217; is used if not otherwise specified.</li>
</ul>
</li>
<li>And that&#8217;s it! Enjoy the speed benefits, but use caching carefully. Pay attention to your cache results; only cache stuff that makes sense (for example, only cache user-specific stuff if you specify the user ID in the cache name).</li>
</ul>
<p>The code:</p>
<pre class="brush: php">
&amp;lt;?php
class AppModel extends Model {
/**
   * Adds support for custom find methods (__findXX) and automatic caching.
   * Automatic caching will kick in when &#039;cache&#039; is passed in the $options
   * array.
   *
   * If &#039;cache&#039; is a string, then it will be used to generate the
   * cache name, which takes the format model_alias_cache_name.
   *
   * If &#039;cache&#039; is an array, then two arguments are valid: &#039;name&#039;, required,
   * and &#039;config&#039;, optional. &#039;name&#039; is used as above, while &#039;config&#039;
   * determines the cache configuration to use - &#039;default&#039; if not specified.
   */
  function find($type, $options = array()) {
      $results = $this-&gt;_getCachedResults($options);
      if (!$results) {
          $method = null;
          if (is_string($type)) {
              $method = sprintf(&#039;__find%s&#039;, Inflector::camelize($type));
          }

          if ($method &amp;&amp; method_exists($this, $method)) {
              $results = $this-&gt;{$method}($options);
          } else {
              $args = func_get_args();
              $results = call_user_func_array(array(&#039;parent&#039;, &#039;find&#039;), $args);
          }
          if ($this-&gt;useCache) {
              Cache::write($this-&gt;cacheName, $results, $this-&gt;cacheConfig);
          }
      }

      return $results;
  }

  function _getCachedResults($options) {
      $this-&gt;useCache = true;
      if (Configure::read(&#039;debug&#039;) &gt; 0 || !is_array($options) || !isset($options[&#039;cache&#039;]) || $options[&#039;cache&#039;] == false) {
          $this-&gt;useCache = false;
          return false;
      }

      if (is_string($options[&#039;cache&#039;])) {
          $this-&gt;cacheName = $this-&gt;alias . &#039;_&#039; . $options[&#039;cache&#039;];
      } else {
          if (!isset($options[&#039;cache&#039;][&#039;name&#039;])) {
              return false;
          }
          $this-&gt;cacheName = $this-&gt;alias . &#039;_&#039; . $options[&#039;cache&#039;][&#039;name&#039;];
          $this-&gt;cacheConfig = isset($options[&#039;cache&#039;][&#039;config&#039;]) ? $options[&#039;cache&#039;][&#039;config&#039;] : &#039;default&#039;;
      }

      $results = Cache::read($this-&gt;cacheName, $this-&gt;cacheConfig);

      return $results;
  }
}
?&gt;
</pre>
]]></content:encoded>
			<wfw:commentRss>http://jamienay.com/2009/11/adding-automatic-caching-to-model-find-in-cakephp-1-2/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Auto Paginate Component for CakePHP (Persisting Pagination Limits 2.0)</title>
		<link>http://jamienay.com/2009/07/auto-paginate-component-for-cakephp-persisting-pagination-limits-20/</link>
		<comments>http://jamienay.com/2009/07/auto-paginate-component-for-cakephp-persisting-pagination-limits-20/#comments</comments>
		<pubDate>Mon, 13 Jul 2009 15:54:00 +0000</pubDate>
		<dc:creator>Jamie</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[usability]]></category>

		<guid isPermaLink="false">http://jamienay.com/?p=267</guid>
		<description><![CDATA[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 &#8211; a system I developed weeks ago, I wondered to myself why I just didn&#8217;t make it a component and boost its portability by a billion percent. So, I whipped it up and [...]]]></description>
			<content:encoded><![CDATA[<p>Nothing like writing a tutorial on your code to realize how you could improve it! Just minutes after writing a tutorial on <a href="http://jamienay.com/2009/07/persisting-pagination-limits-in-cakephp/">persisting pagination limits in CakePHP</a> &#8211; a system I developed weeks ago, I wondered to myself why I just didn&#8217;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&#8217;ll paste in those parts of the tutorial from version 1.0. Speaking of which:</p>
<p>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&#8217;s preference should also persist through the session no matter what data the application is serving up. And, ideally, we don&#8217;t want to POST every time we want to change the pagination limit; I like having it right in the URL for easy reading.</p>
<p>So here&#8217;s a component that shows how easy it is to put in user-modifiable, session-persisting pagination limits.</p>
<p>The full component (<strong>app/controllers/components/auto_paginate.php</strong>):</p>
<pre class="brush: php">
&lt;?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(&#039;Session&#039;);

 /**
 * 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(
 &#039;options&#039; =&gt; array(1, 5, 10, 25, 50, 100),
 &#039;defaultLimit&#039; =&gt; 25
 );

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

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

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

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

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

 return ($this-&gt;Session-&gt;check(&#039;Pagination.limit&#039;) ? $this-&gt;Session-&gt;read(&#039;Pagination.limit&#039;) :
 $this-&gt;settings[&#039;defaultLimit&#039;]);
 }

}

?&gt;
</pre>
<p>Two config options: &#8216;options&#8217;, which determines the available results-per-page options for the user, and &#8216;defaultLimit&#8217;, which, predictably, determines the default pagination results limit.</p>
<p>To use the component, just plug it into your AppController&#8217;s $components array (<strong>app/app_controller.php</strong>):</p>
<pre class="brush: php">
var $components = array(&#039;AutoPaginate&#039;);
</pre>
<p>Now we need to change the way we configure pagination in individual controllers. This is the easy part &#8211; just call AutoPaginate::setPaginate() instead of explicitly defining $this-&gt;paginate when setting up your pagination. For example, in our <em>PlayersController::admin_index()</em> function we&#8217;re used to doing this:</p>
<pre class="brush: php">
$this-&gt;paginate = array(
 &#039;limit&#039; =&gt; 20,
 &#039;contain&#039; =&gt; array(&#039;Player, &#039;Position&#039;)
);
$this-&gt;set(&#039;players&#039;, $this-&gt;paginate());
</pre>
<p>But now we&#8217;ll just do this (<strong>app/controllers/players_controller.php</strong>):</p>
<pre class="brush: php">
function admin_index() {
 $this-&gt;AugoPaginate-&gt;setPaginate(array(
 &#039;contain&#039; =&gt; array(&#039;Team&#039;, &#039;Position&#039;)
 ));

 $this-&gt;set(&#039;players&#039;, $this-&gt;paginate());
}
</pre>
<p>Finally, since our pagination will be standardized through the entire site, we&#8217;ll set up a couple of common view elements.</p>
<p>The top element will present the choices for the number of results per page, as well as the usual &#8220;display X out of X results&#8221; line. It looks like this (<strong>app/views/elements/pagination/top.ctp</strong>):</p>
<pre class="brush: php">
 Results per page:
 &lt;?php
 $results = array();
 foreach ((array)$paginationOptions as $option) {
 if ($paginationLimit == $option) {
 $results[] = $option;
 } else {
 $args = $this-&gt;passedArgs;
 $args[&#039;Paginate&#039;] = $option;
 $results[] = $html-&gt;link($option, $args);
 }
 }
 echo implode(&quot; | &quot;, $results);
 ?&gt;

&lt;?php
echo $paginator-&gt;counter(array(
&#039;format&#039; =&gt; __(&#039;Page %page% of %pages%, showing %current% records out of %count% total, starting on record %start%, ending on %end%.&#039;, true)
));
?&gt;
</pre>
<p>That&#8217;ll produce a string of plain text links &#8211; not a form &#8211; that the user can use to change pagination limits. The output looks like this:</p>
<pre>Results per page: 1 | 5 | 10 | 25 | 50 | 100

Page 1 of 105, showing 10 records out of 1042 total... (etc)</pre>
<p>$paginationOptions and $paginationLimit are set in AutoPaginateComponent.</p>
<p>We&#8217;ll also make a new element for the bottom of paginated results too (<strong>app/views/elements/pagination/bottom.ctp</strong>):</p>
<pre class="brush: php">
&lt;div class=&quot;paging&quot;&gt;
 &lt;?php echo $paginator-&gt;prev(&#039;&lt;&lt; &#039;.__(&#039;previous&#039;, true), array(&#039;url&#039; =&gt; $this-&gt;passedArgs), null, array(&#039;class&#039;=&gt;&#039;disabled&#039;));?&gt;
 | 	&lt;?php echo $paginator-&gt;numbers(array(&#039;url&#039; =&gt; $this-&gt;passedArgs));?&gt;
 &lt;?php echo $paginator-&gt;next(__(&#039;next&#039;, true).&#039; &gt;&gt;&#039;, array(&#039;url&#039; =&gt; $this-&gt;passedArgs), null, array(&#039;class&#039;=&gt;&#039;disabled&#039;));?&gt;&lt;/div&gt;
</pre>
<p>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&#8217;d like. If we&#8217;re paginating in, say, <em>PlayersController::admin_index</em>, it might look something like this (<strong>app/views/players/admin_index.ctp</strong>):</p>
<pre class="brush: php">
&lt;h2&gt;&lt;?php __(&#039;Players&#039;);?&gt;&lt;/h2&gt;
&lt;table cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;table_list&quot;&gt;
 &lt;caption&gt;
 &lt;?php echo $this-&gt;element(&#039;pagination/top&#039;); ?&gt;
 &lt;/caption&gt;
 .... (data in table) ...&lt;/table&gt;
&lt;?php echo $this-&gt;element(&#039;pagination/bottom&#039;); ?&gt;
</pre>
<p>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 &#8220;$this-&gt;paginate =&#8221; business in controllers.</p>
<p>As always, comments, suggestions, improvements and helpful criticisms welcome!</p>
]]></content:encoded>
			<wfw:commentRss>http://jamienay.com/2009/07/auto-paginate-component-for-cakephp-persisting-pagination-limits-20/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Persisting Pagination Limits in CakePHP</title>
		<link>http://jamienay.com/2009/07/persisting-pagination-limits-in-cakephp/</link>
		<comments>http://jamienay.com/2009/07/persisting-pagination-limits-in-cakephp/#comments</comments>
		<pubDate>Mon, 13 Jul 2009 03:46:02 +0000</pubDate>
		<dc:creator>Jamie</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[usability]]></category>

		<guid isPermaLink="false">http://jamienay.com/?p=262</guid>
		<description><![CDATA[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&#8217;s preference should also persist through the session no matter what data the application is serving up. And, ideally, we don&#8217;t want to POST every time [...]]]></description>
			<content:encoded><![CDATA[<p>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&#8217;s preference should also persist through the session no matter what data the application is serving up. And, ideally, we don&#8217;t want to POST every time we want to change the pagination limit: I like having it right in the URL for easy reading.</p>
<p>So, I&#8217;m just going to do a little quickie here to show you how easy it is to put in user-modifiable pagination limits. We&#8217;ll write a common view element for pagination, then we&#8217;ll turn to AppController and write a couple of functions to handle setting the pagination limit and interpreting changes to that limit. Finally, we&#8217;ll slightly modify the way we set up pagination in individual controllers to handle the new code. It&#8217;ll go something like this:</p>
<ul>
<li>Setting up Common View Elements</li>
<li>New Functions in AppController</li>
<li>Modifying Individual Controller Pagination Calls</li>
</ul>
<h3>Setting up a Common View Element</h3>
<p>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 &#8220;display X out of X results&#8221; line. It looks like this (<strong>app/views/elements/pagination/top.ctp</strong>):</p>
<pre class="brush: php">
 Results per page:
 &lt;?php
 $results = array();
 foreach ((array)$paginationOptions as $option) {
 if ($paginationLimit == $option) {
 $results[] = $option;
 } else {
 $args = $this-&gt;passedArgs;
 $args[&#039;Paginate&#039;] = $option;
 $results[] = $htmla-&gt;link($option, $args);
 }
 }
 echo implode(&quot; | &quot;, $results);
 ?&gt;

&lt;?php
echo $paginator-&gt;counter(array(
&#039;format&#039; =&gt; __(&#039;Page %page% of %pages%, showing %current% records out of %count% total, starting on record %start%, ending on %end%.&#039;, true)
));
?&gt;
</pre>
<p>That&#8217;ll produce a string of plain text links &#8211; not a form &#8211; that the user can use to change pagination limits. The output looks like this:</p>
<pre>Results per page: 1 | 5 | 10 | 25 | 50 | 100

Page 1 of 105, showing 10 records out of 1042 total... (etc)</pre>
<p>There are two variables in the pagination/top.ctp that we need to define, as well &#8211; $paginationOptions and $paginationLimit. You just need to set these somewhere in your AppController before the view is rendered, so in, say, beforeFilter() &#8211; I actually set mine in conjunction with my <a href="http://jamienay.com/2009/06/a-quick-dirty-and-useful-widget-component-for-cakephp/">quick and dirty widget component</a> (/end shameless plug <img src='http://jamienay.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> ):</p>
<pre class="brush: php">
// Pagination options
$this-&gt;set(&#039;paginationOptions&#039;, array(1, 5, 10, 25, 50, 100));
$this-&gt;set(&#039;paginationLimit&#039;, $this-&gt;_paginationLimit());
</pre>
<p>We&#8217;ll write the _paginationLimit() function in the next section.</p>
<p>We&#8217;ll also make a new element for the bottom of paginated results too (<strong>app/views/elements/pagination/bottom.ctp</strong>):</p>
<pre class="brush: php">
&lt;div class=&quot;paging&quot;&gt;
 &lt;?php echo $paginator-&gt;prev(&#039;&lt;&lt; &#039;.__(&#039;previous&#039;, true), array(&#039;url&#039; =&gt; $this-&gt;passedArgs), null, array(&#039;class&#039;=&gt;&#039;disabled&#039;));?&gt;
 | 	&lt;?php echo $paginator-&gt;numbers(array(&#039;url&#039; =&gt; $this-&gt;passedArgs));?&gt;
 &lt;?php echo $paginator-&gt;next(__(&#039;next&#039;, true).&#039; &gt;&gt;&#039;, array(&#039;url&#039; =&gt; $this-&gt;passedArgs), null, array(&#039;class&#039;=&gt;&#039;disabled&#039;));?&gt;&lt;/div&gt;
</pre>
<p>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&#8217;d like. If we&#8217;re paginating in, say, <em>PlayersController::admin_index</em>, it might look something like this (<strong>app/views/players/admin_index.ctp</strong>):</p>
<pre class="brush: html">
&lt;h2&gt;&lt;?php __(&#039;Players&#039;);?&gt;&lt;/h2&gt;
&lt;table cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;table_list&quot;&gt;
 &lt;caption&gt;
 &lt;?php echo $this-&gt;element(&#039;pagination/top&#039;); ?&gt;
 &lt;/caption&gt;
 .... (data in table) ...&lt;/table&gt;
&lt;?php echo $this-&gt;element(&#039;pagination/bottom&#039;); ?&gt;
</pre>
<p>The views are finished &#8211; now we just need the methods to make it all work.</p>
<h3>New Functions in AppController</h3>
<p>Two new functions here, one that we&#8217;ll use in the individual controllers in place of defining $this-&gt;paginate, and another one called by that function to set the pagination limit depending on possible user input and the existing session-written value.</p>
<p>The first method, _setPaginate(), is just a shortcut for setting $this-&gt;paginate in controllers, so we don&#8217;t need to set the limit every time. It&#8217;s short and sweet (<strong>app/app_controller.php</strong>):</p>
<pre class="brush: php">
protected function _setPaginate($options = array()) {
 $defaults = array(
 &#039;limit&#039; =&gt; $this-&gt;_paginationLimit()
 );

 $this-&gt;paginate = array_merge($defaults, $options);
}
</pre>
<p>Notice that _setPaginate() calls _paginationLimit(), which is the function that sets and gets the limit in the user&#8217;s session:</p>
<pre class="brush: php">
protected function _paginationLimit() {
 if (isset($this-&gt;params[&#039;named&#039;][&#039;Paginate&#039;])) {
 $this-&gt;Session-&gt;write(&#039;Pagination.limit&#039;, $this-&gt;params[&#039;named&#039;][&#039;Paginate&#039;]);
 }

 // On two lines for blog readability.
 return ($this-&gt;Session-&gt;check(&#039;Pagination.limit&#039;) ? $this-&gt;Session-&gt;read(&#039;Pagination.limit&#039;) :
 Configure::read(&#039;SiteSettings.default_pagination_limit&#039;));
}
</pre>
<p>The system is done &#8211; now we just have to use it!</p>
<h3>Modifying Individual Controller Pagination Calls</h3>
<p>This is the easy part &#8211; just call _setPaginate() instead of explicitly defining $this-&gt;paginate when setting up your pagination. For example, in our <em>PlayersController::admin_index()</em> function we&#8217;re used to doing this:</p>
<pre class="brush: php">
$this-&gt;paginate = array(
 &#039;limit&#039; =&gt; 20,
 &#039;contain&#039; =&gt; array(&#039;Player, &#039;Position&#039;)
);
$this-&gt;set(&#039;players&#039;, $this-&gt;paginate());
</pre>
<p>But now we&#8217;ll just do this (<strong>app/controllers/players_controller.php</strong>):</p>
<pre class="brush: php">
function admin_index() {
 $this-&gt;_setPaginate(array(
 &#039;contain&#039; =&gt; array(&#039;Team&#039;, &#039;Position&#039;)
 ));

 $this-&gt;set(&#039;players&#039;, $this-&gt;paginate());
}
</pre>
<p>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 &#8220;$this-&gt;paginate =&#8221; business in controllers.</p>
<p>As always, comments, suggestions, improvements and helpful criticisms welcome!</p>
]]></content:encoded>
			<wfw:commentRss>http://jamienay.com/2009/07/persisting-pagination-limits-in-cakephp/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Your Splash Page Sucks</title>
		<link>http://jamienay.com/2009/05/your-splash-page-sucks/</link>
		<comments>http://jamienay.com/2009/05/your-splash-page-sucks/#comments</comments>
		<pubDate>Tue, 05 May 2009 18:17:12 +0000</pubDate>
		<dc:creator>Jamie</dc:creator>
				<category><![CDATA[Opinion]]></category>
		<category><![CDATA[accessibility]]></category>
		<category><![CDATA[bad clients]]></category>
		<category><![CDATA[flash]]></category>
		<category><![CDATA[pet peeves]]></category>
		<category><![CDATA[rant]]></category>
		<category><![CDATA[splash pages]]></category>
		<category><![CDATA[usability]]></category>

		<guid isPermaLink="false">http://www.jamienay.com/?p=115</guid>
		<description><![CDATA[You heard me &#8211; it sucks! I&#8217;m especially looking at you, Flash developers. To be fair, yours isn&#8217;t the only one that sucks &#8211; all splash pages (enter gross generalization here) suck. The internet is about content, and splash pages, especially those containing the ubiquitous and oh-so-irrelevant &#8216;Flash introduction&#8217;. The more clicking a user has to do to get to [...]]]></description>
			<content:encoded><![CDATA[<h3>You heard me &#8211; it sucks! I&#8217;m especially looking at you, Flash developers.</h3>
<p>To be fair, yours isn&#8217;t the only one that sucks &#8211; <em>all</em> splash pages (enter gross generalization here) suck. The internet is about <em>content</em>, and splash pages, especially those containing the ubiquitous and oh-so-irrelevant &#8216;Flash introduction&#8217;.</p>
<p>The more clicking a user has to do to get to the content he&#8217;s looking for (or the content you want him to find), the less likely he is to actually get there. If I&#8217;m shopping for an item and I can buy it at two stores, but one of the stores has three doors blocking the item and the other store has no doors at all, which store am I likely to enter? Common sense would dictate that I would enter the store that&#8217;s easier to navigate and has less barriers between me and the content I want.</p>
<p>So why, then, are Flash splash pages so popular? The number of &#8216;professional&#8217; websites that have useless splash pages &#8211; often in a slow-loading medium such as Flash &#8211; astounds me. Clients usually ask for splash pages because it gives them a chance to show off, either that they have a big budget or that they like to be f(F)lashy. But what these clients don&#8217;t realize is that a splash page can often trip up search engine spider bots, since it represents a barrier between the spider and the real content. The worst case scenario is that the spider will index <em>only</em> the splash page, leaving your content in the dark.</p>
<p>Splash pages are usually self-indulgent and extraneous. Moreover, those who push for their inclusion on a website usually don&#8217;t understand the internet as a medium. Flash/splash advocates usually have too much money and not enough brains, so they just throw a whole bunch of cash at a company because they want something that looks &#8216;cool&#8217; or &#8216;flashy&#8217; or &#8216;impressive&#8217; or whatever; usability and content definitely are not priorities. The minority of splash pages that <em>do</em> have content could probably be retooled so that the content is just on the &#8216;main&#8217; homepage of the website. Jared Spool, who is with User Interface at Macromedia, is quoted on <a href="http://www.marketingsherpa.com/sample.cfm?contentID=2529">MarketingSherpa.com</a> as saying:</p>
<blockquote><p>When we have clients who are thinking about Flash splash pages, we tell them to go to their local supermarket and bring a mime with them. Have the mime stand in front of the supermarket, and, as each customer tries to enter, do a little show that lasts two minutes, welcoming them to the supermarket and trying to explain the bread is on aisle six and milk is on sale today. <em>(taken from </em><a href="http://www.seomoz.org/blog/how-to-convince-a-client-they-dont-need-a-splash-page" target="_blank">SEOmoz</a>)<a href="http://www.seomoz.org/blog/how-to-convince-a-client-they-dont-need-a-splash-page" target="_blank"><br />
</a></p></blockquote>
<p>Could you imagine that? I know I&#8217;d last about six seconds before either punching the mime in the face or going to a different store. And, in website terms, since you can&#8217;t usually punch site owners in the face, then you&#8217;ll probably just go somewhere else for the same information.</p>
<p>Jakob Nielson, who would get my vote for the President of the Internet, lists Flash &#8211; that fixture of bad splash pages &#8211; as one of the <a href="http://www.useit.com/alertbox/designmistakes.html">Top Ten Web Design Mistakes  of 2005</a> (all of which still apply today). Nielson writes that Flash intro pages are &#8220;so bad that even the most clueless Web designers won&#8217;t recommend them, even though a few (even more clueless) clients continue to request them&#8221;.<a href="http://www.smashingmagazine.com/2007/10/11/splash-pages-do-we-really-need-them/" target="_blank"> Smashing Magazine</a> has an excellent showcase of splash pages, from the <a title="Why make your visitors guess??" href="http://www.funkypunky.ru/" target="_blank">very worst</a> to the <a title="Talk about completely extraneous." href="http://www.hrubes.com/" target="_blank">poorly designed</a> to the <a title="At least it has some information." href="http://www.apple.com/" target="_blank">not-so-intolerable-but-still-unnecessary</a>. Anytime I come across a splash page &#8211; especially one done in Flash &#8211; I turn around and walk my virtual self out the door.</p>
<p>I&#8217;ll end with another golden Nielson quote from the same article as above &#8211; some advice on how to design your website so that you don&#8217;t need Flash, splash, or anything of the sort:</p>
<blockquote><p>Flash should not be used to jazz up a page. If your content is boring, rewrite text to make it more compelling and hire a professional photographer to shoot better photos. Don&#8217;t make your pages move. It doesn&#8217;t increase users&#8217; attention, it drives them away; most people <strong>equate animated content with useless content</strong>.</p></blockquote>
]]></content:encoded>
			<wfw:commentRss>http://jamienay.com/2009/05/your-splash-page-sucks/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Click here to kiss my&#8230;</title>
		<link>http://jamienay.com/2009/04/click-here-to-kiss-my/</link>
		<comments>http://jamienay.com/2009/04/click-here-to-kiss-my/#comments</comments>
		<pubDate>Wed, 08 Apr 2009 04:41:03 +0000</pubDate>
		<dc:creator>Jamie</dc:creator>
				<category><![CDATA[Opinion]]></category>
		<category><![CDATA[accessibility]]></category>
		<category><![CDATA[pet peeves]]></category>
		<category><![CDATA[rant]]></category>
		<category><![CDATA[usability]]></category>

		<guid isPermaLink="false">http://www.jamienay.com/?p=90</guid>
		<description><![CDATA[If I had to choose my top pet peeve relating to the world of website design, it would definitely be that one, annoying, lazy, useless phrase to indicate a link: click here! Click where? Why here? Why not there? Where else am I going to click? What am I clicking on? Where am I going? Those are just a few [...]]]></description>
			<content:encoded><![CDATA[<p>If I had to choose my top pet peeve relating to the world of website design, it would definitely be that one, annoying, lazy, useless phrase to indicate a link:</p>
<h2><a title="Hah, made you look." href="#">click here!</a></h2>
<p>Click where? Why here? Why not there? Where else am I going to click? What am I clicking on? Where am I going? Those are just a few of the questions that links labeled &#8220;click here&#8221; raise.</p>
<p>The world wide web should be, in my opinion, a loosely linked repository of semantic resources. That&#8217;s the ideal situation, of course; since 99% of everything on the WWW is horrible, we&#8217;re not there yet. But we few decent developers can still try to make our websites as semantic and sensible as possible. So, while they should be obvious, links don&#8217;t need to jump out of the screen and slap the user in the face. &#8220;Click here!&#8221;? Seriously? Don&#8217;t insult a user&#8217;s intelligence. If a visitor wants to follow a link, he&#8217;ll follow the link. <strong>But</strong>, that link needs to speak for itself.</p>
<p>By &#8216;<strong>speak for itself</strong>&#8216;, I mean that the clickable portion of text should describe to the user what&#8217;s on the other side of the click. If you want to link to a page on, say, your favourite hockey team, don&#8217;t write this:</p>
<blockquote><p>Want to read more about the Vancouver Canucks? <span style="text-decoration: underline;">Click here!</span></p></blockquote>
<p>As you can see, the link (&#8220;Click here!&#8221;) doesn&#8217;t actually say anything about the resource to which it is pointing. Sure, there&#8217;s an explanation beforehand. But I would argue that anything <em>surrounding</em> the link is irrelevant; the eye is drawn to the link itself, so that&#8217;s where the important information should be. The World Wide Web Consortium (WC3), in its article &#8220;<a href="http://www.w3.org/QA/Tips/noClickHere">Don&#8217;t use &#8216;click here&#8217; as link text</a>&#8220;, recommends that link text should offer an exaplanation of the page to which it links and the information it offers, even when read out of context. The W3C emphasizes that one should not focus link text around mechanics (&#8220;follow this link here&#8221;), action verbs (&#8220;click&#8221;), and so on.</p>
<p>Website visitors know what links are and how to recognize them without explaining the mechanics of linking in the text. Too many web developers these days don&#8217;t want to give their users any credit. The bottom line is that if a person can figure out how to turn on his computer, open his internet browser, and navigate to a website, it&#8217;s a pretty safe bet that he&#8217;ll be able to recognize <strong>properly styled </strong>links. I say &#8216;properly styled&#8217; because too many website owners try to be too pretty/stylish/different when it comes to presenting links. A link should be <strong>the last spot </strong>that the user expects to have strange styling. If links are recognizable &#8211; underlined, a different colour, an obvious change on hover (larger font, etc.) &#8211; users will find and follow them. One need not make the text blinking with a scrolling marquee; an understated but clear indication of a link to another resource will do the job. And, since we&#8217;re explaining the target resource in the text of the link, we don&#8217;t need to give a silly instruction like &#8216;click here&#8217; to the user. A stop sign doesn&#8217;t say &#8220;press on the brake pedal&#8221;, nor does the door to a house say &#8220;open this to enter&#8221;. If we let the link speak for itself and put it in the context of other text, it will look something like this:</p>
<blockquote><p><a href="#">The Vancouver Canucks</a> are my all-time favourite hockey team. They have speed, grit, scoring, and defense.</p></blockquote>
<p>One would expect to find a website about the Vancouver Canucks on the other side of this link. We don&#8217;t need to say &#8220;click&#8221;, &#8220;follow&#8221;, or even something like &#8220;more information&#8221; &#8211; for example, &#8216;<a href="#">More Information on the Vancouver Canucks</a>&#8216; &#8211; since the mere existence of the link implies that we&#8217;ll be getting more information. Additionally, this link tells the visitor exactly what to expect when he decides to follow without revealing <em>how </em>the visitor should do it.  Remember, not everyone will be &#8216;clicking&#8217; on your links, since only a mouse clicks. Users navigating with the keyboard, a touch phone, a screen reader, and so on will not be clicking anything (admittedly most users will know what &#8216;click&#8217; means even if they&#8217;re not using  a mouse, but it&#8217;s still good practice not to force mechanics on your visitors).</p>
<p>In his excellent article &#8220;<a href="http://www.useit.com/alertbox/designmistakes.html">Top Ten Web Design Mistakes of 2005</a>&#8221; (and, believe me, those mistakes still apply today), web usability and accessibility guru Jakob Nielson advises that links should</p>
<blockquote><p>Explain what users will find at the other end of the link, and include some of the key information-carrying terms in the anchor text itself to enhance scannability and search engine optimization (SEO). Don&#8217;t use &#8220;click here&#8221; or other non-descriptive link text.</p></blockquote>
<p>As Nielson says, since links are the most common and prominent way for resources on the web to interact with each other, making links confusing and non-intuitive &#8220;is a sure way to confuse and delay users&#8221;. Many search engine optimization (SEO) experts also recommend against using &#8216;click here&#8217; since search engines (so they claim) associate webpages with the words used to link to them; vague and irrelevant linking words such as &#8216;click here&#8217; will only hamper a potential visitor&#8217;s ability to find these websites when searching (see, for example, Solo Signal&#8217;s &#8220;<a href="http://www.solosignal.com/seo-tip-please-dont-click-here">SEO Tip: Please Don&#8217;t Click Here</a>&#8220;).</p>
<p>A responsible web developer ensures that a website is as accessible, usable, and sensible as possible. Using ambiguous phrases such as &#8220;click here&#8221; reduce the usabilty of a site by obfuscating one of the web&#8217;s most important elements, the link. For more information and references, see <a href="http://en.wikipedia.org/wiki/Click_here" target="_blank">the Wikipedia page &#8216;Click here&#8217;</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://jamienay.com/2009/04/click-here-to-kiss-my/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>
