In my previous blog post, I discussed the fundamentals of CRUD operations. However, my project demanded more advanced functionality, particularly the integration of 'create' and 'edit' methods using a dynamic form to enhance usability. In this article, I will delve into the process of handling both creation and editing within a single page.
Creating and Editing Using a Single Form
When working with data in Laravel, we typically employ forms with GET and POST requests. This often results in the creation of four primary view pages: index, create, show, and edit. Our controller directs the rendering of each page based on our intentions.
When we create new data, we render a blank create form with empty input fields. Conversely, during editing, we present an edit form pre-filled with stored data. These two forms share the same input fields, making it impractical to duplicate code.
To eliminate redundancy and simplify our codebase, we can create a reusable form template, form.blade.php, within a component folder. Leveraging Laravel's @include()
directive, we can seamlessly incorporate this template into both the create and edit views.
This is a simple app that creates a list of desserts. Below is an example.
routes/web.php
Route::get('/desserts', [DessertController::class, 'index'])->name('desserts.index');
Route::post('/desserts', [DessertController::class, 'store'])->name('desserts.store');
Route::get('/desserts/create', [DessertController::class, 'create'])->name('desserts.create');
Route::get('/desserts/{id}/edit', [DessertController::class, 'edit'])->name('desserts.edit');
Route::put('/desserts/{id}', [DessertController::class, 'update'])->name('desserts.update');
...
DessertController
public function store(Request $request)
{
// Validate the incoming data
$formFields = $request->validate([ 'name' => ['required', 'string'], ]);
// Create new data
Dessert::create($formFields);
// Redirect to the index page
return redirect()->route('desserts.index');
}
public function edit($id)
{
// Find the data by $id
$dessert = Dessert::findOrFail($id);
// By passing the data, a user can see the old data when editing
return view('desserts.edit', compact('dessert'));
}
public function update(Request $request, $id)
{
// Validate the incoming data
$formFields = $request->validate([
'name' => ['required', 'string'],
]);
// Find the data by $id
$dessert = Dessert::findOrFail($id);
// Update the data
$dessert->update($formFields);
// Redirect to the show page
return redirect()->route('desserts.index', $dessert);
}
views/components/desserts-form.blade.php
<div class="mt-2">
<label for="name">Name<span class="text-red-500 ml-1">*</span></label><br>
<input
type="text"
name="name"
value="{{ isset($dessert) ? $dessert->name : old('name') }}"
required
class="font-normal border border-gray-300 rounded-lg mt-2 p-1 pl-2 w-3/4"
/>
</div>
@error('name')
<p class="text-red-500 text-xs mt-1">{{$message}}</p>
@enderror
views/desserts/create.blade.php
@extends('components.layout')
@section('main')
<div class="w-1/3 mx-auto mt-4 text-lg font-bold">
<h1 class="ml-8">Create New Dessert</h1>
<div>
<form method="POST" action="{{ route('desserts.store') }}">
@csrf
@include('components.desserts-form')
<button
type="submit"
class="border-2 border-gray-300 rounded-lg mt-6 ml-8 px-8 py-1"
>
Register
</button>
</form>
</div>
</div>
@endsection
views/desserts/create.blade.php
@extends('components.layout')
@section('main')
<div class="w-1/3 mx-auto mt-4 text-lg font-bold">
<h1 class="ml-8">Update Dessert</h1>
<div>
<form method="POST" action="{{ route('desserts.update', ['id' => $dessert->id]) }}">
@csrf
@method('PUT')
@include('components.desserts-form')
<button
type="submit"
class="border-2 border-gray-300 rounded-lg mt-6 ml-8 px-8 py-1"
>
Update
</button>
</form>
</div>
</div>
@endsection
old()
The old()
function, in my case, old('name')
, in Laravel Blade templates, is used to display previously input data. This data does not come from the database; instead, it displays the data that a user has input in cases where errors, such as input errors, occur.
We can further simplify this by using an if-statement. I still use the file name 'create' to handle both create and edit operations.
DessertController
public function edit($id)
{
// Find the data by $id
$dessert = Dessert::findOrFail($id);
// By passing the data, a user can see the old data when editing
return view('desserts.create', compact('dessert')); // changed this line
}
views/desserts/create.blade.php
@extends('components.layout')
@section('main')
<div class="w-1/3 mx-auto mt-4 text-lg font-bold">
<h1 class="ml-8">{{ isset($dessert) ? 'Update Dessert' : 'Create New Dessert' }}</h1>
<div>
<form method="POST" action="{{ isset($dessert) ? route('desserts.update', ['id' => $dessert->id]) : route('desserts.store') }}">
@csrf
@if(isset($dessert))
@method('PUT')
@endif
@include('components.desserts-form')
<button
type="submit"
class="border-2 border-gray-300 rounded-lg mt-6 ml-8 px-8 py-1"
>
{{ isset($dessert) ? 'Update' : 'Register' }}
</button>
</form>
</div>
</div>
@endsection
While conducting research on this topic, I discovered that other developers have explored alternative methods to achieve the same goal. However, the codes mentioned above serve as a good starting point.
Top comments (0)