Laravel not responding with validator errors - json

I validate a model
$validator = $c->validate($collection);
This is the validate function
public function validate($data){
return Validator::make($data, $this->rules());;
}
These are the rules
public function rules() {
return array([
'name' => [
'required', 'You need to choose a name for your collection.',
'unique:collections,table_name', 'A collection or collection table with this name already exists'
],
...
]);
}
I'm trying to send back a JSON response with the validator's errors, as such:
return response()->json($validator->errors(), 200);
I'm currently testing validation for the 'name' rule, and the validator is failing, as expected.
However, I'm expecting it to return that rule's message ("A collection or collection table with this name already exists")
Instead, I'm getting this returned:
My goal is to have laravel send back the error that I need, thank you in advance for any help.
edit: updated code:
Messages:
public function messages(){
return [
'name.required' => 'A name must be specified for the collection',
'name.unique' => 'A collection or collection table with this name already exists',
'name.min' => 'The collection name is too short',
'fields.*.fieldName.unique' => 'Field names must be unique',
'fields.*.fieldName.required' => 'One or more fields must be specified for the collection',
'fields.*.fieldName.not_in' => 'Illegal field name, please try another one',
'fields.*.fieldName.min' => 'The field name is too short',
'fields.*.dataType.required' => 'A data-type must be specified for fields',
'fields.*.dataType.in' => 'Illegal data-type'
];
}
public function rules() {
return array([
'name' => [
'required', 'You need to choose a name for your collection.',
'unique:collections,table_name', 'A collection or collection table
with this name already exists',
'min:2'
],
'fields.*.fieldName' =>
[
'unique' => 'Please ensure that the fields are uniquely named.',
'required' => 'You must specify a name for your fields.',
'not_in:'.implode(',', self::$illegalFieldNames),
'min:2'
],
'fields.*.dataType' =>
[
'required', 'You must specify a data type for your fields.',
'in:'.implode(',', self::$allowedDataTypes)
]
]);
}
public function validate($data){
return Validator::make($data, $this->rules(), $this->messages());
}

The validator make method takes the third parameter as the messages array. You can't mix the rules and messages like that.
public function rules()
{
return [
'name' => 'required|unique:collections,table_name'
];
}
public function messages()
{
return [
'name.required' => 'You need to choose a name for your collection',
'name.unique' => 'A collection or collection table with this name already exists',
];
}
public function validate($data)
{
return Validator::make($data, $this->rules(), $this->messages());
}

$this->rules($request, array(
'name' =>
'required|alpha_dash|min:5|max:255|unique:posts
));
use java script for revealing error
or you can use something like this .
public function store(Request $request)
$validator = Validator::make($request->all(), [
'title' => 'required|unique:posts|max:255',
'body' => 'required',
]);
if ($validator->fails()) {
return redirect('post/create')
->withErrors($validator)
->withInput();
}
// Store the blog post...
}
}

Related

Laravel validation with custom json respons

