Phalcon 3: Validating form data using \Phalcon\Mvc\Model\Validator - php

Since the latest version of Phalcon was released, the examples provided in the documentation do not seem to work correctly.
Firstly, I create a new model with DevTools at the command line using phalcon model User.
Then, I amend the validation() function. My models\User.php file:
use Phalcon\Mvc\Model\Validator\Email as Email;
use Phalcon\Mvc\Model\Validator\Uniqueness as Uniqueness;
use Phalcon\Mvc\Model\Validator\StringLength as StringLength;
class User extends \Phalcon\Mvc\Model
{
public function validation()
{
$this->validate(
new Email(
array(
'field' => 'email',
'message' => 'Please enter a valid email'
)
)
);
$this->validate(
new Uniqueness(
array(
'field' => 'email',
'message' => 'Your email is already in use'
)
)
);
$this->validate(
new StringLength(
array(
'field' => 'password',
'min' => 4,
'max' => 30,
'minMessage' => 'Your password must be at least 4 characters',
'maxMessage' => 'Your password must be less than 30 characters'
)
)
);
if ($this->validationHasFailed() == true) {
return false;
}
return true;
}
}
However, this throws the following error:
Catchable fatal error: Argument 1 passed to Phalcon\Mvc\Model::validate() must implement interface Phalcon\ValidationInterface, instance of Phalcon\Mvc\Model\Validator\Email given in C:\xampp\htdocs\app\models\User.php on line 27
What does this mean? I believed that Phalcon\Validation and Phalcon\Mvc\Model\Validator were completely different beasties (the latter providing more functionality)?

As of Phalcon 3.0 Phalcon\Mvc\Model\Validation is now deprecated in favor of Phalcon\Validation. This was done to reduce the code base, since both of the components were doing similar jobs. Below you can find examples of what needs to be changed.
Old way:
namespace Invo\Models;
use Phalcon\Mvc\Model;
use Phalcon\Mvc\Model\Validator\Email as EmailValidator;
use Phalcon\Mvc\Model\Validator\Uniqueness as UniquenessValidator;
class Users extends Model
{
public function validation()
{
$this->validate(
new EmailValidator(
[
'field' => 'email',
]
)
);
$this->validate(
new UniquenessValidator(
[
'field' => 'username',
'message' => 'Sorry, That username is already taken',
]
)
);
if ($this->validationHasFailed() == true) {
return false;
}
}
}
New way:
namespace Invo\Models;
use Phalcon\Mvc\Model;
use Phalcon\Validation;
use Phalcon\Validation\Validator\Email as EmailValidator;
use Phalcon\Validation\Validator\Uniqueness as UniquenessValidator;
class Users extends Model
{
public function validation()
{
$validator = new Validation();
$validator->add(
'email', //your field name
new EmailValidator([
'model' => $this,
'message' => 'Please enter a correct email address'
])
);
$validator->add(
'username',
new UniquenessValidator([
'model' => $this,
'message' => 'Sorry, That username is already taken',
])
);
return $this->validate($validator);
}
}
More info and patch notes can be found in the Blog.

Related

CodeIgniter 4 Model Callbacks not hashing password before insert

