CakePHP 3- Showing all error and buildRules messages in controller - cakephp

I have this Model/Table/UsersProfilesTable.php where I have specified all the error messages and buildRules.
My intention is to list all the validation errors in the controller while attempting to save the data.
The code is mentioned below.
// Model/Table/UsersProfilesTable.php
class UserProfilesTable extends Table{
public function validationDefault(Validator $validator){
$validator = new Validator();
$validator
->notEmpty("first_name","First name cannot be empty.")
->requirePresence("first_name")
.......
->notEmpty("email", "Email cannot be empty.")
->requirePresence("email")
->add( "email", "email",[
"rule" => ["email", true],
"message" => "Enter a valid e-mail."
]);
return $validator;
}
public function buildRules(RulesChecker $rules){
$rules->add($rules->isUnique(['email'], 'Email should be unique'));
return $rules;
}
//UsersController.php
$user = $this->Users->patchEntity($user, $this->request->data);
if($this->Users->save($user)){
// Success msg
}
if($user->errors()){
// This shows all the error messages except the one specified in the buildRules for unique email.
pr($user->errors());
}
Can anyone please come up with a way in which I can list all the validation errors including the message specified in the buildRules method?
Any help would be appreciated. Thanks in advance!
Peace! xD

Remember that validation is a 2 phase process, first all the validatiton rules are checked (during marshalling - i.e. patchEntity()), only if they pass are the rules in buildRules are used. This means that the unique email rule will not be run until the standard validation rules all pass.
If you need immediate feedback for email uniqueness you can also add a validation rule for email uniqueness in the validator.

You can use this to force Cake to check the rules even if validation fails:
$this->Users->checkRules($user);

Related

Change request input values from empty string to null in Laravel

In my controller I receive an Illuminate\Http\Request Request that contains some data with values equal to an empty string:
dd($request->input())
//output
array:5 [▼
"_token" => "K43q88mR5zQRqVPAuYX5IWtQ1khQ24JTsuxl8mz4"
"param1" => "1"
"param2" => "2"
"param3" => ""
"param4" => ""
]
(basically that happens when the user had made no selections in the create form BUT due to frontend restrictions see here, I am not able to change the form of the Request so that to exclude the empty string values from being sent to the server)
The request input data is used to attach a relationships to models. For example:
$book->authors()->attach($request->input('param1'));
The problem is that when the input value is equal to the empty string "" the above line throws a QueryException, since it tries to add a foreign key equal to "" to the pivot table.
On the other hand, I noticed that if the value is null instead of "", the attach() method is not executed and, thus, does not throws an Exception, neither updates the database, which is exactly the behavior I want, when the user made no selection in the input form.
My question is how can I change the request values from "" to null? My first thought was to make a helper function that iterates over the request input array and replace "" with null and use it in the controller BUT it does not seems like a good idea for me, because this would not prevent possible unwanted validation errors, when using Form Request Validation.
Any ideas please ???
Thank you in advance
NOTE: Laravel 5.4 now ships with this feature
https://laravel.com/docs/5.4/requests#input-trimming-and-normalization
For versions > 5.4 see answer below or just copy the official middlewares.
HTTP middleware provide a convenient mechanism for filtering HTTP requests entering your application.
Make a middleware and assign it to the routes or controller's methods you want it to take place.
Example:
class SanitizeMiddleware
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
foreach ($request->input() as $key => $value) {
if (empty($value)) {
$request->request->set($key, null);
}
}
return $next($request);
}
}
Route::post('/users', function (Request $request) {
dd($request->input());
})->middleware(SanitizeMiddleware::class);
This being said you can solve your problem as follows:
Comment out or put this in middleware in the
App\Http\Kernel.php file.
protected $middleware = [
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
];
Can't you just check before you add an item to your pivot table?
if(!empty($request->input('paramX'))){
$book->authors()->attach($request->input('paramX'));
}

CakePHP3.x custom validation not working

Inside UsersTable class, I am trying to implement custom validation following the CakeBook but I got an error saying, Object of class App\Model\Table\UsersTable could not be converted to string [CORE/src/Validation/ValidationRule.php, line 128]. Below is my code in UsersTable.php.
class UsersTable extends Table{
public function validationDefault(Validator $validator){
$validator->add(
"password",[
"notEmpty"=>[
"notEmpty"
],
"custom"=>[
"rule"=>[$this,"customFunction"],
"message"=>"foo"
]
]
);
}
public function customFunction($value,$context){
//some logic here
}
}
Looking at ValidationRule.php in core CakePHP library, I have noticed that array_shift() (on line 185) is taking the first element of [$this,"customFunction"], that is, $this and assigning it to $value. But actually $value should be [$this,"customFunction"]. Therefore, for my code to work without any error, I needed to add one more nesting to [$this,"customFunction"](So it is now [[$this,"customFunction"]]). Do I misunderstand something or is this some kind of bug?
UPD: This problem is now fixed.
I think you've spotted that correctly, the problem seems to be that CakePHP expects the rule key value to be in
[string or callable, ...args]
format when it is in array, ie it doesn't test whether the value itself already is a callable.
The documentation says that the non-nested variant should work, so you might want to report this as a bug.
Use this in your model for custom validation
public function validationCustom($validator)
{
return $validator
->notEmpty('username', 'A username is required');
}
Use validation method name except validation keyword in your controller when you want to save or update
$user = $this->Articles->newEntity($this->request->data,
['validate' => 'custom']);

Cake PHP validation, change incoming $data

