All About Validation in CakePHP 1.2 – Part 2

6 07 2007

In my previous article I discussed the new constructs for Model::validate. This article follows on from this and is a run down of the new validation methods and rules available.

First up there are a couple of regular expressions defined, which are the same from CakePHP 1.1 and are pretty self explanatory. These are VALID_NOT_EMPTY, VALID_NUMBER, VALID_EMAIL, and VALID_YEAR (between 1000-2999).

The general format of the following validation methods is

var $validate = array('field' => array('rule' => 'ruleName'));

Where parameters are required, such as between and cc, the format is

var $validate = array('field' =>
        array('rule' => array('ruleName', 'param1', 'param2'));

alphaNumeric
Allows only digits and a-z or A-Z.

between
Checks that a strings length is between a min and max value.

blank
Checks if a field is empty and treats whitespace characters as empty.

cc
Credit Card Number validation, includes luhn check and Card Type to number format. Takes one parameter which can be one of :-

  • fast – skips Card Type to number format check
  • all – checks the number against all card types until it finds a match
  • array of card types – like all but limited to a subset of cards.

e.g.

var $validate = array('field' =>
        array('rule' => array('cc', array('Visa')));

compare
Allows you to compare two numeric values. Takes two parameters:-

  • Operator – one of <, >, <=, >=, == or !=
  • Comparison value to compare against

custom
Allows you to use custom regular expressions. Takes the custom regex as the only parameter.

date
Validates a string as a date. Can take one parameter;

  • Format – default is ‘ymd’. other options are:
    • dmy
    • mdy
    • ymd
    • dMy – short or long month names
    • Mdy
    • My
    • my

decimal
Checks that a number has a decimal point or is scientific notation. Takes the number of decimal places required after the point as the only parameter. If places is null it will check for scientific notation.

email
Checks for a valid email address. If a parameter of true is passed it will also attempt to verify the host. If the parameter passed is false, or none is passed it behaves the same as VALID_EMAIL.

ip
Checks for IPv4 dot notation. e.g. 192.168.0.1

minLength
Checks a string for a minimum length. Length is passed as the only parameter.

maxLength
Checks a string for a maximum length. Length is passed as the only parameter.

money
Checks that a string is numbers, optionally grouped into blocks of 3 separated by a space, comma or period, with an optional block of 2 at the end. Can take a parameter of ‘right’ if you expect the currency symbol at the end, the default is at the start.

numeric
Simply calls is_numeric()

phone
Checks for a valid phone format. Takes regex and country as parameters. Currently only supports ‘us’ country option.

postal
Checks for a valid post code format. Takes regex and country as parameters. Currently only supports ‘us’, ‘uk’ and ‘ca’ country options.

ssn
Checks for a valid social security number format. Takes regex and country as parameters. Currently only supports ‘us’, ‘dk’ and ‘nl’ country options.

url
Checks for valid URL format. Supports http(s), ftp(s), file, news and gopher protocols

userDefined
Calls a userdefined method of the current model passing along any parameters. , the first of which is the method to call. Personally I think this is redundant as you can simply replace userDefined with your method name and it works the same.

There are also some incomplete methods listed below that will be coming soon.

number – checks that a number is within a given range
multiple – will be used for selects and multiple selects
equalTo – direct comparison to another value
file – checks for a file.





All About Validation in CakePHP 1.2

3 07 2007

Validation in v1.1 of CakePHP was quite simple, and many found it lacking in features and flexibility. This is evidenced by the number of alternatives that people have written such as Daniel Hofstetter, Evan Sagge and Adeel Khan’s ruby-esque approach.

However in CakePHP 1.2 there has been a major rework of the Validation class’s inner workings, and the way Model::invalidFields() works.

The New $validate

The new $validate can take a number of different constructs now. There are 3 ways to define your validation rules and you can mix and match as needed.

Construct 1: CakePHP 1.1 way
The old CakePHP 1.1 $validate construct will still work

var $validate = array('fieldName' => 'ruleName')

Construct 2: Single Rule per field
You can now define more complex rules using the following construct. (Parameters explained later)

var $validate = array(
  'fieldName' => array(
    'rule' => 'ruleName' // or 'rule' => array('ruleName',  'param1', 'param2' ...)
    'required' => true,'allowEmpty' => false,
    'on' => 'create', // or update
    'message' => 'Your Error Message'
    )
  );

Constrct 3: Multiple Rules per field
Similar to Construct 2, however you can define multiple validation rules for a single field.

var $validate = array(
  'fieldName' => array(
    'rule_index' => array(
      'rule' => 'ruleName' // or 'rule' => array('ruleName',  'param1', 'param2' ...)
      'required' => true,'allowEmpty' => false,
      'on' => 'create', // or update
      'message' => 'Your Error Message'
      )
    )
  );

The new Parameters

The new $validate supports a number of parameters. The only required parameter is ‘rule’.

rule – mixed:
The Rule parameter defines the validation method and takes either a single value or an array. Rule may be (in order of preference) a method of your model, a method of the Validation class or a Regular Expression. If Rule is an array and ‘ruleName’ is a method, all other members of the array will be passed to the method. eg.

var $validate = array('username' => array('rule' => array('between', 6, 20)));

allowEmpty – bool:
This defines the behaviour when an empty value for the field is found. ‘allowEmpty’ => false will cause the validation to fail when the field data is empty. N.B: This rule is only enforced when there is an actual fieldName index in the data array.

Default is false.

required – bool:
‘required’ => true means that an index with fieldName must exist in the data array, i.e. validation will fail if isset($data['ModelName']['fieldName']) fails. N.B Required does not care if the value is empty – see allowEmpty above.

Default is false.

on – string (‘create’ or ‘update’):
If on is defined , the validation rule will only be applied on model ‘create’ or ‘update’. If not defined it is applied everytime. This may be useful for situations such as created_by is required on record creation, but on update it must not be defined.

Default is null, i.e. apply rule everytime.

message – string:
Message is the error message that will be stored in validationErrors. If message is not define, it will attempt to use rule_index (in Construct 3) if it is a string, otherwise it will default to ‘This field cannot be left blank’.

There is also another parameter ‘last’ defined in the code but it is not used as yet. Not sure what it’s use will be either.

A Full Example

This is the actual validate variable from my User model. It contains a mixture of the above constructs and techniques.

var $validate = array(
  'username'   => array(
    VALID_NOT_EMPTY,
    'alphanumeric' => array(
      'rule' => 'alphanumeric',
      'message' => 'Username may only consist of letter and numbers'),
    'length' => array(
      'rule' => array('between', 6, 20),
      'message' => 'Username must be between 6 and 20 characters in length')
  ),
  'name'     => VALID_NOT_EMPTY,
  'email'   => array(
    'Invalid email format' => VALID_EMAIL,
    VALID_NOT_EMPTY
  ),
  'passwrd'   => array(
    VALID_NOT_EMPTY,
    'length' => array(
      'rule' => array('minLength', 6),
      'message' => 'Password must be at least 6 characters in length'),
    'strong' => array(
      'rule' => 'isStrong',
      'message' => 'Your password is not strong enough')
  )
);

Disclaimer: My knowledge of the new validation was gained from the Bakery Article Multiple Rules of Validation, CakeBaker’s article Validation with CakePHP 1.2 and mostly from looking at the source code. If I have interpreted something incorrectly, please let me know.





Validation Gotcha in CakePHP 1.2

26 06 2007

While trying to write a simple login function for a user model today I came across a small gotcha that it is not apparent at first.

In CakePHP 1.1 if you want to manually validate a model you would write:

if ($this->Model->validates($this->data)){
    //success
}

However in CakePHP 1.2 this throws a nice deprecated warning:

Warning (512): (Model::validates) Parameter usage is deprecated, set the $data property instead [CORE/cake/libs/model/model.php, line 1660]

I first tried just an empty validates() expecting it to use Controller::data, however this was appearing to pass validation. Not good when the entire form was empty with VALID_NOT_EMPTY’s on every field. A quick look-see in the source of model.php reveals that Model::invalidFields() expects Model::data to be populated.

As pointed out by nate in the comments my original solution is not recommended.

The updated solution is to use:

$this->Model->set($this->data);
if ($this->Model->validates()){
    // success
}




CakePHP Test Suite Shell

24 06 2007

In addition to my changes to the core Test Suite to add support for plugins, I have now created a console shell to run test suites via the command line.

To use the Test Suite Shell simply place testsuite.php in /vendors/shells.

Usage:
cake testsuite help
this message
cake testsuite run section test_type [case_type] test
- section – app, core or plugin_name
- test_type – case, group or all
- case_type – only with case, one of behaviors, components, controllers, helpers or models
- test – file without (test|group).php

Examples:
cake testsuite run core all – will run all core tests
cake testsuite run app group models – will exexute /app/tests/groups/models.group.php
cake testsuite run contents case model content – will execute /app/plugins/contents/tests/cases/models/content.test.php





Testing CakePHP Plugins

19 06 2007

With the official Test Suite bundled with CakePHP, it is pretty easy to write unit tests for your models and controllers. The Testing Models with CakePHP Test Suite tutorial on the Bakery is a good introduction to the CakePHP Test Suite.

Now this is great until it comes time to test your plugins. It seems that the official test suite does not like plugins. So I patched my cake install so that I can run plugins test cases and groups that are logically stored in /app/plugins/plugin_name/tests.

Here are the files for those interested. Please leave bugs or issues in the comments for now.

My apologies to those who downloaded 0.1, as it did not contain app/webroot/test.php – instead I had incorrectly included app/webroot/index.php

Plugin tests are exactly the same as those described at Testing Models with CakePHP Test Suite except that the loadModel(‘ModelName’) is left out becomes loadPluginModels(‘plugin’). Here is an example test case for a plugin:

<?php
loadPluginModels('contents');

class ContentTest extends Content {
  var $name = 'ContentTest';
  var $useDbConfig = 'test_suite';
}

class ContentTestCase extends CakeTestCase {
  var $TestObject = null;
  var $fixtures = array( 'content_test' );

  function startTest() {
    $this->TestObject = new ContentTest();
  }

  function endTest() {
    unset($this->TestObject);
  }

  function testSlugBehaviour(){
    $data = ContentTestFixture::$slugTest;
    if($this->TestObject->save($data)){
      $data = $this->TestObject->findById($this->TestObject->getInsertID());
      $expected = 'slug-test';
      $this->assertEqual($data['ContentTest']['slug'], $expected);
    } else {
      $this->fail('Could not save the Test Data');
    }
  }
}
?>

Currently my patch only supports the HtmlManager – web view – and not the TextManager. Also ‘All’ under Test Groups of a plugin will not run the plugin tests, it will run the App Test Cases. I will fix these issues in the next release. The patch supports All under Group Tests and combined with my Test Suite Shell it also supports cli testing

And finally here are some screeshots of Plugin Tests in action.

domain.com/test.php
testsuite-1

domain.com/test.php?plugin=content
testsuite-2

domain.com/test.php?case=cases%2Fmodels%2Fcontent.test.php&plugin=contents
testsuite-3





Bake with MAMP on OS X

14 06 2007

The following details are for CakePHP v1.2. They are intended to make baking easier on a Mac. For details on how to use bake I recommend cakebaker’s series on using the bake script.

Baking on Mac with MAMP can be a long winded, typo ridden experience. Check out the full command.

/Applications/MAMP/bin/php5/bin/php ~/eclipse/workspace/cake_1_2/cake/console/cake.php bake

Typing that even for a small site with 3 or 4 models, views and controllers is not fun. Trust me.

Read the rest of this entry »





CakePHP n00b Tip #1

14 06 2007

This marks the start of an ongoing series of tips for newcomers to Cake. It will be based on posts to the Cake PHP Google Group as well as things that I myself come across.

Action parameters from the Url

Passing parameters to actions via the url is simple. Urls are in the format http://site.com/controller/action/param1/param2 and so on. Your actions will be called with parameters in the order they are in the url .ie action($param1, $parm2)

This is a common scenario when using hasMany, for example Company hasMany Employees. On the company details view you want to add a “New Employee” link that automatically assigns the company_id.

Read the rest of this entry »





Filtering on a List<T> BindingSource

13 06 2007

funnel.gifUpdate: 19/06/2007 – I have attached a copy of ListBindingSource.cs which I have modified slightly to match against partial field matches (similar to SQL’s LIKE) rather than matching the whole field.

ListBindingSource.cs 11.5 Kb

I maintain a C# application and a recent feature request was to be able to search a list of items on a number of attributes. The original implementation used a List<T> as the datasource for a Binding Source. I hit a wall when trying to use the Filter property on the binding source.

Read the rest of this entry »





CakePHP Development on Mac OS X

11 06 2007

I’ve been setting up my Mac OS X development environment lately and I have got the following so far.

Server

For a server I am using the excellent MAMP package. Simple, straight forward and with the added bonus that you can switch between PHP4 and PHP5 at any time by selecting a radio box and restarting the server.

IDE

After reading a couple of posts on the CakePHP google groups I have decided to go with Eclipse as my IDE, which comes in a handy all in one package from Zend.

At work I use ZDE on a Win box. I am happy with ZDE – got to love its auto-complete feature – but I wanted to try something new and a little more flexible at home.

After less than an hour with Eclipse I am quite impressed. I have already created an “external tool” for Bake thanks to the short tutorial here, although I had to modify it for 1.2. Now I can bake with a short menu selection, directly in Eclipse rather than typing the whole thing into the terminal.

Another neat feature that I noticed is in the task view it automatically picks out comments with TODO in them and adds them to the list. Very handy for not forgetting what still needs a bit of attention.

Subversion

There is a nice binary of Subversion avaible from Martin Ott which makes SVN as simple as any package to install. Couple this with Subclipse and code versioning is sorted.

If you need to run a repository locally, the package from Martin Ott is capable of this by running from Terminal:
svnserve -d

Still to Come

I am still looking into a deployment process. I want something that will checkout my current SVN head, run the unit test, run the Selenium test and then FTP them to the remote server if all tests pass. I am looking into ANT and Capistrano, but am still undecided.





Powered By CakePHP Proposal

9 06 2007

I have written the proposal for a website to showcase other sites powered by CakePHP. Have a look at the Powered By CakePHP proposal and leave some feedback, guidence or wisdom to help me on my way with my first OSS project.

I have applied for a CakeForge repository and I’ll let you know when the initial release is available.

Now I’m of to do my exam and then home to set up a decent dev environment on my Intel Mac.