Related
I'm working on a Laravel 9 project and have created a custom validation rule called ValidModelOwnership which should check that the field a user is trying to add is owned by a model based on some values passed to it.
I've written the rule, but when debugging and outputting $model->toSql() the id is empty?
What am I missing?
My rule:
<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
use Illuminate\Support\Facades\Log;
class ValidModelOwnership implements Rule
{
/**
* The model we're checking
*/
protected $model;
/**
* Array of ownership keys
*/
protected $ownershipKeys;
/**
* Create a new rule instance.
*
* #return void
*/
public function __construct($model, $ownershipKeys)
{
$this->model = $model;
$this->ownershipKeys = $ownershipKeys;
}
/**
* Determine if the validation rule passes.
*
* #param string $attribute
* #param mixed $value
* #return bool
*/
public function passes($attribute, $value)
{
$model = $this->model::query();
$model = $model->where($this->ownershipKeys);
Log::debug($model->toSql());
if (!$model->exists()) {
return false;
}
return true;
}
/**
* Get the validation error message.
*
* #return string
*/
public function message()
{
return "The :attribute field doesn't belong to you and/or your company.";
}
}
And my usage in my controller:
/**
* Store a newly created resource in storage.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function store($company_id, $buyer_id, Request $request)
{
$this->authorize('create', BuyerTier::class);
$validator = Validator::make($request->all(), [
'name' => [
'required',
'string',
Rule::unique(BuyerTier::class)
->where('buyer_id', $buyer_id)
->where('company_id', $company_id)
],
'country_id' => [
'required',
'numeric',
new ValidModelOwnership(Country::class, [
['company_id', 80]
])
],
'product_id' => [
'required',
'numeric',
new ValidModelOwnership(Product::class, [
['company_id', 80]
])
],
'processing_class' => 'required|string',
'is_default' => [
'required',
'boolean',
new ValidDefaultModel(BuyerTier::class, $buyer_id)
],
'is_enabled' => 'required|boolean'
]);
if ($validator->fails()) {
return response()->json([
'message' => 'One or more fields has been missed or is invalid.',
'errors' => $validator->messages(),
], 400);
}
try {
$tier = new BuyerTier;
$tier->user_id = Auth::id();
$tier->company_id = $company_id;
$tier->buyer_id = $buyer_id;
$tier->country_id = $request->input('country_id');
$tier->product_id = $request->input('product_id');
$tier->name = trim($request->input('name'));
$tier->description = $request->input('description') ?? null;
$tier->processing_class = $request->input('processing_class');
$tier->is_default = $request->boolean('is_default');
$tier->is_enabled = $request->boolean('is_enabled');
$tier->save();
return response()->json([
'message' => 'Buyer tier has been created successfully',
'tier' => $tier
], 201);
} catch (\Exception $e) {
return response()->json([
'message' => $e->getMessage()
], 400);
}
}
I've hard-coded my id's to illustrate that even when set statically, it's not passed through:
[2023-01-19 09:40:59] local.DEBUG: select * from products where (company_id = ?) and products.deleted_at is null
Laravel (and most other frameworks) extract out variables when building SQL queries to prevent SQL injection.
So the following eloquent query:
User::where('name', 'Larry');
will become:
SELECT * FROM `users` WHERE `name` = ?
and it will also pass an array of bindings: ['Larry']. When SQL processes the query it replaces replaces the ? with the values in the bindings.
So if you want to see the full query you need to log the SQL and the bindings:
Log::debug($model->toSql());
Log::debug($model->getBindings());
I have a database table structure like the following (in laravel):
user 1-1 profile
partner 1-1 profile
user 1-N department
I want to send a save request (post) and have the user validated in UserRequest and have this class call a ProfileRequest.
Is this possible to do?
Is there any way to perform validations of related models?
Class of User request example:
class UserRequest 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|string',
'lastname' => 'required|string',
'user' => [
'required',
Rule::unique('users')->ignore($this),
],
'email' => 'required|string|email|unique:users',
'password' => 'required|string|confirmed',
'headquarter_id' => 'required'
//Validation of profile
];
}
}
Example of controller User
public function store(AdAszaUserRequest $request)
{
$input = $request->all();
$validated = $request->validated();
$input['password'] = \Hash::make($request['password']);
//
$departmentidList = array_column($input['departments'], 'id');
$AszaUser = AdAszaUser::create($input);
$models = [];
foreach ($input['departments'] as $model) {
$models[] = new AdDepartment($model);
}
///important: this line add departments without validation
$AszaUser->departments()->saveMany($models);
$AszaUser->departments()->sync($departmentidList);
return response($AszaUser, 201);
}
And Request Of deparment:
<?php
namespace App\Http\Requests\AD;
use Illuminate\Foundation\Http\FormRequest;
class AdDepartmentRequest 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|string|unique:ad_departments',
'internal_name' => 'required|string|unique:ad_departments'
];
}
}
Example of Json send in post:
{
"id":2,
"name": "Admin2",
"email": "test#gmail.com",
"lastname": "test",
"user": "test",
"password": "test",
"password_confirmation": "test",
"headquarter_id": 1,
"lastname":"test",
"remember_token": "1",
"email_verified_at": "test",
"headquarter": {
"id": 1,
"name": "ASZA ZARAGOZA",
"description": "Sede en Zaragoza",
},
"departments": [
{
"id": 1,
"name": "Intérpretes",
"internal_name": "Interprete",
"description": "Departamento de Intérpretes",
"display_id": "01",
"pivot": {
"user_id": 1,
"department_id": 1
}
},
{
"id": 10,
"name": "Psicología"
}
]
}
Can I call the DepartmentRequest to validate the elements passed in the department array?
UPDATE: 1
I don't think it is necessary, but of course it is possible
public function store(AdAszaUserRequest $request)
{
$input = $request->all();
$validated = $request->validated();
$input['password'] = \Hash::make($request['password']);
//
$departmentidList = array_column($input['departments'], 'id');
$AszaUser = AdAszaUser::create($input);
$models = [];
foreach ($input['departments'] as $model) {
/** To check validation for single item */
$validator = Validator::make($model, (new StoreEventRequest)->rules());
if (!$validator->fails()) {
$models[] = new AdDepartment($model);
} else {
/** Something wrong */
/** $errors = $validator->errors(); */
}
}
/** To check validation for array of data
$validator = Validator::make($request->only(['departments']), collect(array_map(function ($rules, $field): array {
return ['departments.*.' . $field => $rules];
}, (new StoreEventRequest)->rules()))
->collapse()
->toArray()); */
/**
* And then do what you want to do with this object
* $errors = $validator->errors();
*
if ($validator->fails()) {
return redirect('some_url')
->withErrors($validator);
} */
$AszaUser->departments()->saveMany($models);
$AszaUser->departments()->sync($departmentidList);
return response($AszaUser, 201);
}
For more information see documentation https://laravel.com/docs/6.x/validation#manually-creating-validators
UPDATE: 2
If you need to separate your request classes, you also can do it like so
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 array_merge([
'name' => 'required|string',
'lastname' => 'required|string',
'user' => [
'required',
Rule::unique('users')->ignore($this),
],
'email' => 'required|string|email|unique:users',
'password' => 'required|string|confirmed',
'headquarter_id' => 'required'
//Validation of profile
/** Validate of departments */
'departments' => 'nullable|array',
], collect(array_map(function ($rules, $field): array {
return ['departments.*.' . $field => $rules];
}, (new StoreEventRequest)->rules()))
->collapse()
->toArray())
->toArray();
}
}
Yes you can do it like this
public function rules() {
return [
'name' => 'required|string',
'lastname' => 'required|string',
'user' => [ 'required', Rule::unique('users')->ignore($this), ],
'email' => 'required|string|email|unique:users',
'password' => 'required|string|confirmed',
'headquarter_id' => 'required',
//Validation of profile
'profile.some_field' => 'required',
//For array of objects
'profile.*.some_field' => 'required',
];
}
I have a select list, where the first option is disabled, that's way when the user does not choose an valid option, the result of the select list will not be in the request.
In the validation, the field required, if the value of an other field is for example : 1, in case not 1, the field is not required.
The code:
'city_id' => [
'required',
'integer',
Rule::in(City::availableCities()),
],
'district_id' => new DistrictValidation(request('city_id')),
How I can do that, the district_id throw the validation every time, regardless of, it is in the request, or not.
Thanks for answers,
Update:
Maybe you see clearly, if the DistrictValidation rule is here:
class DistrictValidation implements Rule
{
protected $city;
private $messages;
/**
* Create a new rule instance.
*
* #param $cityId
*/
public function __construct($cityId)
{
$this->city = City::find($cityId);
}
/**
* Determine if the validation rule passes.
*
* #param string $attribute
* #param mixed $value
* #return bool
*/
public function passes($attribute, $value)
{
dd('here');
if (!$this->city) {
return false;
}
if (!$this->city->hasDistrict) {
return true;
}
$validator = Validator::make([$attribute => $value], [
$attribute => [
'required',
'integer',
Rule::in(District::availableDistricts()),
]
]);
$this->messages = $validator->messages();
return $validator->passes();
}
/**
* Get the validation error message.
*
* #return string
*/
public function message()
{
return optional($this->messages)->first('district_id');
}
}
You can use required_if condition defined in laravel validation
Here is the link to the proper documentaion Laravel Validation
Validator::make($data, [
'city_id' => [
'required',
'integer',
Rule::in(City::availableCities()),
],
'district_id'=>[
'required_with:city_id,',
]
]);
Try this:
'city_id' => [
'nullable',
'numeric',
Rule::in(City::availableCities())
],
'district_id' => new DistrictValidation(request('city_id')),
try this :
$myValidations = [
"city_id" => [
"required",
"integer"
]
]
// if city_id exists in availableCities so add some rules
if(collect(city::availableCities)->contains(request("city_id"))){
$myValidations["district_id"] = new DistrictValidation(request('city_id'))
}
// validate request fields with $myValidations variable
try using the required_if validation where the field under validation must be present and not empty if the anotherfield field is equal to any value.
required_if:field,value,...
use it like:
$request->validate([
'city_id' => 'required|integer|Rule::in(City::availableCities())',
'district_id' => 'required_if:city_id,1',
]);
try to read more laravel validation here
I'm trying to create customized messages for validation in Laravel 5. Here is what I have tried so far:
$messages = [
'required' => 'Harap bagian :attribute di isi.',
'unique' => ':attribute sudah digunakan',
];
$validator = Validator::make($request->all(), [
'username' => array('required','unique:Userlogin,username'),
'password' => 'required',
'email' => array('required','unique:Userlogin,email'),$messages
]);
if ($validator->fails()) {
return redirect('/')
->withErrors($validator) // send back all errors to the login form
->withInput();
} else {
return redirect('/')
->with('status', 'Kami sudah mengirimkan email, silahkan di konfirmasi');
}
But it's not working. The message is still the same as the default one. How can I fix this, so that I can use my custom messages?
Laravel 5.7.*
Also You can try something like this. For me is the easiest way to make custom messages in methods when you want to validate requests:
public function store()
{
request()->validate([
'file' => 'required',
'type' => 'required'
],
[
'file.required' => 'You have to choose the file!',
'type.required' => 'You have to choose type of the file!'
]);
}
If you use $this->validate() simplest one, then you should write code something like this..
$rules = [
'name' => 'required',
'email' => 'required|email',
'message' => 'required|max:250',
];
$customMessages = [
'required' => 'The :attribute field is required.'
];
$this->validate($request, $rules, $customMessages);
You can provide custom message like :
$rules = array(
'URL' => 'required|url'
);
$messages = array(
'URL.required' => 'URL is required.'
);
$validator = Validator::make( $request->all(), $rules, $messages );
if ( $validator->fails() )
{
return [
'success' => 0,
'message' => $validator->errors()->first()
];
}
or
The way you have tried, you missed Validator::replacer(), to replace the :variable
Validator::replacer('custom_validation_rule', function($message, $attribute, $rule, $parameters){
return str_replace(':foo', $parameters[0], $message);
});
You can read more from here and replacer from here
For Laravel 8.x, 7.x, 6.x
With the custom rule defined, you might use it in your controller validation like so :
$validatedData = $request->validate([
'f_name' => 'required|min:8',
'l_name' => 'required',
],
[
'f_name.required'=> 'Your First Name is Required', // custom message
'f_name.min'=> 'First Name Should be Minimum of 8 Character', // custom message
'l_name.required'=> 'Your Last Name is Required' // custom message
]
);
For localization you can use :
['f_name.required'=> trans('user.your first name is required'],
Hope this helps...
$rules = [
'username' => 'required,unique:Userlogin,username',
'password' => 'required',
'email' => 'required,unique:Userlogin,email'
];
$messages = [
'required' => 'The :attribute field is required.',
'unique' => ':attribute is already used'
];
$request->validate($rules,$messages);
//only if validation success code below will be executed
//Here is the shortest way of doing it.
$request->validate([
'username' => 'required|unique:Userlogin,username',
'password' => 'required',
'email' => 'required|unique:Userlogin,email'
],
[
'required' => 'The :attribute field is required.',
'unique' => ':attribute is already used'
]);
//The code below will be executed only if validation is correct.
run below command to create a custom rule on Laravel
ı assuming that name is CustomRule
php artisan make:rule CustomRule
and as a result, the command was created such as PHP code
if required keyword hasn't on Rules,That rule will not work
<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
class CustomRule implements Rule
{
/**
* Create a new rule instance.
*
* #return void
*/
public function __construct()
{
//
}
/**
* Determine if the validation rule passes.
*
* #param string $attribute
* #param mixed $value
* #return bool
*/
public function passes($attribute, $value)
{
//return true or false
}
/**
* Get the validation error message.
*
* #return string
*/
public function message()
{
return 'The validation error message.';
}
}
and came time using that
first, we should create a request class if we have not
php artisan make:request CustomRequest
CustomRequest.php
<?php
namespace App\Http\Requests\Payment;
use App\Rules\CustomRule;
use Illuminate\Foundation\Http\FormRequest;
class CustomRequest extends FormRequest
{
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules(): array
{
return [
'custom' => ['required', new CustomRule()],
];
}
/**
* #return array|string[]
*/
public function messages(): array
{
return [
'custom.required' => ':attribute can not be empty.',
];
}
}
and on your controller, you should inject custom requests to the controller
your controller method
class FooController
{
public function bar(CustomRequest $request)
{
}
}
You can also use the methods setAttributeNames() and setCustomMessages(),
like this:
$validation = Validator::make($this->input, static::$rules);
$attributeNames = array(
'email' => 'E-mail',
'password' => 'Password'
);
$messages = [
'email.exists' => 'No user was found with this e-mail address'
];
$validation->setAttributeNames($attributeNames);
$validation->setCustomMessages($messages);
For those who didn't get this issue resolve (tested on Laravel 8.x):
$validated = Validator::make($request->all(),[
'code' => 'required|numeric'
],
[
'code.required'=> 'Code is Required', // custom message
'code.numeric'=> 'Code must be Number', // custom message
]
);
//Check the validation
if ($validated->fails())
{
return $validated->errors();
}
$rules = [
'name' => 'required',
'email' => 'required|email',
'message' => 'required|max:250',
];
$customMessages = [
'required' => 'The :attribute field is required.',
'max' => 'The :attribute field is may not be greater than :max.'
];
$this->validate($request, $rules, $customMessages);
In the case you are using Request as a separate file:
public function rules()
{
return [
'preparation_method' => 'required|string',
];
}
public function messages()
{
return [
'preparation_method.required' => 'Description is required',
];
}
Tested out in Laravel 6+
you can customise the message for different scenarios based on the request.
Just return a different message with a conditional.
<?php
namespace App\Rules;
use App\Helpers\QueryBuilderHelper;
use App\Models\Product;
use Illuminate\Contracts\Validation\Rule;
class ProductIsUnique implements Rule
{
private array $attributes;
private bool $hasAttributes;
/**
* Create a new rule instance.
*
* #return void
*/
public function __construct(array $attributes)
{
$this->attributes = $attributes;
$this->hasAttributes = true;
}
/**
* Determine if the validation rule passes.
*
* #param string $attribute
* #param mixed $value
* #return bool
*/
public function passes($attribute, $value)
{
$brandAttributeOptions = collect($this->attributes['relationships']['brand-attribute-options']['data'])->pluck('id');
$query = Product::query();
$query->when($brandAttributeOptions->isEmpty(), function ($query) use ($value) {
$query->where('name', $value);
$this->hasAttributes = false;
});
return !$query->exists();
}
/**
* Get the validation error message.
*
* #return string
*/
public function message()
{
return ($this->hasAttributes) ? 'The Selected attributes & Product Name are not unique' : 'Product Name is not unique';
}
}
Laravel 10.x
If you are using Form Requests, add another method called messages(): array in your request.
class YourRequest extends FormRequest
{
public function rules(): array
{
return [
'name' => 'required',
'email' => 'required|email',
...
];
}
//Add the following method
public function messages(): array
{
return [
'email.required' => 'Custom message for Email Required',
];
}
}
Then the message will be displayed automatically once the request is send from the form.
I have a problem while updating data to the database in laravel. Normally the data gets inserted to the database with store function(POST request). But the update POST request doesn't make a update to the database. And my code is
routes.php
Route::resource('Customer','CustomerDetailController');
Route::get('/', function () {
return view('welcome');
});
Model
CustomerDetail.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class CustomerDetail extends Model
{
//
public $table = "customer_details";
protected $fillable = [
'Name', 'Contact_Number','Address','NoOfCans','Price'
];
}
Controller
CustomerDetailController.php
<?php
namespace App\Http\Controllers;
use App\CustomerDetail;
use Illuminate\Http\Request;
use App\Http\Requests;
class CustomerDetailController extends Controller
{
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
//
$Customers=CustomerDetail::all();
if($Customers){
$response = [
'msg' => 'Customer Found',
'Customer' => $Customers
];
return response()->json($response, 201);
}
else {
$response=[
'msg'=>'No Customer Found',
];
return response()->json($response, 404);
}
}
/**
* Show the form for creating a new resource.
*
* #return \Illuminate\Http\Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function store(Request $request)
{
//
$this->validate($request, [
'Name' => 'required',
'Contact_Number' => 'required',
'Address' => 'required',
'NoOfCans' => 'required',
'Price' => 'required',
]);
$Name = $request->input('Name');
$Contact_Number = $request->input('Contact_Number');
$Address = $request->input('Address');
$NoOfCans = $request->input('NoOfCans');
$Price = $request->input('Price');
$CustomerDetail = new CustomerDetail([
'Name' => $Name,
'Contact_Number' => $Contact_Number,
'Address' => $Address,
'NoOfCans' => $NoOfCans,
'Price' => $Price
]);
if ($CustomerDetail->save()) {
$response = [
'msg' => 'Customer Created',
'Customer' => $CustomerDetail
];
return response()->json($response, 201);
}
$response = [
'msg' => 'An Error Occured',
];
return response()->json($response, 502);
}
/**
* Display the specified resource.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function show($id)
{
//
$Customer=CustomerDetail::find($id);
if($Customer){
$response = [
'msg' => 'Customer Found',
'Customer' => $Customer
];
return response()->json($response, 201);
}
else {
$response=[
'msg'=>'Customer Not Found',
];
return response()->json($response, 404);
}
}
/**
* Show the form for editing the specified resource.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function edit($id)
{
//
}
/**
* Update the specified resource in storage.
*
* #param \Illuminate\Http\Request $request
* #param int $id
* #return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
//
$this->validate($request, [
'Name' => 'required',
'Contact_Number' => 'required',
'Address' => 'required',
'NoOfCans' => 'required',
'Price' => 'required',
]);
$CustomerUpdate = CustomerDetail::find($id);
$CustomerUpdate->Name = $request->input('Name');
$CustomerUpdate->Contact_Number = $request->input('Contact_Number');
$CustomerUpdate->Address = $request->input('Address');
$CustomerUpdate->NoOfCans = $request->input('NoOfCans');
$CustomerUpdate->Price = $request->input('Price');
if ($CustomerUpdate->save()) {
$response = [
'msg' => 'Customer Updated',
'Customer' => $CustomerUpdate
];
return response()->json($response, 201);
}
$response = [
'msg' => 'An Error Occured',
];
return response()->json($response, 502);
}
}
And the data gets inserted when i make a post request for store as
{
"Name":"MRK",
"Contact_Number":"1234567890",
"Address":"newyork",
"NoOfCans":"3",
"Price":"20"
}
with Output
{
"msg": "Customer Created",
"Customer": {
"Name": "MRK",
"Contact_Number": "1234567890",
"Address": "newyork",
"NoOfCans": "3",
"Price": "20",
"updated_at": "2016-11-20 17:17:55",
"created_at": "2016-11-20 17:17:55",
"id": 4
}
}
But when I try to Update the same data with PUT or PATCH request
Input
{
"Name":"MRK",
"Contact_Number":"9876543210",
"Address":"Chennai",
"NoOfCans":"2",
"Price":"20"
}
I get the JSON Response as Updated but I don't see the values got Updated in the database
{
"msg": "Customer Updated",
"Customer": {
"ID": 4,
"created_at": "2016-11-20 17:17:55",
"updated_at": "2016-11-20 17:20:44",
"Name": "MRK",
"Contact_Number": "9876543210",
"Address": "Chennai",
"NoOfCans": "2",
"Price": "20"
}
}
The database contains the values which are originally inserted at first. No updated values are inserted into the database
enter image description here
Update code
public function update(Request $request, $id)
{
//
$this->validate($request, [
'Name' => 'required',
'Contact_Number' => 'required',
'Address' => 'required',
'NoOfCans' => 'required',
'Price' => 'required',
]);
$CustomerUpdate = CustomerDetail::find($id);
$CustomerUpdate->Name = $request->input('Name');
$CustomerUpdate->Contact_Number = $request->input('Contact_Number');
$CustomerUpdate->Address = $request->input('Address');
$CustomerUpdate->NoOfCans = $request->input('NoOfCans');
$CustomerUpdate->Price = $request->input('Price');
if ($CustomerUpdate->save()) {
$response = [
'msg' => 'Customer Updated',
'Customer' => $CustomerUpdate
];
return response()->json($response, 201);
}
$response = [
'msg' => 'An Error Occured',
];
return response()->json($response, 502);
}
The problem is with the model. I have not mentioned the primary key in my model so the database is not updated
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class CustomerDetail extends Model
{
//
public $table = "customer_details";
Protected $primaryKey = "ID";
protected $fillable = [
'Name', 'Contact_Number','Address','NoOfCans','Price'
];
}