DEV Community

Ayako yk
Ayako yk

Posted on

Creating and Editing Using a Single Form

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');
...
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)