Very useful CakePHP Code Snippets

A small collection of useful Cake Snippets for all projects made with Cake.

First of all: Keep it DRY

If you have several projects I recommend extending vendor classes.

require_once(VENDORS.'my_model.php');
class AppModel extends MyModel {}

Same for AppController, AppHelper, bootstrap and CO.
This way you have a common library of those app files and don’t repeat yourself.

UPDATE 2012-04-28 ms

For 2.0 I recommend using Libs instead and App::uses() to include them.

Better feedback

Put this in your AppModel to be notified about mistakes in model associations:

public function __construct($id = false, $table = null, $ds = null) {
    parent::__construct($id, $table, $ds);
        
    // Avoiding AppModel instances instead of real Models without telling you about it
    if (!is_a($this, $this->name) && $this->displayField  !== 'id' && !Configure::read('Core.disableModelInstanceNotice')) {
        trigger_error('AppModel instance! Expected: '.$this->name);
    }
}

It triggers a warning for each relation that is not properly set. $this->displayField !== ‘id’ prevents HABTM models to trigger this.
The reason this is necessary is that Cake automatically (silently!) falls back to AppModel (instead the expected one!) for invalid models. This can cause callbacks not to be triggered – unnoticed, of course!
This happens a lot with plugins. If you don’t use plugins a lot you might not run into this problem as much as I, for example, did. I moved some modules into a plugin and forgot the "Plugin.Model" syntax at some point and never really spotted the error.

SQL queries anywhere in the app?

Did you ever wish to debug SQL queries from find or save calls in actions which don’t have a view or redirect right afterwards?
There are two options: log those queries into a file or dump it right in the controller and die.
With my CommonComponent method here I can do both (using $die parameter to switch between them):

/**
 * quick sql debug from controller dynamically
 * or statically from just about any other place in the script
 * @param bool $die: TRUE to output and die, FALSE to log to file and continue
 */
function sql($die = true) {
    if (isset($this->Controller)) {
        $object = $this->Controller->{$this->Controller->modelClass};
    } else {
        $object = ClassRegistry::init(defined('CLASS_USER')?CLASS_USER:'User');
    }
    
    $log = $object->getDataSource()->getLog(false, false);
    foreach ($log['log'] as $key => $value) {
        if (strpos($value['query'], 'SHOW ') === 0 || strpos($value['query'], 'SELECT CHARACTER_SET_NAME ') === 0) {
            unset($log['log'][$key]);
            continue;	
        }
    }
    // Output and die?
    if ($die) {
        debug($log);
        die();
    }
    // Log to file then and continue
    $log = print_r($log, true);
    CakeLog::write('sql', $log);
}

Assuming that you at least got a (real) User Model of some sort – because it doesnt work with the AppModel.
In my case I needed it in a behavior for example: CommonComponent::sql();

Displaying blob content

At some point it might be better to display the images inline as blob. Especially if there are a lot of very small ones or if they come from the DB or whatever:

    /**
     * display image tag from blob content
     * enhancement for HtmlHelper
     * @param binary $content
     * @param array $options
     * @return string $html imageTag
     */
    function imageFromBlob($content, $options = array()) {
        $text = 'data:image/png;base64,' . base64_encode($content);
        $image = sprintf($this->tags['image'], $text, $this->_parseAttributes($options, null, '', ' '));
        return $image;
    }

Always redirect after posts which changed the database

Put this in a component:

/**
 * @see http://en.wikipedia.org/wiki/Post/Redirect/Get
 * 2011-06-14 ms
 */
public function postRedirect($whereTo, $status = 303) {
    $this->Controller->redirect($whereTo, $status);
}

Example:

if ($this->Model->saveAll($this->data)) {
    $this->Common->postRedirect(array('action'=>'index'));
}

Misc. snippets

Easier control over your page titles and meta tags
Let your app automatically set the title via callbacks for pages which you didn’t manually specify.

This piece of code should be one of the last things of beforeRender() in your AppController:

//  Default title
if (empty($this->pageTitle)) {
    $this->pageTitle = __(Inflector::humanize($this->action), true).' | '.__(Inflector::humanize(!empty($this->params['controller'])?$this->params['controller']:$this->name), true);
}
$this->set('title_for_layout', $this->pageTitle);

Now you can manually set your page titles from the specific actions by using $this->pageTitle (as you used to in cake1.2) as well as manually in the view (overriding $title_for_layout).

Truncating a table

    /**
     * truncate TABLE (already validated, that table exists)
     * @param string table [default:FALSE = current model table]
     */
    function truncate($table = null) {
        if (empty($table)) {
            $table = $this->table;
        }
        $db = &ConnectionManager::getDataSource($this->useDbConfig);
        return $db->truncate($table);
    }

This code goes in your AppModel and can be called from the relevant models with $this->truncate();.

Displaying current revision
If you are working in a team it can be useful to know what the current version of your website is on staging server or the live server. Maybe it needs to be updated again.
While in debug mode (staging server) this can be quite detailed this should be a harmless version number on your live site.

This is a small snippet for SVN reps available as a simple element:

<?php
if (!isset($svnFile)) {
    $svnFile = APP.'.svn/entries';
}
if (file_exists($svnFile) && ($svn = File($svnFile))) {
    $svnrev = $svn[3];
    $lastChange = trim($svn[9]);
    $lastUser = trim($svn[11]);
    
    if (isset($version) && $version === false || Configure::read('debug') > 0) {
        // Display the revision right away
        $versionText = 'Rev. ' . $svnrev . ' (' . h($lastUser) . ' - ' . $this->Datetime->niceDate($lastChange, FORMAT_NICE_YMDHM) . ')';
    } else {
        // In productive mode we want to display a harmless looking version number
        if (strlen($svnrev) > 3) {
            $v = substr($svnrev, 0, strlen($svnrev) - 3) . '.' . substr($svnrev, -3, 1) . '.' . substr($svnrev, -2, 1);
        } elseif (strlen($svnrev) == 3) {
            $v = '0.' . substr($svnrev, -3, 1) . '.' . substr($svnrev, -2, 1);
        } else {
            $v = '0.0.' . substr($svnrev, -2, 1);
        }
        $versionText = 'Version ' . $v;
    }
?>
<div class="svn-revision">
<?php echo $versionText; ?>
</div>

<?php
}
if (isset($svn)) {
    unset($svn);
}
?>

Usage in your layout at the bottom:

<?php echo $this->element('svn_revision', array('plugin'=>'tools')); ?>

By passing 'version'=>false into the element you can force the detailed view even on debug 0.
Note: it does use my custom datetime helper. I am sure you can improvise here 🙂

0.00 avg. rating (0% score) - 0 votes

1 Comment

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.