Creating and Editing Using a Single Form
Tags: php, laravel, beginners
Published: 2023-09-04 00:28:31

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.