CakePHP/MySQL: Want a Boolean? Use TINYINT(1), not BIT(1)

I was recently creating some new MySQL tables that stored booleans, so I thought I would do things (what I presumed to be) properly and make them BIT(1) columns, rather than TINYINT(1). However, when I came to try to save values to this field using CakePHP, everything was saved as 1, even if the value in the data array was 0, false, “”, “0” or anything else ‘zero-ey’ or ‘falsey’.

It turns out that BIT fields are not supported by Cake, and so you should just use TINYINT(1) instead!

Furthermore, Cake will assume a TINYINT(1) field is intended as a boolean field, and will only allow you to assign it a value of 0 or 1, even though TINYINT(1) can store values from -128 to 127 (or 0 to 255 unsigned). If you try to save any value other than 0 or 1 to this field, it will be saved as 1 (but note that false, “” and an empty array will cause a save error).

Eclipse: Adding File Associations

We regularly write PHP, using the CakePHP framework, in Eclipse (Juno, with PHP Development Tools (PDT)). The view template files in CakePHP have the .ctp extension, which Eclipse does not, by default, recognise as PHP files, so you do not benefit from any of the helpful Eclipse PHP tools. However, you can add an association (i.e. tell Eclipse that .ctp files are PHP files), as follows:

  1. Go to Window > Preferences to open the Preferences box
  2. Go to General > Content Types in the menu
  3. In the Content types box, expand Text and select “PHP Content Type”
  4. Click “Add”
  5. Type “.ctp” in the box that appears and click OK
  6. Click OK to close the Preferences box

Note that you will have to close and then reopen any .ctp files that are already open in order for them to be recognised as PHP files.

CakePHP 2.x: Migrate from 1.3

The CakePHP 1.3 to 2.0 migration guide does probably cover most of what we have found below…but it appears to be written by and for the developers of CakePHP rather than for people who use CakePHP to develop their own applications. So here are some of the things we are doing during our 1.3 to 2.4 migration (although I can’t guarantee that they were all actually necessary), in case they help others. Please note that this is a work in progress 🙂

Upgrade Shell

Upgrade Shell – this promised considerably more than it delivered but did at least rename the controllers and changed the case of some of the folders under app. The rest were corrected using the Folder Names section of the migration guide, including the Views sub-folders and renaming ‘helpers’ to ‘Helper’

Files to copy from 2.x code

Copy the following from your download to the same place in \app:

  • \app\webroot\index.php
  • \app\View\Helper\AppHelper.php
  • \app\Config\core.php (contains a new error handling config. info)
  • \app\Config\routes.php (contains a couple of new setting to load plgin routes and ‘default’ CakePHP routes.

Update Paginator code

In view:

echo $this->Paginator->counter(array(
    'format' => __('Page %page% of %pages%, showing %current% courses out of %count% total', true)
    ));

changes to:

echo $this->Paginator->counter(
    'Page {:page} of {:pages}, showing {:current} records out of
     {:count} total'
);

In controller, this syntax:

$this->paginate['Event'] = array(
            'order'=>array(
                ..
                ),
             'conditions'=>array(
                ..
                 )
             );

$this->set('events', $this->paginate());

changes to:

$this->paginate = array(
                'order'=>array(
                     ..
                    ),
                 'conditions'=>array(
                    ..
                     )
                 );
$this->set('events', $this->paginate('Event'));

Change all ‘action’ functions in ctp files from:

function index(){}

to

public function index(){}

Auth Component

Now required to explicitly log users in – not done automagically any more. Where we use WebAuth to authenticate and manually log users in:

if($this->Auth->login($user_id))
     {
     $this->Session->setFlash('Logged in');
     }

becomes:

$this->request->data['User'] = array_merge($this->request->data['User'], array('id' => $user_id));
if($this->Auth->login($this->request->data['User']))
     {
     $this->Session->setFlash('Logged in');
     }

Form2Helper

Although, some of this is specific to the Form2Helper (to make MySQL enum fields show enumerated values rather than a number), some of these points will apply to all helpers:

  1. Change name of helper to Form2Helper.php, instead of Form2.php;
  2. Change App::import(‘Form’) to App::uses(‘FormHelper’, ‘View/Helper’) above class definition (replacement of App::import in 2.x with App::uses)
  3. $this->_introspectModel($modelKey, ‘fields’, $fieldKey) instead of $this->_introspectModel($modelKey) (changes to _introspectModel)
  4. After an hour or two of trying to work out why the first enum input in a form was being output as a standard text box while subsequent inputs for the same field in the db, were coming out correctly as selects, I realised that:
    isset($this->fieldset[$modelKey])

    was not returning false when:

    $this->fieldset[$modelKey]['fields']

    was returning ‘null’ so I have replaced it with:

    if (!isset($this->fieldset[$modelKey]['fields'][$fieldKey]))

    and it’s all working nicely now.

Request Object

Use $this->request->data instead of $this->data

FormHelper

FormHelper::select(string $fieldName, array $options, mixed $selected, array $attributes)

becomes

FormHelper::select(string $fieldName, array $optionsarray $attributes)

and

$html->link

becomes

$this->Html-link()

CakePHP-Mailchimp-datasource 1.3 Read Method

If anyone’s still using CakePHP v1.3 of this brilliant datasource which allows you to treat data on subscribers in MailChimp as if they were in a local model, I had to make a few changes to make it work with v1.3 of the MailChimp API.

 
function read($model, $queryData = array()) {
 $url = $this->buildUrl('listMemberInfo', $queryData['conditions']['emailaddress']);
 $response = json_decode($this->connection->get($url), true);
 if(isset($response['errors'])&&$response['errors']>0) { //this is how errors are indicated
 return false;
 }
 return $response['data']; //allows find('first') to return $response['data'][0]
 }

CakePHP not saving to new field on production server

I wasted a few hours on Saturday morning struggling with the fact that CakePHP on my production server refused to save to a newly added field on a table in my database, while everything worked beautifully on my dev server. After hours of debugging on the live server I suddenly remembered that, with:
Configure::write('debug', 0);
in core.php, as it should be on a production system, Cake uses the cached model definitions in tmp\cache\models and would therefore ignore any changes in the underlying database until these were refreshed. Simply either:

  1. Change temporarily to Configure::write('debug', 2); and then run your code before changing it back again or;
  2. Delete the contents of tmp\cache\models

and it will pick up your new field.