Category: CakePHP

find(‘first’)… gotcha

Just a simple tip…

Here’s a typical find() example:

$this->Post->find('first', array('condition' => array('id' => 5)));

Why does it return me the post with ID = 1, rather than ID = 5?

Of course, a careful reader spotted the spelling issue in the key:
‘condition’ instead of ‘conditions’.

Moral of the story is that find('first') can be a little tricky and misleading and if you are getting an unexpected result be sure to double-check your implementation.

find('first', 'blah');

Installing membase/memcached

These are instructions for getting membase/memcached installed in your local environment.
(I was installing on windows so your setup might be a little different, but general approach is mostly the same).

1. Download the membase server for your OS (community edition is the one you are after):
http://www.couchbase.com/downloads

Once downloaded, the installation is quite simple. Just follow the prompts.
After the server installation is complete, your default browser should open and you will be prompted to continue setting up the “cache bucket”.

Again, it is a simple step by step process. The only thing you’d want to make sure is that you select memcached bucket type.
(This will be 100% compatible with any other default installation).

2. Make sure you have memcache support enabled in your PHP install.
The easiest way to find out is to check the output of phpinfo(). Look for the “memcache” section.

3. What to do if you don’t see the section mentioned above?
First, check your php.ini for: extension=php_memcache.dll (php_memcache.so, on *nix platforms).
In some cases you just might need to uncomment the line above.

Next, you should have an entry for the php/memcached settings, it should look something like this:


[Memcache]
memcache.allow_failover = 1
memcache.max_failover_attempts = 20
memcache.chunk_size = 8192
memcache.default_port = 11211

In my case the php_memcache.dll was missing, so I downloaded the appropriate version from:
http://code.google.com/p/thinkam/downloads/detail?name=php_memcache-cvs-20090703-5.3-VC6-x86.zip&can=2&q=

You’ll have to place the .dll or .so with the rest of your php extensions; if your are using XAMPP it will be somewhere like:
c:\xampp\php\ext

4. Configure cake
Open up app/config/core.php and scroll all the way down to the cache settings.
Presuming you’ve installed everything with all defaults, all you’d have change is:
'engine' => 'File' to 'engine' => 'Memcache'

5. Restart your web server.
You might want to clear your model cache first, to be sure that cake doesn’t fall back on File caching if something isn’t configured properly.
If all goes well, you should see no warnings, nothing should be written to the file system, and in your membase console (Monitor -> Data Buckets -> default) you should begin to see some activity.
(Chart should be updated, as well you should see some records under TOP KEYS).

Congrats, you are now using memcached as your caching mechanism.

Good luck!

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.

Speed things up with a custom-baked JSON view

Yet another gem of brilliance from Mr. jrbasso.

In a couple of my previous posts regarding CakePHP and JQuery, I’ve shown some basic strategies on setting up the communication between the client and the server.

This post, as one can guess from the title, is going to be more about optimization and making things run smoother and faster while respecting MVC and CakePHP’s methodologies.

Let’s consider a typical scenario… Somewhere in your application’s UI, a user will click an element such as a button, a link or what have ya, which will issue an AJAX request to the server. The response expected by the client (browser) will be a JSON object. (Yep, sounds like this has been done once or twice before…)

In order to respect MVC you’d usually create a controller action to process the AJAX request, perhaps grab some data from the DB and then create a view to output the JSON object.
While this is a proper approach to achieve the task at hand, one can certainly consider the overhead caused by loading the View object, the helpers and other “goodness” that comes with the typical MVC request.
Right away it becomes desirable to just echo out a json_encode()'d string right from your controller. But… whoa… I know that you’d never do something so offensive to the MVC paradigm ;)

Instead, let’s reach a middle ground… we don’t have to break MVC or cake, yet we’ll see how to make our custom JSON view act faster and more robustly.

First, we’ll make up a simple controller action such as the one here:

public function get_json_info() {
    $this->view = 'Json';
    $info = $this->MyModel->getSomeInfo();
    $json = array(
      'variableOne' => $info['some_var'],
      'variableTwo' => $info['another_var']
    );
    $this->set(compact('json'));
  }

As you see this is a totally bogus action, but it works in the same manner as you’d expect many controller actions to work when returning some JSON data.
There are two important things going on here, nonetheless:
1. $this->view = 'Json'; instructs cake to use our custom JSON view, rather than a standard view class.
2. We’ve created a $json array with some data and set() it to be used by the view.

Now we can finally move on to look at our custom view class.
Create a file called json.php and place it in your app/views/ directory.

