Laravel9 FullCalendar6 LiveWire EditEventModal not UPDATING event - laravel
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 );
}
}
Related
Laravel redirect with data in vue
I'm trying to redirect my controller with data in laravel to a vue file which then displays the data. It's a SPA and I'm using vue router. Here is my controller redirection: return redirect('/bedankt-voor-je-bericht')->with('mailstatus', 'Bedankt voor uw bericht!'); And here is my vue file: <script> export default { mounted() { axios.get('/bedankt-voor-je-bericht') .then((response) => { console.log(response); }); } } </script> The response data does not include the mailstatus variable at the moment. But how will i accomplish this? Any help would be appreciated. Update: What is I want to send a form how would we accomplish that? Here is my code for the form: <form method="post" action="/api/contact/send"> <div class="flex flex-col mb-[15px]"> <label class="font-bold text-[20px] mb-[5px]" for="">Naam</label> <input class="border border-[#888888] rounded-[10px] min-h-[35px] px-2" type="text" name="name"> </div> <div class="flex flex-col mb-[15px]"> <label class="font-bold text-[20px] mb-[5px]" for="">Emailadres</label> <input class="border border-[#888888] rounded-[10px] min-h-[35px] px-2" type="text" name="email"> </div> <div class="flex flex-col mb-[15px]"> <label class="font-bold text-[20px] mb-[5px]" for="">Bericht</label> <textarea name="body" class="border border-[#888888] rounded-[10px] p-2 min-h-[215px] resize-none"></textarea> </div> <div class="flex justify-end"> <button type="submit" class="border border-[#25d097] bg-white text-[#25d097] w-full box-border p-2 rounded-[8px] hover:bg-[#25d097] hover:text-[#ffffff]">Verstuur</button> </div> </form> Then i have my routes/api like this Route::post('/contact/send', ContactController::class); And in my controller I have this: public function __invoke(Request $request) { if ($request->email && $request->name && $request->body) { $mail = (new Mailsettings)->setup(); $mail->setFrom($request->email, $request->name); $mail->addAddress(env('MAIL_USERNAME'), 'Plugzy'); $mail->addReplyTo($request->email, $request->name); $mail->isHTML(true); $mail->Subject = 'Nieuwe e-mail Plugzy'; $mail->Body = $request->body; // $mail->send(); return response()->json(['mailstatus', 'Bedankt voor uw bericht!'], 200); } return response()->json(['mailstatus', 'Er ontbreekt iets!'], 200); } If the action is pointed to the api then there is no redirection to the next page.
For anyone that might run into this problem I've found an answer for returning a response without any page reloads. There's a npm package called v-form thanks to this package I've managed to make it work as following: This is my vue file for submitting the form: <form #submit.prevent="sendemail()" #keydown="form.onKeydown($event)"> <div class="flex flex-col mb-[15px]"> <label class="font-bold text-[20px] mb-[5px]" for="">Naam</label> <input v-model="form.name" class="border border-[#888888] rounded-[10px] min-h-[35px] px-2" type="text" name="name"> </div> <div class="flex flex-col mb-[15px]"> <label class="font-bold text-[20px] mb-[5px]" for="">Emailadres</label> <input v-model="form.email" class="border border-[#888888] rounded-[10px] min-h-[35px] px-2" type="text" name="email"> </div> <div class="flex flex-col mb-[15px]"> <label class="font-bold text-[20px] mb-[5px]" for="">Bericht</label> <textarea v-model="form.body" name="body" class="border border-[#888888] rounded-[10px] p-2 min-h-[215px] resize-none"> </textarea> </div> <div class="flex justify-end"> <button type="submit" class="border border-[#25d097] bg-white text-[#25d097] w-full box-border p-2 rounded-[8px] hover:bg-[#25d097] hover:text-[#ffffff]">Verstuur</button> </div> </form> <script> import Form from 'vform' export default { data() { return { form: new Form({ name: '', email: '', body: '' }) } }, methods: { sendemail: async function () { const response = await this.form.post('/api/contact/send') console.log(response); } } } </script> And then in my api.php I've got this api route defined: Route::post('/contact/send', ContactController::class);. Which will activate my controller which has the following inside: return response()->json(['mailstatus', 'Bedankt voor uw bericht!'], 200);. So now in the vue file I'll get the correct response without a page reload.
How can I show data names field in vue-multiselect plugin in edit page?
I have a vue laravel SPA, and was working on the edit page of employees page. I already have create employees page and can display data on the vue-multiselect plugin (https://vue-multiselect.js.org/). Right now, I can display the employee id's from an array in vue-multiselect in the edit page. How can I format it to show employee names? <style src="vue-multiselect/dist/vue-multiselect.min.css"></style> <script> import Multiselect from 'vue-multiselect' export default { components: { Multiselect }, data() { return { employee: { designation_id: [], position_id: [], }, designation_names: {}, position_names: {} } }, methods: { updateEmployee() { this.axios .patch(`/api/employees/${this.$route.params.id}`, this.employee) .then((res) => { this.$router.push({ name: 'employees' }); }); }, async getDesignations(id) { const { data } = await this.axios.get(`/employees/${id}/designations`); this.$set(this.designation_names, id, data.join(', ')); }, async getPositions(id) { const { data } = await this.axios.get(`/employees/${id}/positions`); this.$set(this.position_names, id, data.join(', ')); }, }, created() { this.axios .get(`/api/employees/${this.$route.params.id}`) .then((res) => { this.employee = res.data; }); for(const row of this.rows) { this.getDesignations(row.id); } for(const row of this.rows) { this.getPositions(row.id); } }, } </script> <template> <form #submit.prevent="updateEmployee"> <div class="flex space-x-6 md:w-3/4"> <div class="md:w-3/6 mb-4 flex-1"> <label class="block text-gray-700 text-sm font-bold mb-2" for="name">First Name</label> <input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline required" name="name" id="name" type="text" placeholder="First Name" tabindex="1" v-model="employee.first_name" /> </div> <div class="md:w-3/6 mb-4 flex-1"> <label class="block text-gray-700 text-sm font-bold mb-2" for="name">Last Name</label> <input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline required" name="name" id="name" type="text" placeholder="Last Name" tabindex="2" v-model="employee.last_name" /> </div> </div> <div class="flex space-x-6 md:w-3/4"> <div class="md:w-3/6 mb-4 flex-1"> <label class="block text-gray-700 text-sm font-bold mb-2 required" for="designation_id">Designation</label> <multiselect v-model="employee.designation_id" :options="designation_options" :custom-label="opt => designation_options.find(designation => designation.id == opt).name" :multiple="true" :taggable="true" ></multiselect> </div> <div class="md:w-3/6 mb-4 flex-1"> <label class="block text-gray-700 text-sm font-bold mb-2 required" for="position_id">Position</label> <multiselect v-model="employee.position_id" :options="position_options" :multiple="true" :taggable="true" ></multiselect> </div> </div> <div class="flex space-x-6 md:w-3/4"> <div class="md:w-3/6 mb-4 flex-1"> <label class="block text-gray-700 text-sm font-bold mb-2" for="basic_pay">Basic Pay</label> <div class="relative rounded"> <div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none"> <span class="text-gray-700">₱</span> </div> <input class="shadow appearance-none border rounded w-full py-2 pl-8 pr-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline required" name="basic_pay" id="basic_pay" type="number" step="any" placeholder="00.00" tabindex="5" v-model="employee.basic_pay" /> <div class="absolute inset-y-0 right-0 flex items-center"><label for="basic_pay" class="sr-only">Basic Pay</label> </div> </div> </div> <div class="md:w-3/6 mb-4 flex-1"> </div> </div> <button type="submit" class="sm:hidden md:flex bg-blue-500 hover:bg-blue-400 text-white font-bold py-2 px-4 border-b-4 border-blue-700 hover:border-blue-500 rounded outline-none focus:outline-none">Update</button> </form> </template>
I've managed to solve this by retrieving each field, instead of the previous code which I retrieved all in a single object. this.axios .get(`/api/designations/${this.$route.params.id}`) .then((res) => { this.form.name = res.data.name; this.form.description = res.data.description; }); Before: this.axios .get(`/api/designations/${this.$route.params.id}`) .then((res) => { this.form= res.data; });
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
How to Use Select2 Multiple Select in Livewire?
i use select2 in livewire. When adding data, it worked. But when editing the data, and not changing the data in the select option, the previously selected trainer data is all lost. Here is the code I made. Edit.php <?php namespace App\Http\Livewire\Admin\Courses; use App\Models\Course; use App\Models\Trainer; use Livewire\Component; use Jantinnerezo\LivewireAlert\LivewireAlert; use Livewire\WithFileUploads; use \Cviebrock\EloquentSluggable\Services\SlugService; class Edit extends Component { use LivewireAlert; use WithFileUploads; public $title, $slug, $cover, $video, $link, $method, $format, $duration, $price, $description, $isActive, $meta_keywords, $meta_description, $addon_styles, $addon_scripts, $courseId; public $trainer_id; public function mount($id) { $course = Course::findOrFail($id); if ($course) { $this->courseId = $course->id; $this->trainer_id = $course->trainer_id; $this->title = $course->title; $this->slug = $course->slug; $this->link = $course->link; $this->method = $course->method; $this->format = $course->format; $this->duration = $course->duration; $this->price = $course->price; $this->description = $course->description; $this->isActive = $course->isActive; $this->meta_keywords = $course->meta_keywords; $this->meta_description = $course->meta_description; $this->addon_styles = $course->addon_styles; $this->addon_scripts = $course->addon_scripts; } } public function updatedTitle() { $this->slug = SlugService::createSlug(Course::class, 'slug', $this->title); } public function update() { $course = Course::where('id',$this->courseId)->first(); $this->validate([ 'title' => 'required', 'cover' => $this->cover ? 'required|image|mimes:png,jpg,webp,jpeg' : '', 'description' => 'required', ]); if ($this->cover) { \Storage::delete('public/'.$course->cover); $fileName = time().'_'.$this->cover->getClientOriginalName(); $filePath = $this->cover->storeAs('images/courses', $fileName, 'public'); } else { $filePath = $course->cover ?? null; } if ($this->video) { \Storage::delete('public/'.$course->video); $fileName = time().'_'.$this->video->getClientOriginalName(); $video = $this->cover->storeAs('images/courses', $fileName, 'public'); } else { $video = $course->video ?? null; } $course->update([ 'title' => $this->title, 'slug' => $this->slug, 'cover' => $filePath, 'video' => $this->video ? $video : null, 'link' => $this->link, 'method' => $this->method, 'format' => $this->format, 'duration' => $this->duration, 'price' => $this->price, 'description' => $this->description, 'isActive' => $this->isActive, 'meta_keywords' => $this->meta_keywords, 'meta_description' => $this->meta_description, 'addon_styles' => $this->addon_styles, 'addon_scripts' => $this->addon_scripts ]); $course->trainers()->sync($this->trainer_id); $this->alert('success', 'Data updated successfully.'); return redirect()->route('courses.index'); } public function render() { return view('livewire.admin.courses.edit',[ 'trainers' => Trainer::where('isActive',true)->get(), 'course' => Course::find($this->courseId) ]) ->extends('layouts.app') ->section('content'); } } edit.blade.php <div> #section('title', 'Edit Course') #section('styles') <script src="{{ asset('vendor/tinymce/tinymce.min.js') }}"></script> <link href="https://cdn.jsdelivr.net/npm/select2#4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" /> <style> span .selection { display: block; } </style> #endsection <!-- ========== title-wrapper start ========== --> <div class="title-wrapper pt-30"> <div class="row align-items-center"> <div class="col-md-6"> <div class="title mb-30"> <h2>Edit Course</h2> </div> </div> <!-- end col --> <div class="col-md-6"> <div class="breadcrumb-wrapper mb-30"> <nav aria-label="breadcrumb"> <ol class="breadcrumb"> <li class="breadcrumb-item"> Dashboard </li> <li class="breadcrumb-item"> Courses </li> <li class="breadcrumb-item active" aria-current="page"> Edit Course </li> </ol> </nav> </div> </div> <!-- end col --> </div> <!-- end row --> </div> <div class="row"> <div class="col-lg-12"> <div class="card-style mb-3"> <form wire:submit.prevent="update" class="row g-3"> <input type="hidden" wire:model="courseId"> <div class="col-12"> <div class="mb-3"> <label for="cover" class="form-label">Cover</label><br> #if ($cover) Cover Preview: <div class="card mb-3"> <img src="{{ $cover->temporaryUrl() }}" class="w-15 rounded-3"> </div> #else <div class="card mb-3"> <img src="{{ asset('storage/'.$course->cover) }}" class="w-15 rounded-3 img-fluid"> </div> #endif <input type="file" wire:model="cover" class="form-control #error('cover') is-invalid #enderror" id="cover"> #error('cover') <div class="invalid-feedback"> {{ $message }} </div> #enderror </div> </div> <div class="col-md-6"> <div class="mb-3"> <label for="title" class="form-label">Title</label> <input type="text" wire:model="title" class="form-control #error('title') is-invalid #enderror" id="title" placeholder="Course Title"> #error('title') <div class="invalid-feedback"> {{ $message }} </div> #enderror </div> <div class="mb-3"> <label for="slug" class="form-label">Slug</label> <input type="text" wire:model="slug" class="form-control #error('slug') is-invalid #enderror" id="slug" placeholder="course-slug"> #error('slug') <div class="invalid-feedback"> {{ $message }} </div> #enderror </div> <div class="mb-3" wire:ignore> <label for="description" class="form-label">Description</label> <textarea wire:model="description" class="form-control #error('description') is-invalid #enderror" id="description" rows="15"></textarea> #error('description') <div class="invalid-feedback"> {{ $message }} </div> #enderror </div> </div> <div class="col-md-6"> <div class="mb-3"> <label for="video" class="form-label">Video</label> <input type="file" wire:model="video" class="form-control" id="video"> </div> <div class="mb-3"> <label for="link" class="form-label">Link</label> <input type="url" wire:model="link" class="form-control" id="link" placeholder="Youtube link"> </div> <div class="mb-3"> <label for="method" class="form-label">Method</label> <input type="text" wire:model="method" class="form-control" id="method" placeholder="Example: online, hybrid"> </div> <div class="mb-3"> <label for="format" class="form-label">Format</label> <input type="text" wire:model="format" class="form-control" id="format" placeholder="Eg: HD Video, Live Concultation"> </div> <div class="mb-3"> <label for="duration" class="form-label">Duration</label> <input type="text" wire:model="duration" class="form-control" id="duration" placeholder="Eg: 3 mounts"> </div> <div class="mb-3"> <label for="price" class="form-label">Price</label> <input type="number" wire:model="price" class="form-control #error('price') is-invalid #enderror" id="price" placeholder="Eg: 250000"> #error('price') <div class="invalid-feedback"> {{ $message }} </div> #enderror </div> <div class="mb-3" wire:ignore> <label for="trainer" class="form-label">Trainer</label> <select multiple="multiple" id="trainer" class="form-select #error('trainer_id') is-invalid #enderror" multiple> #foreach ($trainers as $trainer) <option {{ $course->trainers()->find($trainer->id) ? 'selected' : '' }} value="{{ $trainer->id }}">{{ $trainer->name }}</option> #endforeach </select> #error('trainer_id') <div class="invalid-feedback"> {{ $message }} </div> #enderror </div> </div> <div class="col-12"> <div class="mb-3"> <label for="meta_keywords" class="form-label">Meta Keywords</label> <input type="text" wire:model="meta_keywords" class="form-control" id="meta_keywords" placeholder="keyword1, keyword2, keyword3"> </div> </div> <div class="col-12"> <div class="mb-3"> <label for="meta_description" class="form-label">Meta Description</label> <input type="text" wire:model="meta_description" class="form-control" id="meta_description" placeholder="Meta description"> </div> </div> <div class="col-12"> <div class="form-check"> <input class="form-check-input" type="checkbox" id="gridCheck" wire:model="isActive"> <label class="form-check-label" for="gridCheck"> Set active </label> </div> </div> <button type="submit" class="w-100 main-btn primary-btn btn-hover btn-sm" wire:target="update" wire:loading.class="deactive-btn"> <span wire:loading.remove wire:target="update"> Update </span> <span wire:loading wire:target="update" class="text-center"> <span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> Loading... </span> </button> </form> </div> </div> </div> </div> #push('scripts') <script src="//cdn.jsdelivr.net/npm/sweetalert2#11"></script> <x-livewire-alert::scripts /> <script> tinymce.init({ selector: 'textarea', menubar: 'file edit view insert format tools table help', plugins: [ "advlist autolink autosave codesample lists link image charmap print preview hr anchor pagebreak", "searchreplace wordcount visualblocks visualchars code fullscreen", "insertdatetime media nonbreaking save table toc directionality", "emoticons template paste textpattern" ], toolbar: "restoredraft insertfile undo redo | styleselect fontselect fontsizeselect | bold italic | alignleft aligncenter alignright alignjustify codesample | bullist numlist outdent indent toc| link image media", setup: function(editor) { editor.on('change', function(e) { console.log('the content ', editor.getContent()); #this.set('description', editor.getContent()); }); } }); </script> <script src="https://cdn.jsdelivr.net/npm/select2#4.1.0-rc.0/dist/js/select2.min.js"></script> <script> $(document).ready(function () { $('#trainer').select2(); $('#trainer').on('change', function (e) { var data = $('#trainer').select2("val"); #this.set('trainer_id', data); }); }); </script> #endpush How to solve this problem? so that, when I edit the data and without changing the data in select2, the data will not be deleted. Thank you
This worked for me. when editing my modal i load my data to my array as follows; public $selectedOn = []; public function edit($id){ $foos = Foo::where('other_id',$id)->get(); foreach($foos as $foo){ array_push($this->selectedOn, $foo->id); } $this->emit('selectLoadOk'); } in my js I do the following; window.livewire.on('selectLoadOk', () =>{ $('#selectChecklist').trigger('change'); });
validation error regarding the validation file in laravel
I am calling a validation request but i am getting this error. Method Illuminate\Validation\Validator::validateFile, does not exist. My UserRequest <?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; use App\Http\Controllers\ProfileController; class UserRequest extends FormRequest { /** * Determine if the user is authorized to make this request. * * #return bool */ public function authorize() { return true; } /** * Get the validation rules that apply to the request. * * #return array */ public function rules() { return [ 'name'=> 'string|required|max:255', 'username'=> 'string|required|max:255|alpha_dash', 'avatar'=>'file,|mimes:jpeg,png,gif,jpg|max:2048', 'email'=> 'email|required|string', 'password'=>'required|min:10|string|confirmed', 'background'=>'file,|mimes:jpeg,png,gif,jpg|max:2048', 'description'=>'string|max:255' ]; } public function messages() { return [ 'name.required' => 'A name is required', 'username.required' => 'A username is required', 'email.required' => 'An email is required', 'password.required' => 'A password is required', 'description' => 'A description should be string', ]; } } My Controller namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Http\Requests\UserRequest; use Illuminate\Http\File ; use App\User; use Hash; use Session; public function update(User $user,UserRequest $request) { $user->update(['name'=>request('name'),'username'=>request('username'),'email'=>request('email'),'password'=> Hash::make(request('password')),'avatar'=>$path,'background'=>$background,'description'=>request('description')]); Session::flash('success', 'You have successfully updated a post!'); return redirect($user->path())->with(['message'=>'Profile updated']); } my blade #extends('layouts.app') #section('content') <h1 class="text-gray-700 text-2xl font-bold text-center">Edit Profile</h1> <div class="mt-5 mb-6"> <img src="{{$user->avatar()}}" alt="" class="rounded-full mr-2 bottom-0 transform " width="150px" style="transform: translateX(16.5rem);" > </div> <form method="POST" action="{{$user->path() }}" enctype="multipart/form-data"> #csrf #method('PATCH') <div id="flip" class="p-5 bg-gray-200">Basic Info</div> <div id="panel"> <div class="mb-6 mt-6"> <label for="avatar">Select Profile to Upload</label> <input id="avatar" type="file" class="#error('avatar') is-invalid #enderror" name="avatar"> #error('avatar') <div class="text-red-800">{{ $message }}</div> #enderror </div> <div class="mb-6"> <label for="name">Name</label> <input id="name" type="text" class="#error('name') is-invalid #enderror border border-gray-400 p-2 w-full" name="name" value="{{$user->name}}"> #error('name') <div class="text-red-800">{{ $message }}</div> #enderror </div> <div class="mb-6"> <label for="username">Username</label> <input id="username" type="text" name="username" class="#error('username') is-invalid #enderror border border-gray-400 p-2 w-full" value="{{$user->username}}"> #error('username') <div class="text-red-800">{{ $message }}</div> #enderror </div> <div class="mb-6"> <label for="email">Email</label> <input id="email" type="email" name="email" class="#error('email') is-invalid #enderror border border-gray-400 p-2 w-full" value="{{$user->email}}"> #error('email') <div class="text-red-800">{{ $message }}</div> #enderror </div> <div class="mb-6"> <label for="password">Password</label> <input id="password" type="password" name="password" class="#error('password') is-invalid #enderror border border-gray-400 p-2 w-full" > #error('password') <div class="text-red-800">{{ $message }}</div> #enderror </div> <div class="mb-6"> <label for="password-confirm">Confirm Password</label> <input id="password-confirm" type="password" name="password-confirm" class="#error('password-confirm') is-invalid #enderror border border-gray-400 p-2 w-full" > #error('password-confirm') <div class="text-red-800">{{ $message }}</div> #enderror </div> </div> <div id="main" class="p-5 bg-gray-200">Background</div> <div id="child"> <div class="mb-6 mt-6"> <label for="background">Select Background image to Upload</label> <input id="background" type="file" class="#error('background') is-invalid #enderror" name="background"> #error('background') <div class="alert alert-danger">{{ $message }}</div> #enderror </div> <div class="mb-6"> <label for="description">Description</label> <input id="description" type="text" name="description" class="#error('description') is-invalid #enderror border border-gray-400 p-2 w-full" value="{{$user->description}}"> #error('description') <div class="alert alert-danger">{{ $message }}</div> #enderror </div> </div> <div class="mt-6"> <button type="submit" class="bg-blue-500 text-white rounded py-2 px-4 hover:bg-blue-800'">Submit</button> </div> </form> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script> <script> $(document).ready(function(){ $("#flip").click(function(){ $("#panel").slideToggle("slow"); }); $("#main").click(function(){ $("#child").slideToggle("slow"); }); }); </script> #endsection I have tried the solutions available on stack overflow but still none of them work Does anyone know why am i getting this error? Any help will be appreciated.
Remove comma in these validation lines. 'avatar'=>'file,|mimes:jpeg,png,gif,jpg|max:2048', 'background'=>'file,|mimes:jpeg,png,gif,jpg|max:2048',
Add password.confirmed in messages array. 'password.confirmed' => 'YOUR ERROR MESSAGE',