I've an update form that contains an image and other data to be updated I changed the default route key to use the name instead of the default key which is the ID and I made a separate form request to validate my requests It works fine when posting new record unfortunately it keeps failing with the name field which is unique field; I've checked all threads on github and in stackoverflow with no use although I have the same project in laravel 5.5 and it works fine and now I'm stuck with laravel 6
hereis my form
let data = new FormData();
data.append('name', this.channel.name);
data.append('base_color', this.channel.baseColor);
data.append('complementary_color', this.channel.complementaryColor);
if (this.file){
data.append('avatar', this.file);
}
data.append('_method', 'PUT');
axios.post(`/dashboard/channels/${this.channel.name}`, data).then(resp => {
this.$parent.$emit('channel_updated', resp.data);
}).catch(error => {
flash(error.response.data, 'danger', 'backEndStyle');
});
and here is my route
Route::resource('/dashboard/channels', 'ChannelController');
and here is my form request
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class ChannelRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'name' => 'required|unique:channels,name,'. $this->id,
'base_color' => 'required',
'complementary_color' => 'required',
];
}
}
here is my controller for update method
public function update(Channel $channel, ChannelRequest $request)
{
$channel->update([
'name' => $request->name,
'bg_base_color' => $request->base_color,
'bg_complementary_color' => $request->complementary_color,
]);
return response($channel->fresh(), 200);
}
Use the ignore constraint to ignore the current model when validating unique
public function rules()
{
return [
'name' => ['required', Rule::unique('channels')->ignore($this->route('channel'))],
'base_color' => 'required',
'complementary_color' => 'required',
];
}
Related
I have not so much practical experience with Laravel yet and I wondered what is the best way to deal with similar validation logic and where to put it.
Let's say I have an API resource Controller for Products with a store and an update method like so:
public function store(Request $request)
{
$request->validate([
'name' => 'required|string|max:100',
'description' => 'nullable|string|max:1000',
'price' =>'required|decimal:0,2|lt:1000'
]);
return Product::create($request->all());
}
public function update(Request $request, Product $product)
{
$request->validate([
'name' => 'string|max:100',
'description' => 'nullable|string|max:1000',
'price' =>'decimal:0,2|lt:1000'
]);
return Product::update($request->all());
}
The only difference between the validation in store and update is that store adds the 'required' rule for 'name' and 'price'. My question is, if I can encapsulate both validations in one Form Request, or how can I avoid code duplication without adding unnecessary code?
With my understanding of Form Requests I would probably create two Form Request classes, StoreProductRequest and UpdateProductRequest, and maybe another helper class that defines the core validation rules. Then each Form request could call for example ProductHelper::getBaseValidationRules() and merge that with their extra requirements. Somehow I find that a bit overkill.
you can create a request for your validations and use them in your controllers
for example
php artisan make:request YOUR_REQUEST_NAME
then inside your request you can add your validations like this
public function rules()
{
return [
'name' => 'required|string|max:100',
'description' => 'nullable|string|max:1000',
'price' => 'required|decimal:0,2|lt:1000'
];
}
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
and in your method you can call it like this
public function update(YOUR_REQUEST_NAME $request, Product $product)
{
return Product::update($request->all());
}
for more information you can read this
https://laravel.com/docs/5.0/validation#form-request-validation
in case you want condition in the rules please check this video
https://www.youtube.com/watch?v=epMaClBOlw0&ab_channel=CodeWithDary
Okay based on the suggestions, I came up with the following solution:
I created a Form Request named ProductRequest and implemented the rules method as follows:
public function rules()
{
$rules = [
'name' => ['string', 'max:100'],
'description' => ['nullable', 'string', 'max:1000'],
'price' => ['decimal:0,2', 'lt:1000'],
];
// If the user wants to create a new Instance some fields are mandatory.
if ($this->method() === 'POST') {
$rules['name'][] = 'required';
$rules['price'][] = 'required';
}
return $rules;
}
This is fine for me. Although in a bigger project I probably would create two Form Requests, StoreProductRequest and UpdateProductRequest. They would share and update a base set of rules as I described in the question.
I made a controller to store tasks, it looks like:
public function store(StoreTaskRequest $request)
{
$validated = $request->validated();
return response()->json($request);
}
For the request validation i made a custom validator, that looks like
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreTaskRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'project_id' => 'required',
'project_name' => 'required',
'task_id' => 'required',
'task_name' => 'required',
'startDate' => 'required',
'endDate' => 'required',
];
}
public function messages()
{
return [
'project_id.required' => 'required!',
'project_name.required' => 'required!',
'task_id.required' => 'required!',
'task_name.required' => 'required!',
'startDate.required' => 'required!',
'endDate.required' => 'required!',
];
}
}
When i make a post request to that controller with incorrect data it returns an html page but when i post it with the correct data it returns the request as json
By the way i make my post request with: reqbin.com
Post request headers:
X-Csrf-Token: ....
Content-Type: application/json
Post Params:
{
"project_name": "Wiza",
"task_id": 1,
"task_name": "test 1",
"startDate": {
"week": 1,
"start" : 11,
"year": 2022
},
"endDate": {
"week": 1,
"start" : 11,
"year": 2022
}
}
Does anyone have any idea why its returning a html page instead of an validation error?
Edit:
api.php is empty
web.php
Route::post('api/v1/tasks/', [TaskController::class, 'store']);
//Also tried
Route::resource('api/v1/tasks/', TaskController::class);
Add this code in your custom validator.
/**
* Get the error messages for the defined validation rules.*
* #return array
*/
protected function failedValidation(ValidationValidator $validator)
{
throw new HttpResponseException(response()->json([
'message' => implode('', collect($validator->errors())->first()),
'status' => true
], 422));
}
I have faced the same problem and below mentioned way works for me.
Header Debugging
First You need to verify the list of headers exists in your request. You can do this by following the below code in your controller store method:
public function store(StoreTaskRequest $request)
{
dd($request->headers); //dump your headers exists in $request
$validated = $request->validated();
return response()->json($request);
}
and that will show you something like:
Solution:
Bestway and clean way to append those headers by using middleware i.e.
I've created ForceJsonResponse middleware.
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class ForceJsonResponse
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle(Request $request, Closure $next)
{
$request->headers->set('Accept', 'application/json');
return $next($request);
}
}
after that, I've registered that middleware in app/Http/Kernel.php as shown in the screenshot.
then attach that middleware with that route as shown in the screenshot:
laravel doc reference for middleware attachement
Worst case scenario
if still, it doesn't work then you can use try catch statement
I want to pass $params['user_id'] to $fieldValidations and check if the hour is unique for specific user_id not for all hours hour in the database table
I created a model post
class Post extends Model
{
protected $fillable = ['user_id', 'hour'];
public static $fieldValidations = [
'user_id' => 'required',
'hour' => 'required|date_format:Y-m-d H:i:s|unique:post,hour,NULL,user_id,'
];
}
and a controller post
class PostController extends Controller
{
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index(Request $request)
{
$params = $request->all();
$params['user_id'] = 12;
$validator = Validator::make($params, Post::$fieldValidations);
if ($validator->fails()) {
return Response::json($validator->errors()->all(), 422);
}
}
}
I don't think you can do this using the unique validation rule. From Laravel 5.7 documentation:
The field under validation must be unique in a given database table.
Note it says table and not column.
You may have to just query the database and return a JSON response error if it fails. Also, in your current code inside the validation rules, you are specifying that user_id is the primary id key column in the post table. I think that is likely an error and should be removed, even though it's irrelevant given that you can't accomplish what you want using the unique rule. Also, you ended the rule with a comma.
if (Post::where(['user_id' => $params['user_id'], 'hour' => $params['hour']])->exists()) {
return response()->json(['status' => 'error', 'msg' => 'Error', 'errors' => ['hour_error' => ['That hour already exists on the user!']]], 422);
}
Lastly, instead of using $params = $request->all(), I prefer to use the request() helper function and just inline it into the rest of the code. But, that's up to you.
I am using Request Validation for validation. but when validation rule becomes fail then i am not getting that message like wrong name is given .
I use Make::Request Method
My Form Request code
namespace xx\xx\xxx\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class InstituteUpdateRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules($id=0)
{
return [
'name' => 'required|regex:/^[(a-zA-Z\s\-)]+$/u|min:3|max:80',
'valid_from' => 'date|max:30|nullable',
'valid_to' => 'date|nullable',
'address' => 'regex:/^[(a-zA-Z.\-,0-9\s)]+$/u|max:150',
'mobile' => 'required', Rule::unique('users')->ignore($id),
'city' => 'regex:/^[(a-zA-Z\-\s)]+$/u|max:100',
'state' => 'regex:/^[(a-zA-Z\-\s)]+$/u|max:100',
'pin' => 'max:6|regex:/^[(0-9)]+$/u',
];
}
}
My Controller code
public function update ( InstituteUpdateRequest $request )
{
DB::transaction(function () use ($request) {
$this->institute_update = Institute::where('class_code' ,$request->class_code)->update([
'name'=>$request->name ,
'valid_from'=>date('Y-m-d',strtotime($request->valid_from)) ,
'valid_to'=>date('Y-m-d',strtotime($request->valid_to)),
'mobile'=>$request->mobile,
'address'=>$request->address,
'city'=>$request->city,
'state'=>$request->states,
'pin'=>$request->pin,
'logo' => 'abc',
]);
});
}
my API Response
{"name":"Demo Institute 1BBBB","valid_from":"29-12-2017","valid_to":"20-12-2018","mobile":"9999999991","address":"Kolar","city":"Bhopal","state":"Madhya Pradesh","pin":"835553","logo":"","class_code":"940370037"}
this is my code.
In that case if i am passing wrong name Demo Institute 1BBBB. I am not getting error message. only redirect into my plugin page. that image i share here.
please tell me wt is going wrong.
I find my Solution by self. Actually i am not try to make request after login. So that it is being redirect to our plugin page.
But when i tried it after login then i got proper format of Error Message.
I'm trying to make simple unique slugs. The slugs are saved correctly in database, so the function is working. I have problems with making them unique.
I have this rule in TagCreateRequest.php
public function rules()
{
$rules = [
'tag' => 'required|min:3',
'tag_slug' => 'required|alpha_dash|unique:tag,tag_slug,'
];
$rule = 'unique:tag';
$segments = $this->segments();
$id = intval(end($segments));
if ($id != 0) {
$rule .= ',tag_slug,' . $id;
}
$rules['tag_slug'][] = $rule;
return $rules;
}
and this in my store function in the controller
public function store(TagCreateRequest $request)
{
$tag = new Tag();
foreach (array_keys($this->fields) as $field) {
$tag->$field = $request->get($field);
}
$tag->save();
return redirect()->route('tags');
}
The error is about trying to add duplicate value
SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'unique slug' for key 'tags_tag_unique'
Can someone help me to fix this issue?
You can access the id field magically. ID must be the same route parameter used in your route.
If you use id parameter like as Route::put('post/{id}/update') then you can magically access the id parameter inside your form request. Otherwise, if you call the parameter of {post} Route::put('post/{post}/update'), in your form request you must be call $this->post instead of $this->id, ok?
Please try it:
public function rules()
{
$rules = [
'tag' => 'required|min:3'
];
$slugRule = 'required|alpha_dash|unique:tag_slug';
if (! empty($this->id)) {
$slugRule = 'required|alpha_dash|unique:tag_slug,'.$this->id;
}
$rules['tag_slug'] = $slugRule;
return $rules;
}
This FormRequest will work fine on the store() and update() methods if you inject him in both methods.
See it:
// Your store route
Route::post('/post/store', ['as' => 'post.store', 'uses' => 'YourController#store']);
// YourController store method
public function store(NameSpaced\FormRequest $request)
{
// ...
}
// Your update route
Route::post('/post/{id}/update', ['as' => 'post.update', 'uses' => 'YourController#store']);
// YourController update method
public function update(NameSpaced\FormRequest $request)
{
// ...
}
$rules = [
'tag' => 'required|min:3',
'tag_slug' => 'required|alpha_dash|unique:[table name],[column name]'
];
Try this the first is table name and the second is column name that you wanted to unique, write without adding square braces. or you just pass table name like this,
$rules = [
'tag' => 'required|min:3',
'tag_slug' => 'required|alpha_dash|unique:[table name]'
];
laravel auto checks for the column.
I hope it helps.
I would suggest that you automatically generate a new slug whenever you are creating a tag. I got myself in same issues that you have listed here, so i decided on automatically generating whenever i am creating a new item. I used laravel-sluggable. It automatically generates unique slugs.
As per your question, i have defined a unique slug rule in one of my demo apps like this:
public function rules()
{
return [
'name' => 'required|string|max:255',
'slug' => 'required|string|max:255|unique:categories,slug,'.$this->segment(3),
];
}
Please note that $this->segment(3) refers to the id of the model being updated in the backend pages, it can be different in your application.