Laravel Image Validation - laravel

I am writing a vue component where users can upload a profile picture that I want to validate on the backend using Laravel's built-in image validation. I use an axios post call with a JSON object that has the following key value pair.
When I go the backend I send it through a custom Laravel request and put the following validation rule on it.
<?php
namespace App\Http\Requests;
use App\Department;
use Illuminate\Validation\Rule;
use Illuminate\Foundation\Http\FormRequest;
class ChangePersonalInfo extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
if(auth()->user()) return true;
else return false;
}
/**
* Convert certain request parameters before validation
*
* #return void
*/
protected function prepareForValidation()
{
$this->merge([
'assigned_states' => explode(',', $this->assigned_states),
'customer_price_level' => json_encode($this->customer_price_level),
'customer_price_list' => json_encode($this->customer_price_list),
'customer_type' => json_encode($this->customer_type),
'email_notifications' => filter_var($this->email_notifications, FILTER_VALIDATE_BOOLEAN),
'employee' => filter_var($this->employee, FILTER_VALIDATE_BOOLEAN),
'read_notification_terms' => filter_var($this->read_notification_terms, FILTER_VALIDATE_BOOLEAN),
'reviewed' => filter_var($this->reviewed, FILTER_VALIDATE_BOOLEAN),
'text_notifications' => filter_var($this->text_notifications, FILTER_VALIDATE_BOOLEAN),
'verified' => filter_var($this->verified, FILTER_VALIDATE_BOOLEAN),
'termsCode' => json_encode($this->termsCode)
]);
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
$departments = Department::select(['department_name'])->pluck('department_name')->toArray();
return [
'api_token' => 'string|alpha_num',
'assigned_states' => 'array|nullable',
'assigned_states_description' => 'string|nullable',
'avatar_file' => 'string|nullable',
'bc_details' => 'string|json|nullable',
'bc_guid' => 'string|nullable',
'company_address_id' => 'numeric|integer|nullable',
'company_name' => 'string|nullable',
'company_website' => 'string|nullable',
'created_at' => 'string|nullable',
'customer_price_level' => 'json',
'customer_price_list' => 'json',
'customer_type' => 'json',
'customer_price_level.id' => 'numeric|integer',
'customer_price_list.id' => 'numeric|integer',
'customer_type.id' => 'numeric|integer',
'department' => ['string', 'nullable', Rule::in($departments)],
'email' => 'required|email',
'email_notifications' => 'boolean',
'employee' => 'boolean',
'first_name' => 'string|nullable|max:30',
'id' => 'string|integer|alpha_num',
'imageExtension' => 'string|nullable',
'imageFile' => 'sometimes|nullable|image|max:2048',
'last_name' => 'string|nullable|max:30',
'marketing_preferences' => 'string|nullable',
'notes' => 'string|nullable',
'price_level_id' => 'required|numeric|integer',
'price_list_id' => 'numeric|integer|nullable',
'primary_address_id' => 'numeric|integer|nullable',
'pimary_billing_address' => 'numeric|integer|nullable',
'primary_phone_number_id' => 'numeric|integer|nullable',
'read_notification_terms' => 'boolean',
'reviewed' => 'boolean',
'statusName' => 'string',
'status_id' => 'numeric|integer|nullable',
'termsCode' => 'json',
'termsCode.id' => 'numeric|integer',
'terms_code_id' => 'numeric|integer|required',
'text_notifications' => 'boolean',
'timezone' => 'timezone',
'title' => 'string|nullable',
'type_id' => 'required|numeric|integer',
'updated_at' => 'string|nullable',
'username' => 'string|nullable',
'verificationStatus' => 'string',
'verification_token' => 'string|nullable',
'verified' => 'boolean'
];
}
}
I'm getting a 500 error back with this message.
{"message":"The given data was invalid.","errors":{"imageFile":["The image file must be an image."]}}
Could anyone help me know why it appears to be an image file on the frontend but fails to meet the backend validation?

