cakephp

POCS Widget

Protect Our Coral Sea

Designed to raise awareness about the dangers that Australia’s coral sea is facing. The site’s design is visually stunning and I faced real problems getting the transparent PNG’s to work correctly in IE6. After discussing it with the client, they agreed to degrade gracefully for IE6 and have a less visually stunning design and urged the visitor to upgrade their browser.

The site runs off the same Content Management System as Without Surgery, Skills Connect. The CMS allows the updating of videos, images, menus and more. They can even update the location of the coral see on the Google map (even though I don’t think it’s going anywhere sometime soon). The site also utilises caching to provide maximum performance when under heavy load, especially when the television commercial comes in late September.

In addition, the site is also integrated with Campaign Monitor.

http://www.protectourcoralsea.org.au

No domain? No problems! Fallback translations in CakePHP

A common methodology in CakePHP is that if something doesn’t exist somewhere, it will look in the next logical spot until it finds it. This is evident with themed views, controllers, models, basically everything in the framework. That’s why I was a little bit suprised dismorning when I was looking into translations within a domain. If the phrase didn’t exist in the domain then it would simply return the original string, without looking inside the default domain.

So I came up with a function that would first look inside the custom domain and if nothing was found then it would naturally work inside the default domain. The function is named __dd(), which is short for default domain.

/**
 * Allows you to override the current domain for a single message lookup.
 * If the msg doesn't exist in the current domain, it will use the default
 * domain.
 *
 * @param string $domain Domain
 * @param string $msg String to translate
 * @param string $return true to return, false to echo
 * @return translated string if $return is false string will be echoed
 */
function __dd($domain, $msg, $return = false) {
	$d = __d($domain, $msg, true);
	if ( $d == $msg ) {
		return __($msg, $return);
	}
	if ( $return ) {
		return $d;
	}
	else {
		echo $d;
	}
}

I’m not sure why the CakePHP gods haven’t done it this way, I’m pretty sure there would be a reason though. The idea came to me with the recent feature addition of domains to my settings plugin (documentation).

CakePHP custom error class doesn’t work when providing translations

So today I was trying to make a form that was friendly between CakePHP and UniForm. Things were going really well, I created a standard options array which converted the markup into UniForm friendly markup, it all looked good until I went to submit. Cake was somehow forgetting the error class I specified. Let’s get to some examples…

The Model Validation Rules

<?php
class Product extends AppModel {
 
	var $name = 'Product';
 
	var $validate = array(
	    'title' => array(
    		'notEmpty' => array(
    			'rule' => 'notEmpty'
    		),
	    )
	);
}
?>

The View Template

<div class="products form">
<?php echo $form->create('Product', array('type' => 'file'));?>
	<fieldset>
 		<legend><?php __('Add Product');?></legend>
	<?php
		$options = array(
			'error' => array(
				'class' => 'errorField',
				'email' => 'Please enter a product title'
			)
		);
		echo $form->input('title', $options);
		echo $this->element('attachments', array('plugin' => 'media'))
	?>
	</fieldset>
<?php echo $form->end('Submit');?>
</div>
<div class="actions">
	<ul>
		<li><?php echo $html->link(__('List Products', true), array('action' => 'index'));?></li>
	</ul>
</div>

Which, when submitting the form with an empty product title would you give the error message of “Please enter a product title” inside a div with the class “errorField”, however Cake forgets the custom error class and gives the “error-message” class.

What Happened?

I got this code…

<div class="input text error">
    <label for="ProductTitle">Title</label>
    <input type="text" class="form-error" id="ProductTitle" value="" maxlength="255" name="data[Product][title]"/>
    <div class="error-message">Please enter a product title</div>
</div>

What was expected?

This…

<div class="input text error">
    <label for="ProductTitle">Title</label>
    <input type="text" class="form-error" id="ProductTitle" value="" maxlength="255" name="data[Product][title]"/>
    <div class="errorField">Please enter a product title</div>
</div>

The Fix

After looking through the core code, I traced the problem down into the error function of the FormHelper. The input() function only passes the $field name and the $text variable as the options submitted in the original “error” key to the options passed to the input() function.

In the error function, if the $text array contains the key of the error type, then it disregards the rest of the $text array, forgetting the custom error class. What we need to do is set the $options variable to $text if it is in array, as well as pull the custom message for the error type, kapeech? OK, I’ll just give you the code…

Before

411
412
413
414
415
416
if (is_array($text) && isset($text[$error])) {
	$text = $text[$error];
} elseif (is_array($text)) {
	$options = array_merge($options, $text);
	$text = null;
}

After

411
412
413
414
if (is_array($text)) {
	$options = array_merge($options, $text);
	$text = isset($text[$error]) ? $text[$error] : null;
}

I never like touching core code so I’m hoking the cake devs will be able to get on this for the next release, and it looks like I just missed the next release, as CakePHP 1.2.5 just popped up on my reader.

I have submitted a ticket at http://code.cakephp.org/tickets/view/76

Ever wondered where the **** that query came from in CakePHP?

I’ve always been frustrated looking at the masses of queries generated by my CakePHP applications and really wondering where they originated from. It would be extremely useful when performing debugging. Sure enough I started looking into cakephp datasources and found that I could implement a solution without even touching the core!