Quick question.
Would it be possible to changes the JSON validation response of laravel?
This is for a custom API that I am building in Laravel.
Validation process
$validation = $this->validate(
$request, [
'user_id' => 'required',
]);
The response shows up like this in json
{
"message": "The given data was invalid.",
"errors": {
"user_id": [
"The user id field is required."
],
}
}
Preferable it would become something like this.
{
"common:" [
"status": "invalid",
"message": "Param xxxx is required",
],
}
What would be the best way to changes this?
Is it even possible?
Thank you.
You can do this, and it will be reflected globally.
Navigate to below folder and use Controller.php
app/Http/Controllers
use Illuminate\Http\Request;
Write below method in Controller.php and change response as you want.
public function validate(
Request $request,
array $rules,
array $messages = [],
array $customAttributes = [])
{
$validator = $this->getValidationFactory()
->make(
$request->all(),
$rules, $messages,
$customAttributes
);
if ($validator->fails()) {
$errors = (new \Illuminate\Validation\ValidationException($validator))->errors();
throw new \Illuminate\Http\Exceptions\HttpResponseException(response()->json(
[
'status' => false,
'message' => "Some fields are missing!",
'error_code' => 1,
'errors' => $errors
], \Illuminate\Http\JsonResponse::HTTP_UNPROCESSABLE_ENTITY));
}
}
I have tried it with Laravel 5.6, maybe this is useful for you.
#Dev Ramesh solution is still perfectly valid for placing inline within your controller.
For those of you looking to abstract this logic out into a FormRequest, FormRequest has a handy override method called failedValidation. When this is hit, you can throw your own response exception, like so...
/**
* When we fail validation, override our default error.
*
* #param ValidatorContract $validator
*/
protected function failedValidation(\Illuminate\Contracts\Validation\Validator $validator)
{
$errors = $this->validator->errors();
throw new \Illuminate\Http\Exceptions\HttpResponseException(
response()->json([
'errors' => $errors,
'message' => 'The given data was invalid.',
'testing' => 'Whatever custom data you want here...',
], 422)
);
}
I was searching for an answer to this and I think I found a better way. There is an exception handler in a default Laravel app - \App\Exceptions\Handler - and you can override the invalidJson method:
<?php
namespace App\Exceptions;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Validation\ValidationException;
class Handler extends ExceptionHandler
{
// ...
protected function invalidJson($request, ValidationException $exception)
{
$errors = [];
foreach ($exception->errors() as $field => $messages) {
foreach ($messages as $message) {
$errors[] = [
'code' => $field,
'message' => $message,
];
}
}
return response()->json([
'error' => $errors,
], $exception->status);
}
}

Validation rule unique requires at least 1 parameters. laravel 5.4

I've got problem with my laravel 5.4 I can't save the data
public function rules()
{
return [
'permission_id' => 'required|unique',
'name' => 'required',
'label' => 'required',
];
}
Error
Validation rule unique requires at least 1 parameters.
the unique rule need at least the name of ur database table.
ur rules function should be like this:
public function rules()
{
return [
'permission_id' => 'required|unique:db_table_name',
'name' => 'required',
'label' => 'required',
];
}
for more informations check laravel doc unique rule

beforeMarshal does not modify request data when validation fails