This particular implementation comes with a nice, little bonus. If you use Mark Story’s Debug Kit, this custom JSON view will ensure that it is working properly and doesn’t break anything as this tool is quite indispensable for many developers. For those who do not use Debug Kit at all, first you should :), and second you can make the view object even lighter by taking out the Debug Kit specific code (but that is something for your to play with).

Hopefully I didn’t mislead anyone with the title of the post, by making you think that you can “bake” such a view using the console tool. To make up for this, I offer a copy & paste solution instead ;)

Well here’s the little guy in all its glory:

<?php

class JsonView extends View {
  var $content = null;
  var $debugKit = null;

  function __construct(&$controller, $register = true) {
    if (is_object($controller) && isset($controller->viewVars['json'])) {
      if (isset($controller->helpers['DebugKit.Toolbar'])) {
        $this->debugKit = $controller->helpers['DebugKit.Toolbar'];
        parent::__construct($controller, $register);
      }
      $this->content = $controller->viewVars['json'];
    }
    if ($register) {
      ClassRegistry::addObject('view', $this);
    }
    Configure::write('debug', 0);
  }

  function render($action = null, $layout = null, $file = null) {
    if ($this->debugKit !== null) {
      DebugKitDebugger::startTimer('viewRender', __d('debug_kit', 'Rendering View', true));
      $this->loaded = $this->_loadHelpers($this->loaded, array('DebugKit.toolbar' => $this->debugKit, 'DebugKit.simpleGraph', 'html', 'number'));
      $this->_triggerHelpers('beforeRender');
    }

    if ($this->content === null) {
      $data = '';
    } else {
      $data = json_encode($this->content);
    }

    if ($this->debugKit !== null) {
      $this->_triggerHelpers('afterRender');

      DebugKitDebugger::stopTimer('viewRender');
      DebugKitDebugger::stopTimer('controllerRender');
      DebugKitDebugger::setMemoryPoint(__d('debug_kit', 'View render complete', true));

      $backend = $this->loaded['toolbar']->getName();
      $this->loaded['toolbar']->{$backend}->send();
    }

    return $data;
  }
}

As you see, our JsonView is a child of CakePHP’s core View class. It implements and overrides a couple of methods and does so in a much lighter fashion.

Just a few things to point out:

  • $this->content = $controller->viewVars['json'];
  • remember our bogus action where we’ve set() $json var for the view? Well, this is exactly what the view is going to expect to transform into the JSON object.

  • It does so quite easily: $data = json_encode($this->content);

And there you have it… light and speedy, not too greedy ;) Perfectly optimized for JSON output.
Enjoy.

Two ways to debug your AJAX queries

Every once in a while you might have a need to trigger an AJAX action in the controller, which should return some results from the server…

When everything works, well it is certainly all fine and dandy, but if things go haywire, first thing to check out is the response given by the server… and more importantly why does it not match your expectations. One of the more typical questions is: “What query was executed on the DB, during the AJAX call”?…
Because AJAX calls are not easily logged to any console, sometimes it becomes a little troublesome to figure out.

When using CakePHP you have two methods to see what’s happening on the DB level.

1. If you use debug kit

There is one little known, but nice feature.
After you load the page, which executes some AJAX request… do the following:
1. Click the “History” tab
2. In the list you should see the given AJAX call to some controller action
3. Click it
4. Switch over the to the SQL log tab and see what has happened.
(The background of the queries should be mild green, which will signify that you are looking at a previous request in the “history” of calls)

… amazing feature indeed. Yet, unfortunately, it doesn’t work 100% of the time. (I wish I had a more in-depth answer as to why).

2. Do not despair, there is another way to log your SQL dealings with a few lines of code.

Using the same scenario as above, in your action which is being triggered by AJAX from the client, add the following snippet:

$db = ConnectionManager::getDataSource('default');
debug($db->getLog());

Now, if you are using firebug (or similar tool, which I hope you certainly do)… You’ll see the exact query and some other debug info generated by the cake’s DB drivers.

p.s. Thanks to jrbasso, for this wonderful and helpful hint.

Make your CakePHP app ridiculously faster with “view caching”

Big or small, once your app goes to production you’d better start thinking about utilizing view caching ;)

Granted, view caching in a dynamic application can be tricky or impossible(?)…
but with proper usage wherever and whenever needed, it can make your app go off the charts in terms of performance.
If you are not familiar with view caching at all, you should read the CakePHP manual… yet I will give you some basic examples and couple of things that may not be so well known.

1. Enable view caching for 10 minutes for the main (index) page

public function index() {
$this->helpers[] = 'Cache';
$this->cacheAction = '10 minutes';
}

