How to minimise CRUD front-end code duplication in Laravel 5.4 / Twig

CRUD (Create/Read/Update/Delete) is something that developers do quite a bit of. Frameworks can help with this, but there’s still a fair amount of code you’ll need to write for every CRUD screen you put together.

One particular area is the difference between Create and Update. For many of the projects I’ve worked on, Create and Update are often very similar. They aren’t identical, but if there’s enough overlap between them, you can minimise duplicate code with the following setup.

Some introductory notes

For simplicity, I won’t be getting into the back-end side of things here – to be honest, I could tidy up my back-end code a little more than I have done so far. However, my front-end code is fairly tidy. To simplify further, I’ve used a fake example form with one field. This example is about creating and editing cheese via an admin screen.

Files would be stored under resources/views/admin/cheese/. (Edit “cheese” to whatever’s appropriate. The real-life example I took this from has a folder name of “games”.)

Step 1: The form

NB: I refer to Create and Update as Add and Edit, as they’re a bit shorter and I prefer the words!

The first thing to do is write the code for your form. This contains the fields that the user needs to fill in. Critically, if the Add and Edit forms have differences such as certain fields only being enabled when you Add, or when you Edit, you’ll need to handle these in the scripts file (more on this in a moment). You could do this in the form, but it could be a bit messy.

Here’s some sample code for our Cheese screen, containing one field only: the name of the cheese. This file should be called form.twig:

<h1>{{ formTitle }}</h1>
<form class="form-horizontal" role="form" method="post" action="{{ formSubmitAction }}">
{{ csrf_field() }}
<div class="form-group">
<label for="name" class="col-md-4 control-label">Name</label>
<div class="col-md-6">
<input id="name" type="text" class="form-control" name="name" required autofocus>
<div class="form-group">
<div class="col-md-8 col-md-offset-4">
<button type="submit" class="btn outline btn-primary">

Note: You might also want to include form errors logic before the opening form tag. Read my post about form errors here.

If you want to add more fields, the form is where you need to do it.

Apart from the CSRF field, the only variable in this code is “formSubmitAction”, which we’ll set up in step 2.

Step 2: Add and Edit

Now we’ve got our form code, we need to create views for the Add and Edit screens. We can create these at the same time. Here’s add.twig:

{% extends 'theme/base.twig' %}

{% block page_body %}

{% set formSubmitAction = route('admin.cheese.add') %}
{% include 'admin/cheese/form.twig' with {'formTitle': 'Add cheese', 'formSubmitAction': formSubmitAction} %}

{% include 'admin/cheese/scripts.twig' %}

{% endblock page_body %}

Let’s go through this, line by line.

  • Line 1: Edit this to extend your base Twig template. This template would contain all of your theme information, including stylesheets, scripts, and your site header and footer.
  • Lines 3 and 10: These are used to refer to a block defined in your base Twig template where the body of your pages can be injected. Edit the name of the block to match the name you’ve used in your Twig code.
  • Line 5: This populates the “formSubmitAction” variable that we need in form.twig. Change the name of the route to whatever you’re using for the submission route for Add.
  • Line 6: This includes form.twig that we created in step 1. Make sure the folder is correct (you’ll want to change “cheese” in this and every other example). Also on this line, you can change the formTitle that will be displayed above the form. This is a good example of how you can pass other variables to form.twig that may differ between Add and Edit.
  • Line 8: This includes scripts.twig, which we’ll create in step 3. As before, make sure the folder is correct.

Right, so here’s edit.twig:

{% extends 'theme/base.twig' %}

{% block page_body %}

{% set formSubmitAction = route('admin.cheese.edit', {'cheeseId': CheeseId}) %}
{% include 'admin/cheese/form.twig' with {'formTitle': 'Edit cheese', 'formSubmitAction': formSubmitAction} %}

{% include 'admin/cheese/scripts.twig' %}

{% endblock page_body %}

Hopefully you’ll see that there’s very little difference between add.twig and edit.twig. You could possibly trim this back a little more, but for the purposes of this guide, we’re mainly interested in removing the form duplication.

Key differences:

  • The route used in formSubmitAction is different.
  • The route also needs to pass in the id of the item we’re editing, whereas with Add, we don’t need to do that. Note that because we’re doing this before we load form.twig, we don’t need to worry about changing anything there.
  • In the line where we include form.twig, we’ve changed the form title. This could be set in a variable as we do with the form action, or it could be set in the back-end – but the example shows you a couple of options you can use. Choose whatever works for you.

Step 3: Scripts

A small caveat: the scripts file uses $ syntax to set a field value. You could do this with pure Javascript if you wish. I’m using jQuery on this project; it’s up to you if you wish to do this another way.

Here’s the code for scripts.twig:

 var formMode = '{{ FormMode }}';
 var formVals = [];

if (formMode == 'edit') {

// This is used when editing existing records.
 formVals['name'] = "{{ }}";

} else {

// This is used for new records.
 // It's also used when submitting the edit version, to avoid the DB data overwriting the form.
 formVals['name'] = "{{ old('name') }}";




Let’s go through this line by line:

  • Line 2: A simple variable that we set in the back-end to determine if we’re in Add or Edit mode. This comes into play shortly.
  • Line 3: Initialising the formVals array so we can use it in a moment.
  • Lines 5 to 9: We need to display the values in the right fields. If we’re editing an existing record, we need to pull the values from the database. On line 8, we’re using “CheeseData”, which will be populated in the back-end with the record we’re editing. We can then include one line for each field, and assign each value to a slot in the formVals array.
  • Lines 10 to 16: If we’re adding a new record, we can’t pull the values from an existing database value. This section of the code is also used if we’ve submitted the form but it hasn’t passed validation, perhaps due to a missing field. The “old” function will load in the entered values from the previous submission. You’ll need to ensure that the formMode is not “edit” if the form is being submitted, otherwise the original values will overwrite any changes if submission fails.
  • Line 18: Finally, we set the value of the field to the relevant value in the array.

If new fields are added to form.twig, they also need to be added to scripts.twig. Also, the sample code won’t cover things such as radio buttons or checkboxes – you’ll need to handle these slightly differently.


This is by no means a perfect solution, and you might see ways to improve my code. However, the principle idea behind not needing to duplicate form fields for both Add and Edit is what I’m sharing here. Hope someone finds this useful.


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s