Laravel livewire dynamic dropdown with lots of relations on a single model - laravel

I have a fairly complex issue, I have an animal
model
class Animal extends Model
{
use HasFactory;
protected $fillable = [
"breed_ID",
"name",
"color_ID",
"eyes_color_ID",
"birth_date",
"animal_types_id",
"born_location",
"profile_picture_id",
"gender_ID",
"status",
"image",
"bio",
"lenght",
"weight",
"passport_url",
"chip_number",
"breeder_ID",
];
protected function genders(): BelongsTo
{
return $this->belongsTo(Gender::class);
}
public function borns(): BelongsTo
{
return $this->belongsTo(Born::class);
}
public function eyeColors(): BelongsTo
{
return $this->belongsTo(EyeColor::class);
}
public function colors(): BelongsTo
{
return $this->belongsTo(Color::class);
}
public function breeders(): BelongsTo
{
return $this->belongsTo(Breeder::class);
}
public function weights(): BelongsTo
{
return $this->belongsTo(Weight::class);
}
public function lengths(): BelongsTo
{
return $this->belongsTo(Length::class);
}
public function users(): BelongsTo
{
return $this->belongsTo(User::class);
}
public function animalTypes(): BelongsTo
{
return $this->belongsTo(AnimalType::class);
}
public function images(): HasMany
{
return $this->hasMany(Image::class);
}
}
This animal has a breed, gender, color e.t.c
When a user wants to add a new animal they are presented a form, this form is a full page livewire component.
<main class="add-animal-page">
<section class="relative">
<div class="container px-4 mx-auto">
<div
class="flex flex-col justify-center w-full min-w-0 mb-6 break-words rounded-lg shadow-xl xl:flex-row bg-gray-50">
<form enctype="multipart/form-data" class="flex justify-center" wire:submit.prevent="upload">
<div class="w-full xl:w-4/6">
<div class="flex justify-center px-4 py-5 sm:p-6">
<div class="grid max-w-4xl grid-cols-6 gap-6">
<div class="col-span-6 sm:col-span-3 lg:col-span-2">
<label for="first_name" class="block text-sm font-medium text-gray-700">Name</label>
<input type="text" name="first_name" id="first_name" autocomplete="given-name"
wire:model.defer="animal.name"
class="block w-full mt-1 border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">
</div>
<div class="col-span-6 sm:col-span-6">
<label for="type" class="block text-sm font-medium text-gray-700">
Type</label>
<select wire:model="animal.breeds_id" name="breed" id="breed"
class="block w-full px-3 py-2 mt-1 bg-white border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">
<option value="">Choose a type</option>
#foreach ($types as $type)
<option value={{ $type->id }}>{{ $type->animal_name }}</option>
#endforeach
</select>
</div>
<div class="col-span-6 sm:col-span-6">
<label for="breed" class="block text-sm font-medium text-gray-700">
Breed</label>
<select wire:model="animal.breeds_id" name="breed" id="breed"
class="block w-full px-3 py-2 mt-1 bg-white border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">
<option value="">Choose a breed</option>
#foreach ($breeds as $breed)
<option value={{ $breed->id }}>{{ $breed->breed_name }}</option>
#endforeach
</select>
</div>
<div class="col-span-6 sm:col-span-6">
<label for="breed" class="block text-sm font-medium text-gray-700">
Breed</label>
<select wire:model="animal.breeds_id" name="breed" id="breed"
class="block w-full px-3 py-2 mt-1 bg-white border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">
<option value="">Choose a breed</option>
#foreach ($breeds as $breed)
<option value={{ $breed->id }}>{{ $breed->breed_name }}</option>
#endforeach
</select>
</div>
<div class="col-span-6 sm:col-span-6">
<label for="gender" class="block text-sm font-medium text-gray-700">
Gender</label>
<select wire:model="animal.genders_id" name="gender" id="gender"
class="block w-full px-3 py-2 mt-1 bg-white border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">
<option value="">Choose a gender</option>
#foreach ($genders as $gender)
<option value={{ $gender->id }}>{{ $gender->type }}</option>
#endforeach
</select>
</div>
<div class="col-span-6 sm:col-span-6">
<label for="eye_color" class="block text-sm font-medium text-gray-700">
Eye color</label>
<select wire:model="animal.eye_color_id" name="eye_color" id="eye_color"
class="block w-full px-3 py-2 mt-1 bg-white border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">
<option value="">Choose an eye color</option>
#foreach ($eyeColors as $eyeColor)
<option value={{ $eyeColor->id }}>{{ $eyeColor->name }}</option>
#endforeach
</select>
</div>
<div class="col-span-6 sm:col-span-6">
<label for="Breeder" class="block text-sm font-medium text-gray-700">
Breeder</label>
<select wire:model="animal.breeders_id" name="Breeder" id="Breeder"
class="block w-full px-3 py-2 mt-1 bg-white border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">
<option value="">Choose a breeder</option>
#foreach ($breeders as $breeder)
<option value={{ $breeder->id }}>{{ $breeder->name }}</option>
#endforeach
</select>
</div>
<div class="col-span-6 sm:col-span-6">
<label for="passport" class="block text-sm font-medium text-gray-700">
Passport URL</label>
<input type="text" wire:model.defer="animal.passport_url" name="passport"
id="passport" autocomplete="text"
class="block w-full mt-1 border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">
</div>
<div class="col-span-6 sm:col-span-6">
<label for="chip_number" class="block text-sm font-medium text-gray-700">
Chip number</label>
<input type="text" wire:model.defer="animal.chip_number" name="chip_number"
id="chip_number" autocomplete="chip_number"
class="block w-full mt-1 border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">
</div>
<div class="col-span-6">
<label for="bio" class="block text-sm font-medium text-gray-700">
Bio
</label>
<div class="mt-1">
<textarea wire:model.defer="animal.bio" id="bio" name="bio" rows="3"
class="block w-full mt-1 border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">
</textarea>
</div>
</div>
</div>
</div>
<div class="max-w-md mx-auto overflow-hidden rounded-lg md:max-w-xl">
<div class="md:flex">
<div class="w-full p-3 ">
<div
class="relative flex items-center justify-center h-48 bg-gray-100 border-2 border-dotted rounded-lg border-primary-light">
<div class="absolute">
<div class="flex flex-col items-center"><i
class="fa fa-folder-open fa-4x text-primary"></i> <span
class="block font-normal text-gray-400">Upload your image
here</span>
</div>
</div>
<input wire:model.defer="image" type="file"
class="w-full h-full opacity-0 cursor-pointer">
</div>
</div>
</div>
</div>
</div>
<div class="absolute bottom-0 right-0 px-6 py-3 mx-4 my-6 text-right bg-gray-50 sm:px-6">
<button type="submit"
class="inline-flex justify-center px-4 py-2 text-sm font-medium text-white border border-transparent rounded-md shadow-sm bg-primary hover:bg-primary-hover focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
Save
</button>
</div>
</form>
</div>
</div>
</section>
</main>
The form is populated via this livewire class.
class AddAnimal extends Component
{
use WithFileUploads;
public User $user;
public Animal $animal;
public Collection $genders;
public Collection $eyeColors;
public Collection $breeds;
public Collection $colors;
public Collection $breeders;
public Collection $types;
public $image;
protected array $rules = [
'animal.name' => 'required|min:2',
'animal.eye_color_id' => 'nullable',
'animal.bio' => 'nullable',
'animal.breeds_id' => 'nullable',
'animal.genders_id' => 'nullable',
'animal.breeders_id' => 'nullable',
'animal.chip_number' => 'nullable',
'animal.passport_url' => 'nullable',
'image' => 'nullable',
];
public function mount(User $user)
{
$this->user = $user;
$this->animal = new Animal();
$this->genders = Gender::all();
$this->eyeColors = EyeColor::all();
$this->breeds = Breed::all();
$this->colors = Color::all();
$this->breeders = Breeder::all();
$this->types = AnimalType::all();
}
public function render()
{
return view('livewire.add-animal')
->layout('components.layouts.dashboard', ['title' => 'Add-animal'])
->with(['user' => $this->user, 'genders' => $this->genders, 'eyeColors' => $this->eyeColors, 'breeds' => $this->breeds, 'colors' => $this->colors, 'breeders' => $this->breeders, 'types' => $this->types]);
}
How can I make it so that my dropdowns become dynamic? e.g if a user selects dog as animal type, in the breed dropdown only relevant dog breeds should be shown, and not cats or horses. I tried using some of the online available tutorials to get me started, but could not figure it out with all the relationships going on in my models.

Since the Breed Model has an animal_type id, we can use Livewire's updated hook to check for changes on the animal type and render only breeds related to the animal type.
so in the livewire component,
class AddAnimal extends Component
{
public User $user;
public Animal $animal;
public Collection $genders;
public Collection $eyeColors;
// public Collection $breeds; we will use a computed property
public Collection $colors;
public Collection $breeders;
public Collection $types;
public $image;
// newly added variable to keep track of animal type changed
public $filters = [
'animal_type_id' => ''
];
protected array $rules = [
'animal.name' => 'required|min:2',
'animal.eye_color_id' => 'nullable',
'animal.bio' => 'nullable',
'animal.breeds_id' => 'nullable',
'animal.genders_id' => 'nullable',
'animal.breeders_id' => 'nullable',
'animal.chip_number' => 'nullable',
'animal.passport_url' => 'nullable',
'image' => 'nullable',
'animal.animal_type_id' => '', // make sure you have the rule can be left empty if its not required
];
public function mount(User $user)
{
$this->user = $user;
$this->animal = new Animal();
$this->genders = Gender::all();
$this->eyeColors = EyeColor::all();
$this->colors = Color::all();
$this->breeders = Breeder::all();
$this->types = AnimalType::all();
}
public function updatedAnimalAnimalTypeId($value)
{
$this->filters['animal_type_id'] = $value;
}
public function getBreedsProperty()
{
return Breed::when($this->filters['animal_type_id'], function($query, $animal_type_id){
return $query->where('animal_type_id', $animal_type_id);
})->get();
}
public function render()
{
return view('livewire.add-animal')
->layout('components.layouts.dashboard', ['title' => 'Add-animal'])
->with(['user' => $this->user, 'genders' => $this->genders, 'eyeColors' => $this->eyeColors, 'breeds' => $this->breeds, 'colors' => $this->colors, 'breeders' => $this->breeders, 'types' => $this->types]);
}
}
Note that I have used computed property to get the breeds.
I also have made use of when clause to avoid the null check.
so in the blade file, we just have to wire:model the animal_type_id.
....
<div class="col-span-6 sm:col-span-6">
<label for="type" class="block text-sm font-medium text-gray-700">
Type</label>
<select wire:model="animal.animal_type_id" name="animal_type_id" id="animal_type_id"
class="block w-full px-3 py-2 mt-1 bg-white border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">
<option value="">Choose a type</option>
#foreach ($types as $type)
<option value={{ $type->id }}>{{ $type->animal_name }}</option>
#endforeach
</select>
</div>
....
Now the breeds will be rendered based on the animal type selected.
I have assumed animal_type_id is the correct column name in Animal model. if its not, please change the column name respectively.

Related

Bind Livewire component to parent array

I have the following structure:
A parent component:
class SiteEditComponent extends Component
{
protected $listeners = ['upClicked', 'downClicked', 'childUpdated'];
public $childBlocks = [];
public function render()
{
return view('livewire.site-edit-component', ['childBlocks' => $this->childBlocks]);
}
}
Which renders that way:
<div>
<button wire:click="addBlock" type="button" class=" mb-3 inline-flex items-center px-2.5 py-1.5 border border-gray-300 shadow-sm text-xs font-medium rounded text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">Block hinzufügen</button>
#foreach($childBlocks as $key => $childBlockContent)
<livewire:templates.text-block-component wire:model.debounce="childBlocks.key" :block="$childBlockContent" :wire:key="$key">
#endforeach
</div>
And the child component(s):
class TextBlockComponent extends Component
{
public $block;
protected $rules = [
'block.title' => ''
];
public function render()
{
return view('livewire.text-block-component', [
'block' => $this->block
]);
}
Which renders that way (simplified):
<div>
<div class="bg-white shadow sm:rounded-lg mb-3">
<div class="px-4 py-5 sm:p-6">
<div
class=" w-1/3 border border-gray-300 rounded-md px-3 py-2 shadow-sm focus-within:ring-1 focus-within:ring-indigo-600 focus-within:border-indigo-600">
<label for="name" class="block text-xs font-medium text-gray-900">Titel</label>
<input wire:model.debounce="block.title" type="text" name="title" id="name"
class="block w-full border-0 p-0 text-gray-900 placeholder-gray-500 focus:ring-0 sm:text-sm"
placeholder="Blocküberschrift">
</div>
</div>
</div>
</div>
In fact both components represent a Laravel model. A site can have multiple blocks.
What would be the right way to keep track of the child components in the parent component?
I want to implement a save button which should collect all items of $childBlocks, save the Site and attach the childBlocks to it. I tried to model it by using wire:model.debounce="childBlocks.key" but the binding doesn't seem to work.
I wanted to create an associative array in which every block is identified by a randomly generated key which holds the block data.

validation depends on radio button selection laravel

I have different inputs depends on radio buttons. How to validate them according to selection?
<div class="sm:col-span-2">
<div class="mt-4">
<nav class=" " aria-label="Tabs">
<div class="border-gray-300 border-2 rounded-lg pl-3 py-2 w-40" :class="tab == 'tab1' ? ' bg-green-200 border-green-500 ' : ''">
<input type="radio" #click="tab = 'tab1'" checked="tab == 'tab1' ? true: false" name="person_company"> <span class="text-gray-600 font-medium text-sm pl-2">person</span> </input>
</div>
<br>
<div class="border-gray-300 border-2 rounded-lg pl-3 py-2 w-40" :class="tab == 'tab2' ? ' bg-green-200 border-green-500 ' : ' '">
<input type="radio" #click="tab = 'tab2'" name="person_company"> <span class="text-gray-600 font-medium text-sm pl-2">company</span> </input>
</div>
</nav>
</div>
</div>
<div class="sm:col-span-2 " x-show="tab == 'tab1'">
<label for="identity_no" class="block text-sm font-medium text-gray-700">Identity</label>
<div class="mt-1">
<input type="text" name="identity_no" id="identity_no" class="block w-full py-4 border-gray-300 rounded-md shadow-sm focus:ring-green-500 focus:border-green-500 sm:text-sm">
</div>
</div>
<div class="sm:col-span-2" x-show="tab == 'tab2'">
<label for="company" class="block text-sm font-medium text-gray-700">Company name</label>
<div class="mt-1">
<input type="text" name="company" id="company" class="block w-full py-4 border-gray-300 rounded-md shadow-sm focus:ring-green-500 focus:border-green-500 sm:text-sm">
</div>
</div>
this code needs both of them that's not what I want
$this->validate($request,[
'identity' => 'required',
'company' => 'required',
]);
You should use jquery to manage it. First of all, you will have to take values to radio inputs. And then apply the required property through jquery condition.
<input type="radio" #click="tab = 'tab1'" checked="tab == 'tab1' ? true: false" name="person_company" value = "tab1"> <span class="text-gray-600 font-medium text-sm pl-2">person</span> </input>
<input type="radio" #click="tab = 'tab2'" name="person_company"> <span class="text-gray-600 font-medium text-sm pl-2" value = "tab2">company</span> </input>
$('input[type=radio][name= person_company]').change(function() {
if (this.value == 'tab1') {
$("#identity_no").prop('required',true);
$("#company").prop('required',false);
}
else if (this.value == 'tab2') {
$("#company").prop('required',true);
$("#identity_no").prop('required',false);
}
});

Dynamic form with dependent dropdown in laravel livewire

I have followed this youtube link for populating dynamic form and i have it working in other livewire component. And also use this link to populate dependent dropdown in another livewire component, and both are working like a charm. Now I need to create dynamic form with dependent dropdown inside one livewire components and cannot get working
Below are from livewire blade
#foreach ($employeeQualifications as $qualiIndex => $empQuali)
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="w-full relative">
<select wire:model.lazy="employeeQualifications.{{ $qualiIndex }}.qualification_id" class="input w-full border-gray-300 text-base text-gray-600 focus:text-green-600 focus:border-green-600 focus:outline-none focus:ring-green-600">
<option>Select Qualification</option>
#foreach ($qualifications as $qualification)
<option value="{{ $qualification->id }}">{{ $qualification->name }}</option>
#endforeach
</select>
<label class="tracking-wide text-gray-500 text-xs font-semibold label" for="qualification">
Qualification*
</label>
</div>
<div class="w-full relative">
<select wire:model.lazy='employeeQualifications.{{ $qualiIndex }}.subject_id' class="input w-full border-gray-300 text-base text-gray-600 focus:text-green-600 focus:border-green-600 focus:outline-none focus:ring-green-600">
<option>Select Subject/Stream</option>
#foreach ($streams as $subject)
<option value="{{ $subject->id }}">{{ $subject->name }}</option>
#endforeach
</select>
<label class="tracking-wide text-gray-500 text-xs font-semibold label" for="subject">
Subject/Stream
</label>
</div>
</div>
#endforeach
<div class="flex w-full mt-2 justify-between">
<button type="button" class="text-green-600 uppercase font-semibold text-sm" wire:click.prevent='addMoreQualification'>Add More</button>
#if (count($employeeQualifications) > 1)
<button type="button" class="text-red-600 uppercase font-semibold text-sm" wire:click.prevent='removeQualification'>Remove</button>
#endif
</div>
And in Livewire components
class EducationDetail extends Component
{
public $qualifications;
public $streams;
public $employeeQualifications = [];
public function addMoreQualification()
{
$this->employeeQualifications[] = ['qualification_id' => null, 'subject_id' => null];
}
public function removeQualification()
{
unset($this->employeeQualifications[array_key_last($this->employeeQualifications)]);
$this->employeeQualifications = array_values($this->employeeQualifications);
}
public function mount()
{
$this->qualifications = Qualification::get();
$this->streams = collect();
$this->employeeQualifications = [
['qualification_id' => null, 'subject_id' => null]
];
}
public function render()
{
return view('livewire.web.auth.education-detail');
}
}
If I do like link2 tutorial like below then it can work
{
public function updatedEmployeeQualifications0QualificationId($qId)
{
if (!is_null($qId)) {
$this->streams = Subject::where('qualification_id', $qId)->get();
}
}
}
but I have to prepare around 10 or more method and it's not a logic for developers, how can I make dynamic function like updatedEmployee{anyno}{anyname}
Sorry for bad English and thanks in advance
Add wire:change="setEmployeeQualifications($event.target.value, {{ $key }})"
#foreach ($employeeQualifications as $qualiIndex => $empQuali)
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="w-full relative">
<select wire:model.lazy="employeeQualifications.{{ $qualiIndex }}.qualification_id"
wire:change="setEmployeeQualifications($event.target.value, {{ $key }})"
class="input w-full border-gray-300 text-base text-gray-600 focus:text-green-600 focus:border-green-600 focus:outline-none focus:ring-green-600">
<option>Select Qualification</option>
#foreach ($qualifications as $qualification)
<option value="{{ $qualification->id }}">{{ $qualification->name }}</option>
#endforeach
</select>
<label class="tracking-wide text-gray-500 text-xs font-semibold label" for="qualification">
Qualification*
</label>
</div>
<div class="w-full relative">
<select wire:model.lazy='employeeQualifications.{{ $qualiIndex }}.subject_id' class="input w-full border-gray-300 text-base text-gray-600 focus:text-green-600 focus:border-green-600 focus:outline-none focus:ring-green-600">
<option>Select Subject/Stream</option>
#foreach ($this->employeeQualifications[$qualiIndex]['streams'] as $subject)
<option value="{{ $subject->id }}">{{ $subject->name }}</option>
#endforeach
</select>
<label class="tracking-wide text-gray-500 text-xs font-semibold label" for="subject">
Subject/Stream
</label>
</div>
</div>
#endforeach
<div class="flex w-full mt-2 justify-between">
<button type="button" class="text-green-600 uppercase font-semibold text-sm" wire:click.prevent='addMoreQualification'>Add More</button>
#if (count($employeeQualifications) > 1)
<button type="button" class="text-red-600 uppercase font-semibold text-sm" wire:click.prevent='removeQualification'>Remove</button>
#endif
</div>
And in Livewire components, I changed the following changes.
class EducationDetail extends Component
{
public $qualifications;
public $streams;
public $employeeQualifications = [];
public function addMoreQualification()
{
$this->employeeQualifications[] = ['qualification_id' => null, 'subject_id' => null, 'streams' => null];
}
public function removeQualification()
{
unset($this->employeeQualifications[array_key_last($this->employeeQualifications)]);
$this->employeeQualifications = array_values($this->employeeQualifications);
}
public function mount()
{
$this->qualifications = Qualification::get();
$this->streams = collect();
$this->employeeQualifications = [
['qualification_id' => null, 'subject_id' => null, 'streams' => null]
];
}
public function render()
{
return view('livewire.web.auth.education-detail');
}
public function setEmployeeQualifications($qId, $key)
{
if (!is_null($qId)) {
$this->employeeQualifications[$key]['streams'] = Subject::where('qualification_id', $qId)->get();
}
}
}

Laravel resource controller with AJAX form validation

I want to validate the form with AJAX, because when the normal validation fails, the uploaded files are gone.
I get a JSON response but at the working URL: /properties, instead of the form properties/create.
This is an example of the response:
{"error":{"title":["The title must be at least 10 max:50 characters."],"asking_price":["The asking price must be at least 10000."],"description":["The description must be at least 100 characters."],"feat_image_path":["The feat image path has invalid image dimensions."]}}
I tried a lot of things, but none is working.
Am I doing something wrong logically, or is it the URL of the AJAX script??
Thanks in advance, Sander
This is the blade file with the AJAX script.
#section('tinymce')
<script src="https://cdn.tiny.cloud/1/izqu9favecmjvbbfp1dhe96fphutb1stn4aqs7363k6j8trm/tinymce/5/tinymce.min.js" referrerpolicy="origin"></script>
<script>
tinymce.init({
selector: '#description'
});
</script>
#stop
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('Properties') }}
</h2>
</x-slot>
<div class="py-12">
<div class=" mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
<div class="p-6 bg-white border-b border-gray-200">
<h1 class="text-2xl px-12 font-bold w-full my-4">Add Property<span class="text-sm ml-2 text-red-500 italic">* is required</span></h1>
<div class="container my-4 xl:text-lg text-center">
<div class="w-full mx-auto">
#if ($errors->any())
<div class="alert alert-danger">
<ul>
#foreach ($errors->all() as $error)
<li>{{ $error }}</li>
#endforeach
</ul>
</div>
#endif
<div class="alert alert-danger print-error-msg" style="display:none">
<ul></ul>
</div>
{{-- Add Porperty Form --}}
<form action="../properties" method="post" enctype="multipart/form-data" id="create_property_form" class="bg-white shadow-md rounded mb-4 xl:mb-8 w-full">
#csrf
<h2 class="text-center text-xl font-bold w-full my-4">General Information</h2>
{{-- Title --}}
<div class="form-wrapper w-full flex flex-wrap px-8">
<div class="mb-4 xl:mb-8 w-full xl:w-1/2 px-4">
<label class="block text-gray-500 text-base font-bold mb-2" for="title">
Property Title*
</label>
<input class="block shadow appearance-none border rounded w-full py-2 px-3 text-gray-500 leading-tight focus:outline-none focus:shadow-outline" id="title" type="text" placeholder="Title of the property" name="title" value="{{ old('title') }}" required>
<span class="error title-error"></span>
</div>
{{-- Asking Price --}}
<div class="mb-4 xl:mb-8 w-full xl:w-1/2 px-4">
<label class="block text-gray-500 text-base font-bold mb-2" for="asking_price">
Asking Price*
</label>
<input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-500 leading-tight focus:outline-none focus:shadow-outline" id="asking_price" name="asking_price" type="number" placeholder="Price in $ USD" value="{{ old('asking_price') }}" required>
<span class="error asking_price-error"></span>
</div>
<h2 class="text-center text-xl font-bold w-full my-4">Adress</h2>
{{-- Street --}}
<div class="mb-4 xl:mb-8 w-full xl:w-2/3 px-4">
<label class="block text-gray-500 text-base font-bold mb-2" for="street">
Street*
</label>
<input class="block shadow appearance-none border rounded w-full py-2 px-3 text-gray-500 leading-tight focus:outline-none focus:shadow-outline" id="street" name="street" type="text" placeholder="Name of the street" value="{{ old('street') }}" required>
<span class="error street-error"></span>
</div>
{{-- Number --}}
<div class="mb-4 xl:mb-8 w-full xl:w-1/3 px-4">
<label class="block text-gray-500 text-base font-bold mb-2" for="number">
Number*
</label>
<input class="block shadow appearance-none border rounded w-full py-2 px-3 text-gray-500 leading-tight focus:outline-none focus:shadow-outline" id="number" name="number" type="text" placeholder="Appt/house number" value="{{ old('number') }}" required>
<span class="error number-error"></span>
</div>
{{-- Zip Code --}}
<div class="mb-4 xl:mb-8 w-full xl:w-1/3 px-4">
<label class="block text-gray-500 text-base font-bold mb-2" for="zip_code">
Zip Code
</label>
<input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-500 leading-tight focus:outline-none focus:shadow-outline" id="zip_code" name="zip_code" type="text" placeholder="Zip / Postal code" value="{{ old('zip_code') }}">
<span class="error zip_code-error"></span>
</div>
{{-- City --}}
<div class="mb-4 xl:mb-8 w-full xl:w-1/3 px-4">
<label class="block text-gray-500 text-base font-bold mb-2" for="city">
City*
</label>
<input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-500 leading-tight focus:outline-none focus:shadow-outline" id="city" name="city" type="text" placeholder="eg. 'New Tork'" value="{{ old('city') }}" required>
<span class="error city-error"></span>
</div>
{{-- Location --}}
<div class="mb-4 xl:mb-8 w-full xl:w-1/3 px-4">
<label class="block text-gray-500 text-base font-bold mb-2" for="location">
Location*
</label>
<select name="location_id" id="location_id" class="w-full">
#foreach ($locations as $location)
<option value="{{ $location->id }}">{{ $location-> name}}</option>
#endforeach
</select>
</div>
{{-- Coördinates --}}
<h2 class="text-center text-xl font-bold w-full my-4">Position<span class="text-sm ml-2 text-gray-500">Get coördinates with Google Maps or Latlong </span></h2>
{{-- Latitude --}}
<div class="mb-4 xl:mb-8 w-full xl:w-1/2 px-4">
<label class="block text-gray-500 text-base font-bold mb-2" for="latitude">
Latitude*
</label>
<input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-500 leading-tight focus:outline-none focus:shadow-outline" id="latitude" name="latitude" min="11.979144" max="18.145982" step="0.000001" type="number" placeholder="eg. 12.123456" value="{{ old('latitude') }}" required>
<span class="error latitude-error"></span>
</div>
{{-- Longitude --}}
<div class="mb-4 xl:mb-8 w-full xl:w-1/2 px-4">
<label class="block text-gray-500 text-base font-bold mb-2" for="longitude">
Longitude*
</label>
<input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-500 leading-tight focus:outline-none focus:shadow-outline" id="longitude" name="longitude" min="-68.440281" max="-62.966999" step="0.000001" type="number" placeholder="eg. -64.123456" value="{{ old('longitude') }}" required>
<span class="error longitude-error"></span>
</div>
{{-- Details --}}
<h2 class="text-center text-xl font-bold w-full my-4">Property Details</h2>
{{-- Built Year --}}
<div class="mb-4 xl:mb-8 w-full xl:w-4/12 px-4">
<label class="block text-gray-500 text-base font-bold mb-2" for="built_in">
Built in Year*
</label>
<input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-500 leading-tight focus:outline-none focus:shadow-outline" id="built_in" name="built_in" min="1900" step="1" type="number" placeholder="YYYY" value="{{ old('built_in') }}" required>
<span class="error built_in-error"></span>
</div>
{{-- Indoor Area --}}
<div class="mb-4 xl:mb-8 w-full xl:w-4/12 px-4">
<label class="block text-gray-500 text-base font-bold mb-2" for="area_size_indoor">
Indoor Area Size*
</label>
<input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-500 leading-tight focus:outline-none focus:shadow-outline" id="area_size_indoor" name="area_size_indoor" type="number" placeholder="Size in sqm (m2)" step="1" value="{{ old('area_size_indoor') }}" required>
<small>1 sqft = 0.09290304 sqm</small>
<span class="error area_size_indoor-error"></span>
</div>
{{-- Outdoor Area --}}
<div class="mb-4 xl:mb-8 w-full xl:w-4/12 px-4">
<label class="block text-gray-500 text-base font-bold mb-2" for="area_size_outdoor">
Outdoor Area Size*
</label>
<input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-500 leading-tight focus:outline-none focus:shadow-outline" id="area_size_outdoor" name="area_size_outdoor" type="number" placeholder="Size in sqm (m2)" step="1" value="{{ old('area_size_outdoor') }}" required>
<small>1 sqft = 0.09290304 sqm</small>
<span class="error area_size_outdoor-error"></span>
</div>
{{-- Bedrooms --}}
<div class="mb-4 xl:mb-8 w-full xl:w-4/12 px-4">
<label class="block text-gray-500 text-base font-bold mb-2" for="bedrooms">
Bedrooms*
</label>
<input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-500 leading-tight focus:outline-none focus:shadow-outline" id="bedrooms" name="bedrooms" type="number" step="1" value="{{ old('bedrooms') }}" required>
<span class="error bedrooms-error"></span>
</div>
{{-- Bathrooms --}}
<div class="mb-4 xl:mb-8 w-full xl:w-4/12 px-4">
<label class="block text-gray-500 text-base font-bold mb-2" for="bathrooms">
Bathrooms*
</label>
<input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-500 leading-tight focus:outline-none focus:shadow-outline" id="bathrooms" name="bathrooms" type="number" step="1" value="{{ old('bathrooms') }}" required>
<span class="error bathrooms-error"></span>
</div>
{{-- Property Type --}}
<div class="mb-4 xl:mb-8 w-full xl:w-4/12 px-4">
<label class="block text-gray-500 text-base font-bold mb-2" for="property_type">
Property Type*
</label>
<select name="property_type" id="property_type" class="w-full">
<option value="Villa">Villa</option>
<option value="Chalet">Chalet</option>
<option value="Appartment">Appartment</option>
<option value="Condo">Condo</option>
<option value="Lot">Lot</option>
<option value="Penthouse">Penthouse</option>
</select>
</div>
{{-- Desciption --}}
<div class="mb-4 xl:mb-8 w-full">
<label class="block text-gray-500 text-base font-bold mb-2" for="description">
Description*
</label>
<textarea class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-500 leading-tight focus:outline-none focus:shadow-outline" id="description" name="description">
{{ old('description') }}
</textarea>
<span class="error description-error"></span>
</div>
{{-- Featured Image --}}
<div class="mb-4 xl:mb-8 w-full xl:w-1/2 px-4">
<label class="block text-gray-500 text-base font-bold mb-2" for="feat_image_path">
Featured Image*
</label>
<input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-500 leading-tight focus:outline-none focus:shadow-outline" id="feat_image_path" name="feat_image_path" type="file" value="{{ old('feat_image_path') }}" required>
<span class="error feat_image_path-error"></span>
</div>
{{-- Gallery Images --}}
<div class="mb-4 xl:mb-8 w-full xl:w-1/2 px-4">
<label class="block text-gray-500 text-base font-bold mb-2" for="bathrooms">
Gallery Images
</label>
<input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-500 leading-tight focus:outline-none focus:shadow-outline" type="file" name="file[]" id="file" required="" multiple>
<span class="error file-error"></span>
</div>
{{-- Submit --}}
<div class=" mb-4 xl:mb-8 mx-auto w-48">
<input class="py-2 bg-green-300 hover:bg-green-200 text-white w-full font-bold rounded shadow" data-resource="" value="Submit Property" type="submit">
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
The AJAX script:
$(document).ready(function() {
$("#create_property_form").click(function(e) {
e.preventDefault();
let _token = $("input[name='_token']").val();
let title = $("input[name='asking_price']").val();
let latitude = $("input[name='latitude']").val();
let longitude = $("input[name='longitude']").val();
let description = $("textarea[name='description']").val();
let feat_image_path = $("textarea[name='feat_image_path']").val();
$.ajax({
url: "{{ url('/properties/create') }}"
, type: 'POST'
, data: {
_token: _token
, title: title
, latitude: latitude
, longitude: longitude
, description: description
, feat_image_path: feat_image_path
}
success: function(data) {
console.log(data.error)
if ($.isEmptyObject(data.error)) {
alert(data.success);
} else {
printErrorMsg(data.error);
}
}
});
});
function printErrorMsg(msg) {
$.each(msg, function(key, value) {
console.log(key);
$('.' + key + '_err').text(value);
});
}
});
I have one route for the resource controller:
Route::resource('properties', 'App\Http\Controllers\User\PropertyController');
My goal is to only show errors, when no error I add everything in the DB.
Here is the store method:
public function store(Request $request)
{
// Validate input
$validator = Validator::make($request->all(),[
'title' => 'required|unique:properties|min:10 max:50',
'asking_price' => 'required|numeric|min:10000|max:1000000000',
'street' => 'required|min:3',
'number' => 'required',
'city' => 'required|min:3',
'latitude' => 'required|between:9,12',
'longitude' => 'required|between:9,12',
'built_in' => 'required|numeric',
'area_size_indoor' => 'required|numeric',
'area_size_outdoor' => 'required|numeric',
'bedrooms' => 'required|numeric',
'bathrooms' => 'required|numeric',
'description' => 'required|min:100',
'feat_image_path' => 'required|image|dimensions:min_width=1500,min_height=800|max:2500',
]);
if ($validator->passes()) {
return response()->json(['success'=>'Added new records.']);
}
return response()->json(['error'=>$validator->errors()]);
// Generate random number for filename
$random = rand(1111,9999) * rand(1, 9);
// Validate Featured Image
if($request->hasFile('feat_image_path'))
{
// Make a full size imaye
ImageIvn::make($request->feat_image_path)->resize(1500,800)->save(public_path('img/' . $random . '_feat.jpg'))
->resize(400,300)->save(public_path('img/' . $random . '_feat_thumb.jpg'));
}
// Store Property
$property = new Property([
'title' => $request->title,
'slug' => Str::slug($request->title),
'user_id' => Auth::id(),
'street' => $request->street,
'number' => $request->number,
'zip_code' => $request->zip_code,
'city' => $request->city,
'asking_price' => $request->asking_price,
'status' => 'For Sale',
'location_id' => $request->location_id,
'property_type' => $request->property_type,
'latitude' => $request->latitude,
'longitude' => $request->longitude,
'built_in' => $request->built_in,
'area_size_indoor' => $request->area_size_indoor,
'area_size_outdoor' => $request->area_size_outdoor,
'bedrooms' => $request->bedrooms,
'bathrooms' => $request->bathrooms,
'description' => $request->description,
'feat_image_path' => 'img/' . $random . '_feat.jpg',
]);
$property->save();
/**
* Uploaded gallery images are stored with the use of the property ID as FK
*/
// Validate Gallery Images
if($request->hasFile('file'))
{
$request->validate([
'file.*' => 'image'
]);
$files = $request->file('file');
// Generate images
foreach($files as $file) {
// Generate random number for filename
$rand = $property->id . '-' . rand(99,999) * rand(99,999);
// Create Full size image
ImageIvn::make($file)->resize(1200,900)->save(public_path('img/' . $rand . '_full.jpg'));
// Save in DB
$image = new Image([
'property_id' => $property->id,
'image_path' => 'img/' . $rand . '_full.jpg',
'size' => 'full',
]);
$image->save();
// Create thumbnail size image
ImageIvn::make($file)->resize(1200,900)->save(public_path('img/' . $rand . '_thumb.jpg'));
// Store in DB
$image = new Image([
'property_id' => $property->id,
'image_path' => 'img/' . $rand . '_thumb.jpg',
'size' => 'thumb',
]);
$image->save();
}
}
return $this->index()->with(["message" => "Property " . $property->name . " is added"]);
}

