I'm using a simple registration form for CakePHP.
This is my edit view:
<h2>Add User</h2>
<?php echo $form->create('User');
echo $form->input('first_name');
echo $form->input('last_name');
echo $form->input('email');
echo $form->input('address');
echo $form->input('phone');
echo $form->end('Edit');?>
These are my validation rules in User.php
class User extends AppModel {
var $name = 'User';
var $validate = array(
(... more rules here ...)
'password' => array('rule' => array('confirmPassword', 'password'),
'message' => 'Passwords do not match'),
'password_confirm' => array('rule' => 'alphanumeric',
'required' => true)
);
It's a stupid question, but when I use the edit form, the saving of the data fails, because I require a password input in the last rule. That rule was added for the register page, of course.
I've found billions of ways to bypass this problem (disable validation on saving, selecting which fields to save, ...), but I wonder what the "correct" CakePHP way of doing this is? Seems like such a simple problem, but I can't find any documentation on how it has to be done the clean way.
To make my question more clear: I don't want an edit user page where passwords can be changed.
the Cake way is to simply set the validation rule to apply only on create, allowing you to freely edit your data without having to validate the field again.
var $validate = array(
'fieldName' => array(
'rule' => 'ruleName'
'required' => true,
'allowEmpty' => false,
'on' => 'create', // or update <-- here
'message' => 'Your Error Message'
)
);
And of course, if you don't want the password field edited here, just remove the appropriate input
I use multiple validation sets:
In your model, or app_model, override the validates function with this:
function validates($options = array()) {
// copy the data over from a custom var, otherwise
$actionSet = 'validate' . Inflector::camelize(Router::getParam('action'));
if (isset($this->validationSet)) {
$temp = $this->validate;
$param = 'validate' . $this->validationSet;
$this->validate = $this->{$param};
} elseif (isset($this->{$actionSet})) {
$temp = $this->validate;
$param = $actionSet;
$this->validate = $this->{$param};
}
$errors = $this->invalidFields($options);
// copy it back
if (isset($temp)) {
$this->validate = $temp;
unset($this->validationSet);
}
if (is_array($errors)) {
return count($errors) === 0;
}
return $errors;
}
In your controller:
class UsersController extends AppController {
function forgot() {
$this->User->set($this->data);
$this->User->validationSet = 'forgotpassword';
if ($this->User->validates()) {
// send email to reset password and show success message
}
}
}
And finally back in your model, build a validation array, you can have your default validation array, then add a supplemental array such as
class User extends AppModel {
var $validate = array( ... );
var $validateForgotpassword = array( ... );
}
More details here:
http://snook.ca/archives/cakephp/multiple_validation_sets_cakephp
(note the code sample in the article has a bug in it, so be sure to change $param = 'validate' . $validationSet; to $param = 'validate' . $this->validationSet;)
remove your 'required' => true, it is not doing what you think its doing.
'required' => true means that the field should be present in any data that is saved. you are not passing it.
Related
I am new to CakePHP and have created a normal form to submit first name.
My table name is "registers. I have created a controller named RegistersController (RegistersController.php) and a model named Register (Register.php) . Each time i submit after entering first name, it still displays error (First name is must) which it should only if i submit it without entering anything. Next i added validation for having minimum 6 characters. That validation is also not working. I mean, cakephp is not validating that rule. Could anyone please tell me where i have done anything wrong?
Model:-
class Register extends AppModel {
//put your code here
//public $useTable = "registers";
public $validate = array(
'first'=>array(
'minLength' => array(
'rule' => array('minlength','6'),
'field' => 'first',
'message' => 'Minimum 6 characters required'
),
'required' => array(
'rule'=>array('notEmpty'),
'required' => true,
'message' => array('First name is must')
)
)
);
}
Controller:-
class RegistersController extends AppController {
public $uses = array("Register");
//put your code here
public function index() {
if($this->request->is('post')){
//echo "Data";
if($this->Register->validates()){
//$this->Register->create();
//echo "Data validated";
print_r($this->Register->validationErrors);
}else{
//echo "Data not validated";
print_r($this->Register->validationErrors);
}
}
}
My view is as follows:-
<?php
echo $this->Form->create('Register');
echo $this->Form->input('first');
echo $this->Form->end("Submit");
?>
You are missing this line
$this->Register->set($this->request->data);
Put it before the validation call, i.e.
$this->Register->validates()
This my database:
http://gyazo.com/c6a86127d6f91aae947cf45ee535cecd
Example:
http://gyazo.com/c23fec3fabb7e4504c42453980fbc372
When I press the edit button , they able to retrieve the data however the page show me empty field instead of the field that have old data.
Secondly, unable to undate because they keep send empty id_field back to controller.
p.s. add,edit,delete method work totally fine.
Below are my code:
Model
<?php
App::uses('AppModel', 'Model');
class EventDate extends AppModel {
public $useTable = 'eventdate';
public $primaryKey = 'eventDate_id';
public $validate = array(
'event_date' => array(
'rule' => array('date','ymd'),
'message' => 'Enter a valid date in YY-MM-DD format.',
'required' => true,
'allowEmpty' => false
)
);
}
Controller
public function edit($id = null) {
//Retrieve from database
$post = $this->EventDate->findByEventdateId($id);
$this->set('edate', $post) ;
// debug($post);
//Without Id
if (!$id) {
throw new NotFoundException(__('Invalid Event Date'));
}
//If Not Exist the id
if (!$post) {
throw new NotFoundException(__('Invalid Event Date'));
}
//After press the submit
if ($this->request->is(array('post','put'))) {
$this->EventDate->eventDate_id = $id;
debug($_POST);
if ($this->EventDate->save($this->request->data)) {
$this->Session->setFlash(__('The Event has been saved.'));
return $this->redirect(array('action' => 'index'));}
else
{$this->Session->setFlash(__('The Event could not be saved. Please, try again.'));}
}
//Set the data same as retrieved
if (!$this->request->data) { $this->request->data = $post;}
}
edit.ctp
<h2>Edit Event</h2>
<?php
echo $this->Form->create('eventdate');
//echo $this->Form->input('eventDate_id', array('type' => 'hidden'));
echo $this->Form->hidden('eventDate_id');
echo $this->Form->input('event_date',array(
'label' => 'Event Date',
'type' => 'text',
'id' => 'datepicker'));
echo $this->Form->end('Save Post');
?>
Below link is the debug($_Post) been shown:
http://gyazo.com/5e569e6cc6b3026fc8896c315197a938
Should be:
echo $this->Form->create('EventDate'); // notice the capital CamelCase
Side note: The info that displays in the fields will be out-of-date, since you 1) get the data from the DB and set to var, THEN do the save based on posted data.
Another side note: There are quite a few things that you're doing that are non-standard and not consistent w/ the recommended conventions. Cleaning that up will make it easier to work with AND easier to receive help.
I'm trying to build on the example code at http://book.cakephp.org/2.0/en/models/saving-your-data.html in the section that starts off with "Saving Related Model Data (hasOne, hasMany, belongsTo)". However, I'm getting the following error message when I call unset:
Indirect modification of overloaded property AppModel::$MealContent has no effect [APP\Controller\MealsController.php, line 15]
Attempt to modify property of non-object [APP\Controller\MealsController.php, line 15]
Naturally, the data isn't saved either.
As you can see, my application doesn't have Companies or Accounts. Instead, I have Meals and MealContents but the relationships seem to have been set up the same. Obviously, there's a problem somewhere though so here's some code.
Meals.php:
class Meals extends Entry {
public $hasMany = 'MealContents';
public $validate = array(
'Timestamp' => array(
'validDate' => array(
'rule' => array('garbageDateChecker'),
'required' => true,
'message' => 'The date could not be understood.'),
'noTimeTravel' => array(
'rule' => array('noTimeTravel'),
'required' => true,
'message' => 'You cannot enter times in the future.')
)
);
}
MealContents.php:
class MealContents extends Entry {
public $belongsTo = 'Meals';
public $validate = array(
'Meals_ID' => array(
'notEmpty' => array(
'rule' => 'notEmpty',
'required' => true,
'message' => 'This field cannot be blank')
),
'Item_ID' => array(
'notEmpty' => array(
'rule' => 'notEmpty',
'required' => true,
'message' => 'This field cannot be blank')
)
);
}
And finally, the controller's index() function:
public function index() {
$this->set('title_for_layout', "Food Log");
if ($this->request->is('post')) {
$this->Meal->create();
unset($this->Meal->MealContent->validate['Meals_ID']);
if ($this->Meal->saveAssociated($this->request->data)) {
$this->Session->setFlash('The meal was logged.');
$this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash("Couldn't save the meal.");
}
}
}
Entry.php
abstract class Entry extends AppModel {
public function garbageDateChecker($dateToCheck) {
date_default_timezone_set("America/Tijuana");
$result = TRUE;
try {
new DateTime($dateToCheck['Timestamp']);
} catch (Exception $e) {
$result = FALSE;
}
return $result;
}
public function noTimeTravel($dateToCheck) {
date_default_timezone_set("America/Tijuana");
$result = TRUE;
$objectifiedDateToCheck = new DateTime($dateToCheck['Timestamp']);
$currentTime = new DateTime("now");
if ($objectifiedDateToCheck > $currentTime) {
$result = FALSE;
}
return $result;
}
}
I'm pretty sure the save() failure isn't due to the validation because even when I comment out the validation rule and the unset() line, the data isn't saved.
It would be easy to blame it on bad data or a screwed up view. However, the data looks OK:
$this->request->data
--MealContents
---[0]
-----[Food_Item_ID] = "0"
--Meals
---[Comments] = ""
---[Timestamp]
-----[day] = "12"
-----[hour] = ...
What have I missed when I read the CakePHP book?
In all of your model class declarations, you should be extending the AppModel class not "Entry". Also, you need to change your model names to singular nouns. Meal instead of Meals.
class Meal extends AppModel {
//your model's code
}
class MealContent extends AppModel {
//your model's code
}
In your controller, if you would like to skip validation on the saveAssociated call, you can pass an options array, with element "validate" set to False as the second parameter. You should never use unset on your model like that as it will affect the rest of your app.
$this->Meal->saveAssociated($this->request->data, array("validate" => false));
How to get an input type file to validate for notempty in cake?
When you submit the form without adding a file the validation says it is empty even though $this->request->data shows the file.
// Model/Product.php
class Product extends AppModel {
public $validate = array(
'name' => array(
'rule' => 'notEmpty'
),
);
}
// Controller/ProductController.php
public function add() {
if ($this->request->is('post')) {
$this->Product->create();
if ($this->Product->save($this->request->data)) {
$this->Session->setFlash('Your product has been saved.');
} else {
$this->Session->setFlash('Unable to add your product.');
debug($this->request->data);
debug($this->Product->validationErrors);
}
}
}
// View/Products/add.ctp
echo $this->Form->create('Product', array('type' => 'file'));
echo $this->Form->input('name', array('type' => 'file'));
echo $this->Form->end('Save Post');
I don't think that you can actually use notEmpty on the -somewhat special- file field. The file field is handled differently than any other input field, as it returns the superglobal $_FILES as a result. As such, you should check it a little bit differently. There is actually quite a good example in the CakePHP Documentation.
Now this is for actually uploaded files, but you can easily change it by checking if the name key is not empty and actually set. Something like this as a custom validation rule in your Model should do the trick:
public function fileSelected($file) {
return (is_array($file) && array_key_exists('name', $file) && !empty($file['name']));
}
And then just set this as validation rule for your file field:
public $validate = array(
'name' => array(
'rule' => 'fileSelected'
),
);
Can anyone help me with a clear and complete example on how to set validations for 2 fields, say an email and password, with error messages?
From my understanding, the correct format is:
var $validate = array(
'password' => array(
'rule' => array('minLength', '8'),
'message' => 'Minimum 8 characters long'
),
'email_id' => array('email')
);
but I can’t seem to get it work (show a validation message, or halt the execution of the action) in my tests.
Validations work fine but no way for the custom messages to appear!
EDIT
The validations and page redirections work fine now. Only the specific messages do not appear. That is, if I enter a password less than 8 characters, the message "minimum 8 characters needed" should appear immediately or after I click the register button. Is there any method to do this?
EDIT 2
My view file
<!-- File: /app/views/forms/index.ctp -->
<?php
echo $javascript->link('prototype.js');
echo $javascript->link('scriptaculous.js');
echo $html->css('main.css');
?>
<div id="register">
<h3>Register</h3>
<?php
echo $form->create('User',array('action'=>'register'));
echo $form->input('User.name');
echo $form->input('User.email_id');
echo $form->input('User.password');
echo $form->end('Register');
?>
</div>
<div id="login">
<h3>Login</h3>
<?php
echo $form->create('User',array('action'=>'login'));
echo $form->input('User.email_id');
echo $form->input('User.password');
echo $form->end('Login');
?>
</div>
Controller:
<?php
class UsersController extends AppController
{
var $name = 'Users';
var $uses=array('Form','User','Attribute','Result');
var $helpers=array('Html','Ajax','Javascript','Form');
function register()
{
$userId=$this->User->registerUser($this->data);
$this->User->data=$this->data;
if (!$this->User->validates())
{
$this->Flash('Please enter valid inputs','/forms' );
return;
}
$this->Flash('User account created','/forms/homepage/'.$userId);
}
function login()
{
$userId=$this->User->loginUser($this->data);
$this->User->data=$this->data;
if (!$this->User->validates())
{
$this->Flash('Please enter valid inputs','/forms' );
return;
}
if($userId>0){
$this->Flash('Login Successful');
$this->redirect('/forms/homepage/'.$userId);
break;
}
else{
$this->flash('Username and password do not match.','/forms');
}
}
}
?>
Model:
<?php
class User extends AppModel {
var $name = 'User';
var $components=array('Auth');
var $validate = array(
'name' => array(
'rule' => VALID_NOT_EMPTY,
'message' =>'Name cannot be null.'
),
'password' => array(
'rule' => array('minLength', '6'),
'message' => 'Minimum 6 characters long.'
),
'email_id' => array(
'rule'=> array('email'),
'message'=>'Invalid email.'
)
);
function registerUser($data)
{
if (!empty($data))
{
$this->data['User']['name']=$data['User']['name'];
$this->data['User']['email_id']=$data['User']['email_id'];
$this->data['User']['password']=$data['User']['password'];
if($this->save($this->data))
{
$this->data['User']['id']= $this->find('all',array('fields' => array('User.id'),
'order' => 'User.id DESC'
));
$userId=$this->data['User']['id'][0]['User']['id'];
return $userId;
}
}
}
function loginUser($data)
{
$this->data['User']['email_id']=$data['User']['email_id'];
$this->data['User']['password']=$data['User']['password'];
$login=$this->find('all');
foreach($login as $form):
if($this->data['User']['email_id']==$form['User']['email_id'] && $this->data['User']['password']==$form['User']['password'])
{
$this->data['User']['id']= $this->find('all',array('fields' => array('User.id'),
'conditions'=>array('User.email_id'=> $this->data['User']['email_id'],'User.password'=>$this->data['User']['password'])
));
$userId=$this->data['User']['id'][0]['User']['id'];
return $userId;
}
endforeach;
}
}
?>
Here is a live example from my project..
This is how you set up your validation in your model: Article model
Ignore the fact that I'm initializing the validate array from constructor, you can keep doing it like you're doing it now if you don't plan on implementing I18n and L10n.
Handling validation errors in controller: Articles controller
From line 266 to 280 you can see validation and save errors being handled with setFlash() + return.
That's pretty much all you need to do, just don't forget you need to use the FormHelper for your forms for the messages to work as expected.
Common error: you must not do a $this->redirect() after failed validation.
Hopefully this will set you on the right track :)
Why dont you try $this->modelName->invalidFields(), which will return you an array with the fields that failed validation and the message to display.
http://book.cakephp.org/view/1182/Validating-Data-from-the-Controller