Bug or Feature? If I change request data with beforeMarshal and there is a validation error, the request data will not be given back modified.
This question may be related to How to use Trim() before validation NotEmpty?.
Modifying Request Data Before Building Entities
If you need to modify request data before it is converted into entities, you can use the Model.beforeMarshal event. This event lets you manipulate the request data just before entities are created. Source: CakePHP 3 Documentation
According to the book I would expect the request data is always changed, no matter if there is a validation error or not.
Example or test case:
// /src/Model/Table/UsersTable.php
namespace App\Model\Table;
use Cake\ORM\Table;
// Required for beforeMarshal event:
use Cake\Event\Event;
use ArrayObject;
// Required for Validation:
use Cake\Validation\Validator;
class UsersTable extends Table {
public function beforeMarshal(Event $event, ArrayObject $data, ArrayObject $options) {
$data['firstname'] = trim($data['firstname']);
}
public function validationDefault(Validator $validator) {
$validator
->add('firstname', [
'minLength' => [ 'rule' => ['minLength', 2], 'message' => 'Too short.' ],
])
;
return $validator;
}
}
If I enter " d" (Space-d) the validation error is shown, but the space itself is not removed in the form. I would expact the form showing only "d" because the space is removed from the request data with the beforeMarshal event. So... bug or feature?
My solution would be to use the trim()-function in the controller instead of the beforeMarshal event:
// /src/Controller/UsersController.php
// ...
public function add() {
$user = $this->Users->newEntity();
if ($this->request->is('post')) {
// Use trim() here instead of beforeMarshal?
$this->request->data['firstname'] = trim($this->request->data['firstname']);
$user = $this->Users->patchEntity($user, $this->request->data );
if ( $this->Users->save($user) ) {
$this->Flash->succeed('Saved');
return $this->redirect(['controller' => 'Users', 'action' => 'index']);
} else {
$this->Flash->error('Error');
}
}
$this->set('user', $user);
}
This way the space will be removed even if there is a validation error. Or did I miss another function similar to beforeMarshal which is really modifying the request data?
The main purpose of beforeMarshal is to assist the users to pass the validation process when simple mistakes can be automatically resolved, or when data needs to be restructured so it can be put into the right columns.
The beforMarshal event is triggered just at the start of the validation process, one of the reasons is that beforeMarshal is allowed to change the validation rules and the saving options, such as the field whitelist. Validation is triggered just after this event is finished.
As documentation explains, if a field does not pass validation it will automatically removed from the data array and not be copied into the entity. This is to prevent having inconsistent data in the entity object.
More over, the data in beforeMarshal is a copy of the request. This is because it is important to preserve the original user input, as it may be used elsewhere.
If you need to trim your columns and display the result of the trimming to your user, I recommend doing it in the controller:
$this->request->data = array_map(function ($d) {
return is_string($d) ? trim($d) : $d;
}, $this->request->data);
Not work. This is my beforeMarshal :
public function beforeMarshal(Event $event, ArrayObject $data, ArrayObject $options)
{
$schema = $this->schema();
foreach($schema->columns() as $idx => $field ) {
$sc = $schema->getColumn($field);
if (isset($data[$field]) && $data[$field] != null) {
if ($sc['type'] == 'date') {
$date = DateTime::createFromFormat('d/m/Y',$data[$field]);
if ($date)
$data[$field] = $date->format('Y-m-d');
}
if ($sc['type'] == 'datetime') {
debug($data[$field]);
$date = DateTime::createFromFormat('d/m/Y',$data[$field]);
if ($date)
$data[$field] = $date->format('Y-m-d H:i:s');
}
}
}
debug($data);
}
The date commission_approved_date is correctly modified in beforeMarshal:
/src/Model/Table/AccountsTable.php (line 265)
object(ArrayObject) {
_referer => 'http://localhost/gessin/Accounts/edit/ODc?filter=eyJBY2NvdW50cy51c2VyX2lkIjoiMTA4NSIsIjAiOiJNT05USChBY2NvdW50cy5jb21taXNzaW9uX2RhdGUpID4gMCIsIllFQVIoQWNjb3VudHMuY29tbWlzc2lvbl9kYXRlKSI6IjIwMjAifQ=='
description => 'Provvigione su attivazione prodotto vod002'
notes => 'asd'
totalpaid => '0'
commission_approved_date => '2020-02-23 18:34:22'
}
But the same date is not, after patchEntity:
/src/Controller/AccountsController.php (line 203)
object(App\Model\Entity\Account) {
'id' => (int) 87,
'identifier' => null,
'company_id' => null,
'created' => object(Cake\I18n\FrozenTime) {
'time' => '2020-02-29 14:01:50.000000+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'modified' => object(Cake\I18n\FrozenTime) {
'time' => '2020-02-29 18:30:24.000000+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'notes' => 'asd',
'total' => null,
'totaltax' => null,
'invoice_id' => null,
'description' => 'Provvigione su attivazione prodotto vod002',
'in_out' => null,
'is_refund' => null,
'client_id' => null,
'contract_id' => (int) 32,
'totalpaid' => (float) 0,
'user_id' => (int) 1085,
'commission_date' => object(Cake\I18n\FrozenTime) {
'time' => '2020-02-04 00:00:00.000000+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'commission_approved_date' => object(Cake\I18n\FrozenTime) {
'time' => '2028-08-12 00:00:00.000000+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
},

Setting default values on create and update in yii

I am trying to update some fields in yii 1.1 using the following rules, but it is not working.
public function rules()
{
return [
['CreatedOn','default','value'=>time(),'isEmpty'=>true,'on'=>'insert'],
['CreatedBy','default','value'=>\Yii::$app->user->identity->id,'isEmpty'=>true,'on'=>'insert'],
['ModifiedOn','default','value'=>time(),'isEmpty'=>true,'on'=>'update'],
['ModifiedBy','default','value'=>\Yii::$app->user->identity->id,'isEmpty'=>true,'on'=>'update'],
];
}
I am looking to update CreatedBy and CreatedOn when inserting, and ModifiedBy and ModifiedOn when updating.
From soju's excellent answer, with Yii2:
By default, a model supports only a single scenario named default
You should therefore set the scenario manually in your controller i.e:
$model->scenario = 'insert';
You could also use when instead of on i.e:
['CreatedOn', 'default', 'value'=>time(), 'isEmpty'=>true, 'when'=>
function($model) { return $model->isNewRecord; }
],
['ModifiedOn', 'default', 'value'=>time(), 'isEmpty'=>true, 'when'=>
function($model) { return !$model->isNewRecord; }
],
An alternative to setting them in rules() would be to use beforeSave() to set them:
public function beforeSave($insert) {
if ($insert) {
$this->CreatedBy = \Yii::$app->user->identity->id;
$this->CreatedOn = time();
} else {
$this->ModifiedBy = \Yii::$app->user->identity->id;
$this->ModifiedOn = time();
}
return parent::beforeSave($insert);
}
This is the correct way to do it:
Behaviors:
public function behaviors()
{
return [
'timestamp' => [
'class' => TimestampBehavior::className(),
'attributes' => [
ActiveRecord::EVENT_BEFORE_INSERT => 'created_on',
ActiveRecord::EVENT_BEFORE_UPDATE => 'modified_on',
ActiveRecord::EVENT_BEFORE_DELETE => 'deleted_at',
],
'value' => function () {
return date('Y-m-d H:i:s');
}
],
[
'class' => BlameableBehavior::className(),
'createdByAttribute' => 'created_by_id',
'updatedByAttribute' => 'updated_by_id',
],
];
}
If you need just a simple rule for default value, this is enough:
public function rules()
{
return [
['CreatedOn','default','value'=>time()],
['ModifiedOn','default','value'=>time(),'isEmpty'=>true],
...
]
}
The 'isEmpty'=>true option override the default isEmpty() function and returns true (it is always seen as empty) dues it is always populated with time()
For Yii2 version 2.0.8 from April 2016 I had an error with 'isEmpty'=>true because according to documentation it expects a function so you must to do like this:'isEmpty' => function ($value) {return true;}.
When you use this solution you get a value for ModifiedBy even on create and I believe that was not an intention. It is possible to write isEmpty to return true in case of an update but I simply used 'when' because it is much more readable for me. So, my solution for rules in a model was :
['CreatedBy', 'default', 'value' => Yii::$app->user->id],
['ModifiedBy', 'default', 'value' => Yii::$app->user->id,
'when' => function ($model) { return !$model->isNewRecord;}],
As a side note for this question is that for timestamps you should rely on database to fill them, CreatedOn with default value and a before update trigger for ModifiedOn.

Yii2: Returning an array of primary keys

Maybe I'm missing the essentials but why the following code will throw a Bad Request error (#400) complaining on "Missing parameter id" when rendering a view on a MySQL view?
In model:
public static function primaryKey()
{
return [
'vcostumbre_id',
'vbibliografia_id',
'vpagina_inicial',
];
}
In controller:
public function actionView($id)
{
return $this->render('view', [
'model' => $this->findModel($id),
]);
}
But this will work:
public function actionView($vcostumbre_id, $vbibliografia_id, $vpagina_inicial)
{
$id = [
'vcostumbre_id' => $vcostumbre_id,
'vbibliografia_id' => $vbibliografia_id,
'vpagina_inicial' => $vpagina_inicial,
];
return $this->render('view', [
'model' => $this->findModel($id),
]);
}
Because in the URL you have not the parameter "id".
It should be /mycontroller/view?id=42".
Check the view file where the link is. It should be :
Url::to(['/controller/view', 'id' => 42])