I am totally new to CI4 but do have some experience with PHP. I having trouble getting the Model class callback functions to work when inserting a new user row.
Here is the Controller:
<?php use App\Models\UserModel;
class Users extends BaseController
{
public $users_model;
public function __construct() {
$this->users_model = new UserModel();
}
public function register() {
$data = [
'page_title' => 'Register An Account',
'validation' => NULL //must have this set to null until/if any validation rules are broken
];
if ($this->request->getMethod() == 'post') {
//validation rules
$rules = [
'username' => [
'rules' => 'required|min_length[4]|max_length[20]|validateUsername[username]',
'errors' => [
'required' => 'A username is required',
'min_length' => 'Username must be at least {param} characters long',
'max_length' => 'Username cannot be more than {param} characters long',
'validateUsername' => 'Username can only contain letters and numbers',
],
] ,
'email' => [
'rules' => 'required|valid_email|is_unique[users.email]',
'errors' => [
'required' => 'An Email is required',
'valid_email' => 'Enter a valid email',
'is_unique' => 'That email has already been registerd',
],
],
'password' => [
'rules' => 'required|min_length[6]|max_length[16]|validatePassword[password]',
'errors' => [
'required' => 'A password is required',
'min_length' => 'Password must contain at least {param} characters',
'max_length' => 'Password cannot be more than {param} characters in length',
'validatePassword' => 'Password must have at least 1 numeric value',
],
],
'confirm_password' => [
'rules' => 'required|matches[password]',
'errors' => [
'required' => 'Must confirm password',
'matches' => 'Passwords do not match'
],
]
];
if ($this->validate($rules)) {
//all fields passed validation so need to save to the db
$user_data = [
'username' => $this->request->getVar('username', FILTER_SANITIZE_STRING),
'email' => $this->request->getVar('email', FILTER_SANITIZE_EMAIL),
'password' => $this->request->getVar('password')
];
if ($this->users_model->createUser($user_data)) {
echo 'user stored in the db.';
} else {
echo 'user not stored in the db.';
}
} else {
//there are some validation errors
$data['validation'] = $this->validator;
}
}//post request check ends here
return view('users/register', $data);
}// register method ends here.
public function login() {
$data = [
'page_title' => 'Login'
];
return view('users/login', $data);
}
public function logout() {
//not implemented yet
}
}
And here is the Model class:
<?php
namespace App\Models;
use CodeIgniter\Model;
class UserModel extends Model
{
protected $table = 'users';
protected $allowedFields = ['username', 'email', 'password'];
protected $beforeInsert = ['beforeInsert'];
protected $beforeUpdate = ['beforeUpdate'];
protected $allowCallbacks = TRUE;
protected $builder;
public function createUser(array $data) {
$this->builder = $this->db->table($this->table);
$this->db->transStart();
$this->builder->insert($data);
$this->db->transComplete();
if($this->db->affectedRows() == 1) {
return TRUE;
} else {
return FALSE;
}
}
protected function beforeInsert(array $data) {
if (isset($data['data']['password']))
$data['data']['password'] = password_hash($data['data']['password'], PASSWORD_DEFAULT);
return $data;
}
protected function beforeUpdate(array $data) {
if (isset($data['data']['password']))
$data['data']['password'] = password_hash($data['data']['password'], PASSWORD_DEFAULT);
return $data;
}
}
What is weird to me is that the record does get stored in the database but the password is plain text and not hashed. Also, in the controller class, the if statement:
if ($this->users_model->createUser($user_data)) {
echo 'user stored in the db.';
} else {
echo 'user not stored in the db.';
}
Always echo's the 'user not stored in the db' message and the user is stored in the db.
In the model:
if($this->db->affectedRows() == 1) {
return TRUE;
} else {
return FALSE;
}
I have echoed out the result of $this->db->affectedRows()
and it is 1 on a successful insert (although the password is not hashed) so I thought this would cause the method to return true, therefore over in the controller, I would see the truthy part of the if condition, 'user stored in the db'.
Can anyone tell me where I am going wrong in all of this?
I have successfully used the password_hash() function in the controller itself on the $this->request->getVar('password') data so I know it works. But I wanted to leverage the in-built Model callbacks and take care of the hashing for inserts and updates there.
I have also shared/posted this question in the CodeIgniter forums as well.
Thanks for any help.
Codeigniter4 documentation is not really clear about it, but whenever you want a model callback to be triggered, you need to use directly the model functions and not the builder ones.
For example, inside your model, using $this->builder->insert() will NOT trigger the beforeInsert nor the afterInsert functions of your model but using $this->insert() will do the trick.
So in your case you should replace your createUser function with
public function createUser(array $data) {
$this->db->transStart();
$this->insert($data);
$this->db->transComplete();
if ($this->db->affectedRows() == 1) {
return TRUE;
} else {
return FALSE;
}
}

phalcon validate form fields without saving to database