In this little snippet we’ll add the Cache helper, only when required by the given action. No need to load for the entire controller. (Once the page is “visited” for the first time, the given view will be cached… you can see the evidence of this in your app/tmp/cache/views directory).

2. Be mindful of what you are caching, certain things should remain dynamic

Whenever there is dynamic content within the the view, such as a div, which is updated by AJAX, for example, you’d want to utilize the cake:nocache tags, like so:

<cake:nocache><?php echo microtime(); ?></cake:nocache>

… but there are a few “gotchas” to be aware of…

a. Callbacks such as beforeRender() or beforeFilter() do not fire by default.

For larger scale apps this nearly kills the ability to use view caching.
However, not all is lost… In your controller you can setup a property to ensure that callbacks do run for certain actions where they are truly required (as shown in CakePHP manual):

public $cacheAction = array(
	'view' => array('callbacks' => true, 'duration' => 21600),
	'add' => array('callbacks' => true, 'duration' => 36000),
	'index'  => array('callbacks' => true, 'duration' => 48000)
);

Additional option proposed by jrbasso, is to fire-up the necessary logic by adding the following snippet to the layout:

<cake:nocache><?php
if (isset($controller) && is_a($controller, 'Controller')) {
  $controller->constructClasses();
  $controller->startupProcess();
}
?></cake:nocache>

Still a little overhead, but much much much faster overall. Both options achieve the same basic goal and I will leave it up to you to see, which one suits your needs better.

b. Spaces

Another excellent catch by mr. jrbasso is that if you have spaces between your cake:nocache and php tags, the “no-caching features” may not work as expected.
So be sure to always place your dynamic PHP code like so:

<cake:nocache><?php
if($dynamic) {
  echo 'This is a timestamp: ' . microtime();
}
?></cake:nocache>

c. Don’t surround your elements with cake:nocache tags.

Your entire element will not be cached by doing this:

<cake:nocache><?php echo $this->element('my_element'); ?></cake:nocache>

Yep, you have to go inside the element and decide what to cache or what not to cache. Which, in some cases makes sense, depends on the element really.
Alternatively, you can cache an entire element using the 'cache' key:

<?php echo $this->element('my_element', array('cache' => array(
   'time' => '+1 day',
   'key' => 'my_cached_element'
))); ?>

Since the element itself can have dynamic content, the 'key' can be used to create different “versions” of the cached element. Of course in the above example it is a static key, but in other cases you could create a dynamic key based on some conditions or criteria, which would make sense for your application.

Real-time updates vs view caching

It goes without saying that once your view is cached any changes to the underlying model will not be reflected until the cache is invalidated. In some cases it is perfectly fine, in others you might want to clear the cache once the model has been updated.
It is quite easily achieved by adding the following to your model:

public function afterSave() {
  clearCache('related_view*');
}

Granted, on the first hit to the page the cache will be rebuilt and some “extra” weight will be put on the application, yet by using this technique you’ll have the benefit of using the cache and real-time reflection of the updated data.

Don’t let APC get in the way

I am not sure if other PHP opcode engines can cause the same trouble, but at least with APC we’ve found that it can conflict with cake’s view caching. It would be a bit too much to go into details of the issue, but if you use both APC and view caching for your application and notice some strange behavior be sure to run APC with stat “ON” (which is the default behavior).

To learn a little more about this, you can take a look here

In summary I might as well throw this out: when comparing framework performance, using examples such as “Hello world!” don’t forget that any framework or application requires caching. As many other things, ensuring high-availability and excellent performance is quite easy in cake, and beyond that be sure to utilize other tools (APC, memcached, etc.), which are just as necessary in any modern web application as a web server itself.

Checking for SSL and then some…

A simple way to check and enforce SSL can be done by using the RequestHandler component.
Here’s an example:

private function checkHttps() {
  if(!$this->RequestHandler->isSSL()) {
    return $this->redirect('https://' . env('SERVER_NAME') . $this->here);
  }
}

Simple enough, right?

Yet there is a little caveat, which causes a problem in a specific load balanced environment.
To give a little further insight into the whole situation, it is not uncommon nowadays to setup your SSL certs on the load balancer and have it handle the decryption, while passing “regular” HTTP requests down to the web servers.

So what happens in this case?
If we use the approach described above what would happen is that load balancer would handle the HTTPS request and pass a regular HTTP request to apache (or your web server).
At this point the application would say: “Wait this is not secure, redirect the URL to HTTPS… and so on to the point where we’d get stuck in the infinite loop of redirects).

How to fix?