check this format
'imageFile' => 'required|file|max:512|mimes:jpg,png,jpeg'

You can Try This
'image' => 'required|file|image|max:2048'

I used the FormData in order to get this work. It was fairly simple after that with a basic axios post request. I will add the finalized code I used. This JavaScript code was able to send an image properly to the backend which was validated in Laravel as an image.
let formData = new FormData();
Object.keys(this.form).forEach(key => {
formData.append(key, this.form[key]);
});
formData.append('imageFile', this.imageFile);
axios({
method: 'POST',
url: this.routes.save,
data: formData
})

Related

when i update two entities in postman i have null in laravel api

**when i update two entities in postman i have null in laravel api and
I have two entities one is for employees and the other is for personalDetails
this is my model employee:
class Employee extends Model implements HasMedia
{
use HasFactory, InteractsWithMedia;
protected $guarded = [];
protected $casts = [
];
public function personalDetails()
{
return $this->hasOne(PersonalDetails::class,'employee_id');
}
and this is the personalDetails model
class PersonalDetails extends Model implements HasMedia
{
use HasFactory, InteractsWithMedia;
protected $guarded = [];
protected $casts = [
'Date_of_birth' => 'date',
'joining_Date' => 'date',
];
public function employee()
{
return $this->belongsTo(Employee::class,'employee_id');
}
the controller is :
public function update(UpdateEmployeeRequest $request,Employee $employee)
{
$employee->update($request->validated());
return new EmployeeResource($employee);
the rout:
Route::middleware('auth:sanctum')->post('employee', [EmployeeController::class, 'store']);
and the UpdateEmployeeRequest is :
class UpdateEmployeeRequest extends FormRequest
{
public function rules()
{
return [
'Name_kanji' => ['required'],
'Name_katakana' => ['required'],
'Name_family_kanji' => ['required'],
'Name_family_katakana' => ['required'],
'employee_number' => ['required','numeric'],
'image' => ['required'],
'Employee_state' => ['required', Rule::in([EmployeeState::Employed, EmployeeState::OnLeave, EmployeeState::Resigned])],
'Employee_type' => ['required', Rule::in([EmployeeType::FullTime, EmployeeType::PartTime, EmployeeType::Director])],
'Department' => ['required', Rule::in([Department::Design, Department::Management, Department::Sales])],
'gender' => ['required', Rule::in([gender::male, gender::female])],
'Current_age' => ['required'],
'Company_day_based_age' => ['required'],
'jop_title' => ['required'],
'Daily_travel_expenses' => ['required'],
'office' => ['required'],
'Years_of_service' => ['required'],
'Email_address' => ['required'],
'My_number' => ['required','numeric'],
'address_id' => ['required'],
'domicile' => ['required'],
'Contact_number' => ['required'],
'Emergency_contact' => ['required'],
'Emergency_contact_relation' => ['required'],
'Need_work_Instruction' => ['required'],
'Date_of_birth*' => ['required|array'],
'Date_of_birth.*day' => ['required'],
'Date_of_birth.*year' => ['required'],
'Date_of_birth.*month' => ['required'],
'joining_Date*' => ['required|array'],
'joining_Date.*day' => ['required'],
'joining_Date.*year' => ['required'],
'joining_Date.*month' => ['required'],
];
}
public function validated($key = null, $default = null)
{
return [
'Name_kanji' => $this->Name_kanji,
'Name_katakana' => $this->Name_katakana,
'Name_family_katakana' => $this->Name_family_katakana,
'Name_family_kanji' => $this->Name_family_kanji,
'employee_number' => $this->employee_number,
'image' => $this->image,
'Employee_state' => $this->Employee_state,
'Employee_type' => $this->Employee_type,
'Department' => $this->Department,
'gender' => $this->gender,
'Current_age' => $this->Current_age,
'Company_day_based_age' => $this->Company_day_based_age,
'jop_title' => $this->jop_title,
'Daily_travel_expenses' => $this->Daily_travel_expenses,
'office' => $this->office,
'Years_of_service' => $this->Years_of_service,
'Email_address' => $this->Email_address,
'My_number' => $this->My_number,
'address_id' => $this->address_id,
'domicile' => $this->domicile,
'Contact_number' => $this->Contact_number,
'Emergency_contact' => $this->Emergency_contact,
'Emergency_contact_relation' => $this->Emergency_contact_relation,
'Need_work_Instruction' => $this->Need_work_Instruction,
'Date_of_birth' => Carbon::create(
$this->Date_of_birth['year'],
$this->Date_of_birth['month'],
$this->Date_of_birth['day'])->format('Y-m-d'),
'joining_Date' => Carbon::create(
$this->joining_Date['year'],
$this->joining_Date['month'],
$this->joining_Date['day'])->format('Y-m-d'),
];
}
}
and the EmployeeResource:
class EmployeeResource extends JsonResource
{
public function toArray($request)
{
return
[ 'id' => $this->id,
'Name_kanji' => $this->Name_kanji,
'Name_katakana' => $this->Name_katakana,
'Name_family_kanji' => $this->Name_family_kanji,
'Name_family_katakana' => $this->Name_family_katakana,
'employee_number' => $this->employee_number,
'image' => $this->personalDetails?->getFirstMediaUrl('PersonalDetails'),
'Employee_state' => $this->personalDetails?->Employee_state,
'My_number' => $this->personalDetails?->My_number,
'Employee_type' => $this->personalDetails?->Employee_type,
'Daily_travel_expenses' => $this->personalDetails?->Daily_travel_expenses,
'Current_age' => $this->personalDetails?->Current_age,
'Company_day_based_age' => $this->personalDetails?->Company_day_based_age,
'department' => $this->personalDetails?->department,
'office' => $this->personalDetails?->office,
'Gender' => $this->personalDetails?->Gender,
'Date_of_birth' =>[
'month' => $this->personalDetails?->Date_of_birth->month,
'day' => $this->personalDetails?->Date_of_birth->day,
'year' => $this->personalDetails?->Date_of_birth->year,
],
'joining_Date' =>[
'month' => $this->personalDetails?->joining_Date->month,
'day' => $this->personalDetails?->joining_Date->day,
'year' => $this->personalDetails?->joining_Date->year,
],
'Years_of_service' => $this->personalDetails?->Years_of_service,
'Email_address' => $this->personalDetails?->Email_address,
'address_id' => $this->personalDetails?->address_id,
'jop_title' => $this->personalDetails?->jop_title,
'domicile' => $this->personalDetails?->domicile,
'Contact_number' => $this->personalDetails?->Contact_number,
'Emergency_contact' => $this->personalDetails?->Emergency_contact,
'Emergency_contact_relation' => $this->personalDetails?->Emergency_contact_relation,
'Need_work_Instruction' => $this->personalDetails?->Need_work_Instruction,
and When I run the code in postman it shows empty values ​​and it doesn't store what's the problem with that
You have no route parameter named employee. When you type-hint a variable on your route action (Controller method signature in this case) you are getting Dependency injection, not Implicit Route Model Binding; there is no parameter to bind. You are getting an empty Employee model instance. This instance has no attributes so you are getting nulls for all the attributes you are trying to access.
The call to update is instantly just returning since the Model instance is non-existing.
What you have is the functional equivalent of this:
return new EmployeeResource(new Employee());
You would have to have a route parameter defined on your route and be passing some identifier for this parameter in the URL you are sending the request to:
Route::post('employee/{employee}', ...);
http://yoursite.com/employee/1

Why does an inertia route change?

In a Laravel/Inertia application, I try to store vinylRecords.
Therefore I created a vinylRecords resource.
Route::resource('vinylRecords', VinylRecordController::class)->only(['index', 'create','store', 'edit', 'update']);
In the frontend, the store function looks like:
methods: {
submitForm() {
this.$inertia.post(route("vinylRecords.store"), this.form, {
onSuccess: (response) => {
alert(Object.keys(response.props))
this.form.reset();
},
});
}
},
Sometimes, the routing is right and the Laravel stores the new record. But most of time, Laravel redirects to the index method without storing the data.
The store method:
public function store(StoreVinylRecordRequest $request)
{
$data = $request->validated();
$record = VinylRecord::create($data);
$record->labels()->sync($data['label_ids']);
$record->styles()->sync($data['style_ids']);
$record->specials()->sync($data['special_ids']);
return Inertia::render('vinylRecord/index', [
'records' => VinylRecordResource::collection(VinylRecord::all()),
'vinylRecordId' => $record->id
]);
}
To solve the problem, I created a new controller with a new route to store the data:
Route::post('storeVinylRecord', [StoreVinylRecordController::class, 'store'])->name('storeVinylRecord');
But the problem was the same.
How is it possible, that the routing changes from one request to the other? Is there an big error in the code from my side?
Edited: Add the StoreVinylRecordRequest
public function rules()
{
return [
'artist' => 'required|string',
'title' => 'required|string',
'barcode' => 'nullable|integer',
'genre_id' => 'nullable|integer',
'country' => 'nullable',
'year' => 'nullable|integer',
'label_ids' => 'nullable',
'style_ids' => 'nullable',
'special_ids' => 'nullable',
'thumb' => 'nullable|string',
'cover_image' => 'nullable|string',
'quantity' => 'nullable|integer',
'speed' => 'nullable|integer',
'part_type' => 'nullable|string',
'storage_location' => 'nullable|string',
'supplier_id' => 'nullable|in:suppliers,id',
'purchasing_price' => 'nullable|numeric',
'selling_price' => 'nullable|numeric',
];
}

Laravel Livewire Mixed Content error in production

I deployed a Laravel-Livewire on Digital Ocean and now I'm having a Mixed content problem when I try to upload a file.
Here is the error:
UploadManager.js:131 Mixed Content: The page at 'https://intake.freejiji.ca/clients/3/extensions' was loaded over HTTPS, but requested an insecure XMLHttpRequest endpoint 'http://intake.freejiji.ca/livewire/upload-file?expires=1625251608&signature=9d98c598db4f6fccc01c009bcfc3051c6a97b56f4058f4d9489a8d30d6d497c2'. This request has been blocked; the content must be served over HTTPS.
The error happens when after I click "Select File" and chose the .csv file I want. Since I'mdoing this on a livewire component I'm not sure how to fix this so that the request goes over HTTPS instead of HTTP.
I was able to fix similar problems on the app by changing "asset()" with "secure_asset()"
and "route()" with "secure_url()" but in this case I'm not sure what to do.
Here is the whole "Import" component:
<?php
namespace App\Http\Livewire\Modals;
use Validator;
use Livewire\Component;
use App\Http\Traits\Csv;
use App\Models\AccountUser;
use Livewire\WithFileUploads;
use Illuminate\Validation\Rule;
use Illuminate\Support\Facades\Auth;
class ImportExtensions extends Component
{
use WithFileUploads;
public $clientID;
public $showModal = false;
public $upload;
public $columns;
public $fieldColumnMap = [
'first_name' => '',
'last_name' => '',
'email' => '',
'password' => '',
'extension' => '',
'user_type' => '',
];
protected $rules = [
'fieldColumnMap.first_name' => 'required|max:255',
'fieldColumnMap.last_name' => 'required|max:255',
'fieldColumnMap.email' => 'required|max:255',
'fieldColumnMap.password' => 'required|max:255',
'fieldColumnMap.extension' => 'required|max:255',
'fieldColumnMap.user_type' => 'required|max:255',
];
protected $validationAttributes = [
'fieldColumnMap.first_name' => 'First Name',
'fieldColumnMap.last_name' => 'Last Name',
'fieldColumnMap.email' => 'Email',
'fieldColumnMap.password' => 'Password',
'fieldColumnMap.extension' => 'Extension',
'fieldColumnMap.user_type' => 'User Type',
];
public function updatingUpload($value)
{
Validator::make(
['upload' => $value],
['upload' => 'required|mimes:csv'],
)->validate();
}
public function updatedUpload()
{
$this->columns = Csv::from($this->upload)->columns();
$this->guessWhichColumnsMapToWhichFields();
}
public function import()
{
// Validate that you are importing any data
$this->validate();
$importCount = 0;
Csv::from($this->upload)
->eachRow( function ($row) use (&$importCount){
$eachRow = $this->extractFieldsFromRow($row);
// Validate each Row of the csv file
$validatedData = Validator::make([
'first_name' => $eachRow['first_name'],
'last_name' => $eachRow['last_name'],
'email' => $eachRow['email'],
'password' => $eachRow['password'],
'extension' => $eachRow['extension'],
'user_type' => $eachRow['user_type'],
],[
'first_name' => 'required',
'last_name' => 'required',
'password' => 'required|max:255',
'user_type' => 'required|in:user,admin',
'email' => 'required|email|unique:account_users',
'extension' => ['required', 'numeric', Rule::unique('account_users', 'extension')
->where(function($query)
{return $query->where("account_id", $this->clientID);
})],
],);
if($validatedData->fails()){
$this->notify(['error','Oops something went wrong!']);
}else{
AccountUser::create([
'user_id' => Auth::user()->id,
'account_id' => $this->clientID,
'first_name' => $eachRow['first_name'],
'last_name' => $eachRow['last_name'],
'email' => $eachRow['email'],
'password' => $eachRow['password'],
'extension' => $eachRow['extension'],
'user_type' => $eachRow['user_type'],
]);
$importCount++;
}
});
$this->reset();
$this->emit('refreshExtensions');
if($importCount!=0) $this->notify(['success','Successfully Imported '.$importCount.' Extensions']);
}
public function guessWhichColumnsMapToWhichFields()
{
$guesses = [
'first_name' => ['first_name', 'name'],
'last_name' => ['last_name'],
'email' => ['email'],
'password' => ['password', 'pass'],
'extension' => ['extension', 'ext'],
'user_type' => ['user_type', 'user', 'type'],
];
foreach ($this->columns as $column) {
$match = collect($guesses)->search(fn($options) => in_array(strtolower($column), $options));
if ($match) $this->fieldColumnMap[$match] = $column;
}
}
public function extractFieldsFromRow($row)
{
$attributes = collect($this->fieldColumnMap)
->filter()
->mapWithKeys(function($heading, $field) use ($row) {
return [$field => $row[$heading]];
})
->toArray();
return $attributes;
}
public function downloadTemplate()
{
$filename = 'extensions_template.xls';
$path = public_path('files/' . $filename);
return response()->download($path, $filename, [
'Content-Type' => 'application/vnd.ms-excel',
'Content-Disposition' => 'inline; filename="' . $filename . '"'
]);
}
}
If you get mixed content problem it is mostly about you fetching the assets or resources from different http scheme. Here you are using HTTP to fetch data in HTTPS site. Change all the links to have HTTPS link.
If you want to force all the routes to use https you can achieve this by using following code.
if(env('APP_ENV', 'production') == 'production') { // use https only if env is production
\URL::forceScheme('https')
}
The above should solve your problem as all contents now will load from https.

Laravel Phpunit testing a request that take give output based on the request

I'm still new to laravel and I have a simple app and aSo I have a route that will store data based on the request in my controller.
public funtion store(Request $request, $id){
if ($request->has('work_experiences')) {
WorkExperience::create([
'user_id' => $user->id,
'position' => $request->work_experiences['position'],
'company' => $request->work_experiences['company'],
'start_date' => $request->work_experiences['start_date'],
'end_date' => $request->work_experiences['end_date'],
]);
}
if ($request->has('education')) {
Education::create([
'user_id' => $user->id,
'degree' => $request->education['degree'],
'university' => $request->education['university'],
'start_date' => $request->education['start_date'],
'end_date' => $request->education['end_date'],
]);
}
if ($request->has('job_interests')) {
JobInterest::create([
'user_id' => $user->id,
'job_position' => $request->job_interests['position'],
]);
}}
}
and in my test
public function test_authenticated_user_can_edit_education_profile()
{
$this->withoutExceptionHandling();
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->post('/candidate' . '/' . $user->id, [
'user_id' => $user->id,
'position' => 'position',
'company' => 'company',
'start_date' => Carbon::now(),
'end_date' => Carbon::now(),
]);
$this->assertCount(1, WorkExperience::all());
}
when I run the test, the assertCount seems to fail because the response didn't work/insert the data to DB. where do I do wrong?
Well, the test is right.
It should fail because there is no work_experiences key in your request data.
The test request should look like:
$response = $this->post('/candidate' . '/' . $user->id, [
'work_experiences' => [
'user_id' => $user->id,
'position' => 'position',
'company' => 'company',
'start_date' => Carbon::now(),
'end_date' => Carbon::now(),
]
]);
So your data should go under a work_experiences key such that $request->has('work_experiences') returns true and executes the WorkExperience::create() statement.
Currently your endpoint only allows for a single "work experience" to be created. Seeing that you've named it work_experiences I assume you'd want to pass in an array/collection of "work experiences" - but that won't work with the current implementation; you'll have to loop over them instead - something like this:
if ($request->has('work_experiences')) {
foreach ($request->input('work_experiences') as $experience) {
WorkExperience::create([
'user_id' => $request->user()->id,
'position' => $experience['position'],
'company' => $experience['company'],
'start_date' => $experience['start_date'],
'end_date' => $experience['end_date'],
]);
}
}
And then your test should look something like this:
$response = $this->post('/candidate' . '/' . $user->id, [
'work_experiences' => [
[
'user_id' => $user->id,
'position' => 'position',
'company' => 'company',
'start_date' => Carbon::now(),
'end_date' => Carbon::now(),
],
// more "work experiences"
]
]);

Laravel's db:seed is not inserting any record in database

I am using Laravel 5.6 and database seeder is not working. I am trying to insert 100 new data in my database but nothing is being inserted. No error is being shown and I don't know why php artisan db:seed is not working
Here's my DatabaseSeeder.php
<?php
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
public function run()
{
$students = factory(App\Student::class, 100)->make();
}
}
Here's my StudentFactory.php
use Faker\Generator as Faker;
$factory->define(App\Student::class, function (Faker $faker) {
return [
'id' => $faker->unique()->numberBetween(1,1000),
'rfid_number' => $faker->unique()->numberBetween(1,1000),
'first_name' => $faker->firstName,
'middle_name' => $faker->lastName,
'last_name' => $faker->lastName,
'name_extension' => $faker->suffix,
'email' => $faker->safeEmail,
'password' => '$2y$10$TKh8H1.PfQx37YgCzwiKb.KjNyWgaHb9cbcoQgdIVFlYg7B77UdFm', // secret
'photo' => '',
'house_number' => $faker->buildingNumber,
'barangay' => $faker->streetName,
'city' => $faker->city,
'province' => $faker->state,
'zip_code' => $faker->postCode,
'birth_date' => $faker->date('Y-m-d'),
'birth_place' => $faker->streetAddress,
'gender' => 'Male',
'religion' => 'Roman Catholic',
'landline_number' => $faker->tollFreePhoneNumber,
'mobile_number' => $faker->tollFreePhoneNumber,
'father_name' => $faker->name,
'father_occupation' => $faker->jobTitle,
'mother_name' => $faker->name,
'mother_occupation' => $faker->jobTitle,
'guardian_name' => $faker->name,
'guardian_occupation' => $faker->jobTitle,
'guardian_address' => $faker->streetAddress,
'year' => $faker->numberBetween(1,6),
'section' => $faker->cityPrefix,
'created_at' => now(),
'updated_at' => now()
];
});
and it shows no error.
Use create() method:
$students = factory(App\Student::class, 100)->create();

Resources