Laravel Livewire sync($request->input)

public function modelData()
{
$user = User::create([
'name' => $this->name,
'npm' => $this->npm,
'email' => $this->email,
'jurusan' => $this->jurusan,
'fakultas' => $this->fakultas,
'password' => Hash::make($this->password),
]);
$user->roles()->sync($this->input('roles', []));
}
this is my code. please someone help me how to use request -> input in livewire
class Usermanajemen extends Component
{
public $role = [];
public $users;
public $name;
public $npm;
public $email;
public $password;
public $jurusan;
public $fakultas;
use WithPagination;
public $modalFormVisible = false;
public $modelid;
public function render()
{
$this->users = User::orderBy('created_at', 'DESC')->get();
$roles = Role::pluck('title', 'id');
return view('livewire.usermanajemen', compact('roles'));
}
public function create()
{
$this->validate();
User::create($this->modelData());
$this->modalFormVisible = false;
$this->reset();
}
public function closeModal()
{
$this->modalFormVisible = false;
}
public function createShowModal()
{
$this->resetValidation();
$this->reset();
$this->modalFormVisible = true;
}
public function mount()
{
$this->resetPage();
}
/**
* The update function.
*
* #return void
*/
public function rules()
{
return [
'name' => ['required', 'string', 'max:255'],
'npm' => ['required', 'numeric', Rule::unique('users', 'npm')->ignore($this->modelid)],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
'password' => ['required', 'min:6'],
'jurusan' => 'required',
'fakultas' => 'required',
];
}
/**
* The data for the model mapped
* in this component.
*
* #return void
*/
public function modelData()
{
$user = User::create([
'name' => $this->name,
'npm' => $this->npm,
'email' => $this->email,
'jurusan' => $this->jurusan,
'fakultas' => $this->fakultas,
'password' => Hash::make($this->password),
]);
$user->roles()->sync($this->role);
}
}
this is my livewire usermanajemen i change my role in create and add role=[]in public
<form>
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<div class="">
<div class="mb-4">
<label for="name" class="block text-gray-700 text-sm font-bold mb-2">Nama:</label>
<input type="text" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" id="name" wire:model="name">
#error('name') <span class="text-red-500">{{ $message }}</span>#enderror
</div>
<div class="mb-4">
<label for="npm" class="block text-gray-700 text-sm font-bold mb-2">Npm:</label>
<input type="text" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" id="npm" wire:model="npm">
#error('npm') <span class="text-red-500">{{ $message }}</span>#enderror
</div>
<div class="mb-4">
<label for="email" class="block text-gray-700 text-sm font-bold mb-2">Email:</label>
<input type="email" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" id="email" wire:model="email">
#error('email') <span class="text-red-500">{{ $message }}</span>#enderror
</div>
<div class="mb-4">
<label for="password" class="block text-gray-700 text-sm font-bold mb-2">Password:</label>
<input type="password" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" id="password" wire:model="password">
#error('password') <span class="text-red-500">{{ $message }}</span>#enderror
</div>
<div class="mb-4">
<label for="jurusan" class="block text-gray-700 text-sm font-bold mb-2">Jurusan:</label>
<input type="text" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" id="jurusan" wire:model="jurusan">
#error('jurusan') <span class="text-red-500">{{ $message }}</span>#enderror
</div>
<div class="mb-4">
<label for="fakultas" class="block text-gray-700 text-sm font-bold mb-2">Fakultas:</label>
<input type="text" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" id="fakultas" wire:model="fakultas">
#error('fakultas') <span class="text-red-500">{{ $message }}</span>#enderror
</div>
<div class="px-4 py-5 bg-white sm:p-6">
<label for="role" class="block font-medium text-sm text-gray-700">Roles</label>
<select wire:model = "role" name="role" id="role" class="form-multiselect block rounded-md shadow-sm mt-1 block w-full" multiple="multiple">
#foreach($roles as $id => $role)
<option value="{{ $id }}"{{ in_array($id, old('roles', [])) ? ' selected' : '' }}>{{ $role }}</option>
#endforeach
</select>
#error('roles')
<p class="text-sm text-red-600">{{ $message }}</p>
#enderror
</div>
</div>
</div>
this is my blade file for create
i change in my livewire and my blade and still error but users added
Why do you want to use request ?
You can add a property like public roles = []; in the class
and bind this property with select/checkbox ie: <select wire:model="role" multiple>
and then you can use sync like this : $user->roles()->sync($this->roles);
PS: I assumed that you already defined many to many relationship

Resources