private function checkHttps() {
  $lbEnv = env('HTTP_X_FORWARDED_PROTO');
  if (!is_null($lbEnv) && (env('HTTP_X_FORWARDED_PROTO') != 'https')) {
    return $this->redirect('https://' . env('SERVER_NAME') . $this->here);
  }
}

Helpful jQuery modals

This post is actually mostly about jQuery (with some cake sprinkles), but why not…

Let’s examine a typical situation:

We have a Product model, which belongsTo a Category model.

Now we need to add some products…

An admin interface would have a category select input (or drop-down) and a few form fields to fill-in some product details. Happily, we venture to add our first product, only to realize that we have not yet created any categories… backtracking to create a category would be a shame and with a little help from jQuery we can easily solve this problem.

One state that developers rarely consider during (and after) development, is the “blank” state of the application, which in turn can lead to some unexpected behavior…

The goal is to do the following:

  1. Get to the www.example.com/products/add page
  2. Using jQuery, “realize” that our Category drop-down is empty
  3. Prompt the user through… “lightwindow-modal-jquery-ui-dialog”… To create at least one category, so we could get moving
  4. Refresh the product/add page with the newly created category neatly in place within the select input

Since I use jQuery extensively, I am going to pick jQuery UI’s dialog widget to accomplish my needs. (It may not be the most robust or feature-rich, but it plays nice with the jQuery core… and does what I need).

(It goes without saying that you should include jQuery core and jQuery UI libraries… load them from Google, if you’d like).

Anyway…
First, let’s take a look at the products/add view:

<?php $this->Html->script('jquery.form.js', array('inline' => FALSE)); ?>
<?php $this->Html->script('parent_list.js', array('inline' => FALSE)); ?>
<?php $this->Html->css(array('smoothness/jquery'), NULL, array('inline' => FALSE)); ?>

<div>
	<?php
		echo $this->Form->create('Product');
		echo $this->Form->input('category_id', array('class' => 'parent-list'));
		echo $this->Form->input('name');
		echo $this->Form->end('Add product');
	?>
</div>

<div id="category-form">
	<?php
		echo $this->Form->create('Category', array('action' => 'add'));
		echo $this->Form->input('name');
		echo $this->Form->end('Create category');
	?>
</div>

Stepping through the code, the first thing we see is the jquery.form plugin. This will be used for submitting the “category add” form via AJAX, from the modal. (Will show that later on).
Next, we load parent_list.js, which is going to handle most of the magic.
Finally, in the view we have a div category-form (which will be turned by jQuery UI into a dialog/modal).

As you can see both forms are very simple, for purpose of this example.

Now, let’s see the jQuery file, which does all the heavy lifting:

(function ($) {
	var getModal = function () {
		$('#category-form').dialog({
			modal: true,
			autoOpen: false,
			title: 'Create a category'
		});

		if($('.parent-list').children().size() === 0) {
			$('#category-form').dialog('open');
		}
	};

	var showResponse = function (response) {
		if(response == 'saved') {
			$('#category-form').dialog('close');
			window.location.reload();
		}
	};

	$(document).ready(function() {
		getModal();

		var submitOptions = {
			success: showResponse,
			dataType: 'json'
		};

		$('#CategoryAddForm').ajaxForm(submitOptions);
	});
}(jQuery));

This is more JavaScript/jQuery than I usually show, so let’s take a look at some interesting things.
First, you’ll notice that the entire code is wrapped into a “self-calling anonymous function”. To keep things simple, the two benefits of using such pattern is to avoid conflicts and global vars (although it is not evident from this example, but a little googlin’ will explain things in more detail).

Next, notice the function declarations: var getModal = function () ... by using such assignment we avoid a couple of problems in everybody’s favorite IE (the above method will define the function at parse-time, rather than run-time). It also kind of “forces” the developer to declare and call things in proper sequence without hoping that the browser might pick up some of the slack, where developer was careless. At any rate, run your JavaScript through JSLint before going to production ;)

Now that the general stuff is out of the way, let’s see the “most important” line of code:
if($('.parent-list').children().size() === 0)
The above will check the category select input (i.e. parent of the product) to see if it has any options (options are children of the category select in this case). If not, the jQuery goodness kicks in and we get a modal/dialog which displays the category add form, as you saw from the products/add view above.

Once the name is entered and submitted via AJAX, I send back “saved” from the server as a response (assuming that all goes well). At which point we close the dialog and refresh the page. Now, we have our newly added category as one of the options… and can continue to add the product without backtracking to create a category in some other UI.

Hopefully this serves as a decent example on how to build a much more intelligent UI and improve user experience, with the help from the lovely jQuery and CakePHP.