I got a problem with my cake validation.
The user enters e.g. "20€" in a form field and I only want to get the number "20" with stripped out Euro-sign. But the validation methods only allow checking if something is what it´s supposed to be.
I´m about to create a function in my rule-set but I´m struggling going further because what the function returns in cake context is either true or false, not a modified variable like I need ...
'rule-3' => array(
'rule' => 'checkEuro',
'message' => 'don´t need one because it should be a conversion only :-('
)
public function checkEuro($data) {
$data = str_replace('€','',$data);
return preg_replace('/[^0-9]/','', html_entity_decode($data));
}
I hope you can help.
Thanks in advance
If you only need to store the amount itself inside the database and not the euro-sign, you should strip the Euro-sign before validating.
You can do this inside the beforeValidate() callback of your model;
public function beforeValidate(array $options = array())
{
if (!empty($this->data[$this->alias]['FIELDNAME']) {
// Strip the euro-sign
// NOTE: should also take plain, non-entity € into account here?
$this->data[$this->alias]['FIELDNAME'] = str_replace(
'€', '', $this->data[$this->alias]['FIELDNAME']
);
}
return parent::beforeValidate($options);
}
(Of course, replace FIELDNAME with the actual fieldname)
After stripping the currency-symbol, validate the value using the decimal or numeric validation rule
note
If you do want to store the value including the currency-symbol, use the core money validation rule.

cakephp invalidate element array

I am using cakephp. I have a form with an element array.
For ex:-
<textarea name="data[User][0][description]>
<textarea name="data[User][1][description]>
From the controller, I need to invalidate (manually) the array field if it is empty and need to show errors to the respective field.
What is the correct syntax for invalidating the field if it an element array ?
I know, the following will work for single element . How it will be for an element array ?
$this->User->invalidate("description");
Unfortunately you cannot invalidate the field with that function.
But what invalidate() does?
function invalidate($field, $value = true) {
if (!is_array($this->validationErrors)) {
$this->validationErrors = array();
}
$this->validationErrors[$field] = $value;
}
It just set validationErrors of the model.
So, you can do following in your Controller (but I also appeal you to move that validation in the Model):
$this->User->validationErrors[1]['description'] = 'Your error message';
The following code will invalidate the second description in the list.
HTH
You can type in view:
<?php
echo $this->Form->error("User.1.description");
?>
Thanks Nik,
your answer helped me, but halfway, because my problem was with a compound field by other subfields.
account_number {
bank_code,
bank_office,
check_digit,
account
}
In this case if we need put in validation error in one subfield, this is the solution:
$this->Model->validationErrors['account_number']['bank_code'][0] = 'Your message error';
I hope this help someone.
Regards.
I had similar problem, since it was for admin panel I showed error message on the first level of field i.e. for this portion only.
If you are validation on controller, just create an array of error with field name and error message, set it in controller and display message if in_array($field, $withErrorArray) in view.

save() returning false, but with no error in CakePHP

My debug value is set to 2, and it's displaying all the queries, except the one I need.
I have an Items controller method that is calling this method in the User model (Item belongsTo User):
function add_basic($email, $password) {
$this->create();
$this->set(array(
'email' => $email,
'password' => $password
));
if($this->save()) {
return $this->id;
}
else {
return false;
}
}
I have confirmed that $email and $password are being passed into the function correctly (and are populated with legit data). email and password are the names of the fields in the User model.
I have also confirmed that on $this->save() it is returning false, but when I view the page where this occurs, the query is not being printed in the debug, and there is no error being thrown, so I have no idea whats going wrong.
Any ideas on how I can see the error, or why the query doesn't seem to be getting executed?
It's weird, cause right after this, I have another model saving data to it in the exact same fashion, it goes off without a hitch.
This will probably give you the info you need (assuming it's not saving because of invalid data, of course):
if(!$this->save()){
debug($this->validationErrors); die();
}
Have you got a beforeValidate() or beforeSave() method in the model or app model? Ifso, are they returning true? Failing that, use a debugger, set a break point in your IDE at the top of cake/libs/models/model.php save() method and step through the code until it returns false. Failing that add die('here'); calls.
Try this:
if ($this->save()) {
return $this->id;
}
else {
var_dump($this->invalidFields());
return false;
}
#cakePHP 3.6 and above: By default, the request data will be validated before it is converted into entities. If any validation rules fail, the returned entity will contain errors. It can be read by getErrors() method.
The fields with errors will not be present in the returned entity:
Say, you have an entity
use App\Model\Entity\Article;
$entity = $this->ModelName->newEntity([
'id' => 1,
'title' => 'New Article',
'created' => new DateTime('now')
]);
$result = $this->ModelName->save($entity);
\Cake\Log\Log::debug($entity->getErrors());
If you’d like to disable validation when converting request data, set the validate option to false:
$article = $articles->newEntity(
$this->request->getData(),
['validate' => false]
);
Ref: https://book.cakephp.org/3/en/orm/validation.html
Make sure to check your tables:
Does ID have auto increment enabled?
Is id your primary key?
the auto_increment issues killed me.
Easy way to check: if any of your rows have ID = 0, auto_increment is likely disabled.
CakePHP 3.6
$entity = $this->Model->newEntity([
'account_id' => $id,
'gallery_id' => $gallery_id
]);
$result = $this->Model->save($entity);
print_r($entity->getErrors());
The other situation where CakePHP fails to report any $this->Model->validationErrors and no other errors is potentially when $this->request->data isn't as Cake expects and is simply ignoring your data, not saving, no validation errors. For example if your data was provided by DataTables you might see this format $this->request->data[0]['Model']['some_field'].
$this->Model->save($this->request->data[0]) will work however.

Resources