Tag Archives: cakephp validation

Key/value tables and how to use them in CakePHP 1.3

The key/value tables (EAV model) are a nice a approach to database modeling, when we need to store some arbitrary data about another model.

For example, let’s take a User model.
We could create a single users table to hold all of the potential data or even create an additional table such as user_details with all additional fields, which “might” be needed.

However, what happens when we don’t exactly know how much or what data will be eventually required by the application?

  • How many phones does the user have? Should we create a table with fields phone1, phone2, phone3, etc.?
  • What about emails? (Some users have 1 some have 5)
  • What happens if the business later on decides to ask for a fax number?.. and person’s height or favorite color?

If we attempt to predefine all the place-holders (columns or fields in a table) for the potential data, the table can grow horizontally into an unmanageable monster.

This is the perfect time to enter key/value tables.

Let’s consider our User model and, therefore the users table:
[sourcecode language=”sql”]
CREATE TABLE `users` (
`id` char(36) COLLATE utf8_unicode_ci NOT NULL DEFAULT ”,
`username` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`password` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`created` datetime DEFAULT NULL,
`modified` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
[/cc]

The table couldn’t be simpler. All we’ll store here is the user’s login information. The rest will be handled by the user_details table…
It’s worth to mention that our models will have a pretty standard association:
User hasMany UserDetail and on the flip side UserDetail belongsTo User

Alright, next, we have the user_details table:
[sourcecode language=”sql”]
CREATE TABLE `user_details` (
`id` char(36) COLLATE utf8_unicode_ci NOT NULL DEFAULT ”,
`user_id` char(36) COLLATE utf8_unicode_ci DEFAULT NULL,
`field` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`value` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
[/cc]

The columns field and value will actually store the user details. Because this table is going to grow vertically, potentially we can store an unlimited and completely arbitrary information about a user. As a matter of fact two different parts of your app can handle storing the user details completely independent of each other. For example, when a user plays the role of a “Guest” versus a user who represents a “Member”, or better yet… a user who is not event a human being, but rather a corporate entity, in which case typical fields like first_name and last_name, may become completely irrelevant.

Hopefully you can start seeing how the key/value approach can help our modeling be a lot more flexible.

… but let’s look closer at some details… how do we deal with this in cake-like-ways?

Actually, it is not that different from what you are already accustomed to.

Let’s see our UsersController:

class UsersController extends AppController {
 
  public function add () {
    if(!empty($this->data)) {
      $this->User->saveAll($this->data, array('validate' => 'first'));
    }
  }

}

Nothing unusual so far…

Next comes our add.ctp view:

echo $this->Form->create('User');
echo $this->Form->input('User.username');
echo $this->Form->input('UserDetail.0.dob');
echo $this->Form->input('UserDetail.1.gender', array(
                        'type' => 'radio',
                        'legend' => FALSE,
                        'options' => array(
                                  'M' => 'M',
                                  'F' => 'F'
                       )));
echo $this->Form->input('UserDetail.2.phone');
echo $this->Form->input('UserDetail.3.email');
echo $this->Form->end('Save');

Nothing unusual so far, yet again.
We are going to save a User with some username as well as some User details: date of birth, gender, phone and an email.
Thinking back, once the save happens, our user_details table will have four records for a single user ID.

Let’s see the models then:

User:

class User extends AppModel {
  public $hasMany = array('UserDetail');

  public $validate = array(
    'username' => array(
        'rule' => 'isUnique',
        'message' => 'Username already exists'
    )
  );
}

UserDetail:

class UserDetail extends AppModel {
  public $belongsTo = array('User');

  public $validate = array(
     'dob' => array(
        'rule' => 'notEmpty',
        'message' => 'Please select a date of birth'
     ),
     'gender' => array(
        'rule' => 'notEmpty',
        'message' => 'Please select a gender'
     ),
     'phone' => array(
        'rule' => 'phone',
        'message' => 'Please enter a phone'
     ),
     'email' => array(
        'rule' => 'email',
        'message' => 'Please enter a valid email'
     )
  );

  public function beforeSave() {
     foreach($this->data[$this->alias] as $field => $value) {
        if($field !== 'user_id') {
          $this->data[$this->alias]['field'] = $field;
          $this->data[$this->alias]['value'] = $value;
        }
     }
     return true;
  }
}

So the validation is handled “as always”.

The only little bit of trickery is in our beforeSave() method…
Here we take our existing fields from the data array (dob, gender, phone…), and convert them to the expected key/value pair (or field/value based on the column names). We are skipping the user_id which is automatically injected by saveAll() just as needed.

Well, this should be a good starting point…

The more extravagant example would be showing the ability to store validation rules and field types in the DB as well. This is helpful if you would like an admin back-end to manage the requirements for user details. As of this example, we’d have to manually adjust the views and validation rules to allow for any new fields. If we store the rules (as serialized array for example) as well as field type(s), the form building and validation would be done on the fly and would allow business users to manage the specifics of the required data.

… but that’s a topic for another day.

JQuery in the CakePHP world (part 2 – is client side code all that great?)

(part 1)

Now, how is this an introduction to jQuery + CakePHP?… well, let’s see in a minute.

First, I’d like to say that with the advent of frameworks such as jQuery, better browsers, which do a nicer job of supporting standards, and even faster PC’s and larger/better monitors, we can happily write better client-side code.

Meaning the following rules are slightly not as important anymore:

  1. A client’s browser may not support my code
  2. A client is not fast enough to process the code
  3. A client is never to be trusted

While the first two rules are becoming less and less applicable, the third one stays true for a while (and for a good reason).

Where does it come into play?

For purposes of this topic, and I can imagine many other cases, where it would be true, we’ll say “Data Validation”.

Indeed, if you validate data on the client-side only, you are running into a serious risk of getting some “really bad” data. So, always double-check the data on the server (that’s a rule that should rarely be bent).

The good news is that nothing is stopping us from doing data validation on the client, the server and might even throw in a little AJAX.

So, with that little foreword let’s see how we can do jQuery-based field validation, using CakePHP’s model validation rules.
(Worth to note, that there is an excellent jQuery plug-in, which is made to work with CakePHP that does great client/server validation for you, but this is an intro, so we’ll just take a look at some basics).

Let’s start with our typical User Model.
We’ll do a very simple validation to keep the code from getting obnoxious:

    class User extends AppModel {

          var $name = 'User';

          var $validate = array('username'=>
                                  array('rule'=>'isUnique',
                                          'message'=>'Sorry, this username already exists'));

    }

We’ll do one single check, whether or not the “username” field is unique. (Nothing unusual so far).

Let’s do a simple view (add.ctp):

<?php
    //let's load up the jQuery core
    echo $javascript->link('jquery/jquery.min', false);

    //and now... some file that will be specific to this view (page)
    echo $javascript->link('jquery/page_specific/users_add', false);
?>

<?php echo $form->create(); ?>
<?php echo $form->input('username', array('id'=>'username')); ?>
<?php echo $form->input('some_other_field'); ?>
<?php echo $form->end('Add User'); ?>

If you haven’t read part 1 (or haven’t worked much with JS/jQuery), some of this might get a little confusing. So I urge you
to do so and then come back.

Anyways, the only thing of interest here is the fact that I gave the “username” field an ID, that’s different from defau< array(‘id’=>’username’), the default DOM ID would be “UserUsername” (i.e. Camel-cased model and field name), but this is different than the field in the DB and what a model would expect. So this is just a little preparation to make our lives easier down the road.

Now comes our jQuery code, which is users_add.js:

$(document).ready( function() {

    $('#username').blur( function () {

        fieldName = $(this).attr('id');
        fieldValue = $(this).val();

        $.post('/users/ajax_validate', {
                                        field: fieldName,
                                        value: fieldValue
                                        },
               function(error) {

                   if(error.length != 0) {

                       $('#username').after('<div class="error-message" id="'+ fieldName +'-exists">' + error + '</div>');
                   }
                   else {
                       $('#' + fieldName + '-exists').remove();
                   }
               });
     });

});

To make the long story short, we post our field name (username) and our value (whatever it happens to be) to our User’s controller ajax_validate action. This only happens when the “username” field loses focus $(‘#username’).blur(… either by the user clicking away with the mouse or tabbing onto “some_other_field”.

So, we are going to let the user know that the username might be already taken while she is filling out the rest of the form.

Let’s use our Model’s validation rule and some good ol’ controller logic to get this working…

class UsersController extends AppController {

      var $name = 'Users';

      function add() {
          if(!empty($this->data)) {
              $this->User->save($this->data);
          }
      }

      function ajax_validate() {
          Configure::write('debug', 0);

          if($this->RequestHandler->isAjax()) {

              $this->data['User'][$this->params['form']['field']] = $this->params['form']['value'];

              $this->User->set($this->data);

              if($this->User->validates()) {
                  $this->autoRender = false;
              }
              else {
                 $errorArray = $this->validateErrors($this->User);

                 $this->set('error', $errorArray[$this->params['form']['field']]);
              }
          }
      }
    }

You can happily skip the add() action, since you’ve seen an example of that about a million times already.

Let’s look at our ajax_validate()
(By the way don’t forget to add var $components = array(’RequestHandler’); to your app controller or Users controller, as one of my kind readers had pointed out).

First, we create the data ($this->data[‘User’][‘username’]) array to validate…
$this->data[‘User’][$this->params[‘form’][‘field’]] = $this->params[‘form’][‘value’];

Remember, that our $this->params[‘form’][‘field’] and $this->params[‘form’][‘value’] come from jQuery. And because we gave the field the right ID the $this->params[‘form’][‘field’] can be applied to any field, thus making it all pretty dynamic (and the data array is built exactly as CakePHP would expect).

So then, we simply attempt to validate the data, as often with $this->User->validates().

In case it does, we need not return or render anything. (You could, but for this example we’ll keep it simple). The idea is that if the “username” is unique, there isn’t effectively anything further that needs to be done.

On the other hand, if the “username” is not unique, we extract the error message and set it for the view, to return back to our jQuery.

In this case we simply return the string, that was defined in our model. (‘Sorry, this username already exists’). You can return xml, JSON or whatever else… again keeping things simple.

Alright, we do need some view to “display” (in proper terms return) the error message back to jQuery script.

Here it is (ajax_validate.ctp):

<?php
    echo $error;
?>

Whew…

Now let’s take another look at a part of our jQuery code:

[sourcecode language=”javascript”]

function(error) {

if(error.length != 0) {
$(‘#username’).after(‘

‘ + error + ‘

‘);
}
else {
$(‘#’ + fieldName + ‘-exists’).remove();
}
}
[/cc]

If anything came back from our ajax_validate() action, we add a div with the error message”

[sourcecode language=”javascript”]
$(‘#username’).after(‘

‘ + error + ‘

‘);
[/cc]

If the user corrects the error, we happily remove the div from the DOM and… be done with it!

'required'=>true… the source of major confusion

In all the time I’ve been following cake on IRC (almost daily), it has become very clear that other than ACL and maybe Auth, ‘required’=>true in the model validation is the most confusing part of CakePHP for many people.

On IRC it comes up, probably, once a week… even from people who’ve “been around”.
And I can imagine that there are a lot of apps out there, where ‘required’=>true is used, without fully understanding as to what it really does.

The problem is that people are quick to assume that ‘required’ means ‘notEmpty’.
Even after pointing people to RTFM, they still come back confused as to the real meaning of ‘required’.

Who cares, right?

Well… we often hear a complain about save() failing (very often) without any apparent reason.
The issue boils down to people having ‘required’=>true in the validation rules, while in reality they only wanted ‘rule’=>’notEmpty’.

When first creating the Model such setup has no ill effects, because all fields are in the data array and the record can be successfully added and even edited (assuming some basic forms).
Then at some point the app becomes a little more complex and people decide to save only a few fields at a time… and of course the validation fails.

This leads to debauchery on IRC, dents in the wall from banging the head, and other nasty side-effects.

My proposal… rename ‘required’ to something else. Doesn’t matter what, just not ‘required’.

The reality of the situation is that, if the key was named ‘mustBeInDataSet’, people would never even bother to include it, unless it was really needed… and lots of mess and confusion would be avoided.

Looking at the code, it doesn’t seem like a major change… although I’ve not done any testing (but I promise that I will).
Just wanted to get some feedback on this issue first….

m4s0n501

'required'=>true… the source of major confusion

In all the time I’ve been following cake on IRC (almost daily), it has become very clear that other than ACL and maybe Auth, ‘required’=>true in the model validation is the most confusing part of CakePHP for many people.

On IRC it comes up, probably, once a week… even from people who’ve “been around”.
And I can imagine that there are a lot of apps out there, where ‘required’=>true is used, without fully understanding as to what it really does.

The problem is that people are quick to assume that ‘required’ means ‘notEmpty’.
Even after pointing people to RTFM, they still come back confused as to the real meaning of ‘required’.

Who cares, right?

Well… we often hear a complain about save() failing (very often) without any apparent reason.
The issue boils down to people having ‘required’=>true in the validation rules, while in reality they only wanted ‘rule’=>’notEmpty’.

When first creating the Model such setup has no ill effects, because all fields are in the data array and the record can be successfully added and even edited (assuming some basic forms).
Then at some point the app becomes a little more complex and people decide to save only a few fields at a time… and of course the validation fails.

This leads to debauchery on IRC, dents in the wall from banging the head, and other nasty side-effects.

My proposal… rename ‘required’ to something else. Doesn’t matter what, just not ‘required’.

The reality of the situation is that, if the key was named ‘mustBeInDataSet’, people would never even bother to include it, unless it was really needed… and lots of mess and confusion would be avoided.

Looking at the code, it doesn’t seem like a major change… although I’ve not done any testing (but I promise that I will).
Just wanted to get some feedback on this issue first….

Set::merge() and dynamic validation rules

Here’s another trick with Set::merge()…

Let’s say we’ve defined some basic validation rules in our Profile model, something like:

var $validate = array (
   'name' => array(
      'rule' => array('notEmpty'),
      'required' => false,
      'message' => 'Please enter a name'
     )

  .... and so on for all other fields ...

);

Now we’ve got an action where this particular set of rules isn’t going to fly…

Maybe the data is coming from an external (somewhat untrusted) source and in this case we need to make sure that the ‘name’ is present in the data array (i.e. ‘required’=>true) and that it’s not only ‘notEmpty’, but now it has to be ‘alphaNumeric’.

So we do an easy “update” to our default rules, which we’ve previously defined in the model:

$this->Profile->validate = Set::merge($this->Profile->validate, array(
      'name'=>array(
         'required'=>true,
         'rule'=>array('alphaNumeric')
      )
));

What happened is that now all our default rules remain as we have defined them in the model, but the ‘name’ field is now going to be validated using this new set of rules.

The theory is that you set your $validate array with some default rules that will apply in most of the situations, and use this technique when your validation rules have to change for a few fields in any given action.

To make things even cleaner you should probably add a method to AppModel called something like modifyValidate, and then you could use:

$this->Profile->modifyValidate(… array of new rules…);

notEmpty validation rule

With the recent changeset (https://trac.cakephp.org/changeset/7399) a new, ‘notEmpty’, validation rule is now available. As you can guess, it checks to make sure that a field contains something other than a white space.

You will have to upgrade to the latest build, to ensure that this rule is available in your cake core.

Otherwise you can rely on this simple regex, to achieve the same thing:

‘rule’ => array(’custom’, ‘/\S+/’)

Validating a checkbox in CakePHP 1.2

In a few simple words: use the ‘comparison’ rule to validate a checkbox.

To give you an example, let’s say a user needs to agree to the terms of service when registering a new account. In your User model you setup a rule for the checkbox as follows:

 'agree' => array(
              'rule' => array('comparison', '!=', 0),
              'required' => true,
              'message' => 'You must agree to the terms of use',
                'on' => 'create'
       )

As you can probably guess from the code the empty checkbox will send a default value of zero, and that is what your validation rule is going catch. The ‘on’ key will ensure that the rule is only enforced on account creation and not when the User is editing an existing account.

15 Essential CakePHP Tips

Note, this article was written a long time ago for CakePHP version 1.x … many of the points described here still apply in the latest 2.x versions of CakePHP.

1. Save() does not work!
Sometimes it happens that save() fails without any obvious reason. Your data array looks fine and you’ve build the form correctly, etc., etc., but no query was executed. It is very possible that save had failed due to validation errors. Maybe you are updating some model and while the current fields in the form pass the validation, there is a chance that some “other ones” are causing the validation rules to fail. An easy (and helpful) way to see what’s going on with validation is to do pr($this->validationErrors); in your view. By employing this method you’ll see exactly what’s happening with your model’s validation. The other option is to pass false as a second parameter to save(); in order to disable the validation. However, the latter method does not give much of hint and should not be used to fix a failing problem, but rather to purposely avoid validation.

2. Save() still does not work!
Do you have beforeSave(); in your model or app model? Always double-check for this method, and even more importantly, ensure that it returns true.

3. Validating on create or update
CakePHP has an ‘on’ key to be used in your $validate array. It allows you to specify whether the rule should be enforced during a new record creation or during an update of an existing record. For example, if you only want to check for a unique email address when you are creating a new User account, you’d add ‘on’ => ‘create’ to your $validate array. Therefore, this rule will be ignored when you are updating/editing some user.

4. Mind your cache
You’ve made some changes to your tables, but your app “ignores” them… You’ve moved some things around, bur your app doesn’t seem to “notice” that…
Clear the cache. Delete the files from your app/tmp/cache. Be sure to only delete the files and not the directory structure.
Basically if you notice some strange behavior in your app, just keep in mind that it could be caused by the cache.

5. I’m losing the extra URL parameters when paginating
You need to retain some extra URL parameters during pagination. For example the URL is something like /products/view/45. Yet, when you build the pagination the ID (45) is lost… Well, all you need to do is add this line of code to your view: $paginator->options(array(‘url’ => $this->passedArgs));

Update: this is likely fixed in the recent builds of CakePHP, but still good to be aware of.

6. Using afterFind()
Model::afterFind() allows you to perform some data manipulation after the Model’s find method. Here’s a sample usage in the model:

function afterFind($results, $primary=false) {
  if($primary == true) {
  // do stuff to the $results
  }

  return $results;
}

$primary will be set to true after your find() method is executed and some results are returned. After you modify the $results you will return them back to your app (controller).

7. I need to know the basic information about my table
Try this: pr($this->ModelName->schema())

(Thanks, gwoo, for the hint)

8. How do I check for a non-empty field in CakePHP 1.2?
Update: cake core now has a ‘notEmpty’ rule built-in, so definitely use it instead.

For historic purposes only:
The old VALID_NOT_EMPTY constant is now deprecated and there does not seem to be a rule to replace it… Well, it’s easy enough by using: ‘rule’ => array(‘minLength’, ‘1’)

Martin Bavio pointed out that having space characters (only) as your field data will make the validation rule pass and this is probably not a desirable effect. Using this simple regex, instead, will catch an empty string with space characters: ‘rule’ => array(‘custom’, ‘/\S+/’)

9. Avoid using the $uses array
You’ve got two completely unrelated models, but you need info from one in the controller of another. The first idea is to add them to the $uses array. Hey, it’s easy and gets the job done. Well, to make the long story short, it’s bad practice. Think about your model bindings and ensure that models are really not related to one another. Sometimes your User model is definitely not related to CommentRating, but you absolutely need it in your users controller. Well, just by chance it appears that User->Post->Comment->CommentRating. It’s a deep binding that may not be obvious at first, but by employing such a chain of models you can easily avoid using the $uses array, when it’s really not necessary.

Update: if you truly need to load some random (and definitely unrelated) model in your controller, do it like so in the given action:

$this->load(‘MyModel’);
As soon as this line executed you’ll have an instance of MyModel available in $this->MyModel (pr($this->MyModel); to see your freshly loaded object.

Another option is to use this approach:
$MyModel = ClassRegistry::init(‘MyModel’);

Which is nice, because it allows you to do things like this:

$someData = ClassRegistry::init(‘MyModel’)->find(‘all’, array(‘conditions’ => array(‘MyModel.name LIKE’ => ‘% ‘. $name . ‘%’ ))); (the latter, chaining, example is PHP5 only)

10. Clean-up ugly HTML

It’s no secret that CakePHP often outputs some very ugly and hard to read HTML. This little trick will make it a lot cleaner.
Create a file named app_helper.php in your app’s root directory.

Next add this function to it:


function output($string) {
return parent::output($string . “\n”);
}

Take a look at your HTML now… much better.

(Thanks, TommyO, for the hint)

11. How can I access session data in the view?
Very easy. You have the $session helper available.
Just try it: pr($session)
Update: if you have already written some variables to the session, then try pr($session->read());

12. I need to save data from multiple models
Don’t even think about using loops and other trickery. There is a very nice (but not well documented) method saveAll() available. It will let you save data from multiple models at once. Granted, it takes a little time to figure it out, but once you get it working it is really a time saver. Try using it with some dummy data, and make sure that your data array is properly formatted and associations are setup correctly.

13. Static pages and routes
I really don’t like having /pages/ as part of the URL for my static pages. Well, they are static… so let’s make them .html instead (at the same time we sprinkle just a little of security by obscurity mantra).
If you had links pointing to www.example.com/pages/myPage/ they should now point to www.example.com/myPage.html and add this to your routes:

Router::connect(‘/(.*).html’, array(‘controller’ => ‘pages’, ‘action’ => ‘display’));

Sweet.

14. The fastest way to build a form in CakePHP


echo $form->create();
echo $form->inputs();
echo $form->end();

It is just a step beyond scaffolding, but really this is how easy it is to build a form in CakePHP. Just give it a go.

15. Get to know CakePHP by using Github

CakePHP is constantly evolving and if you are serious about developing on top of this framework it is very important to keep up with the latest and greatest and to get familiar with some of the new and upcoming features. https://github.com/cakephp is a great place for this.

Do I have any errors in my form?

A nice little trick to check if you have some errors in the view is to use the $session object.

Try this (in the view): pr($this->validationErrors) or pr($session->validationErrors), be sure to actually have some errors in the form…

I leave it up to you to come with some nice usage for it.

Well, here’s a little hint: try creating an element that will check if the above array is empty or set, and if so display some generic message, such as “Please fix the errors below…” this is especially useful for long HTML forms where the actual error message (or field error) might fall below the screen fold.