Something like…

sqllog

I’ve released the datasource to make this happen as part of my Util plugin for CakePHP. To get started please see the Wiki page.

Util is just a plugin full of unrelated libraries for CakePHP, most of the code there is from other people.

Currently only for MySQL but you only have to change one line for different databases.

CakePHP Layouts – Leave your default as default

For the past few months I’ve been following a convention of leaving the default.ctp as the default.ctp and putting my main layout in app.ctp, the only reason being is that when you get those missing controller, missing view, missing method errors it looks ugly in any custom default layout.

You can get your application to use the app.ctp layout by specifying

var $layout = 'app';

in your AppController.php. The only problem with this is that cake will still use this layout when an error occurs, to overcome this we create the app_error.php file and override _outputMessage() method.

<?php
class AppError extends ErrorHandler
{
/**
 * Output message
 *
 * @access protected
 */
	function _outputMessage($template) {
		$this->controller->layout = 'default';
		return parent::_outputMessage($template);
	}
}
?>

The above code reverts the layout back to the default.ctp layout whenever an error occurs so you can see those nasty but nice CakePHP errors.

Tip: Debug on a live site only for you (yes even with a shared IP)

There was a bug on one of our live sites which required some level of debugging. Unfortunately with my CakePHP app turning on debugging meant a whole lot of nastiness that would appear for users of the website. I needed to see the debug information on the live site without affecting any other users (including my bosses in the same office, which meant I couldn’t just turn on debugging for our office IP address).

Here’s what I did:

  • Download User Agent Switcher
  • Setup a new User Agent and copy “Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.0.10) Gecko/2009042316 Firefox/3.0.10 FirePHP/0.3  Debugger” in there. It’s important to keep the firefox related information in there as this allows hacks and stuff to continue knowing that it’s Firefox making the request
  • Setup a condition in your code that checks if “Debugger” is present in the user agent string and turns on debugging if so.

My condition code looks this:

if ( strpos($_SERVER['HTTP_USER_AGENT'], 'Debugger') !== false ) {
	$debug = 2;
}

Enjoy full debug freedom on a production site!

Luscious International

When I first joined Topia, Luscious was supposed to be a finished website and on top of that we still needed to build a client room which they could privately interact with their clients. After finishing the website with way too many rounds of changes, I knew we needed to do the client room right. So I met with Luscious to work out exactly what they wanted from a client room and wrote a specification that we could both agree on. The specificaton was signed off and I began to work on the build, the end result was a website that was both in our minds eye and everybody was happy.

The client room features cascading permissions allowing fine grained control on what clients could do (read, upload, download, preview, mark favourites) etc from a top level folder right down to individual files.

http://www.luscious.com.au

Our Tribute

Fresh out of the think tank of Novanate, we realised there was no way to remember loved ones in this new digital age. We meeted several times and specced out a website that would be easy to use and also respectful. The website is acting an online wall where users can remember loved ones from anywhere in the world.

http://www.ourtribute.net

BP North Manly

As you might have noticed, the relationship with Little Miss Nobody gets things done. I was sent the designs and I implemented the site into a working site within 2 afternoons.

http://www.bpnorthmanly.com.au

In Concerts

What In Concerts needed was essentially a mini Ticketek. The ability to create events, add session times, set the number of tickets available and accept credit card details securely online. A completely custom web application was developed to meet their exact needs.

http://www.inconcerts.com.au/tickets

When CakePHP save doesn’t work – a possible solution

After some frustrating experiences with Cake’s save() and saveAll() functions. They would simply not save the data! I thought the whole thing was messed up and I actually wrote a massive blog post about how to fix it last night. However in my editing and checking dismorning I found the following snippet of code in model.php’s method set.

731
732
733
734
735
736
737
738
739
740
741
if (is_array($one)) {
	$data = $one;
	if (empty($one[$this->alias])) {
		$keys = array_keys($one);
		if (in_array($keys[0], array_keys($this->_schema))) {
			$data = array($this->alias => $one);
		}
	}
} else {
	$data = array($this->alias => array($one => $two));
}

I was trying to save some data which had some fields that wasn’t going to be saved into the database, these were things like credit card numbers which I would process in beforeSave etc. Since the code above will disable your save attempts if the first key in your data array is not a field in the model schema.

Combine your JS & CSS files for faster loading..

With the relatively recent publication of YSlow, it brought to my attention that while seperating your CSS and JS files into seperate files for a more structured application – it can greatly reduce the load time for your users. This solution aims to solve that by keeping your files seperate, and implementing the advantages of cake so ultimately your users will only have to download one aggregated JS and CSS file.

Read More »

Qcodo vs CakePHP

At work our main PHP framework is Qcodo. However I’m in love with CakePHP. So I set out on creating a presentation which is a comparision of the two frameworks. In the end I concluded that it was like comparing apples and oranges. Qcodo seems like a port of ASP.NET, relies havily on Code Generation whereas CakePHP seems like a port of Ruby on Rails and relies on Metaprogramming.

http://docs.google.com/present/view?id=dph9vvc_89fjgp24cq