How to write validation rule with resource in Laravel? - php

I use Laravel 8 with Resource to define routes in api.php and DestroyProductRequest in my controller:
Route::resources([
'product' => ProductController::class,
]);
In the controller:
public function destroy(DestroyProductRequest $request, Product $product) {
$product->delete();
return Response::HTTP_OK;
}
In the DestroyProductRequest.php:
public function rules() {
return [
'id' => 'required|integer|exists:products,id',
];
}
Now the key is the Route::resource convert the incoming id from the url into a model. But now how can I write the correct rule in the the rules() function?
Now it sais id: ["The id field is required."] in the error response.
Any idea?

By default Laravel does not validate the route parameters.
You have add custom logic in order to work
public function rules()
{
return [
'product' => 'required|integer|exists:products,id',
];
}
protected function validationData()
{
// Add the Route parameters to you data under validation
return array_merge($this->all(),$this->route()->parameters());
}
In a normal use case, you wouldn't validate route request. Laravel would return a 404 Not Found exception. Which is a standard and appropriate response.

Since you are using the resource, the route parameter is implicit biding key (product id), So Laravel bind the passed id as product implicitly. Change your Rules code segment as following,
public function rules() {
return [
'product' => 'required|integer|exists:products,id',
];
}
public function destroy(Product $product) {
$product->delete();
return Response::HTTP_OK;
}
If you wanna add ACL
if ($user->can('destroy', $product)) {
$product->delete();
return Response::HTTP_OK;
}

use Validator for your question
use Illuminate\Support\Facades\Validator;
public function destroy(DestroyProductRequest $request, Product $product) {
$rules = [
'id' => 'required|integer|exists:products,id',
];
$messages = [
'id.required' => 'The id field is required.',
'id.integer' => 'The id field need to be integer.',
'id.exists' => 'The id field is exists.',
];
Validator::make($request->all(), $rules, $messages)->validate();
$product->delete();
return Response::HTTP_OK;
}
Read more here: https://laravel.com/docs/8.x/validation

Related

Insert record after Laravel form validation

I'm trying a Laravel 5.8 request validation. I managed to return errors and display them to my view. The problem is when I try to not trigger any validation rule, for whatever reason I cannot insert records into my table.
Error
Too few arguments to function
App\Http\Requests\FieldRequest::Illuminate\Foundation\Providers{closure}(),
0 passed and exactly 1 expected
Controller
class FormController extends Controller
{
public function create()
{
return view('create');
}
public function store(FieldRequest $req)
{
$validate_data = $req->validate();
Form::create($validate_data);
return redirect()->back()->with('message', 'Success!');
}
}
FormRequest
class FieldRequest extends FormRequest
{
public function authorize()
{
return true;
}
public function rules()
{
return [
'item_name' => 'bail|required|max:255',
'sku_no' => 'required|alpha_num',
'price' => 'required|numeric',
];
}
public function messages()
{
return [
'item_name.required' => 'An Item Name is required',
'sku_no.required' => 'An SKU NO is required',
'price.required' => 'The price is required',
];
}
}
I'm expecting something to be inserted in my table. Do I need to perform the validation in my controller or not to achieve this? Thanks in advance!
public function store(FieldRequest $req)
{
$data = $req->all();
Form::create($data);
return redirect()->back()->with('message', 'Success!');
}
when you are working with form request you no need to use validate() function because your request goes in form request to validate your data then it will store records

How to use Custom and Default Validation Together?

I have custom validation for validating data. The custom validation doesn't have unique rule as I need to ignore this on update, therefore I am using unique rule on store() method. But this is ignored, and it only works if I change the custom validation with default validation.
It works if I have the following:
public function store(Request $request)
{
if (!$this->user instanceof Employee) {
return response()->json(['error' => 'Unauthorized'], 401);
}
$request->validate([
'name' => 'required|max:50|unique:centers'
]);
$center = Center::create($request->all());
return response()->json($center, 201);
}
But this doesn't work if I change the method signature to the following:
public function store(CustomValidation $request)
How can I use both together? I do not want to move the custom validation code inside the method as I have to repeat msyelf for update method then.
I think it will help you
use Illuminate\Contracts\Validation\Rule;
class CowbellValidationRule implements Rule
{
public function passes($attribute, $value)
{
return $value > 10;
}
public function message()
{
return ':attribute needs more cowbell!';
}
}
and
public function store()
{
// Validation message would be "song needs more cowbell!"
$this->validate(request(), [
'song' => [new CowbellValidationRule]
]);
}
or
public function store()
{
$this->validate(request(), [
'song' => [function ($attribute, $value, $fail) {
if ($value <= 10) {
$fail(':attribute needs more cowbell!');
}
}]
]);
}

