3
votes

Short version

I have some HABTM checkboxes on a form. Validation is working correctly (at least one checkbox needs to be checked for validation to pass) but the CakePHP error message divs aren't being generated as they should be.

Long Version

I have a from which allows users to fill in their name and email address and then choose from a list of brochures (checkboxes) they'd like to receive.

The form looks like this:

<?php
echo $this->Form->create('Request',array('action' => 'index'));
echo $this->Form->input('id');
echo $this->Form->input('name');
echo $this->Form->input('email');
echo $this->Form->input('Brochure',array(
        'label' => __('Information Required:',true),
        'type' => 'select',
        'multiple' => 'checkbox',
        'options' => $list,
        'selected' => $this->Html->value('Brochure.Brochure'),
));
echo $this->Form->submit('Submit');
echo $this->Form->end();
?>

In my controller, $list is set as like this:

$this->Request->Brochure->find('list',array('fields'=>array('id','name')));

After reading the 2nd answer (posted by user448164) in HABTM form validation in CakePHP on Stack Overflow, I set my Request model up like this:

<?php

class Request extends AppModel {

var $name = 'Request';

function beforeValidate() {
    foreach($this->hasAndBelongsToMany as $k=>$v) {
        if(isset($this->data[$k][$k]))
        {
            $this->data[$this->alias][$k] = $this->data[$k][$k];
        }
    }
}

var $validate = array(
    'name' => array(
        'rule' => 'notEmpty',
        'message' => 'Please enter your full name'
    ),
    'email' => array(
        'rule' => 'email',
        'message' => 'Please enter a valid email address'
    ),
    'Brochure' => array(
        'rule' => array('multiple', array('min' => 1)),
        'message' => 'Please select 1'
    ),
);
?>

This actually works 99% well. If none of the checkboxes are checked, validation fails as it should do. However, the only problem is that Cake isn't setting the "error" class on the <div>, nor is it creating the <div class="error-message">Please select 1</div> as it should.

For name and email, there is no problem - the error divs are being created properly.

So, to clarify, validation is working for my HABTM checkboxes. The only problem is that the error divs aren't being generated.

1
What cake version, and what does $this->validationErrors look like?jeremyharris
@jeremyharris I'm using Cake 2.1.1 but I've actually had this problem since Cake 1.3.x. $this->validationErrors is null but $this->Request->validationErrors gives me the array containing the 'Please select 1' error message I set in the model.Joseph
Yes but what's the format of the validation error array? That's how Cake determines if a field should show the error. You'll probably need to massage it a bit, which is what I'm getting at, something like the answer given in the question you linked to.jeremyharris
@jeremyharris Yes, you're right, the Accepted Answer in the question I linked to actually works perfectly. I tried it before and it wasn't working but I've made some model changes since then so I guess it was that. Sorry to waste your time. I have flagged this as a duplicate question.Joseph
No waste, don't worry. Glad you got it working!jeremyharris

1 Answers

0
votes

I'm posting this here as this is actually a much better question than the related question you found.

I was banging my head against a wall trying to handle the same problem of getting the validation error to show up in the page. I'm using CakePHP v1.2 and I hit a similar problem although I have actually split out the HABTM into the individual tables i.e. Request->BrochuesRequest->Brochure. This is because I can't have it deleting and re-adding the joining table rows at will.

Firstly I think the accepted answer from your linked question assumes that you are doing a save / saveAll when the beforeValidate call is triggered, however I was doing it through a validates call. The difference is that you need to call the Request->set method first. It was an article by Jonathan Snook on Multiple Validation Sets pointed me to that issue.

The second issue is actually getting the error message to appear was down to the $field value you use when calling invalidate. For ages I was including the model as well as the field assuming that this was how it matched the invalidate call to the input, i.e. you have $form->input('BrochuresRequest.brochures_id') so you need $this->BrochuresRequest->invalidate('BrochuresRequest.brochures_id').

However that is wrong you just want $this->BrochuresRequest->invalidate('brochures_id').

<?php   
// requests/add view
echo $form->input('BrochuresRequest.brochures_id', array('multiple' => true));

// requests_controller
function add() {
    if (!empty($this->data)) {
        $this->Request->create();
        // critical set to have $this->data 
        // for beforeValidate when calling validates
        $this->Request->set($this->data); 
        if ($this->Request->validates()) {
            $this->Request->saveAll($this->data);
        }
    }
}

// request model
function beforeValidate() {
    if (count($this->data['BrochuresRequest']['brochures_id']) < 1) {
        $this->invalidate('non_existent_field'); // fake validation error on Project
        // must be brochures_id and not BrochuresRequest.brochures_id
        $this->BrochuresRequest->invalidate('brochures_id', 'Please select 1');
        return false;
    }
    return true;
}
?>

A few of the other things that I picked up on the way through:

  1. You don't need a separate $form->error in the view
  2. I couldn't for the life of me get the 'multiple' validation rule to work in the model
  3. The accepted answer checks for an isset but I believe that this isn't required and masked the problem of there being no $this->data being passed through.
  4. The beforeValidate should return false if you want it to prevent any save action.