Dynamic form with dependent dropdown in laravel livewire - laravel

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();
}
}
}

Related

Laravel9 FullCalendar6 LiveWire EditEventModal not UPDATING event

I've managed to convert over most of the FullCalendar 6 JS into a Laravel Livewire component. I've created a Modal that, when an empty date is clicked on the calendar, creates an event. I've similarly constructed an EditEventModal that allows the created event data to update the database...However, it's not actually UPDATING the data, it DUPLICATES the event with the updated data and doesn't destroy the old event. Suggestions?
Here's my Livewire Calendar View:
<style>
#calendar-container {
display: grid;
grid-template-columns: 200px 1fr;
padding: 20px;
}
#events {
grid-column: 1;
}
#calendar {
grid-column: 2;
height: 700px;
}
.dropEvent {
background-color: DodgerBlue;
color: white;
padding: 5px 16px;
margin-bottom: 10px;
text-align: center;
display: inline-block;
font-size: 16px;
border-radius: 4px;
cursor:pointer;
}
</style>
<div>
#include('livewire.eventmodal')
<div>
<!-- sidebar -->
<div id="calendar-container" wire:ignore>
<div id="events">
<div data-event='{"title":"Evénement A"}' class="dropEvent">Event One Drag</div>
<div data-event='{"title":"Evénement B"}' class="dropEvent">Event Two Draggable</div>
</div>
<div id="calendar"></div>
</div>
</div>
<!-- start add event modal -->
<div id="AddEventModal" class="modal fade">
<form action="/events" method="POST">
{{csrf_field()}}
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4>Add Event</h4>
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span> <span class="sr-only">close</span></button>
</div>
<!-- <form wire:submit.prevent="saveEvent"> -->
<div id="modalBody" class="modal-body">
<div class="bg-white sm:p-6">
<label for="title" class="block font-medium text-sm text-gray-700">Title</label>
<input type="text" name="title" id="title" type="text" class="form-input rounded-md shadow-sm mt-1 block w-full"
value="{{ old('title', '') }}" />
#error('title')
<p class="text-sm text-red-600">{{ $message }}</p>
#enderror
</div>
<div class="bg-white sm:p-6">
<label for="acronym" class="block font-medium text-sm text-gray-700">Acronym</label>
<input type="text" name="acronym" id="acronym" type="text" class="form-input rounded-md shadow-sm mt-1 block w-full"
value="{{ old('acronym', '') }}" />
#error('acronym')
<p class="text-sm text-red-600">{{ $message }}</p>
#enderror
</div>
<div class="bg-white sm:p-6">
<label for="city" class="block font-medium text-sm text-gray-700">City</label>
<input type="text" name="city" id="city" type="text" class="form-input rounded-md shadow-sm mt-1 block w-full"
value="{{ old('city', '') }}" />
#error('city')
<p class="text-sm text-red-600">{{ $message }}</p>
#enderror
</div>
<div class="bg-white sm:p-6">
<label for="venue" class="block font-medium text-sm text-gray-700">Venue</label>
<input type="text" name="venue" id="venue" type="text" class="form-input rounded-md shadow-sm mt-1 block w-full"
value="{{ old('venue', '') }}" />
#error('venue')
<p class="text-sm text-red-600">{{ $message }}</p>
#enderror
</div>
<div class="bg-white sm:p-6">
<label for="value" class="block font-medium text-sm text-gray-700">Value</label>
<input type="text" name="value" id="value" type="text" class="form-input rounded-md shadow-sm mt-1 block w-full"
value="{{ old('value', '') }}" />
#error('value')
<p class="text-sm text-red-600">{{ $message }}</p>
#enderror
</div>
<div class="bg-white sm:p-6">
<label for="start" class="block font-medium text-sm text-gray-700">Start Date</label>
<input type="date" name="start" id="start" class="form-input rounded-md shadow-sm mt-1 block w-full"
value="{{ old('start', '') }}" />
#error('start')
<p class="text-sm text-red-600">{{ $message }}</p>
#enderror
</div>
<div class="bg-white sm:p-6">
<label for="end" class="block font-medium text-sm text-gray-700">End Date</label>
<input type="date" name="end" id="end" class="form-input rounded-md shadow-sm mt-1 block w-full"
value="{{ old('end', '') }}" />
#error('end')
<p class="text-sm text-red-600">{{ $message }}</p>
#enderror
</div>
</div>
<div class="modal-footer">
<button class="btn" data-dismiss="modal" aria-hidden="true">Cancel</button>
<button type="submit" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#AddEventModal" id="submit">Save</button>
</div>
</form>
</div>
</div>
</div>
<!--end add event modal -->
<!--start edit event modal -->
<div id="EditEventModal" class="modal fade" role="dialog">
<form action="/events" method="POST">
{{csrf_field()}}
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4>Edit Event</h4>
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span> <span class="sr-only">close</span></button>
</div>
<form wire:submit.prevent="saveEvent">
<div id="modalBody" class="modal-body">
<div class="bg-white sm:p-6">
<label for="title" class="block font-medium text-sm text-gray-700">Title</label>
<input type="text" name="title" id="title" type="text" class="form-input rounded-md shadow-sm mt-1 block w-full"
value="{{ old('title', '') }}" />
#error('title')
<p class="text-sm text-red-600">{{ $message }}</p>
#enderror
</div>
<div class="bg-white sm:p-6">
<label for="acronym" class="block font-medium text-sm text-gray-700">Acronym</label>
<input type="text" name="acronym" id="acronym" type="text" class="form-input rounded-md shadow-sm mt-1 block w-full"
value="{{ old('acronym', '') }}" />
#error('acronym')
<p class="text-sm text-red-600">{{ $message }}</p>
#enderror
</div>
<div class="bg-white sm:p-6">
<label for="city" class="block font-medium text-sm text-gray-700">City</label>
<input type="text" name="city" id="city" type="text" class="form-input rounded-md shadow-sm mt-1 block w-full"
value="{{ old('city', '') }}" />
#error('city')
<p class="text-sm text-red-600">{{ $message }}</p>
#enderror
</div>
<div class="bg-white sm:p-6">
<label for="venue" class="block font-medium text-sm text-gray-700">Venue</label>
<input type="text" name="venue" id="venue" type="text" class="form-input rounded-md shadow-sm mt-1 block w-full"
value="{{ old('venue', '') }}" />
#error('venue')
<p class="text-sm text-red-600">{{ $message }}</p>
#enderror
</div>
<div class="bg-white sm:p-6">
<label for="value" class="block font-medium text-sm text-gray-700">Value</label>
<input type="text" name="value" id="value" type="text" class="form-input rounded-md shadow-sm mt-1 block w-full"
value="{{ old('value', '') }}" />
#error('value')
<p class="text-sm text-red-600">{{ $message }}</p>
#enderror
</div>
<div class="bg-white sm:p-6">
<label for="start" class="block font-medium text-sm text-gray-700">Start Date</label>
<input type="date" name="start" id="start" class="form-input rounded-md shadow-sm mt-1 block w-full"
value="{{ old('start', '') }}" />
#error('start')
<p class="text-sm text-red-600">{{ $message }}</p>
#enderror
</div>
<div class="bg-white sm:p-6">
<label for="end" class="block font-medium text-sm text-gray-700">End Date</label>
<input type="date" name="end" id="end" class="form-input rounded-md shadow-sm mt-1 block w-full"
value="{{ old('end', '') }}" />
#error('end')
<p class="text-sm text-red-600">{{ $message }}</p>
#enderror
</div>
</div>
<div class="modal-footer">
<button class="btn" data-dismiss="modal" aria-hidden="true">Cancel</button>
<button type="submit" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#EditEventModal" id="updateBtn">Save</button>
</div>
</form>
</div>
</div>
</div>
#push('scripts')
<script src='https://cdn.jsdelivr.net/npm/moment#2.27.0/min/moment.min.js'></script>
<script src='https://cdn.jsdelivr.net/npm/fullcalendar-scheduler#5.11.3/main.min.js'></script>
<script src='https://cdn.jsdelivr.net/npm/#fullcalendar/moment#5.11.3/main.global.min.js'></script>
<script>
create_UUID = () => {
let dt = new Date().getTime();
const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
let r = (dt + Math.random() * 16) % 16 | 0;
dt = Math.floor(dt / 16);
return (c == 'x' ? r :(r&0x3|0x8)).toString(16);
});
return uuid;
}
document.addEventListener('livewire:load', function () {
const Calendar = FullCalendar.Calendar;
const calendarEl = document.getElementById('calendar');
const Draggable = FullCalendar.Draggable;
new Draggable(document.getElementById('events'), {
itemSelector: '.dropEvent'
});
const calendar = new Calendar(calendarEl, {
headerToolbar: {
left: 'promptResource prev,next today',
center: 'title',
right: 'resourceMonth,dayGridMonth,timeGridWeek,timeGridDay,listMonth'
},
views: {
resourceMonth: {
type: 'resourceTimelineMonth',
buttonText: 'personnel'
}
},
customButtons: {
promptResource: {
text: "+ personnel",
click: function() {
var title = prompt("Name");
if (title) {
calendar.addResource({
title: title
});
fetch("add_resources.php", {
method: "POST",
headers: {
Accept: "application/json"
},
body: encodeFormData({ title: title })
})
.then(response => console.log(response))
.catch(error => console.log(error));
}
}
}
},
initialView: 'resourceTimelineMonth',
locale: '{{ config('app.locale') }}',
events: JSON.parse(#this.events),
resourceAreaHeaderContent: 'Personnel',
resources: JSON.parse(#this.resources),
editable: true,
eventResize: info => #this.eventChange(info.event),
eventDrop: info => #this.eventChange(info.event),
eventReceive: info => {
const id = create_UUID();
info.event.setProp('id', id);
#this.eventAdd(info.event);
},
formatDate(date) {
return moment.utc(date).format("YYYY-MM-DD HH:mm:ss")
},
selectable: true,
select: function(selectionInfo) {
$('#AddEventModal').find('#start').val(selectionInfo.start);
$('#AddEventModal').find('#end').val(selectionInfo.end);
$('#AddEventModal').modal('show');
$('#saveBtn').click(function() {
var title = $('#title').val();
var start = moment(start).format("YYYY-MM-DD HH:mm:ss");
var end = moment(end).format("YYYY-MM-DD HH:mm:ss");
$.ajax({
url:"{{ route('event.store') }}",
type:"POST",
dataType:'json',
data:{ title, acronym, city, venue, value, start, end, color },
success:function(response)
{
$('#AddEventModal').modal('hide')
$('#calendar').fullCalendar('renderEvent', {
'title': response.title,
'acronym': response.acronym,
'city': response.city,
'venue': response.venue,
'value': response.value,
'start' : response.start,
'end' : response.end,
'color' : response.color
});
},
error:function(error)
{
if(error.responseJSON.errors) {
$('#titleError').html(error.responseJSON.errors.title);
}
},
});
});
},
eventClick: function(info) {
$('#EditEventModal').modal('show');
$('#EditEventModal').find('#id').val(info.event.id);
$('#EditEventModal').find('#title').val(info.event.title);
$('#EditEventModal').find('#acronym').val(info.event.extendedProps.acronym);
$('#EditEventModal').find('#city').val(info.event.extendedProps.city);
$('#EditEventModal').find('#venue').val(info.event.extendedProps.venue);
$('#EditEventModal').find('#value').val(info.event.extendedProps.value);
$('#EditEventModal').find('#start').val(info.event.start);
$('#EditEventModal').find('#end').val(info.event.end);
$('#EditEventModal').modal('show');
$.ajax({
url:"{{ route('event.update', '') }}" +'/'+ id,
type:"PATCH",
dataType:'json',
data:{ start, end },
success:function(response)
{
swal("Good job!", "Event Updated!", "success");
},
error:function(error)
{
console.log(error)
},
});
},
});
calendar.render();
$("#AddEventModal").on("hidden.bs.modal", function () {
$('#saveBtn').unbind();
});
$("#EditEventModal").on("hidden.bs.modal", function () {
$('#updateBtn').unbind();
});
$('.fc-event').css('font-size', '13px');
$('.fc-event').css('width', '20px');
$('.fc-event').css('border-radius', '50%');
});
</script>
<link href='https://cdn.jsdelivr.net/npm/fullcalendar-scheduler#5.11.3/main.min.css' rel='stylesheet' />
#endpush
Here's the applicable routes:
Route::get('calendar/index', [CalendarController::class, 'index'])->name('calendar.index');
Route::post('calendar', [EventController::class, 'store'])->name('event.store');
Route::patch('calendar/update/{id}', [EventController::class, 'update'])->name('event.update');
Route::delete('calendar/destroy/{id}', [EventController::class, 'destroy'])->name('event.destroy');
And here's the Controller:
<?php
namespace App\Http\Controllers;
use App\Http\Requests\StoreEventRequest;
use App\Http\Requests\UpdateEventRequest;
use App\Models\Event;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
use Symfony\Component\HttpFoundation\Response;
class EventsController extends Controller
{
public function index()
{
abort_if(Gate::denies('event_access'), Response::HTTP_FORBIDDEN, '403 Forbidden');
$events = Event::all();
return view('events.index', compact('events'));
}
public function create()
{
abort_if(Gate::denies('event_access'), Response::HTTP_FORBIDDEN, '403 Forbidden');
return view('events.create');
}
public function store(Request $request)
{
$request->validate([
'title' => 'required|string'
]);
$event = Event::create([
'title' => $request->title,
'acronym' => $request->acronym,
'city' => $request->city,
'venue' => $request->venue,
'value' => $request->value,
'start' => $request->start,
'end' => $request->end,
]);
$color = null;
if($event->title == 'Test') {
$color = '#924ACE';
}
return redirect('/dashboard')-> with('success', 'Event Added'); response()->json([
'id' => $event->id,
'title' => $event->title,
'acronym' => $request->acronym,
'city' => $request->city,
'venue' => $request->venue,
'value' => $request->value,
'start' => $event->start,
'end' => $event->end,
]);
}
public function show(Event $event)
{
abort_if(Gate::denies('event_access'), Response::HTTP_FORBIDDEN, '403 Forbidden');
return view('events.show', compact('event'));
}
public function edit(Event $event)
{
abort_if(Gate::denies('event_access'), Response::HTTP_FORBIDDEN, '403 Forbidden');
return view('events.edit', compact('event'));
}
public function update(Request $request ,$id)
{
$event = Event::find($id);
if(! $event) {
return response()->json([
'error' => 'Unable to locate the event'
], 404);
}
$event->update([
'start' => $request->start,
'end' => $request->end,
]);
return response()->json('Event updated');
}
public function destroy(Event $event)
{
abort_if(Gate::denies('event_access'), Response::HTTP_FORBIDDEN, '403 Forbidden');
$event->delete();
return redirect()->route('events.index');
}
}
EDIT--
Here's the Livewire Calendar component:
<?php
namespace App\Http\Livewire;
use Livewire\Component;
use App\Models\Event;
use App\Models\Resource;
use Illuminate\Support\Arr;
class Calendar extends Component
{
public $events, $resources, $title, $acronym, $city, $venue, $value ;
protected function rules()
{
return [
'title' => 'required|string|min:6',
'acronym' => 'required|string',
];
}
public function eventChange ( $event )
{
$e=Event::find($event['id']) ;
$e->start=$event['start'] ;
if(Arr::exists($event,'end')) {
$e->end=$event['end'];
}
$e->save();
}
public function saveEvent()
{
$validatedData = $this->validate();
Event::create($validatedData);
session()->flash('message','Event Added Successfully');
$this->resetInput();
$this->dispatchBrowserEvent('close-modal');
}
public function render()
{
$this->events=json_encode(Event::all());
$this->resources=json_encode(Resource::all());
return view('livewire.calendar');
}
public function eventAdd ( $event )
{
Event::create ( $event );
}
public function eventRemove ( $id )
{
Event::destroy ( $id );
}
public function resourceAdd ( $resources )
{
Resource::create ( $resources );
}
public function resourceRemove ( $id )
{
Resource::destroy ( $id );
}
}

How do I uncheck checkbox in Laravel livewire for the changes to reflect in the database?

Please I need assistance solving this problem, I am using laravel Spatie roles and permission, I want to add permissions to the role using checkboxes, so far adding permission to roles is working fine, but when editing roles if I unchecked permission for a role it doesn't reflect the changes in the database,any help will be appreciated
This is view
<!-- Modal -->
<div class="modal " id="roleModal" tabindex="-1" aria-labelledby="roleModalLabel" aria-
hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="roleModalLabel">Role</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<form method="POST">
#csrf
<div class="col-span-6 sm:col-span-4">
<x-jet-label for="role" value="{{ __('Role') }}" />
<x-jet-input id="role" type="text" class="mt-1 block w-full"
wire:model.defer="roleName" />
<x-jet-input-error for="role" class="mt-2" />
</div>
<div class="block">
<strong>Permission:</strong>
<br/>
#foreach($permissions as $key => $permission)
<x-jet-label value="{{ $permission->name }}" />
<x-jet-checkbox id="permission" value="{{$permission->id}}"
type="checkbox" class="mt-1 block " wire:model.defer="permissionId" />
<br/>
#endforeach
</div>
{{-- {{dd($permission->id)}} --}}
</form>
</div>
<div class="modal-footer">
<button type="button" class="bg-red-500 text-white active:bg-indigo-600 px-3 py-2
rounded outline-none focus:outline-none" wire:click="closeModal">Close</button>
#if ($editMode)
<button type="button" class="bg-indigo-500 text-white active:bg-indigo-600 px-3
py-2 rounded outline-none focus:outline-none" wire:click="updateRole">Save
changes</button>
#else
<button type="button" class="btn btn-primary" wire:click="storeRole">Create
Role</button>
#endif
</div>
</div>
This is the role livewire component
class Roles extends Component
{
public $editMode = false;
public $roleId;
public $roleName;
public $permissionId = [];
public function showEditModal($id)
{
$this->reset();
$this->editMode = true;
$this->roleId = $id;
$this->loadRole();
$this->dispatchBrowserEvent('modal', ['modalId' => '#roleModal', 'actionModal' => 'show']);
}
public function loadRole()
{
$role = Role::find($this->roleId);
$this->roleName = $role->name;
$this->permissionId = $role->permissions->pluck('id');
}
public function updateRole()
{
$role = Role::find($this->roleId);
$role->update(['name' => $this->roleName ]);
$role->syncPermissions($this->permissionId);
$this->reset();
$this->dispatchBrowserEvent('modal', ['modalId' => '#roleModal', 'actionModal' => 'hide' ]);
session()->flash('message', 'Role Updated Successfully');
}
public function deleteRole($id)
{
$role = Role::find($id);
$role->delete();
session()->flash('message', 'Role Deleted Successfully');
}
public function closeModal()
{
$this->reset();
$this->dispatchBrowserEvent('modal', ['modalId' => '#roleModal', 'actionModal' => 'hide' ]);
}
public function render()
{
$roles = Role::orderBy('id','DESC')->paginate(5);
$permissions = Permission::get();
return view('livewire.roles', ['roles' => $roles, 'permissions' => $permissions ]);
}
}
you can check this answer Retrive ids and check related boxes
#foreach($permissions as $key => $permission)
<x-jet-label value="{{ $permission->name }}" />
<x-jet-checkbox id="permission" value="{{$permission->id}}"
type="checkbox" class="mt-1 block " wire:model.defer="permissionId.{{ $permission->id }}" #if(in_array($permission->id,$permissionId)) checked #endif />
<br/>
#endforeach

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

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.

How to fix a strange behaviour with laravel livewire and pagination?

I started with Laravel 8 and livewire a few days ago. I still have a lot to discover but I am on my way.
Lastly, I encountered a behaviour I have trouble understanding.
My goal
I want to create a page to CRUD posts. What I want is to have a post list that is paginated displayed at the bottom of the page, a button to create a new post and the possibility to click a button on each post line to edit the post.
I also want the editor of the post displayed at the top of the page while the list is hidden (but this last possibility is not absolutely necessary).
I could manage to have this working as long as the post list is not paginated but not with pagination.
To do this I use a liveewire component whose code is herebelow:
Component's code in app/Http/livewire/posts/Posts.php
<?php
namespace App\Http\Livewire\Posts;
use App\Models\Post;
use Livewire\Component;
class Posts extends Component
{
public $posts;
public $links;
public $post_id,$title, $abstract, $body,$category,$diaporama_dir,
$beg_date,$end_date,$close_date,$receive_registration,
$sticky,$user_id,$inscription_directive;
/**
* The attributes that are mass assignable.
*
* #var array
*/
public function render()
{
$this->mode='list';
$this->posts=Post::select('id','title')->orderBy('created_at','DESC')->paginate(15)->toArray();
$this->links=$this->posts['links'];
//dd($this->links);
$this->user_id=auth()->user()->id;
return view('livewire.posts.posts');
}
public function donothing(){
}
/*
* The attributes that are mass assignable.
*
* #var array
*/
public function resetInputFields(){
$this->title = '';
$this->body = '';
$this->title='';
$this->abstract='';
$this->body='';
$this->category='';
$this->diaporama_dir='';
$this->beg_date='';
$this->end_date='';
$this->close_date='';
$this->receive_registration='';
$this->sticky='';
$this->inscription_directive='';
}
/**
* The attributes that are mass assignable.
*
* #var array
*/
public function store()
{
$validatedData = $this->validate([
'title' => 'required',
'body' => 'required',
'abstract'=>'required',
'category'=>'',
'diaporama_dir'=>'',
'beg_date'=>'sometimes',
'end_date'=>'sometimes',
'close_date'=>'sometimes',
'receive_registration'=>'',
'sticky'=>'',
'user_id'=>'required',
'inscription_directive'=>''
]);
Post::create($validatedData);
session()->flash('message', 'Bravo ! Votre article a été enregistré.');
// $this->resetInputFields(); //user may want to keep the input stable
}
/**
* The attributes that are mass assignable.
*
* #var array
*/
public function edit($id)
{
$post = Post::findOrFail($id);
$this->post_id = $id;
$this->title = $post->title;
$this->abstract=$post->abstract;
$this->body=$post->body;
$this->category=$post->category;
$this->diaporama_dir=$post->diaporama_dir;
$this->beg_date=$post->beg_date;
$this->end_date=$post->end_date;
$this->close_date=$post->close_date;
$this->receive_registration=$post->receive_registration;
$this->sticky=$post->sticky;
$this->inscription_directive=$post->inscription_directive;
$this->dispatchBrowserEvent('notify','je passe en mode edit');//to switch browser page to edit mode
}
/**
* The attributes that are mass assignable.
*
* #var array
*/
public function update()
{
$validatedData = $this->validate([
'title' => 'required',
'body' => 'required',
'abstract'=>'required',
'category'=>'',
'diaporama_dir'=>'',
'beg_date'=>'sometimes',
'end_date'=>'sometimes',
'close_date'=>'sometimes',
'receive_registration'=>'',
'sticky'=>'',
'user_id'=>'required',
'inscription_directive'=>''
]);
$post = Post::find($this->post_id);
$post->update([
'title' => $this->title,
'body'=> $this->body,
'abstract'=> $this->abstract,
'category'=> $this->category,
'diaporama_dir'=>$this->diaporama_dir,
'beg_date'=>$this->beg_date,
'end_date'=>$this->end_date,
'close_date'=>$this->close_date,
'receive_registration'=>$this->receive_registration,
'sticky'=>$this->sticky,
'user_id'=>$this->user_id,
'inscription_directive'=>$this->inscription_directive
]);
session()->flash('message', "Bravo ! L'article a été mis à jour.");
// $this->resetInputFields();//user may like to keep the input fields stable
}
}
and views are these:
View 1 : the main page in resources/views/livewire/posts/posts.blade.php
<div class="container m-auto w-10/12">
<div x-data="{ mode: 'list' }">
#if (session()->has('message'))
<div class="bg-green-200 p-4 w-full my-8 text-xl text-orange-500">
{{ session('message') }}
</div>
#endif
<div x-on:notify.window="mode = 'update'">
<div x-show="mode==='update'">
#include('livewire.posts.update')
</div>
<div x-show="mode === 'edit'">
#include('livewire.posts.create')
</div>
</div>
{{-- <div x-show="mode === 'list'" class="">--}}
<div>
<div>
<button x-on:click="mode = 'edit'" class="bg-red-400 px-2 py-1 border rounded-lg mt-2">Nouvel
article</button>
</div>
<table class=" bg-green-400 w-full table table-bordered mt-5 ">
<thead>
<tr class="bg-red-50 mb-2">
<th>Id.</th>
<th>Titre</th>
<th width="150px">Action</th>
</tr>
</thead>
<tbody>
#for ($i = 0; $i < $posts['per_page']; $i++)
<tr class="bg-red-400 mb-2 p-2 space-y-2 border-8 border-red-50 ">
<td>{{ $posts['data'][$i]['id'] }}</td>
<td>{{ $posts['data'][$i]['title'] }}</td>
<td>{{--les actions--}}
<button wire:click="edit({{ $posts['data'][$i]['id'] }})"
class="btn btn-primary btn-sm">Edit</button>
{{-- <button wire:click="delete({{ $post->id }})"
class="btn btn-danger btn-sm">Delete</button>--}}
</td>
</tr>
#endfor
</tbody>
</table>
<div class="flex flex-row mt-2">
#for ($i = 0; $i < count($links); $i++)
<div class="flex p-2 mr-2 border w-max-content">
{{$links[$i]['label']}}
</div>
#endfor
</div>
</div>
</div>
2- create include in resources/views/livewire/posts/create.blade.php
<div class="container bg-green-500 p-4">
<form>
This is the create form
<input type="hidden" name="user_id" wire:model="user_id" >
<div class="flex flex-col md:flex-row" >
<div class=" mt-2 flex flex-col w-max-content">
<label for="category">Catégorie:</label>
<select class="" name="category" id="category" wire:model="category">
<option value="Sans">Sans</option>
<option value="Annoncement">Annonce d'un événement</option>
</select>
#error('category') <span class="text-danger">{{ $message }}</span>#enderror
</div>
<div class=" mt-2 flex flex-col flex-auto ml-4">
<label for="title">Title:</label>
<input type="text" class="form-control" name="title" id="title" placeholder="Saisissez un titre"
wire:model="title" value="">
#error('title') <span class="text-danger">{{ $message }}</span>#enderror
</div>
</div>
<div class="flex flex-col md:flex-row" >
<div class=" mt-2 flex flex-col w-max-cbeg_date ">
<label for="beg_date">Date de début</label>
<input type="text" class="" name="beg_date" id="beg_date" wire:model="beg_date">
#error('beg_date') <span class="text-danger">{{ $message }}</span>#enderror
</div>
<div class=" mt-2 flex flex-col w-max-cbeg_date ml-4">
<label for="end_date">Datend_date</label>
<input type="text" class="" name="end_date" id="end_date" wire:model="end_date">
#error('end_date') <span class="text-danger">{{ $message }}</span>#enderror
</div>
<div class=" mt-2 flex flex-col w-max-cbeg_date ml-4">
<label for="close_dclose">Date limite</label>
<input type="text" class="" name="close_date" id="close_date" wire:model="close_date">
#error('close_date') <span class="text-danger">{{ $message }}</span>#enderror
</div>
<div class=" mt-2 flex flex-col w-max-content ml-4">
<label for="receive_registration">Accepte les inscriptions:</label>
<select class="" name="receive_registration" id="receive_registration" wire:model="receive_registration">
<option value="no">Non</option>
<option value="yes">Oui</option>
</select>
#error('receive_registration') <span class="text-danger">{{ $message }}</span>#enderror
</div>
<div class=" mt-2 flex flex-col flex-auto ml-4">
<label for="title">Dossier du diaporama</label>
<input type="text" class="form-control" name="diaporama_dir" id="diaporama_dir" placeholder="ex: admin/1"
wire:model="diaporama_dir">
#error('diaporama_dir') <span class="text-danger">{{ $message }}</span>#enderror
</div>
</div>
<div class="mt-2 flex flex-col">
<label for="abstract">Résumé</label>
<textarea class="form-control" name="abstract" id="abstract" wire:model="abstract"
placeholder="Saisissez votre article"></textarea>
#error('abstract') <span class="text-danger">{{ $message }}</span>#enderror
</div>
<div class="mt-2 flex flex-col">
<label for="body">Corps de l'article</label>
<textarea class="form-control" name="body" id="body" wire:model="body" rows=30
placeholder="Saisissez votre article"></textarea>
#error('body') <span class="text-danger">{{ $message }}</span>#enderror
</div>
<button wire:click.prevent="store()" class="bg-red-400 px-2 py-1 border rounded-lg mt-2">Enregistrer</button>
<button wire:click.prevent="resetInputFields()" class="bg-red-400 px-2 py-1 border rounded-lg mt-2">Effacer tout</button>
<button #click.prevent="mode = 'list'" class="bg-red-400 px-2 py-1 border rounded-lg mt-2 ml-16">Retour à la liste</button>
</form>
update include
The update include is exactly the same as the create include except an additional hidden field for the post id.
What is happening?
At startup, I mean when I visit the localhost:8000/posts page, the page 1 is correctly displayed and the links at the bottom of the page are like localhost:8000/posts?page=3 whichever the number of the page may be.
From this page I can normally go to another page using the bottom links, and this several times.
The troube arises when I click a link to edit a post. The post is correctly sent back by the server but instantly we are switched to page 1 of the paginated posts and the bottom links take a strange form such as localhost:8000/livewire/message/posts.posts?page=3 wichever the page number may be.
The trouble arises also when, after having displayed the create form, I type a first char in the fields. In fact it seems that it arises each time a sync is required.
How can I fix this?
replace
<div class="flex flex-row mt-2">
#for ($i = 0; $i < count($links); $i++)
<div class="flex p-2 mr-2 border w-max-content">
{{$links[$i]['label']}}
</div>
#endfor
</div>
with
<div class="flex flex-row mt-2">
{{ $posts->links() }}
</div>
and in component
use WithPagination;
ref link https://laravel-livewire.com/docs/2.x/pagination

Laravel - How to display child goal type name on select dropdown on edit form

In my Laravel-5.8 project, I am trying to child name (text) on select list in edit form.
I have this table:
CREATE TABLE `goal_types` (
`id` int(11) NOT NULL,
`name` varchar(200) NOT NULL,
`parent_id` int(11) DEFAULT NULL,
`max_score` int(11) DEFAULT 0,
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Model
class GoalType extends Model
{
public $timestamps = false;
protected $table = 'goal_types';
protected $primaryKey = 'id';
protected $fillable = [
'name',
'parent_id',
'max_score',
];
protected $casts = [];
public function children()
{
return $this->hasMany('App\Models\GoalType', 'parent_id');
}
public function parent()
{
return $this->hasOne(App\Models\GoalType::class, 'id', 'parent_id');
}
}
Controller
public function index()
{
$categories = GoalType::with('children')->whereNull('parent_id')->get();
return view('goal_types.index')->with('categories', $categories);
}
public function create()
{
return view('goal_types.create');
}
public function store(StoreGoalTypeRequest $request)
{
$data = GoalType::create([
'name' => $request->name,
'parent_id' => $request->parent_id,
'max_score' => $request->max_score,
]);
Session::flash('success', 'Goal Type is created successfully');
return redirect()->route('goal_types.index');
}
I have a modal form for the edit
view
<div class="row">
<div class="col-md-8">
<div class="card card-secondary">
<div class="card-header">
<h3 class="card-title">Goal Type(s)</h3>
</div>
<div class="card-body">
<ul class="list-group">
#foreach ($categories as $category)
<li class="list-group-item">
#if ($category->children)
<ul class="list-group mt-2">
#foreach ($category->children as $child)
<li class="list-group-item">
<div class="d-flex justify-content-between">
{{ $child->name }}
<div class="button-group d-flex">
#can('goal_type_edit')
<button type="button" class="btn btn-sm btn-primary mr-1 edit-category" data-toggle="modal" data-target="#editCategoryModal" data-id="{{ $child->id }}" data-name="{{ $child->name }}">Edit</button>
#endcan
</div>
</div>
</li>
#endforeach
</ul>
#endif
</li>
#endforeach
</ul>
</div>
</div>
</div>
<div class="modal fade" id="editCategoryModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Edit Goal Type</h4>
<form action="" method="POST">
#csrf
#method('PUT')
<div class="modal-body">
<div class="form-group">
<label class="control-label"> Parent Goal Type:</label>
<select class="form-control select2bs4" data-placeholder="Choose Parent Goal Type" tabindex="1" name="parent_id" style="width: 100%;">
<option value="">Select Goal Type</option>
#foreach ($categories as $category)
<option value="{{ $category->id }}">{{ $category->name }}</option>
#endforeach
</select>
</div>
<div class="form-group">
<label class="control-label"> Name:<span style="color:red;">*</span></label>
<input type="text" name="name" class="form-control" value="" placeholder="Category Name" required>
</div>
<div class="form-group">
<label class="control-label"> Max. Weight (%):</label>
<input type="number" name="max_score" class="form-control" value="" step="0.01" placeholder="Enter maximum weight here: 15, 50, 75 etc" style="width: 100%;">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary">Update</button>
</div>
</div>
</form>
</div>
</div>
</div>
When I click on edit from the index a modal form should popup to display the child name where the parent_id equals the id of the parent as it is in the database. But the select dropdown value/text is not displaying anything until when I click on the select dropdown.
How do I modify
#foreach ($categories as $category)
<option value="{{ $category->id }}">{{ $category->name }}</option>
#endforeach
in the edit to achieve that (displaying the name of the select child on the dropdown)?
Thanks
In your edit view you must pass your category id in your form action like this action="{{ route('admin.categories.update' , $category->id) }}" and then compare it with parent_id and in your category model you have to pass children.
category model:
public function children()
{
return $this->hasMany(GoalType::class, 'parent_id' , 'id')->with('children');
}
edit.blade.php view
<div class="col-md-12">
<label for="parent_id" class="form-control-label">parents category name</label>
<select class="form-control" data-toggle="select" data-live-search="true" name="parent_id" id="parent_id">
<option value="0" {{ $category->parent_id === 0 ? 'disabled' : '' }} selected> - Default</option>
#foreach(\App\Models\Category::latest()->get() as $cate)
<option value="{{ $cate->id }}" {{ $cate->id === $category->parent_id ? 'selected' : '' }} {{ $cate->id === $category->id ? 'disabled' : '' }} {{ $cate->id === $category->parent_id ? 'disabled' : '' }}>{{ $cate->name }}</option>
#endforeach
</select>
</div>

Resources