Am in a need to validate form fields and manipulate them with out saving to database.
This is what i have done
In controller
<?php
use Phalcon\Mvc\Model\Criteria;
use Phalcon\Paginator\Adapter\Model as Paginator;
use Phalcon\Mvc\View;
class UsersController extends ControllerBase {
public function loginAction() {
if($this->request->isPost()) {
$user = new Users();
$validates = $user->validation($this->request->getPost());
// now validation works fine, but cancelOnFail in model doesn't seems to work,
if($validates) {
echo 'valid inputs';
}
else {
print_r($user->getMessages());
// now how can we show these error messages below the corresponding input fields in the view.
// we would also like to show error message as follows, if a field has more than one validation conditions,
// Eg: say username have notempty and valid e-mail validation set in model so if username is empty show only not empty message,
// similarly if username is not empty and if its not a valid e-mail , show not valid email message.
}
exit();
}
}
}
?>
Am trying to validate from the Model and it looks like as follows
<?php
use Phalcon\Mvc\Model\Validator;
use Phalcon\Mvc\Model\Validator\PresenceOf;
use Phalcon\Mvc\Model\Validator\Email;
class Users extends \Phalcon\Mvc\Model {
public function validation() {
$this->validate(new PresenceOf(
array(
'field' => 'username',
'message' => 'Username is required.',
'cancelOnFail' => true
)
));
$this->validate(new Email(
array(
'field' => 'username',
'message' => 'Username must be a valid e-mail.'
)
));
$this->validate(new PresenceOf(
array(
'field' => 'password',
'message' => 'Password is required.'
)
));
return $this->validationHasFailed() != true;
}
}
?>
My view file is as follows
<?php
echo $this->tag->form(array("users/login", "role" => "form"));
echo $this->tag->textField(array('username', 'class' => 'form-control', 'placeholder' => 'E-mail', 'type' => 'email', 'tabindex' => 1));
echo $this->tag->passwordField(array('password', 'class' => 'form-control', 'placeholder' => 'Password', 'type' => 'password', 'tabindex' => 2));
echo $this->tag->submitButton(array('Login','class' => 'btn btn-sm btn-success btn-block', 'tabindex' => 5));
?>
</form>
How can i achieve the following,
1) Check if form fields validates correctly as given in the Model from the Controller.
2) Am not looking to save the form data, only validate it.
3) Show the corresponding error messages below the input field in the view.
Thankz
You need to create the form, bind your entity and then validate on post request. See http://docs.phalconphp.com/en/latest/reference/forms.html#validation
EDIT: To display error messages you can do this in your controller
// ...
$messages = array();
foreach ($user->getMessages() as $message) {
$messages[$message->getField()] = $message->getMessage();
}
$this->view->messages = $messages;
//...
now you have $messages in your view.
I think you really should use a form in this case. You trying to validate a user login in model, but is a simple form validation. In model, you validates business rules of users in the app.

cakephp update more field unique

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;
}
}
}

Display Error Message for Custom Validation in Laravel 4

