Laravel 5.6 Validation on empty fields - laravel

Somehow I feel like this should be a common question, but I can't seem to find a definite answer on that one.
The problem is quite simple:
On validating a form, I would like to exclude the empty non-required fields from the resulting array - and this in order to use the default value set at database level.
Since Laravel is using the ConvertEmptyStringsToNull middleware by default (and I am not so keen on changing that), it means that my empty fields will be converted to 'null' and sent to my database (hence not getting their default value, and actually breaking the query since those fields are not nullable at database level).
$userData = $request->validate([
'email' => 'required|string|email|max:255|unique:users',
'number_of_whatever' => //if this field is empty, I want it stripped out the $userData array - or automatically default to the database default
]);
Any help on how to solve this in the cleanest way possible would be much appreciated! I was thinking about making a custom rule that would exclude the field itself (so I could reuse this validation rule across the project without having to manually do it every time we come across the situation).
Another option would be to set it at Model level - but not so keen on doing that, it seems weird to have to do it there when it's already done at DB level.
Thanks!

i think you can use nullable rule
$userData = $request->validate([
'email' => 'required|string|email|max:255|unique:users',
'number_of_whatever' => 'nullable'
]);

Hey so i've found your issue and also a sort of work around for this. Based on the below example i've replicated and now understood your issue properly, even if the validator allows null values the create method throws an error and does not set default values.
Controller
$validator = Validator::make($data, [
'name' => 'max:255',
'email' => 'max:255',
'password' => 'max:255',
]);
if ($validator->fails()) {
dd('Validator has failed');
}
// This throws an error saying that the fields cannot be null!
User::create($data);
Users Table
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->string('name')->default('Ken');
$table->string('email')->default('ken#stackoverflow.com');
$table->string('password')->default(bcrypt('password'));
$table->rememberToken();
$table->timestamps();
});
The work around i've devised is this, remove all null values from the post request before it hits the validator like so.
Example
$data = ['name' => null, 'email' => null, 'password' => null];
foreach($data as $key => $value)
{
if($value == null)
{
unset($data[$key]);
}
}
The logic here is by removing the fields from the post request that are null, the USER object does not see them as having a value, therefore allowing the tables default values to table place, but if the value is null this is still deemed as a value so the default value will be ignored.
I hope this makes sense.
Result of my full code

Create a FormRequest and filter out the null values using the prepareForValidation method:
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class TestRequest extends FormRequest
{
public function authorize()
{
return true;
}
public function rules()
{
return
[
'username' => 'required|string|email|max:255|unique:users',
'number_of_whatever' => 'sometimes|integer',
];
}
protected function prepareForValidation()
{
if($this->number_of_whatever == null) {
$this->request->remove('number_of_whatever');
}
}
}
You can apply any validation other than 'required' after the 'sometimes' rule and will be applied only if the value isn't null.

Related

The unique Rule validation is not applied

