Laravel 9 – How to use same form for create and update data

I thought I should try a Laravel tutorial too. Actually, no. The thing is that I’ve encountered a problem. You know that being a programmer means being lazy. In Laravel context, while working on a controller that needed a create and an update method, and looking into documentation, I was wondering myself why should we create two different forms (views) for the two methods.

Less talk, more work. So, let’s just get down to business. Usually the two methods where the create and update form(s) appear, are the create() and the edit() ones. So let me show you how those look in my example:

public function create()
{
	return view('companies.createupdate');
}

public function edit(Company $company)
{
	return view('companies.createupdate', ['company' => $company]);
}

I was assuming that since the controller is a resource type of controller, and since I receive the model inside the edit() method, I will send the model to the form view.

Now, taking a look to the form view…

@extends('layouts.app')

@section('content')

<div class="container">

  <div class="row justify-content-center">

    @if (isset($company))

    <form class="col-6" method="post" action="{{ route('companies.update', $company->id) }}">

    @method('PUT')

    @else

    <form class="col-6" method="post" action="{{ route('companies.store') }}">

    @endif

    @csrf

      <h1>@if (isset($company)) Edit @else Add @endif companie</h1>

      <div class="mb-3">

        <label for="ticker" class="form-label">Simbol companie</label>

        <input id="ticker" type="text" name="ticker" class="form-control @error('ticker') is-invalid @enderror" value="{{ old('ticker', $company->ticker ?? '') }}" />

        @error('ticker')

            <div class="alert alert-danger">{{ $message }}</div>

        @enderror

      </div>

      <div class="mb-3">

        <label for="name" class="form-label">Nume companie</label>

        <input id="name" type="text" name="name" class="form-control @error('name') is-invalid @enderror" value="{{ old('name', $company->name ?? '') }}" />

        @error('name')

            <div class="alert alert-danger">{{ $message }}</div>

        @enderror

      </div>

      <div class="d-grid col-6 mb-3 mx-auto">

        <button type="submit" class="btn btn-primary">Salveaza</button>

      </div>

    </form>

  </div>

</div>

As you can see, depending on receiving the company variable (the model), I am sending the form the its specific url. So, if we have a $company variable, we decide on the form and also on passing or not the method hidden field:

@if (isset($company))
	<form class="col-6" method="post" action="{{ route('companies.update', $company->id) }}">

	@method('PUT')

@else
	<form class="col-6" method="post" action="{{ route('companies.store') }}">
@endif

Also, depending on the presence of the $company variable, we decide on what the output can be, no matter if we talk of simple string or the values of the inputs:

<h1>@if (isset($company)) Edit @else Add @endif company</h1>
<input id="ticker" type="text" name="ticker" class="form-control @error('ticker') is-invalid @enderror" value="{{ old('ticker', $company->ticker ?? '') }}" />

The value of the input here is using either the old input (for the times when the form hasn’t passed the validator), or, as default value, the value sent from the $company. Take note here on this part:

... value="{{ old('ticker', $company->ticker ?? '') }}"

It is important to first make sure we have a value, otherwise we would have problems on the create form.

Maybe a good idea here would be to also mention the validation rules. So, for the store() method we have:

public function store(Request $request)
{
	$validated = $request->validate([
		'name' => 'required|unique:companies|max:255',
		'ticker' => 'required|unique:companies|max:10',
	]);

	Company::create([
		'name' => $validated['name'],
		'ticker' => strtoupper($validated['ticker']),
	]);

	return redirect(route('companies.index'));

}

And for the update() method we have:

public function update(Request $request, Company $company)
{

	$validated = $request->validate([
		'name' => 'required|unique:companies,ticker,' . $company->id . '|max:255',
		'ticker' => 'required|unique:companies,ticker,' . $company->id . '|max:10',
	]);

	$company->name = $validated['name'];
	$company->ticker = strtoupper($validated['ticker']);
	$company->save();

	return redirect(route('companies.index'));

}

Is there something I’m missing? Please, help me learn 🙂

Leave a Reply

Your email address will not be published.

No spam? * Time limit is exhausted. Please reload CAPTCHA.