I'm writing a customer management system using cakePHP (the first thing I've ever attempted to build using this framework), and I'm struggling to figure out how to validate certain fields when adding a new customer.
Each customer has an id that I need to be able to add manually, and a username that has to be unique but can be empty.
Here is what I want to happen:
When adding a new customer:
Check whether the id already exists and alert the user if it does (and not add the user)
Check whether the username already exists and alert the user if it does (and not add the user)
When updating a customer profile (the id cannot be modified at this point):
If the username has been modified, check whether it already exists and alert the user.
As it now stands, whenever I try adding a user with an existing id, cakePHP simply overwrites the existing id's info with the new info.
I've tried several validation options but nothing seems to work.
Here's the first:
public $validate = array(
'id' => array(
'idRule-1' => array(
'on' => 'create',
'rule' => 'uniqueID',
'message' => 'This Oracle ID already exists.',
'last' => false
),
'idRule-2' => array(
'rule' => 'numeric',
'message' => 'Oracle ID can only contain numbers.',
'last' => false
),
),
'username' => array(
'userRule-1' => array(
'on' => 'create',
'rule' => 'uniqueUser',
'message' => 'This username already exists',
'last' => false,
),
'userRule-2' => array(
'on' => 'update',
'rule' => 'oneUser',
'message' => 'The eBay username you are trying to modify already belongs to another seller',
'last' => false,
),
)
);
public function uniqueID() {
return ($this->find('count', array('conditions' =>array('Seller.id' => $this->data['Seller']['id']))) == 0);
}
public function uniqueUser() {
return ($this->find('count', array('conditions' =>array('Seller.username' => $this->data['Seller']['username']))) == 0);
}
public function oneUser() {
return ($this->find('count', array('conditions' =>array('Seller.username' => $this->data['Seller']['username']))) == 1);
}
and the second (only for the id):
public $validate = array(
'id' => array(
'unique' => array(
'rule' => 'isUnique',
'on' => 'create',
'message' => 'This Oracle ID already exists.',
)
)
);
And here are the add() and edit() methods of the controller:
public function add() {
if ($this->request->is('post')) {
$this->Seller->create();
if ($this->Seller->save($this->request->data)) {
$this->Session->setFlash(__('The seller has been saved'));
$this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash(__('The seller could not be saved. Please, try again.'));
}
}
$accountManagers = $this->Seller->AccountManager->find('list');
$primaries = $this->Seller->Primary->find('list');
$thirdParties = $this->Seller->ThirdParty->find('list');
$sites = $this->Seller->Site->find('list');
$meetings = $this->Seller->Meeting->find('list');
$this->set(compact('accountManagers', 'primaries', 'thirdParties', 'sites', 'meetings'));
}
/**
* edit method
*
* #throws NotFoundException
* #param string $id
* #return void
*/
public function edit($id = null) {
if (!$this->Seller->exists($id)) {
throw new NotFoundException(__('Invalid seller'));
}
if ($this->request->is('post') || $this->request->is('put')) {
$this->Seller->id = $id;
if ($this->Seller->save($this->request->data)) {
$this->Session->setFlash(__('The seller has been saved'));
$this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash(__('The seller could not be saved. Please, try again.'));
}
debug($this->Seller->validationErrors);
} else {
$options = array('conditions' => array('Seller.' . $this->Seller->primaryKey => $id));
$this->request->data = $this->Seller->find('first', $options);
}
$accountManagers = $this->Seller->AccountManager->find('list');
$primaries = $this->Seller->Primary->find('list');
$thirdParties = $this->Seller->ThirdParty->find('list');
$sites = $this->Seller->Site->find('list');
$meetings = $this->Seller->Meeting->find('list');
$this->set(compact('accountManagers', 'primaries', 'thirdParties', 'sites', 'meetings'));
}
Any and all tips will be greatly appreciated!
If the data you are trying to save contains an id, CakePHP assumes that you are updating an existing record. For a validation rule to be triggered on "create", the id should not be set. If the id is set, Cake will check for "update" rules.
So you should let your database affect ids for new records with an autoincrement, and then only manage validation when editing your existing users.
Related
I have searched many topic in here but I can't solve my problem. Please check this for me.
I made the register page and when I made for password field...
I had users_controller.php like:
class UsersController extends AppController
{
var $name = "Users";
var $helpers = array('Paginator','Html');
var $paginate = array();
//Doi tuong component de thuc thi thao tac login
public $components = array
(
'Auth' => array
(
'authorize' => 'controller',
'loginRedirect' => array
(
'admin' => FALSE,
'controller' => 'users',
'action' => 'dashboard'
),
'loginError' => 'Invalid account',
'authError' => 'You don\'t have permission'
),
'Session'
);
//Ham loc cac user truoc khi truy cap trang
public function beforeFilter()
{
parent::beforeFilter();
$this->Auth->allow('add');
$this->Auth->allow('viewuserall');
}
//Ham them moi user
public function add()
{
$this->layout = 'TDCake';
$this->User->set($this->data);
if($this->User->valid_user() == TRUE)
{
if(!empty($this->data))
{
$this->User->create();
if($this->User->save($this->data))
{
$this->Session->setFlash('User has been created!');
$this->redirect(array('action'=>'login'));
}
else
{
$this->Session->setFlash('Please correct the errors');
}
};
}
else
{
$this->Session->setFlash("Your data is NOT available");
}
}
//Ham login cho user
public function login()
{
$this->layout = 'TDCake';
if
(
!empty($this->data) &&
!empty($this->Auth->data['User']['username'])&&
!empty($this->Auth->data['User']['password'])
)
{
$user = $this->User->find
(
'first',array
(
'conditions'=>array
(
'User.email'=>$this->Auth->data['User']['username'],
'User.password'=>$this->Auth->data['User']['password']
),
'recursive' => -1
)
);
if(!empty($user) && $this->Auth->login($user))
{
if($this->Auth->autoRedirect)
{
$this->redirect($this->Auth->redirect());
}
}
else
{
$this->Session->setFlash
(
$this->Auth->loginError,
$this->Auth->flashElement,
array(),'auth'
);
}
}
}
//Ham logout cho user
public function logout()
{
$this->redirect($this->Auth->logout());
}
//Ham gi cha biet, de do tinh sau =))
public function dashboard()
{
$this->layout = 'TDCake';
}
//Ham view cac user khong dieu kien trong table users
function viewuserall()
{
$this->layout = 'TDCake';
$this->paginate=array
(
'limit' => 10,
'order' => array('id' => 'asc'),
);
$data = $this->paginate("User");
$this->set("data",$data);
}
}
User.php in Model is:
class User extends AppModel
{
var $name = "User";
var $validate = array();
function validate_passwords()
{
if($this->data[$this->alias]['pass'] == $this->data[$this->alias]['rpass'])
{
return $this->data[$this->alias]['pass'] = $this->data['User']['password'];
}
else return FALSE;
}
function valid_user()
{
$this->validate = array
(
//Kiem tra username truoc khi add
'username' => array
(
'rule01_notEmpty' => array
(
'rule' => 'notEmpty',
'message' => 'You must enter your Username !'
),
'rule02_max16' => array
(
'rule' => array('maxLength', 20),
'message' => 'Your Username must be less than 20 chars !'
),
'rule03_exists' => array
(
'rule' => 'isUnique',
'message' => 'Your Username have already existed !'
)
),
//Kiem tra email truoc khi add
'email' => array
(
'rule01_notEmpty' => array
(
'rule' => 'notEmpty',
'message' => 'You must enter your Email !'
),
'rule02_exists' => array
(
'rule' => 'isUnique',
'message' => 'Your Email have already existed !'
),
'rule03_emailtype' => array
(
'rule' => 'email',
'message' => 'You didn\'t type a email !'
)
),
//Kiem tra password truoc khi add
'pass' => array
(
'length' => array
(
'rule' => array('between', 6, 20),
'message' => 'Your password must be between 8 and 40 characters.',
),
),
'rpass' => array
(
'length' => array
(
'rule' => array('between', 6, 20),
'message' => 'Your password must be between 8 and 40 characters.',
),
'compare' => array
(
'rule' => 'validate_passwords',
'message' => 'The passwords you entered do not match.',
)
)
);//End this->validate=array
if($this->validates($this->validate==TRUE))
{
return TRUE;
}
else
{
return FALSE;
}
}//End function valid_user
}
add.ctp is
echo $this->Session->flash('auth');
echo $this->Form->create();
echo $this->Form->input('username', array('label' => ('Username')));
echo $this->Form->input('email', array('label' => ('Email')));
echo $this->Form->input('pass', array('label' => ('Password'),'type' => 'password', 'value' => ''));
echo $this->Form->input('rpass', array('label' => ('Repeat Password'), 'type' => 'password', 'value' => ''));
echo $this->Form->input('firstname', array('label' =>('Firstname')));
echo $this->Form->input('lastname', array('label' =>('Lastname')));
echo $this->Form->input('dob', array('label' =>('DOB'),'type' => 'date'));
echo $this->Form->end('Register');
Explanation:
So, in this case, I can validate 2 Password Fields (empty, not equal,...), but it can't insert to the database. That mean it INSERTED current data into the DB but password column in DB is EMPTY. In database, my password column name "password" also.
In another case, I change the name "pass" into "password" for the
echo $this->Form->input('pass', array(
Of course, I have changed any place related to...
and in that case, it can be inserted the password but can not validate anything.
I am too confused about this...I don't know what I am wrong is....can anybody help me.
I am not sure why you are doing an assignment in your validate function:
return $this->data[$this->alias]['pass'] = $this->data['User']['password'];
And even if you were doing an assignment, it should be:
return $this->data['User']['password'] = $this->data[$this->alias]['pass'];
Realize that the field "password" is getting the value from $this->data which has the information, not the other way around.
Also. It would be better (in terms of clarity), to break this code in two lines.
$this->data['User']['password'] = $this->data[$this->alias]['pass'];
return $this->data['User']['password'];
You should name your field the exact name "password" if that's what it is called in the database AND if you are not explicitly assigning it.
Your add function is not doing the above, and further more, as a best practice, you should be hashing the password.
See the CakePHP book on tutorials and examples.
Take some time to go through it with all the snippets and recommendations. And don't forget the standards. :)
I have a site developed in cakephp.
I have a model called User like this:
class User extends AppModel {
public $name = 'User';
public $validate = array(
'username' => array(
'not_empty' => array(
'rule'=> 'notEmpty',
'message'=> 'Username not empty'
)
),
'email' => array(
'email_invalid' => array(
'rule' => 'email',
'message' => 'Invalid mail'
),
'email_unique' => array(
'rule' => 'isUnique',
'message' => 'Mail already exist inside database'
)
)
);
public function beforeSave(){
if (isset($this->data['User']['password'])){
$this->data['User']['password'] = AuthComponent::password($this->data['User']['password']);
}
}
}
Into my validate I have the rules email_unique that check if inside the database is already present another email equal.
When I update a user I make this inside my controller:
$this->User->id = $this->request->data['User']['id'];
if ($this->User->save($this->request->data)) {
$this->redirect (array ('action'=>'index'));
}
else{
$this->Session->write('flash_element','error');
$this->Session->setFlash ('Error');
}
It always fail because email isn't unique but is the same record!
I would like to know what is the best method to escape the validation if the save is an update not a create?
Or something like: check if the page is edit escape validation or I don't know.. maybe there are many system, I would like to know what is the more correct for my problem.
Thanks
You can adjust your validation rules to only apply when a new record is created, not when an existing record is updated. You can do this by setting the on key in your validation rule to create, so it will look like this:
'email_unique' => array(
'rule' => 'isUnique',
'message' => 'Mail already exist inside database',
'on' => 'create' // Only apply this rule upon creation of a new record
)
See the documentation on this for further details.
If you also want to block duplicate e-mails upon updating, create a beforeSave method in your User model, looking for the e-mail address:
public function beforeSave($options = array()) {
// If the email key is set in the data to be saved...
if (isset($this->data[$this->alias]['email'])) {
// Make sure the email is not already in use by another user
if ($this->find('count', array(
'conditions' => array(
$this->alias . '.id !=' => $this->data[$this->alias]['id'],
$this->alias . '.email' => $this->data[$this->alias]['email']
)
)) > 0) {
// The email is found for a user with another id, abort!
return false;
}
}
}
I'm running a 2.2.2 CakePHP Application, everything works as desired. Now I'm developing a Android App for it and therefore need to create the interfaces between those two apps. That's why I need to login users manually. So I created a whole new controller, the AndroidController, in order to bundle everything at one place. First thing to do would be the Login-Action. So I setup the following controller:
<?php
App::uses('AppController', 'Controller');
/**
* Android Controller
*
* #package app.Controller
*/
class AndroidController extends AppController {
public $components = array('RequestHandler','Auth');
public $uses = array('User');
public function beforeFilter() {
$this->Auth->allow();
}
public function login() {
//For testing purposes
$postarray = array('_method' => 'POST','data' => array('User' => array('email' => 'user#gmail.com', 'password' => 'THISisDEFINITELYaWRONGpassword')));
$id = $this->tryToGetUserID($postarray['data']['User']['email']);
if($id == 0){
//return Error json, unknown User
$this->set('result', array(
'tag' => 'login',
'success' => 0,
'error' => 1,
'error_msg' => 'Unknown User'
));
}else{
// if ($this->request->is('post')) {
$postarray['data']['User'] = array_merge($postarray['data']['User'], array('id' => $id));
$this->User->id = $id;
if ( $this->Auth->login($postarray['data']['User'])) {
// Login successfull
$this->User->saveField('lastlogin', date(DATE_ATOM));
$user = $this->User->find('all', array(
'recursive' => 0, //int
'conditions' => array('User.id' => $id)
));
$loggedInUser = array(
'tag' => 'login',
'success' => 1,
'error' => 0,
'uid' => '??',
'user' => array(
'name' => $user['0']['User']['forename'].' '.$user['0']['User']['surname'],
'email' => $user['0']['User']['email'],
'created_at' => $user['0']['User']['created'],
'updated_at' => $user['0']['User']['lastlogin']
)
);
$this->set('result', $loggedInUser);
} else {
// Login failed
$this->set('result', array(
'tag' => 'login',
'success' => 0,
'error' => 2,
'error_msg' => 'Incorrect password!'
));
}
// }
}
}
public function tryToGetUserID($email = null) {
$user = $this->User->find('list', array(
'conditions' => array('User.email' => $email)
));
if(!empty($user)){
return array_keys($user)['0'];
}else{
return 0;
}
}
}
You need to know that this method will be called as a POST request, but for testing purposes I manually created a post-array. In future I will use the $_POST array.
So, what happens: The Login with a registered user works, but it works every time! Even though the password is wrong or missing! The program never reaches the part in code with the "Login failed" comment.
Am I missing something here..?
Thank you!
If you take a closer look at the documentation you will notice that AuthComponent::login() will ...
In 2.x $this->Auth->login($this->request->data) will log the user in with whatever data is posted
OKay so i have two tables Employee and User
my employee model looks like this:
class Employee extends AppModel{
public $name = 'Employee';
public $primaryKey = "employee_id";
public $actsAs = array('Containable');
public $belongsTo = array(
'User' => array(
'className' => 'User',
'dependent' => false,
'foreignKey' => 'user_id'
)
);
}
And my user model looks like this:
App::uses('AuthComponent', 'Controller/Component');
class User extends AppModel {
// ...
public function beforeSave($options = array()) {
if (isset($this->data[$this->alias]['password'])) {
$this->data[$this->alias]['password'] = AuthComponent::password($this->data[$this->alias]['password']);
}
return true;
}
public $validate = array(
'username' => array(
'required' => array(
'rule' => array('notEmpty'),
'message' => 'A username is required'
)
),
'password' => array(
'required' => array(
'rule' => array('notEmpty'),
'message' => 'A password is required'
)
),
'role' => array(
'valid' => array(
'rule' => array('inList', array('employee', 'client')),
'message' => 'Please enter a valid role',
'allowEmpty' => false
)
)
);
}
In my employee controller i have an action that allows employees to add other employees the action looks like this:
public function add() {
if ($this->request->is('post')) {
$this->Employee->User->create();
if ($this->Employee->User->save($this->request->data)) {
$this->Session->setFlash(__('The user has been saved'));
$this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash(__('The user could not be saved. Please, try again.'));
}
}
}
My employee table looks like this
employee_id user_id
Now whenever i add a user the user is correctly added in my user table and a row is also added in my employee table however there are two mistakes in the employee table:
The employee_id is an auto increment this does not happen and it seems it keeps overriting 1. (so that every user i try to create is employee_id = 1)
the user_id is always 0 however in the user table the user_id is for example 21.
Can anyone tell me why this is happening and how i can fix it?
update
My add action in my employee controller now looks like this:
public function add() {
if ($this->request->is('post')) {
$this->Employee->User->create();
if ($this->Employee->User->saveAll($this->request->data)) {
$this->Session->setFlash(__('The user has been saved'));
$this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash(__('The user could not be saved. Please, try again.'));
}
}
}
ive added a hasMany to my user model:
public $hasMany = array(
'Employee' =>array(
'className' => 'Employee',
'dependent' => true,
'foreignKey' => 'user_id'
)
);
Still no change
A few issues...
1) Your primary key for employees table should be id, not employee_id. Primary keys are always named id, according to cakephp conventions - http://book.cakephp.org/2.0/en/getting-started/cakephp-conventions.html
2) Just as you've got a belongsTo in your Employee model, you should also add a hasOne relationship to your user model - see http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#hasone
3) In order to save a record, along with it's related data, the method you want is saveAll - check out the documentation here: http://book.cakephp.org/2.0/en/models/saving-your-data.html#model-saveall-array-data-null-array-options-array
I'm trying to set up validation for user registration but I'm having troubles. When I only have the email,role and password fields in the $validation array (remove the others) it works and will save a new user. When I try to add the other fields it fails and gives the flash message error "The user could not be saved. Please, try again."
I'm pretty sure it's the re_password check. When I remove the validation for that it works. However, the re_password validation does display an error when the passwords are different, so I'm not sure where to look
Here's my users table
id | email | password | location | website | role | created | modified
Here's the validation requirements. To get it to save a new user I have to remove everything but email, password and role.
public $validate = array(
'email' => 'email'
,
'password' => array(
'required' => array(
'rule' => array('minLength', '8'),
'message' => 'A password with a minimum length of 8 characters is required'
)
),
're_password' => array(
'required' => array(
'rule' => array('equalTo', 'password' ),
'message' => 'Both password fields must be filled out'
)
),
'role' => array(
'valid' => array(
'rule' => array('inList', array('admin', 'author')),
'message' => 'Please enter a valid role',
'allowEmpty' => false
)
),
'location' => array(
'valid' => array(
'rule' => array('notEmpty'),
'message' => 'Please select a location'
)
)
);
Here's the form (the options array is above, figured it's not necessary to show)
echo $this->Form->input('email');
echo $this->Form->input('password');
echo $this->Form->input('re_password', array('type'=>'password', 'label'=>'Re-Enter Password', 'value'=>'', 'autocomplete'=>'off'));
echo $this->Form->input('location', array('options' => $options, 'label' => 'Select Nearest Location'));
echo $this->Form->input('website',array('label'=>'Enter your website, such as www.example.com. '));
echo $this->Form->input('role', array('type' => 'hidden', 'default' => 'user'));
Here's the re_password checking function in the User model
function check_user_password($userid) {
$salt = Configure::read('Security.salt');
$this->User->id = $userid;
$hashed_password = $this->User->field('password');
// check password
if($hashed_password == md5($data['User']['re_password'].$salt)) {
return true;
} else {
return false;
}
}
And finally, here's the add function in UsersController
public function add() {
if ($this->request->is('post')) {
$this->User->create(); //create initiates a form on User/add.ctp
if ($this->User->save($this->request->data)) { //save the form data
$this->Session->setFlash(__('The user has been saved'));
$this->redirect(array('controller' => 'demos', 'action' => 'index'));
} else {
$this->Session->setFlash(__('The user could not be saved. Please, try again.'));
}
}
}
Please let me know if there's anything else you need to see
I believe that your re_passwords valiadtion rule equalTo compares its value to string password and not the actual field. I like to use custom functions for this.
so try replacing re_passwords rule array
//'rule' => array('equalTo', 'password' ),
'rule' => array('equalToField', 'password'),
and declare equalToField function in that model
function equalToField($array, $field) {
return strcmp($this->data[$this->alias][key($array)], $this->data[$this->alias][$field]) == 0;
}
** Also in the future when you seem to have a problem with validation rules
try this in your controllers action (its faster than removing every single rule)
if ($this->User->save($this->request->data)) {
...
} else {
debug($this->User->validationErrors);
...
}
I hope this helps.
Hi Please use following code for your requirement :
override equalTo function by putting your own method in user model:
function equalTo( $field=array(), $compare_field=null )
{
foreach( $field as $key => $value ){
$v1 = $value;
$v2 = $this->data[$this->name][ $compare_field ];
if($v1 !== $v2) {
return FALSE;
} else {
continue;
}
}
return TRUE;
}
Attention, in #luboss answer, where he declares:
function equalToField($array, $field) {
return strcmp($this->data[$this->alias][key($array)], $this->data[$this->alias][$field]) == 0;
}
That cannot work as we are comparing inconsistent fields:
the left member of strcmp has already been hashed, but not the right member.
This happens as a CakePHP automation because the field is called password.
The way I got this to work was to reuse the hashing function in the equalToField helper:
public function equalToField($array, $field) {
$valueFirstOccurrence = $this->data[$this->alias][$field];
$valueSecondOccurrence = Security::hash($this->data[$this->alias][key($array)], $type = 'sha1', $salt = true) ;
return !strcmp($valueFirstOccurrence, $valueSecondOccurrence);
}
Other point :
If you are interested in adding a minLength validation field for your password field, you want to read this good post first:
minLength data validation is not working with Auth component for CakePHP