开发者

HABTM form validation in CakePHP

开发者 https://www.devze.com 2022-12-16 14:48 出处:网络
I have a Projects table and a Users table which are linked by a HABTM relation. In t开发者_如何学编程he \"add\" new Project page I have a multiple checkbox section to select Users for the new project.

I have a Projects table and a Users table which are linked by a HABTM relation. In t开发者_如何学编程he "add" new Project page I have a multiple checkbox section to select Users for the new project. I want to have at least one User for the Project. What's the best way to approach this in CakePHP ?


Try this:

// app/models/project.php
/**
 * An additional validation check to ensure at least one User is
 * selected. Spoofs Cake into thinking that there are validation
 * errors on the Project model by invalidating a non-existent field
 * on the Project model, then also invalidates the habtm field as
 * well, so when the form is re-displayed, the error is displayed
 * on the User field.
 **/
function beforeValidate() {
  if (!isset($this->data['User']['User'])
  || empty($this->data['User']['User'])) {
    $this->invalidate('non_existent_field'); // fake validation error on Project
    $this->User->invalidate('User', 'Please select at least one user');
  }
  return true;
}


I stumbled on the same issue, but now - 3 years later - with CakePHP 2.3.

To be clear; Group has and belongs to User. I've had a form like this:

// View/Groups/add.ctp
echo $this->Form->input('name');
echo $this->Form->input('User');

With the validation rule like in user448164's answer:

// Model/Group.php
public $validate = array(
    'User' => array(
        'rule' => array('multiple', array('min' => 1)),
        'message' => 'Please select one or more users'
    )
);

That didn't work, after Googling for it, I found this question which couldn't still be the best solution. Then I tried several things, and discovered this to work just fine:

// View/Groups/add.ctp
echo $this->Form->input('name');
echo $this->Form->input('Group.User');

Way too easy solution, but had to dig into it to find out it works this way.

Hopefully it helps somebody one day.

Update for CakePHP 2.4.x (possibly 2.3.x as well)

When I wrote this answer, I was using CakePHP 2.3.x. Back then it worked perfectly for both validating and saving the data. Now when applying the same code on a new project, using CakePHP 2.4.x, it didn't work anymore.

I created a test case, using the following code:

$data = array(
    'User' => array(
        'Client' => array(8)
    ),
);
$this->User->create();
$this->User->saveAll($data);

My first thought was: Saving all means saving all "root" models, what actually makes sense to me. To save deeper than just the "root" ones, you'll have to add the deep option. So I ended up with the following code:

$data = array(
    'User' => array(
        'Client' => array(8)
    ),
);
$this->User->create();
$this->User->saveAll($data, array('deep' => true));

Works like a charm! Happy coding. :)

Update (2014/03/06)

Struggling with the same problem again, in this case with hasMany instead of habtm. Seems like it behaves the same way. But I found myself looking for this answer again, and got confused.

I'd like to make clear that it's key to use Group.User instead of User in your input. Else it won't use the User model validation.


I've just been looking at his problem myself on a project and came across a slightly more elegant solution, as long as you're only dealing with a habtm relationship and you need to ensure that at least one checkbox is selected.

so for example you're editing a Project and you want it to be associated with at least one user

Add this to beforeValidate()

// check habtm model and add to data

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

In the validation rules add the following:

'User' => array(
   'rule' => array('multiple', array('min' => 1)),
   'message' => 'Please select one or more users'
  )


teknoid's blog has a pretty in depth solution to your issue here. The most Cakey way of doing this would be to add custom validation to your model, as you mention in your comment above. Check out http://teknoid.wordpress.com/2008/10/16/how-to-validate-habtm-data/

From the article, where Tag HABTM Post (:: Project HABTM Users):

First, we validate the Tag model, by using the data from the form to ensure that at least one Tag was selected. If so, we save the Post and the relevant Tags.


2016 update for CakePhp 2.7

Full answer here : HABTM form validation with CakePHP 2.x


TL;DR;

AppModel.php

public function beforeValidate($options = array()){
   foreach (array_keys($this->hasAndBelongsToMany) as $model){
     if(isset($this->data[$model][$model]))
       $this->data[$this->name][$model] = $this->data[$model][$model];
   }
   return true;
 }

 public function afterValidate($options = array()){
   foreach (array_keys($this->hasAndBelongsToMany) as $model){
     unset($this->data[$this->name][$model]);
     if(isset($this->validationErrors[$model]))
       $this->$model->validationErrors[$model] = $this->validationErrors[$model];
   }
   return true;
 }

In the main model of your HABTM :

public $validate = array(
    'Tag' => array(
        'rule' => array('multiple', array('min' => 1)),
        'required' => true,
        'message' => 'Please select at least one Tag for this Post.'
        )
    );


If you are using CakePHP 2.3.x, you may need to add this code to your model in addition to the code that GuidoH provided, otherwise your HABTM model data may not save:

public function beforeSave($options = array()){
  foreach (array_keys($this->hasAndBelongsToMany) as $model){
    if(isset($this->data[$this->name][$model])){
      $this->data[$model][$model] = $this->data[$this->name][$model];
      unset($this->data[$this->name][$model]);
    }
  }
  return true;
}


As per my comment on Guido's answer above, I use Guido's answer exactly, however I modify the data with the beforeSave callback before it saves to the database.

I have this issue on Cake 2.4.5+

public function beforeSave($options = array()) {
    $temp = $this->data['Group']['User'];
    unset($this->data['Group']['User']);
    $this->data['User']['User'] = $temp;

    return true;
}
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号