I am using laravel 9.
And I have this very simple validation rule (form request validation):
public function rules()
{
return [
'first_name' => 'required|max:50',
'last_name' => 'required|max:50',
'email' => [
'email',
Rule::unique('unknown_table')->where(function ($query) {
return $query->where('library_id', $this->library->id);
})
],
.......
What I do not understand is why my post is working with this table name "unknown_table", because this table really does not exist. I expected an exception but nothing happens !
Is this rule correctly written ?
I have understood my error. The "email" validation was twice in the rule array ! And it was the last one which was used of course.

SQLSTATE[42S22]:Column not found:1054 Unknown column'tabl_name.id' in'where clause'select*from`tabl_name`where'tabl_name`.`id`=16

my id field in a database named des_id
is there a specific way to change from table_name.id to table_name.des_id?
Route::post('Specific/uri', function (Request $request) {
$request->validate([
'destination_id' => 'required|exists:Database Name.Table Name,des_id',
'from' => 'required|numeric|min:0',
'to' => 'required|numeric|min:0',
]);
$destination_id = Destination::findOrFail($request->destination_id);
$from = $request->from;
$to = $request->to;
dispatch(new testJob($destination_id, $from, $to));
return response()->json([
'status' => true
]);
});
You won't be able to use the find or findOrFail if you don't use the standard naming convention for the identifier as laravel expects which is id. findOrFail expects the primary key to be named "id".
Because your primary key is named "des_id", for you to have the same exception behavior, you should use the where clause then end it with the firstOrFail() method which will offer a similar behavior to findOrFail.
You can learn more about findOrFail and firstOrFail here.
This is how you should perform that call to have the same Not Found Exception handling as findOrFail.
$destination_id = Destination::where('des_id','=',$request->destination_id)->firstOrFail();

Laravel validate if an ID has an instance of the relation

This if kind of a finicky problem, and the title may be a bit confusing, so I'll try and explain as best as I can.
Basically, I am creating a Recipe application, where users can create lists and add recipes to them.
I want the lists to only be able to have one instance of each recipe, so for example, if I add Recipe 10 to List number 1, I don't want the user to be able to add Recipe 10 any more to List 1.
Right now I've only used validation for checking if the meal_id (the id for the recipe) already exists in the recipes table, but with this I am only able to add each recipe to one list.
$this->validate($request, [
'name' => 'required',
'meal_id' => 'required|unique:recipes,meal_id',
'list_id' => 'required'
]);
My relations are simply that a Recipe hasOne RecipeList, and a RecipeList hasMany Recipes, but I'm fairly certain changing that up wont be the fix.
Recipe model
class Recipe extends Model
{
use HasFactory;
protected $fillable = [
'name',
'meal_id',
'list_id'
];
public function lists()
{
return $this->hasOne(RecipeList::class);
}
}
RecipeList model
class RecipeList extends Model
{
use HasFactory;
protected $fillable = [
'name',
'user_id'
];
public function user()
{
return $this->belongsTo(User::class);
}
public function recipes()
{
return $this->hasMany(Recipe::class);
}
}
Recipes table
public function up()
{
Schema::create('recipes', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('meal_id');
$table->unsignedBigInteger('list_id');
$table->timestamps();
});
}
However maybe I need to set up some form of pivot table?
Does anyone have any ideas on some smart way of achieving what I want through the validation, or any other ideas?
You can use Rule and create a custom validation to add your conditions.
Here I assume your condition is to add a unique restriction on both list_id and meal_id
$this->validate($request, [
'name' => 'required',
'list_id' => 'required',
'meal_id' => [
required,
unique:recipes,meal_id,
\Illuminate\Validation\Rule::unique('recipes')->where(function($query) use ($request){
$query->where('list_id', $request->list_id)
->where('meal_id', $request->meal_id);
}),
],
]);
Use firstOrCreate(). Read more here: https://laravel.com/docs/8.x/eloquent#retrieving-or-creating-models
Example:
$recipeData = $request->validate([
'name' => 'required',
'meal_id' => 'required',
'list_id' => 'required'
]);
$recipe = Recipe::firstOrCreate($recipeData);
This will use your 3 fields, name, meal_id & list_id and will either select one or create it if it doesn't exist already.
Alternatively you can do a query to check if it exists. If you want to return an error/message to the user explaining that the new one is a duplicate.
$existingRecipe = Recipe::where([
'meal_id' => $recipeData['meal_id'],
'list_id' => $recipeData['list_id'],
'name' => $recipeData['name']])
->first();
if(!is_null($existingRecipe)) {
//return exists
}
Of course, using rules will be more efficient, depends on what you want your UI to express.
See BABAK ASHRAFI's answer

Laravel Validator - Check custom validation rule after other rules get checked

How are you? Hope you are doing great
I need one help for Laravel Validator, i have created one custom validation rule like below
$validation = Validator::make($request->all(), [
'user_id' => 'required',
'role' => ['required', new RoleExist($request->user_id)],
]);
See i have passed one argument to rule's constructor new RoleExist($request->user_id) but laravel giving me 500 error if i do not pass user_id in the request
The error is
Argument 1 passed to App\Rules\RoleExist::__construct() must be of the type integer, null given
I know user_id is not passed in the request so laravel giving above error, but here my custom rule should be execute after 'user_id' => 'required',
Custom Rule Code
private $userId;
public function __construct(Int $userId)
{
$this->userId= $userId;
}
public function passes($attribute, $value)
{
return empty(\App\User::where('user_id', $this->userId)->where('status', '1')->first());
}
Is there any way to do the same
Thank you in advance

Laravel avoid duplicate entry from model

I'm building a Laravel API. I have a models called Reservations. I want to avoid that a user creates two reservations for the same product and time period.
I have the following:
$reservation = Reservation::firstOrCreate([
'listing_id' => $request->listing_id,
'user_id_from' => $request->user_id_from,
'start_date' => $request->start_date,
'end_date' => $request->end_date,
]);
Edit after comments:
I'm also using validation
$validator = Validator::make($request->all(), [
'listing_id' => 'required|exists:listings,id',
'user_id_from' => 'required|exists:users,id',
'start_date' => 'required|date_format:"Y-m-d"|after:today',
'end_date' => 'required|date_format:"Y-m-d"|after:start_date'
]);
if ($validator->fails()) {
return response()->json(['error' => 'Validation failed'], 403);
}
Validation is working properly.
End of Edit
In my model I have casted the start_date and end_date as dates.
class Reservation extends Model
{
protected $fillable = ['listing_id', 'start_date', 'end_date'];
protected $dates = [
'start_date',
'end_date'
];
....
....
Documentation says:
The firstOrCreate method will attempt to locate a database record
using the given column / value pairs
However I notice that I'm still able to insert entries with the same attributes.
Any idea what I'm doing wrong or suggestions to fix it?
Probably there's a better way than this, but you can create an static method on Reservation to do this, like:
public static function createWithRules($data) {
$exists = $this->where('product_id', $data['product_id'])->whereBetween(*date logic that i don't remember right now*)->first();
if(!$exists) {
* insert logic *
} else {
* product with date exists *
}
}
So you can call Reservation::createWithRules($data)
You can achieve this using Laravel's built in ValidateRequest class. The most simple use-case for this validation, is to call it directly in your store() method like this:
public function store(){
$this->validate($request, [
'listing_id' => 'required|unique,
'start_date' => 'required|unique,
//... and so on
], $this->messages);
$reservation = Reservation::firstOrCreate([
'listing_id' => $request->listing_id,
'user_id_from' => $request->user_id_from,
'start_date' => $request->start_date,
'end_date' => $request->end_date,
]);
}
With this, you're validating users $request with by saying that specified columns are required and that they need to be unique, in order for validation to pass.
In your controller, you can also create messages function to display error messages, if the condition isn't met.
private $messages = [
'listing_id.required' => 'Listing_id is required',
'title.unique' => 'Listing_id already exists',
//... and so on
];
You can also achieve this by creating a new custom validation class:
php artisan make:request StoreReservation
The generated class will be placed in the app/Http/Requests directory. Now, you can add a few validation rules to the rules method:
public function rules()
{
return [
'listing_id' => 'required|unique,
'start_date' => 'required|unique,
//... and so on
];
}
All you need to do now is type-hint the request on your controller method. The incoming form request is validated before the controller method is called, meaning you do not need to clutter your controller with any validation logic:
public function store(StoreReservation $request)
{
// The incoming request is valid...
// Retrieve the validated input data...
$validated = $request->validated();
}
If you have any additional question about this, feel free to ask. Source: Laravel official documentation.

Resources