Override Backpack validation roles

What I did:
I am trying to override backpack form validation roles (update request).
UserUpdateCrudRequest.php
use App\Http\Requests\Backpack\PermissionManager\UserUpdateCrudRequest as UpdateRequest;
class UserUpdateCrudRequest extends \Backpack\PermissionManager\app\Http\Requests\UserUpdateCrudRequest
{
function __construct()
{
parent::__construct();
}
public function authorize()
{
// only allow updates if the user is logged in
return \Auth::check();
}
public function rules()
{
$rules = [
'name' => 'required',
'password' => 'confirmed',
];
return $rules;
}
}
app/Http/Controllers/Admin/Backpack/PermissionManager/UserCrudController.php
public function update(UpdateRequest $request)
{
//code
}
What I expected to happen:
The email field is mandatory on create , and not mandatory on update.
What happened:
ErrorException in UserCrudController.php line 18:
Declaration of App\Http\Controllers\Admin\Backpack\PermissionManager\UserCrudController::update() should be compatible with Backpack\PermissionManager\app\Http\Controllers\UserCrudController::update(Backpack\PermissionManager\app\Http\Requests\UserUpdateCrudRequest $request)
If I'm right,
inside UserCrudController you have,
use Backpack\PermissionManager\app\Http\Requests\UserStoreCrudRequest as StoreRequest;
use Backpack\PermissionManager\app\Http\Requests\UserUpdateCrudRequest as UpdateRequest;
If you want to make the email field not mandatory on update you have to edit the UserUpdateCrudRequest.php inside your-project/vendor/backpack/permissionmanager/src/app/Http/Requests and remove the line
'email' => 'required',

Yii2 dataprovider with manytomany relation

I try to build a grid view with many-to-many relations. So I need a query for the ActiveDataProvider .
I have a table 'ressource', a table 'type' and between them a table 'historique'.
I have the good relation in my models but I don't know how to create the dataProvider.
In my model Ressource :
public function getHistorique()
{
return $this->hasMany(Historique::className(), ['idType' => 'idType']);
}
public function getType()
{
return $this->hasMany(Type::className(), ['idType' => 'idType'])
->viaTable(Historique::className(), ['idRessource' => 'idRessource']);
}
In my model Historique :
public function getType()
{
return $this->hasOne(Type::className(), ['idType' => 'idType']);
}
public function getRessource()
{
return $this->hasOne(Ressource::className(), ['idRessource' => 'idRessource']);
}
and finally in my model Type :
public function getHistorique()
{
return $this->hasMany(Historique::className(), ['idType' => 'idType']);
}
public function getRessource()
{
return $this->hasMany(Ressource::className(), ['idRessource' => 'idRessource'])
->viaTable(Historique::className(), ['idType' => 'idType']);
}
So in the Controller (in fact my ModelSearch), I want to have ressources with type from the table historique. I don't know what I have to add after
Ressource::find();
I think you use RessourceSearch()->search() method. So inside it you have something like this:
$query = Ressource::find();
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
if (!($this->load($params) && $this->validate())) {
return $dataProvider;
}
// Here is list of searchable fields of your model.
$query->andFilterWhere(['like', 'username', $this->username])
->andFilterWhere(['like', 'auth_key', $this->auth_key])
return $dataProvider;
So, basically, you need to add additional Where you your query and force to join relation table. You can do that using joinWith method to join additional relation and andFilterWhere using table.field notation for adding filter parameters. For example:
$query = Ressource::find();
$query->joinWith(['historique', 'type']);
$query->andFilterWhere(['like', 'type.type', $this->type]);
$query->andFilterWhere(['like', 'historique.historique_field', $this->historique_field]);
Also do not forget to add rules for additional filters in your search model. For example above, you should add to your rules() array something like that:
public function rules()
{
return [
// here add attributes rules from Ressource model
[['historique_field', 'type'], 'safe'],
];
}
You can use any additional validation rules for that fields

Validating a Unique Slug on Update in Laravel 5