I have created a custom error function by creating a class;
<?php
class CoreValidator extends Illuminate\Validation\Validator
{
public function validatePostcode($attribute, $value, $parameters = null)
{
$regex = "/^((GIR 0AA)|((([A-PR-UWYZ][0-9][0-9]?)|(([A-PR-UWYZ][A-HK-Y][0-9][0-9]?)|(([A-PR-UWYZ][0-9][A-HJKSTUW])|([A-PR-UWYZ][A-HK-Y][0-9][ABEHMNPRVWXY])))) [0-9][ABD-HJLNP-UW-Z]{2}))$/i";
if(preg_match($regex ,$value)) {
return true;
}
return false;
}
}
I reference it in my model;
public static $rules = array(
'first_name' => 'required|Max:45',
'surname' => 'required|Max:45',
'address_line_1' => 'required|Max:255',
'address_line_2' => 'Max:255',
'address_line_3' => 'Max:255',
'town' => 'required|Max:45',
'county' => 'Max:45',
'postcode' => 'required|Postcode',
'phone_number' => 'required|Max:22'
);
It has been registered in my global.php;
Validator::resolver(function($translator, $data, $rules, $messages) {
return new CoreValidator($translator, $data, $rules, $messages);
});
It all works well, but the error message it returns is
validation.postcode
How/where do I set a custom error message for this?
I have tried setting app/lang/en/validation.php with (neither work);
'custom' => array(
"validation.postcode" => "my error message 1",
"postcode" => "my error message 2"
)
P.S. I know that there is a regex validation method already, but this problem is more generic for me.
I think I have cracked it.
I added the message to the main array in app/lang/en/validation.php, not into the custom sub-array.
return array(
...
"url" => "The :attribute format is invalid.",
"postcode" => "my error message 2",
...
)
If this isn't the correct way, then someone is free to correct me.
You can use setCustomMessages() method to assign custom messages like the bellow code
<?php
class CoreValidator extends Illuminate\Validation\Validator
{
private $custom_messages = array(
"customvalidation" => "my error message.",
);
public function __construct($translator, $data, $rules, $messages = array(), $customAttributes = array())
{
parent::__construct($translator, $data, $rules, $messages, $customAttributes);
$this->setCustomMessages($this->custom_messages);
}
public function validateCustomvalidation($attribute, $value, $parameters = null)
{
// validation code here
}
}
maybe this code more better :
// for example I am using sub-array custom at default validation file, but you can do it in other file as you wishes.
..other..
'custom' => array(
'email' => array(
'required' => 'We need to know your e-mail address!',
),
"required" => "Hey!!! don't forget at :attribute field is required.",
),
..other..
// you can determine your custom languages at your wishes file
$messages = \Lang::get('validation.custom');
Validator::make($input, $rules, $messages);
From documentation:
In some cases, you may wish to specify your custom messages in a language file instead of passing them directly to the Validator. To do so, add your messages to custom array in the app/lang/xx/validation.php language file.
'custom' => array(
'email' => array(
'required' => 'We need to know your e-mail address!',
),
),
That means, in your case,
'custom' => array(
'postcode' => array(
'PostCode' => 'error message for PostCode rule',
'required' => 'error message for required rule',
),
),
If you want to utilize the custom validation messages array in app/lang/xx/validation.php, the correct way is as follows:
'custom' => array(
'formFieldName' => array(
'postcode' => 'error message for PostCode rule',
'iamalwayslowercase' => 'error message for this rule'
),
),
Note that you use the name of the form field and then in the array you use the lowercased name of the rule.
The code below also works perfectly, take note of the underscore on the index of the $customValidatorMessages array. Hope it helps someone :-)
class CoreValidator extends Illuminate\Validation\Validator
{
/**
* The array of custom validator error messages.
*
* #var array
*/
protected $customValidatorMessages = array();
public function validatePostcode($attribute, $value, $parameters = null)
{
$regex = "/^((GIR 0AA)|((([A-PR-UWYZ][0-9][0-9]?)|(([A-PR-UWYZ][A-HK-Y][0-9][0-9]?)|(([A-PR-UWYZ][0-9][A-HJKSTUW])|([A-PR-UWYZ][A-HK-Y][0-9][ABEHMNPRVWXY])))) [0-9][ABD-HJLNP-UW-Z]{2}))$/i";
if(preg_match($regex ,$value)) {
return true;
}
$this->customValidatorMessages['post_code'] = 'Postcode error message.';
$this->setCustomMessages($this->customValidatorMessages);
return false;
}
}

Cake php Model Validation Not Working

I want insert record in a table.For this i have model,view and controller.Everything in my code is working perfectly but my model code for validation not showing any validation message.What should i do?I am giving below the code :
My Controller Code :
public function send_money()
{
$this->layout='agent';
$this->Agent->create();
$this->Agent->set($this->data);
if(empty($this->data) == false)
{
//$this->Agent->saveAll($this->data['Agent'], array('validate' => 'only')); //This code Id New
$this->Agent->saveAll($this->data['Agent']);
$this->Session->setFlash('Information Added Successfully.');
$this->redirect('send_money');
}
else
{
$this->set('errors', $this->Agent->invalidFields());
}
}
And My Model Code is :
App::uses('AppModel', 'Model');
/**
* Admin Login Model
*
*/
class Agent extends AppModel
{
public $name='Agent';
public $usetables='agents';
public $validate = array(
'contact' =>array(
'rule' => 'notEmpty', // or: array('ruleName', 'param1', 'param2' ...)
'allowEmpty' => false,
'message' => 'Please Enter Contact No.'
),
'name' =>array(
'rule' => 'notEmpty', // or: array('ruleName', 'param1', 'param2' ...)
'allowEmpty' => false,
'message' => 'Please Enter Name.'
),
'email_add' =>array(
'rule' => 'email', // or: array('ruleName', 'param1', 'param2' ...)
'allowEmpty' => false,
'message' => 'Please Enter Valid Email.'
),
);
}
Use this in your controller:
if($this->Agent->validates($this->data)) {
Instead of:
if(empty($this->data) == false)
change:
$this->Form->create('Agents',
to
$this->Form->create('Agent',
As your model name is Agent not Agents
See here: Model Validation
try this:
public function send_money()
{
$this->layout='agent';
$this->Agent->create();
$this->Agent->set($this->data);
if($this->Agent->saveAll($this->data['Agent'])) {
$this->Session->setFlash('Information Added Successfully.');
$this->redirect('send_money');
}
else {
$this->set('errors', $this->Agent->invalidFields());
}
}
Note : to log the error validation use this debug($this->Agent->validationErrors);.

Categories