I am validating a user login and would like to attach an error message to the form if they the user submits details that do not authenticate.
In FieldSet I can see function setMessages() but this only appears to match and set against an element key.
How can I attach an error message to the form and not to a form element?
The following code is in within the LoginForm class.
public function isValid()
{
$isValid = parent::isValid();
if ($isValid)
{
if ($this->getMapper())
{
$formData = $this->getData();
$isValid = $this->getMapper()->ValidateUandP($formData['userName'], $formData['password']);
}
else
{
// The following is invalid code but demonstrates my intentions
$this->addErrorMessage("Incorrect username and password combination");
}
}
return $isValid;
}
The first example is validating from a database and simply sending back an error message to the form:
//Add this on the action where the form is processed
if (!$result->isValid()) {
$this->renderLoginForm($form, 'Invalid Credentials');
return;
}
This next one is adding simple validation to the form itself:
//If no password is entered then the form will display a warning (there is probably a way of changing what the warning says too, should be easy to find on google :)
$this->addElement('password', 'password', array(
'label' => 'Password: ',
'required' => true,
));
I hope this is of use.
In ZF1: in order to attach an error message to a form - you can create a decorator element for this:
Taken from:
http://mwop.net/blog/165-Login-and-Authentication-with-Zend-Framework.html
class LoginForm extends Zend_Form
{
public function init()
{
// Other Elements ...
// We want to display a 'failed authentication' message if necessary;
// we'll do that with the form 'description', so we need to add that
// decorator.
$this->setDecorators(array(
'FormElements',
array('HtmlTag', array('tag' => 'dl', 'class' => 'zend_form')),
array('Description', array('placement' => 'prepend')),
'Form'
));
}
}
And then as an example in your controller:
// Get our authentication adapter and check credentials
$adapter = $this->getAuthAdapter($form->getValues());
$auth = Zend_Auth::getInstance();
$result = $auth->authenticate($adapter);
if (!$result->isValid()) {
// Invalid credentials
$form->setDescription('Invalid credentials provided');
$this->view->form = $form;
return $this->render('index'); // re-render the login form
}
Unsure if this still works in ZF2
Related
I'm having trouble displaying the validation errors of a form using a custom validator.
The errors does exist as the debug method shows, it just won't be displayed in the form.
I'd like to be able to show the error message under (or above, or anywhere) the field.
What I've tried
Well, the documentation does state:
When using View\Helper\FormHelper::control(), errors are rendered by
default, so you don’t need to use isFieldError() or call error()
manually.
Nevertheless, I added the following in the form (just below the email control), which didn't do anything more. No message displayed.
if ($this->Form->isFieldError('email')) {
echo $this->Form->error('email', 'Yes, it fails!');
}
I've also found several questions and answers about this issue on SO, but they look outdated (from '09 to '13) and do not seem to correspond to today's CakePHP syntax.
What I've done
Users/forgot_password.ctp
<?= $this->Form->create() ?>
<?= $this->Form->control('email', ['type' => 'email']) ?>
<?= $this->Form->button(__('Reset my password')) ?>
<?= $this->Form->end() ?>
UsersController.php
(notice the specific validation set, as explained in documentation)
public function forgotPassword()
{
if ($this->request->is('post')) {
$user = $this->Users->newEntity($this->request->getData(), ['validate' => 'email']);
if ($user->errors()) {
debug($user->errors()); // <- shows the validation error
$this->Flash->error(__('An error occurred.'));
} else {
// ... procedure to reset password (which works fine!) and redirect to login...
return $this->redirect(['action' => 'login']);
}
}
}
UsersTable.php
public function validationEmail(Validator $validator)
{
$validator
->email('email')
->notEmpty('email', __('An email address is required.'));
return $validator;
}
What it looks like
Update
Thanks to #ndm comment, here is the correct way to display the error.
In UsersController.php:
public function forgotPassword()
{
// user context for the form
$user = $this->Users->newEntity();
if ($this->request->is('post')) {
$user = $this->Users->patchEntity(§user, $this->request->getData(), ['validate' => 'email']); <- validation done on patchEntity
if ($user->errors()) {
$this->Flash->error(__('An error occurred.'));
} else {
// ... procedure to reset password and redirect to login...
return $this->redirect(['action' => 'login']);
}
}
// pass context to view
$this->set(compact('user'));
}
And in the view forgotPassword.ctp:
<?= $this->Form->create($user) ?>
//modify your function as below
public function forgotPassword()
{
if ($this->request->is('post')) {
$user = $this->Users->newEntity($this->request->getData(), ['validate' => 'email']);
if ($user->getErrors()) {
debug($user->getError('email')); // <- shows the validation error
$this->Flash->error(__($user->getError('email')['_empty']));
} else {
// ... procedure to reset password (which works fine!) and redirect to login...
return $this->redirect(['action' => 'login']);
}
}
}
I try to find a good and clean way to deal with an admin panel "edit user" in cakePHP v.2.7.
To be clear : I want to be able to edit my user with or without overwriting their password, but the cakePHP validator tool don't let me do what I want...
I've already take a look at CakePHP: Edit Users without changing password and Updating user email and password with CakePHP but it seem really dirty :
the first one don't apply the rules onUpdate
the second display the hash (just no... u_u")
There is no other way to do it ? (with as few line as possible)
TL;DR :
View
// add in your view `app/View/Users/edit.ctp`
// a 'fake' field you'll only use on the controller
echo $this->Form->input('new_password');
Controller
// add in your controller `app/Model/User.php#edit()`
// if we have a new password, create key `password` in data
if(!empty($new_password = $this->request->data['User']['new_password']))
$this->request->data['User']['password'] = $new_password;
else // else, we remove the rules on password
$this->User->validator()->remove('password');
Ok, I finally get what I want, here is my code :
On your app/View/Users/edit.ctp you add a field to your form (a custom one, don't add it to your DB)
<?php
// app/View/Users/edit.ctp
echo $this->Form->create('User');
// your other fields
// a 'fake' field you'll only use on the controller
echo $this->Form->input('new_password');
Don't change your app/Model/User.php ; here is mine :
<?php
// app/Model/User.php
App::uses('AuthComponent', 'Controller/Component');
class User extends AppModel {
public $validate = array(
// [...] other rules
'password' => array(
'passwordLength'=>array(
'rule' => array('minLength', 8),
'message' => 'Too short...',
),
'passwordNotBlank'=>array(
'rule' => 'notBlank',
'required' => true,
'allowEmpty' => false,
'message' => 'A password is required',
),
),
);
public function beforeSave($options = array()) {
if (!empty($pwd = $this->data[$this->alias]['password']))
$this->data[$this->alias]['password'] = AuthComponent::password($pwd);
return true;
}
}
And on your app/Controller/UsersController.php you use this :
<?php
public function edit($id = null) {
$this->User->id = $id;
if (!$this->User->exists())
throw new NotFoundException(__('Invalid user'));
if ($this->request->is('post') || $this->request->is('put')) {
// IMPORTANT >>>>>>>>>>>
// if we have a new password, create key `password` in data
if(!empty($new_password = $this->request->data['User']['new_password']))
$this->request->data['User']['password'] = $new_password;
else // else, we remove the rules on password
$this->User->validator()->remove('password');
// <<<<<<<<<<<<<<<<<<<<<
// then we try to save
if ($this->User->save($this->request->data)) {
$this->Flash->success(__('The user has been updated'));
$this->redirect(array('action' => 'index'));
}
else
$this->Flash->warning(__('The user could not be updated.'));
}
else {
$this->request->data = $this->User->read(null, $id);
unset($this->request->data['User']['password']);
}
}
With the 4 important lines, you are now able to set a new password if needed or disable the validation on the password.
I used this for reference
http://book.cakephp.org/2.0/en/models/data-validation.html#removing-rules-from-the-set
to make it a bit short. I just made a registration form fully working with a controller, the routes and the view. Now I know it's common sense to use a Model for it and in the controller only call the method in the model. So i thought okay lets fix that. Now when I register an account I get a blank page. I bet the redirect is going wrong but I can't fix it maybe you can?
RegisterController.php
public function doRegister(){
$user = new User();
$user->doRegister();
}
User.php (model)
public function doRegister()
{
// process the form here
// create the validation rules ------------------------
$rules = array(
'username' => 'required|unique:users',
'email' => 'required|email|unique:users',
'password' => 'required|min:5',
'serial_key' => 'required|exists:serial_keys,serial_key|unique:users'
);
// create custom validation messages ------------------
$messages = array(
'required' => 'The :attribute is important.',
'email' => 'The :attribute is not a legit e-mail.',
'unique' => 'The :attribute is already taken!'
);
// do the validation ----------------------------------
// validate against the inputs from our form
$validator = Validator::make(Input::all(), $rules);
// check if the validator failed -----------------------
if ($validator->fails()) {
// get the error messages from the validator
$messages = $validator->messages();
// redirect our user back to the form with the errors from the validator
return Redirect::to('/register')
->withErrors($validator)
->withInput(Input::except('password', 'password_confirm'));
} else {
// validation successful ---------------------------
// our duck has passed all tests!
// let him enter the database
// create the data for our duck
$duck = new User;
$duck->username = Input::get('username');
$duck->email = Input::get('email');
$duck->password = Hash::make(Input::get('password'));
$duck->serial_key = Input::get('serial_key');
// save our user
$duck->save();
// redirect with username ----------------------------------------
return Redirect::to('/registered')->withInput(Input::old('username'));
}
}
you need to make $user->doRegister(); a return statement
in your RegisterController you have to do
public function doRegister(){
$user = new User();
return $user->doRegister();
}
try this
return Redirect::to('/registered')
->with('bill_no', Input::get('username'));
in the '/registered' controller,..
use this
$username = Session::get("username");
above worked for me,...
Lets say I have a login form and it has fields username and password. Both of the fields are required to be filled in and when you submit the form it has two different lines for them.
Username field is required.
Password field is required.
What I want to do is to show only the username field errors and when they don't have any errors with username field, it will show the password field errors.
How would this be done?
Also, I remember there is a way to show errors only regarding a field, what was the snippet for it?
I suggest using a custom callback function tied to just one input, that checks both inputs and conditionally sets the desired message.
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class YourController extends CI_Controller {
public function save()
{
//.... Your controller method called on submit
$this->load->library('form_validation');
// Build validation rules array
$validation_rules = array(
array(
'field' => 'username',
'label' => 'Username',
'rules' => 'trim|xss_clean|callback_required_inputs'
),
array(
'field' => 'password',
'label' => 'Password',
'rules' => 'trim|xss_clean'
)
);
$this->form_validation->set_rules($validation_rules);
$valid = $this->form_validation->run();
// Handle $valid success (true) or failure (false)
if($valid)
{
//
}
else
{
//
}
}
public function required_inputs()
{
if( ! $this->input->post('username'))
{
$this->form_validation->set_message('required_inputs', 'The Username field is required');
return FALSE;
}
else if ($this->input->post('username') AND ! $this->input->post('password'))
{
$this->form_validation->set_message('required_inputs', 'The Password field is required');
return FALSE;
}
return TRUE;
}
}
I have a form on my website header where i allow the user to log in with his username/password... then i POST to /signin page and check if the username exists to allow the user to log in.. if there is a problem upon login i output these errors...
i tried using the following code to show a custom error but with no luck
if ($this->form_validation->run() == false){
$this->load->view("login/index", $data);
}else{
$return = $this->_submitLogin();
if ($return == true){
//success
}else{
$this->form_validation->set_message('new_error', 'error goes here');
//error
}
$this->load->view("login/index", $data);
}
how does set_message work and if this is the wrong method, which one allow me to show a custom error in this case?
EDIT :
validation rules:
private $validation_rules = array(
array(
'field' => 'username',
'label' => 'Username',
'rules' => 'trim|required|callback__check_valid_username|min_length[6]|max_length[20]|xss_clean'
),
array(
'field' => 'password',
'label' => 'Password',
'rules' => 'trim|required|min_length[6]|max_length[32]'
),
);
The set_message method allows you to set your own error messages on the fly. But one thing you should notice is that the key name has to match the function name that it corresponds to.
If you need to modify your custom rule, which is _check_valid_username, you can do so by perform set_message within this function:
function _check_valid_username($str)
{
// Your validation code
// ...
// Put this in condition where you want to return FALSE
$this->form_validation->set_message('_check_valid_username', 'Error Message');
//
}
If you want to change the default error message for a specific rule, you can do so by invoking set_message with the first parameter as the rule name and the second parameter as your custom error. E.g., if you want to change the required error :
$this->form_validation->set_message('required', 'Oops this %s is required');
If by any chance you need to change the language instead of the error statement itself, create your own form_validation_lang.php and put it into the proper language folder inside your system language directory.
As you can see here, you can display the custom error in your view in the following way:
<?php echo form_error('new_error'); ?>
PS: If this isn't your problem, post your corresponding view code and any other error message that you're getting.
The problem is that your form is already validated in your IF part! You can fix the problem by this way:
if ($this->form_validation->run() == false){
$this->load->view("login/index", $data);
}else{
$return = $this->_submitLogin();
if ($return == true){
//success
}else{
$data['error'] = 'Your error message here';
//error
}
$this->load->view("login/index", $data);
}
In the view:
echo $error;
The CI way to check user credentials is to use callbacks:
$this->form_validation->set_rules('username', 'Username', 'callback_username_check');
...
public function username_check($str) {
// your code here
}
I recommend you to read CI documentation: http://codeigniter.com/user_guide/libraries/form_validation.html
The way I did this was to add another validation rule and run the validation again. That way, I could keep the validation error display in the view consistent.
The following code is an edited excerpt from my working code.
public function login() {
$this->form_validation->set_rules('email', 'Email', 'required');
$this->form_validation->set_rules('password', 'Password', 'required');
$data['content'] = 'login';
if($this->form_validation->run()) {
$sql = "select * from users where email = ? and password = ?";
$query = $this->db->query($sql, array($this->input->post('email'), $this->input->post('password')));
if($query->num_rows()==0) {
// user not found
$this->form_validation->set_rules('account', 'Account', 'callback__noaccount');
$this->form_validation->run();
$this->load->view('template', $data);
} else {
$this->session->set_userdata('userid', $query->id);
redirect('/home');
}
} else {
$this->load->view('template', $data);
}
}
public function _noaccount() {
$this->form_validation->set_message('_noaccount', 'Account must exist');
return FALSE;
}
Require Codeigniter 3.0
Using callback_ method;
class My_controller extends CI_Controller {
function __construct() {
parent::__construct();
$this->form_validation->set_message('date_control', '%s Date Special Error');
}
public function date_control($val, $field) { // for special validate
if (preg_match("/^[0-9]{2}.[0-9]{2}.[0-9]{4}$/", $val)) {
return true;
} else {
return false;
}
}
public function my_controller_test() {
if ($this->input->post()) {
$this->form_validation->set_rules('date_field', 'Date Field', 'trim|callback_date_control[date_field]|xss_clean');
if ($this->form_validation->run() == FALSE) {
$data['errors']=validation_errors();
$this->load->view('my_view',$data);
}
}
}
}
Result:
if date = '14.07.2017' no error
if date = '14-7-2017' Date Field Date Special Error