I currently have a model that has a text field and a slug field.
I validate that the slug is unique in my form request class:
public function rules()
{
return [
'name' => 'required|min:3',
'slug' => 'required|alpha_dash|unique:questions'
];
}
This works fine on create and properly denies the creation of duplicate slugs. However on my update method, it won't let me save a record because the slug already exists. Of course the slug does exist, but it exists on the record being edited, so I would like to continue to allow it to be saved. However, it should not be able to be changed to a slug on ANOTHER record.
Here's what my update ArticlesController method looks like:
public function update(Article $article, ArticleRequest $request)
{
$article->update($request->all());
return redirect('articles');
}
Is there a way to make this work in L5?
Try to modify your rule like following(in form request class):
public function rules()
{
return [
'name' => 'required,min:3',
'slug' => 'required|alpha_dash|unique:categories,slug,'.$this->id')
];
}
It works for me.
In unique rule you may specify id you want to ignore.
You can create 2 separate request (one for create and one for update), but you can do it also this way checking if if is set(I assume your update url looks like /questions/2 ):
public function rules()
{
$rules = [
'name' => 'required|min:3',
'slug' => ['required', 'alpha_dash']
];
$rule = 'unique:questions';
$segments = $this->segments();
$id = intval(end($segments));
if ($id != 0) {
$rule .= ',slug,' . $id;
}
$rules['slug'][] = $rule;
return $rules;
}
}
If you must have the ability to update a slug, projects I've worked on usually require it is not editable after creation, then you can use laravel's built in rule to ignore a certain record on the table by primary key.
$rules['slug'] = "required|unique:questions,slug,{$id}";
http://laravel.com/docs/5.0/validation
see "Forcing a unique rule to ignore a given ID"
In EditArticleRequest:
public function $rules ()
{
$id = $this->id;
return [
'name' => 'required|min:3',
'slug' => "required|alpha_dash|unique:articles,slug,$id",
];
}
Here is how I do it in Laravel 5.3 in details:
1- Create a new Form Request class by executing the next command in your terminal:
php artisan make:request ArticleFormRequest
Where ArticleFormRequest is the name of the form request class. This command will create a file called ArticleFormRequest.php in app/Http/Requests directory.
2- Open that created file and remove its content then place the next content in it:
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use App\Article;
class ArticleFormRequest extends FormRequest
{
protected $rules = [
'name' => 'required|min:3',
'slug' => 'required|alpha_dash|unique:articles,slug',
];
// protected $user; // in case you want the current authenticated user
protected $request_method;
protected $id;
public function __construct(Request $request)
{
// $request->user() returns an instance of the authenticated user
// $this->user = $request->user(); // in case you want the current authenticated user
// $request->method() returns method of the request (GET, POST, PUT, DELETE, ...)
$this->request_method = strtoupper($request->method());
// segments(): Returns an array containing all of the segments for the request path
// it is important to assign the returned "segments" array to a variable first before using it, otherwise an error will occur
$segments = $request->segments();
// note this way will be valid only if "id" of the element is the last segment
$this->id = end($segments);
}
/**
* 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()
{
$rules = $this->rules;
if ($this->request_method == "POST") {
// do nothing..
} elseif (in_array($this->request_method, ["PUT", "PATCH"])) {
$article = Article::find($this->id);
if ($article) {
// forcing a unique rule to ignore a given id | https://laravel.com/docs/5.3/validation
$rules["slug"] = [
"required",
"alpha_dash",
Rule::unique("articles", "slug")->ignore($article->id, "id"),
];
// this is also can be used
// $rules['slug'] = "required|alpha_dash|unique:articles,slug,$article->id,id";
}
}
return $rules;
}
}
3- In your controller, you can use that ArticleFormRequest in store() and update() methods like this:
<?php
namespace App\Http\Controllers;
use App\Http\Requests\ArticleFormRequest;
class ArticlesController extends Controller
{
public function store(ArticleFormRequest $request)
{
// your code here..
}
public function update(ArticleFormRequest $request, $id)
{
// Your code here..
}
}
As already mentioned you can use the ignore feature in the validator functionality.
Just reference the id of the item you wish to ignore and make sure that when you update you use a patch request!
See more info here! http://laravel.com/docs/5.0/validation#rule-unique
protected $rules = [
'name' => 'required|min:3',
'slug' => 'required|alpha_dash|unique:questions'
];
public function rules()
{
$rules = $this->rules;
if ($this->isMethod('patch'))
{
$id = $this->articles;
$rules['slug'] = $rules['slug'].',slug,'.$id;
}
return $rules;
}

Categories