Validating not all fields in form - Laravel - laravel

In laravel, I have created a form. At the moment, I am working on the validation of the input fields of this form. I ran into a problem when I tried to validate some input fields and others not. For example, mail should be validated but catering_name not (it isn't necessary to fill in this field, its an option)
I have tried all validation methods I could find. I keep getting the same error.
Method Illuminate\Validation\Validator::validatePhone does not exist.
I guess I am missing something.
I have tried:
Validator::make($request->...
$this->validate(request(), [ ...
$request->validate([ ...
Bellow, you will find all the data that should be inputted in the database.
If I remove the validation part, the data got inserted into the database. I think the problem lays with how I try to validate. Thanks for any help.
$this->validate(request(), [
'add_name' => 'required|min:3',
'add_mail' => 'required|email',
'name' => 'required|min:3',
'email' => 'required|email',
'telefone' => 'numeric|phone',
'gsm' => 'numeric|phone',
'event' => 'required|min:3',
'date_start' => 'required|date|after:tomorrow',
'date_end' => 'required|date|after_or_equal:event_date_start',
'location' => 'required|min:3',
'number' => 'required',
]);
$event = new Event;
$event->add_name = request('add_name');
$event->add_mail = request('add_mail');
$event->name = request('name');
$event->email = request('email');
$event->telefone = request('telefone');
$event->gsm = request('gsm');
$event->name = request('name');
$event->date_start = request('date_start');
$event->date_end = request('date_end');
$event->location = request('location');
$event->number = request('number');
$event->catering = request('catering');
$event->catering_name = request('catering_name');
$event->remarks = request('remarks');
$event->status = Event::STATUS_0;
$event->save();

Unfortunately phone is not one of the default validation. You can try something like:
[
'telefone' => 'required|regex:/(01)[0-9]{9}/',
]
You can see the available list of validations given by Laravel here.
There are a wide variety of more complex options depending on how important it is to you.
There are packages for easy plug and play like Laravel-Phone.
You can create your own custom validation using php artisan make:rule phone_number and then editing the new rule made:
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
class PhoneNumber implements Rule
{
/**
* Determine if the validation rule passes.
*
* #param string $attribute
* #param mixed $value
* #return bool
*/
public function passes($attribute, $value)
{
// logic here, most likely some sort of regex.
}
/**
* Get the validation error message.
*
* #return string
*/
public function message()
{
return 'The :attribute must be a valid phone number.';
}
}

Related

Laravel Email validation rules fail only on live server. Possible causes?

I run a customer order input through a simple form validation.
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class CreateOrderWebstoreRequest 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()
{
// Use additional Address valudation rules if Order is not picked up
// dd('debugging halt',request());
$address_rules = [];
if (! request()->is_pickup) {
$address_rules = [
'street' => 'required|min:3|max:100',
'house_number' => 'required|numeric|max:100000',
'house_number_suffix' => 'nullable|max:10',
'postal_code' => 'required|max:10',
'town' => 'required|min:3|max:50',
'country' => 'required|min:2|max:2',
'region' => 'nullable|min:3|max:50',
'owner' => 'nullable|min:3|max:25',
];
}
return array_merge([
'first_name' => 'required|min:3|max:50',
'middle_name' => 'nullable|min:1|max:15',
'last_name' => 'required|min:3|max:50',
'company_name' => 'nullable|min:3|max:50',
'email_address' => 'required|email:rfc,dns|max:50',
'telephone_number' => 'nullable|min:10|max:25',
'description' => 'nullable|min:5|max:250',
], $address_rules);
}
public function messages()
{
return [
];
}
}
The weird thing this has been working without customer having errors for months. But in de last week or so we suddenly got multiple customers complaining that the validation of their email failed (the first 'rule' that triggers the 'email' response from validation.php
The problem is that testing this on a local host we can't reproduce this, only on the live server.
The host is a shared host, dedicated for Laravel apps, currently running Laravel 6.18.15 and PHP 7.4.16. Local is running running Laravel 6.18.10 and PHP 7.4.2 We had some problems before where the host failed to update PHP, but that doesn't seem to be the case here (if even possible)
The problem is that I don't really know how to fix this or even circumvent it. Changing the email validation to
'email' => 'required|regex:/(.+)#(.+)\.(.+)/i'
is giving me an 'IDN Conversion Failed' error.

How can I feature test for a valid duration using Laravel's validation helpers?

I am trying to write a Feature test that checks that the provided end_time does not occur before the provided start_time. I've spent the last few days; I think I've seen about every SO post and still having trouble getting it through my head.
I have been able to successfully test/validate based on a condition:
MyTest.php
/** #test */
public function a_blackout_that_does_not_close_the_location_requires_an_end_time()
{
$table = 'my_table';
$blackout = factory(Blackout::class)->create([
'closed' => 0,
]);
$this->assertDatabaseHas($table, [
'end_time' => $blackout->end_time,
]);
}
MyController.php
public function store(Request $request) {
$attributes = $request->validate([
...
'date' => ['required'],
'closed' => ['nullable'],
'start_time' => ['required_if:closed,0'], // works great
'end_time' => ['required_if:closed,0'], // works great
]);
$blackout = new Blackout($attributes);
...
}
Now I want to make sure that any provided end_time is not before a provided start_time.
MyTest.php
/** #test */
public function a_blackout_end_time_cannot_be_before_the_start_time()
{
$blackout = factory(Blackout::class)->raw([
'closed' => 0,
'start_time' => '08:00:00',
'end_time' => '07:00:00',
]);
$response = $this->post('api/v1/blackouts', $blackout)
->assertSessionHasErrors('end_time');
}
This is what I'm trying to do:
MyController.php
public function store(Request $request) {
$attributes = $request->validate([
...
'date' => ['required'],
'closed' => ['nullable'],
'start_time' => ['required_if:closed,0'],
'end_time' => ['required_if:closed,0|after:start_time'],
]);
$blackout = new Blackout($attributes);
...
}
I keep getting an error in my test:
1) Tests\Feature\BlackoutsTest::a_blackout_end_time_cannot_be_before_the_start_time
Session is missing expected key [errors].
Failed asserting that false is true.
That suggests that it isn't getting the correct error(s) set/returned from the validation. I've tried a bunch of ways to format the start_time and end_time to try to compare.
Further in my controller, I'm able to compare the times relatively easily:
MyController.php
...
$start_time = strtotime($blackout->start_time);
$end_time = strtotime($blackout->end_time);
if ($end_time <= $start_time) {
// Invalid duration
} else {
$blackout->save();
}
...
I am struggling how to validate through the validator so I can leverage the error message(s) better.
Thank you for any suggestions!
EDIT
Thank you #Thomas Van der Veen. Clearly, I'm learning so while looking through the json examples I believe I'm doing a number of things wrong (big surprise).
I've updated my test to now use postJson instead of post. I'm checking for a 422 response and that there should be an end_time error.
/** #test */
public function a_blackout_end_time_cannot_be_before_the_start_time()
{
$blackout = factory(Blackout::class)->raw([
'closed' => 0,
'start_time' => '08:00:00',
'end_time' => '07:00:00',
]);
$response = $this->postJson('api/v1/blackouts', $blackout)
->assertStatus(422)
->assertJsonValidationErrors('end_time');
}
MyController.php
$attributes = $request->validate([
...
'date' => ['required'],
'closed' => ['nullable'],
'start_time' => ['required_if:closed,0|date|'],
'end_time' => ['required_if:closed,0|date|after:start_time'],
]);
...
$blackout->save();
return response()->json(['data' => $blackout], 201);
The test response is:
1) Tests\Feature\BlackoutsTest::a_blackout_end_time_cannot_be_before_the_start_time
Expected status code 422 but received 201.
Failed asserting that false is true.
Which tells me my validation is bad since it's getting through to a 201.
Thank you both for your help. I feel like I'm starting to talk myself in circles & I'm making this way harder than it is.
Thank you both for your suggestions! They both helped get me to this answer. Although I believe I am still writing/executing tests incorrectly
Here is the solution I ended up with, If there is a better way please let me know so I can update my answer.
I ended up writing a custom validation rule and passing in my time_start to compare the two times.
MyTest.php
/** #test */
public function a_blackout_end_time_cannot_be_before_the_start_time()
{
$blackout = factory(Blackout::class)->raw([
'closed' => 0,
'time_start' => '08:00',
'time_end' => '07:00',
]);
$response = $this->postJson('api/v1/blackouts', $blackout)
->assertStatus(422)
->assertJsonValidationErrors('time_end');
}
MyController.php
public function store(Request $request)
{
$attributes = $request->validate([
...
'date' => ['required', new DateFormatRule], // Custom rule
'closed' => 'nullable',
'time_start' => 'required_if:closed,0',
'time_end' => ['required_if:closed,0', new TimeDurationRule($request->time_start)],
]);
$blackout = new Blackout($attributes);
...
}
TimeDurationRule.php
**
* Create a new rule instance.
*
* #return void
*/
public function __construct($time_start)
{
$this->time_start = $time_start;
}
/**
* Determine if the validation rule passes.
*
* #param string $attribute
* #param mixed $value
* #return bool
*/
public function passes($attribute, $value)
{
return strtotime($value) > strtotime($this->time_start);
}
/**
* Get the validation error message.
*
* #return string
*/
public function message()
{
return 'Time duration is invalid.';
}
In my test I am getting a 422 response as expected. I can verify things are working by changing the name of the expected error from time_end to time_end123 and I'll get an error -- saying It was expecting time_end not `time_end123.

Call to a member function first() on null in laravel resource

i try to build a customized response in my resource like this:
class ApplicationResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'sort'=> $this->sort,
'is_seen' => $this->is_seen,
'name' => $this->name,
'position' => $this->position,
'company' => $this->company,
'education' => $this->education,
'degree' => $this->degree,
'phone' => $this->phone,
'university' => $this->university,
'cv_folder_id' => $this->cv_folder_id,
'cv' => route('applications.cvShow', ['candidateCv' => $this->candidate_cv]),
'comments'=> ApplicationCommentsResource::collection($this->applicationComments),
'ratingFields'=> ApplicationRatingsResource::collection($this->applicationRatings()->get()),
'jobPostRatingFields' => JobPostRatingFieldsResource::collection($this->jobPost->jobPostRatingFields),
];
}
}
but i just get errors. the error i get is:
Call to a member function first() on null
i dont know how to build my response that if the collection is empty i dont get any error?
That simply means that you want to retrieve value that does not exist.
You can make simple condition like that:
if(is_null($this->sort)){
return "-";
}
Good luck!
I'm pretty sure the relationship is the problem.
But since there is not enough information, first of all find out in which line the error is, then check the relationships.
For example:
'comments'=> ApplicationCommentsResource::collection($this->applicationComments)
Model Application must have relationship applicationComments

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.

Laravel Validation unique/exists with different database connection

In the documentation, I saw you could set a connection for the unique rule which is great. However, the exists doesn't seem to follow the same logic. Take this for example:
$rules = [
'username' => 'required|max:40|unique:user',
'name' => 'sometimes|required',
'email' => 'required|email|max:255|unique:int.user',
'password' => 'sometimes|required|confirmed|min:6',
'password_current' => 'sometimes|required'
];
The unique rule works GREAT in this instance. It uses my database connection called 'int' and calls the user table. HOWEVER, when the rules are reversed like so:
$rules['email'] = 'required|email|max:255|exists:int.user';
I got this error:
SQLSTATE[42S02]: Base table or view not found: 1146 Table 'int.user'
doesn't exist (SQL: select count(*) as aggregate from int.user where
email = haleybuggs6#gmail.com)
It's trying to call an int.user table instead of using the int database connection.
Is there a reason exists doesn't act the same way as unique? Thanks.
instead of using connection name you can try with straight Database name which is defined in "int" connection. faced similar problem and these way worked for me. like
$rules['email'] = 'required|email|max:255|exists:DB_Name.user';
You can use
'email' => 'exists:mysql2.users|required'
Where mysql2 is second database settings array in the database.php file
Try it.
$rules = [
'username' => 'required|max:40|unique:connection_name.user',
'name' => 'sometimes|required',
'email' => 'required|email|max:255|unique:connection_name.user',
'password' => 'sometimes|required|confirmed|min:6',
'password_current' => 'sometimes|required'
];
Ultimately for Laravel 5.6.* you need to look at an existing instance of the model you are trying to validate, or specify ...
{db_connection_name}.{schema_name}.{table_name}
... to ensure that you are looking at the proper table.
Validation Example
validate it...
<?php
// for instance...
// maybe auth user is in a different db
// = so you cannot validate with your default db connection
$default_user = Auth::user();
// pass the instance in order to allow Validator to qualify the proper connection/name
\App\Validation\User::validate($_POST, $default_user);
User Validation class
<?php
namespace App\Validation;
class User extends Validator
{
/**
* #param \Illuminate\Database\Eloquent\Model|string $mixed
* #param string $default
* #return string
*/
public static function table($mixed,$default='default_connection.app_schema.users_table')
{
if($mixed instanceof \Illuminate\Database\Eloquent\Model){
$table = $mixed->getConnectionName().'.'.$mixed->getTable();
} else {
if (! empty($mixed)) {
$table = $mixed;
} else {
$table = $default;
}
}
return $table;
}
/**
* validation to create a new user
*
* #param array $data
* #param \App\User|string $mixed
* #return array
* #throws \Illuminate\Validation\ValidationException
*/
public static function validate(array $data, $mixed='default_connection.app_schema.users_table'){
return Validator::validate($data,[
'username' => 'required|max:40|unique:'.self::table($mixed),
'name' => 'sometimes|required',
'email' => 'required|email|max:255|unique:'.self::table($mixed),
'password' => 'sometimes|required|confirmed|min:6',
'password_current' => 'sometimes|required'
]);
}
}
$default_connection = 'db_name';
$rules = [
'username' => 'required|max:40|unique:{$default_connection}.user',
'name' => 'sometimes|required',
'email' => 'required|email|max:255|unique:int.user',
'password' => 'sometimes|required|confirmed|min:6',
'password_current' => 'sometimes|required'
];

Resources