Archive: March, 2011

Speed up your pagination with a simple hack…

Before I go into the example in this little post, let me just say that this situation won’t be applicable to everyone…

But let’s consider the following:
We have a table with tens of thousands of records, that need to be paginated.

As you know, cake will execute two queries; first to get the count of total records, second to get the actual records.

The questions one might ask are:
“Would any user really go through 5 thousand pages to find what they are looking for?”
“If I bring the last 1,000 records wouldn’t that be enough for a vast majority of needs?”
“How many pages in Google do you go through, when searching for something, before you give up?”
“Is it not better to provide filters, or search tools to help your users narrow down the results to something manageable?”

If you’ve answered “Yes” to two or more questions, please consider the hack…

In your model, which needs to be paginated, do the following:

public function paginateCount($conditions = null,
                                 $recursive = 0,
                                 $extra = array()) {
   return 1000;
}

Yep, we are overriding paginateCount() and simply returning 1,000 because we know that this will be the maximum amount of records that our paginator needs to know about.
Depending on how complex the underlying query is (for example you might have JOIN’s or various conditions, which would usually need to be taken into the account in your typical count query), the above hack can dramatically increase the performance of your pagination.

Dealing with static pages v2 (or… 3?)

Over the years of cake development we’ve seen a number of ways to get rid of the the /pages/ path in the URL for static pages.

By default if you create an “about us” page, such as in app/views/pages/about.ctp, the resulting URL would be:
www.example.com/pages/about

I’m sure you’ve seen a ton of complaints and solutions about how to get rid of this seemingly “annoying” /pages/
To keep the URL’s clean, most people would obviously prefer to have www.example.com/about instead. (No /pages/ in sight).

Recently, my favorite solution has become the following setting in the routes.php:

$staticPages = array(
    'about',
    'legal',
    'policy',
    'something'
);

$staticList = implode('|', $staticPages);

Router::connect('/:static', array(
    'plugin' => false,
    'controller' => 'pages',
    'action' => 'display'), array(
        'static' => $staticList,
        'pass' => array('static')
        )
    );

Go ahead, create your about.ctp and then attempt to access it by going to www.yoursite.com/about

The Router will nicely remove the /pages/ from the URL.

Q.
Why keep the pages in the array() and then use implode()? Couldn’t I just have the following?

$staticList = 'about|legal|policy|something';

A.
You sure could, but array structure allows to keep things organized neater and if you have a large amount of pages it’s easier to scan visually to insert/remove pages as necessary. Either way, you have both options to work with.

Be mindful of the redirect(s)

Just a couple of tidbits about CakePHP redirects, prefix routing and Auth.

1. Auth mysteriously redirects your logged in user into the abyss…

After you’ve checked all your setting in beforeFilter()’s of App Controller and relevant Controllers, it still seems like a completely bizarre situation where all of a sudden your well-authenticated users get completely kicked out of their “designated” area.
One more thing to check is any element (which is called from an “Authed” view) that might be using requestAction() from some Controller, which had not been granted the right privileges. To explain in more detail, requestAction() might attempt to access information (action) from a slightly unrelated controller, to which the current user has not been granted any permission. At this point the Auth component will kick-in and do its job by redirecting the user to a homepage or other “strange” location.
This one is always tricky to spot, since the bugger is hiding in the view/element, yet behaves as though something that should be taking place in your controllers.

2. Be explicit about your prefix routing destinations

If you have more than one routing prefix, such as “user” and “admin”, there are a few ways to move from one “prefixed” area to another.

For example:

$this->redirect(array(
   'controller' => 'users',
   'action' => 'something',
   'admin' => false
));

In many cases this will get you out of the “admin” area and move to the Users Controller.
However, depending on other (routing) issues, a more detailed instruction would be:

$this->redirect(array(
  'controller' => 'users',
  'action' => 'something',
  'admin' => false,
  'plugin' => false,
  'user' => true
));

The above situation might happen in case your something action is actually an alias of prefixed user_ action (such as user_details).
The plugin key is not as common, but also good to keep in mind.