Using the Form Builder

In this article by Christopher John Pecoraro, author of the book, Mastering Laravel, you learn the fundamentals of using the form builder.

(For more resources related to this topic, see here.)

Building web pages with Laravel

Laravel's approach to building web content is flexible. As much or as little of Laravel can be used to create HTML. Laravel uses the filename.blade.php convention to state that the file should be parsed by the blade parser, which actually converts the file into plain PHP. The name blade was inspired by the .NET's razor templating engine, so this may be familiar to someone who has used it. Laravel 5 provides a working demonstration of a form in the /resources/views/ directory. This view is shown when the /home route is requested and the user is not currently logged in. This form is obviously not created using the Laravel form methods.

The route is defined in the routes file as follows:

Route::get('home', 'HomeController@index');

The master template

This is the following app (or master) template:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,
initial-scale=1">
<title>Laravel</title>
<link href="/css/app.css" rel="stylesheet">
<!-- Fonts -->
<link href='//fonts.googleapis.com/css?family=Roboto:400,300'
rel='stylesheet' type='text/css'>
<!-- HTML5 shim and Respond.js for IE8 support
of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view
the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/
html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/
respond.min.js"></script>
<![endif]-->
</head>
<body>
<nav class="navbarnavbar-default">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle
collapsed" data-toggle="collapse" datatarget="#
bs-example-navbar-collapse-1">
<span class="sr-only">Toggle Navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Laravel</a>
</div>
<div class="collapse navbar-collapse" id="
bs-example-navbar-collapse-1">
<ul class="navnavbar-nav">
<li><a href="/">Home</a></li>
</ul>
<ul class="navnavbar-navnavbar-right">
@if (Auth::guest())
<li><a href="{{ route('auth.login')
}}">Login</a></li>
<li><a href="/auth/register">
Register</a></li>
@else
<li class="dropdown">
<a href="#" class="dropdown-toggle"
data-toggle="dropdown" role="button"
aria-expanded="false">{{
Auth::user()->name }} <span
class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="/auth/
logout">Logout</a></li>
</ul>
</li>
@endif
</ul>
</div>
</div>
</nav>
@yield('content')
<!-- Scripts -->
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/
2.1.3/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/twitterbootstrap/
3.3.1/js/bootstrap.min.js"></script>
</body>
</html>

The Laravel 5 master template is a standard HTML5 template with the following features:

  • If the browser is older than Internet Explorer 9:
    • Uses the HTML5 Shim from the CDN
    • Uses the Respond.js JavaScript code from the CDN to retrofit media queries and CSS3 features
  • Using @if (Auth::guest()), if the user is not authenticated, the login form is displayed; otherwise, the logout option is displayed
  • Twitter bootstrap 3.x is included in the CDN
  • The jQuery2.x is included in the CDN
  • Any template that extends this template can override the content section

An example page

The source code for the login page is as follows:

@extends('app')
@section('content')
<div class="container-fluid">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">Login</div>
<div class="panel-body">
@if (count($errors) > 0)
<div class="alert alert-danger">
<strong>Whoops!</strong> There were
some problems with your
input.<br><br>
<ul>
@foreach ($errors->all()
as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<form class="form-horizontal" role="form"
method="POST" action="/auth/login">
<input type="hidden" name="_token"
value="{{ csrf_token() }}">
<div class="form-group">
<label class="col-md-4 controllabel">
E-Mail Address</label>
<div class="col-md-6">
<input type="email" class="formcontrol"
name="email" value="{{
old('email') }}">
</div>
</div>
<div class="form-group">
<label class="col-md-4 controllabel">
Password</label>
<div class="col-md-6">
<input type="password"
class="form-control"
name="password">
</div>
</div>
<div class="form-group">
<div class="col-md-6 col-md-offset-4">
<div class="checkbox">
<label>
<input type="checkbox"
name="remember">
Remember Me
</label>
</div>
</div>
</div>
<div class="form-group">
<div class="col-md-6 col-md-offset-4">
<button type="submit"
lass="btn btn-primary"
style="margin-right: 15px;">
Login
</button>
<a href="/password/email">Forgot
Your Password?</a>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

From static HTML to static methods

This login page begins with the following:

@extends('app')

It obviously uses the object-oriented paradigm to state that the app.blade.php template will be rendered. The following line overrides the content:

@section('content')

For this exercise, the form builder will be used instead of the static HTML.

The form tag

We will convert a static form tag to a FormBuilder method. The HTML is as follows:

<form class="form-horizontal" role="form" method="POST"   action="/auth/login">

The method facade that we will use is as follows:

Form::open();

In the FormBuilder.php class, the $reserved attribute is defined as follows:

protected $reserved = ['method', 'url', 'route',   'action', 'files'];

The attributes that we need to pass to an array to the open() method are class, role, method, and action. Since method and action are reserved words, it is necessary to pass the parameters in the following manner:

Laravel form facade method array element

HTML Form tag attribute

method

method

url

action

role

role

class

class

Thus, the method call looks like this:

{!!
Form::open(['class'=>'form-horizontal',
'role =>'form',
'method'=>'POST',
'url'=>'/auth/login'])
!!}

The {!! !!} tags are used to start and end parsing of the form builder methods. The form method, POST, is placed first in the list of attributes in the HTML form tag.

The action attribute actually needs to be a url. If the action parameter is used, then it refers to the controller action. In this case, the url parameter produces the action attribute of the form tag.

Other attributes will be passed to the array and added to the list of attributes. The resultant HTML will be produced as follows:

<form method="POST" action="http://laravel.example/auth/login"
accept-charset="UTF-8" class="form-horizontal" role="form">
<input name="_token" type="hidden"
value="wUY2hFSEWCzKHFfhywHvFbq9TXymUDiRUFreJD4h">

The CRSF token is automatically added, as the form method is POST.

The text input field

To convert the input fields, a facade is used. The input field's HTML is as follows:

<input type="email" class="form-control" name="email" 
value="{{ old('email') }}">

Converting the preceding input field using a façade looks like this:

{!! Form::input('email','email',old('email'), 
['class'=>'form-control' ]) !!}

Similarly, the text field becomes:

{!! Form::input('password','password',null, 
['class'=>'form-control']) !!}

The input fields have the same signature. Of course, this can be refactored as follows:

<?php $inputAttributes = ['class'=>'form-control'] ?>
{!! Form::input('email','email',old('email'),
$inputAttributes ) !!}
...
{!! Form::input('password','password',null,$inputAttributes ) !!}

The label tag

The label tags are as follows:

<label class="col-md-4 control-label">E-Mail Address</label>
<label class="col-md-4 control-label">Password</label>

To convert the label tags (E-Mail Address and Password), we will first create an array to hold the attributes, and then pass this array to the labels, as follows:

$labelAttributes = ['class'=>'col-md-4 control-label'];

Here is the form label code:

{!! Form::label('email', 'E-Mail Address', $labelAttributes) !!}
{!! Form::label('password', 'Password', $labelAttributes) !!}

Checkbox

To convert the checkbox to a facade, we will convert this:

<input type="checkbox" name="remember"> Remember Me

The preceding code is converted to the following code:

{!! Form::checkbox('remember','') !!} Remember Me

Remember that the PHP parameters should be sent in single quotation marks if there are no variables or other special characters, such as line breaks, inside the string to parse, while the HTML produced will have double quotes.

The submit button

Lastly, the submit button will be converted as follows:

<button type="submit" class="btn btn-primary"
style="margin-right: 15px;">
Login
</button>

The preceding code after conversion is as follows:

  {!!
Form::submit('Login',
['class'=>'btn btn-primary',
'style'=>'margin-right: 15px;'])
!!}

Note that the array parameter provides an easy way to provide any desired attributes, even those that are not among the list of standard HTML form elements.

The anchor tag with links

To convert the links, a helper method is used. Consider the following line of code:

<a href="/password/email">Forgot Your Password?</a>

The preceding line of code after conversion becomes:

{!! link_to('/password/email', $title = 'Forgot Your 
Password?', $attributes = array(), $secure = null) !!}

The link_to_route() method may be used to link to a route. For similar helper functions, visit http://laravelcollective.com/docs/5.0/html.

Closing the form

To end the form, we'll convert the traditional HTML form tag </form> to a Laravel {!! Form::close() !!} form method.

The resultant form

By putting everything together, the page now looks like this:

@extends('app')
@section('content')
<div class="container-fluid">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">Login</div>
<div class="panel-body">
@if (count($errors) > 0)
<div class="alert alert-danger">
<strong>Whoops!</strong> There were some
problems with your input.<br><br>
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<?php $inputAttributes = ['class'=>'form-control'];
$labelAttributes = ['class'=>'col-md-4
control-label']; ?>
{!! Form::open(['class'=>'form-horizontal','role'=>
'form','method'=>'POST','url'=>'/auth/login']) !!}
<div class="form-group">
{!! Form::label('email', 'E-Mail
Address',$labelAttributes) !!}
<div class="col-md-6">
{!! Form::input('email','email',old('email'),
$inputAttributes) !!}
</div>
</div>
<div class="form-group">
{!! Form::label('password',
'Password',$labelAttributes) !!}
<div class="col-md-6">
{!! Form::input('password',
'password',null,$inputAttributes) !!}
</div>
</div>
<div class="form-group">
<div class="col-md-6 col-md-offset-4">
<div class="checkbox">
<label>
{!! Form::checkbox('remember','') !!}
Remember Me
</label>
</div>
</div>
</div>
<div class="form-group">
<div class="col-md-6 col-md-offset-4">
{!! Form::submit('Login',['class'=>
'btn btn-primary', 'style'=>
'margin-right: 15px;']) !!}
{!! link_to('/password/email', $title =
'Forgot Your Password?', $attributes =
array(), $secure = null); !!}
</div>
</div>
{!! Form::close() !!}
</div>
</div>
</div>
</div>
</div>
@endsection

Our example

If we want to create a form to reserve a room in our accommodation, we can easily call a route from our controller:

/**
* Show the form for creating a new resource.
*
* @return Response
*/
public function create()
{
return view('auth/reserve');
}

Now we need to create a new view that is located at resources/views/auth/reserve.blade.php.

In this view, we can create a form to reserve a room in an accommodation where the user can select the start date, which comprises of the start day of the month and year, and the end date, which also comprises of the start day of the month and year.

The form would begin as before, with a POST to reserve-room. Then, the form label would be placed next to the select input fields. Finally, the day, the month, and the year select form elements would be created as follows:

{!! Form::open(['class'=>'form-horizontal',
'role'=>'form',
'method'=>'POST',
'url'=>'reserve-room']) !!}
{!! Form::label(null, 'Start Date',$labelAttributes) !!}
{!! Form::selectMonth('month',date('m')) !!}
{!! Form::selectRange('date',1,31,date('d')) !!}
{!! Form::selectRange('year',date('Y'),date('Y')+3) !!}
{!! Form::label(null, 'End Date',$labelAttributes) !!}
{!! Form::selectMonth('month',date('m')) !!}
{!! Form::selectRange('date',1,31,date('d')) !!}
{!! Form::selectRange('year',date('Y'),
date('Y')+3,date('Y')) !!}
{!! Form::submit('Reserve',
['class'=>'btn btn-primary',
'style'=>'margin-right: 15px;']) !!}
{!! Form::close() !!}

Month select

Firstly, in the selectMonth method, the first parameter is the name of the input attribute, while the second attribute is the default value. Here, the PHP date method is used to extract the numeric portion of the current month—March in this case:

{!! Form::selectMonth('month',date('m')) !!}

The output, shown here formatted, is as follows:

<select name="month">
<option value="1">January</option>
<option value="2">February</option>
<option value="3" selected="selected">March</option>
<option value="4">April</option>
<option value="5">May</option>
<option value="6">June</option>
<option value="7">July</option>
<option value="8">August</option>
<option value="9">September</option>
<option value="10">October</option>
<option value="11">November</option>
<option value="12">December</option>
</select>

Date select

A similar technique is applied for the selection of the date, but using the selectRange method, the range of the days in the month are passed to the method. Similarly, the PHP date function is used to send the current date to the method as the fourth parameter:

{!! Form::selectRange('date',1,31,date('d')) !!}

Here is the formatted output:

<select name="date">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
...
<option value="28">28</option>
<option value="29">29</option>
<option value="30" selected="selected">30</option>
<option value="31">31</option>
</select>

The date that should be selected is 30, since today is March 30, 2015.

For the months that do not have 31 days, usually a JavaScript method would be used to modify the number of days based on the month and/or the year.

Year select

The same technique that is used for the date range is applied for the selection of the year; once again, using the selectRange method. The range of the years is passed to the method. The PHP date function is used to send the current year to the method as the fourth parameter:

{!! Form::selectRange('year',date('Y'),date('Y')+3,date('Y')) !!}

Here is the formatted output:

<select name="year">
<option value="2015" selected="selected">2015</option>
<option value="2016">2016</option>
<option value="2017">2017</option>
<option value="2018">2018</option>
</select>

Here, the current year that is selected is 2015.

Form macros

We have the same code that generates our month, date, and year selection form block two times: once for the start date and once for the end date. To refactor the code, we can apply the DRY (don't repeat yourself) principle and create a form macro. This will allow us to avoid calling the form element creation method twice, as follows:

<?php
Form::macro('monthDayYear',function($suffix='')
{
echo Form::selectMonth(($suffix!=='')?'month-
'.$suffix:'month',date('m'));
echo Form::selectRange(($suffix!=='')?'date-
'.$suffix:'date',1,31,date('d'));
echo Form::selectRange(($suffix!=='')?'year-
'.$suffix:'year',date('Y'),date('Y')+3,date('Y'));
});
?>

Here, the month, date, and year generation code is placed into a macro, which is inside the PHP tags, and it is necessary to add echo to print out the result. The monthDayYear name is given to this macro method. Calling our macro two times: once after each label; each time adding a different suffix via the $suffix variable. Now, our form code looks like this:

<?php
Form::macro('monthDayYear',function($suffix='')
{
echo Form::selectMonth(($suffix!=='')?'month-
'.$suffix:'month',date('m'));
echo Form::selectRange(($suffix!=='')?'date-
'.$suffix:'date',1,31,date('d'));
echo Form::selectRange(($suffix!=='')?'year-
'.$suffix:'year',date('Y'),date('Y')+3,date('Y'));
});
?>
{!! Form::open(['class'=>'form-horizontal',
'role'=>'form',
'method'=>'POST',
'url'=>'/reserve-room']) !!}
{!! Form::label(null, 'Start Date',$labelAttributes) !!}
{!! Form::monthDayYear('-start') !!}
{!! Form::label(null, 'End Date',$labelAttributes) !!}
{!! Form::monthDayYear('-end') !!}
{!! Form::submit('Reserve',['class'=>'btn btn-primary',
'style'=>'margin-right: 15px;']) !!}
{!! Form::close() !!}

Conclusion

The choice to include the HTML form generation package in Laravel 5 can ease the burden of having to create numerous HTML forms. This approach allows developers to use methods, create reusable macros, and use a familiar Laravel approach to build the frontend. Once the basic methods are learned, it is very easy to simply copy and paste the previously created form elements, and then change their element's name and/or the array that is sent to them.

Depending on the size of the project, this approach may or may not be the right choice. For a very small application, the difference in the amount of code that needs to be written is not very evident, although, as is the case with the selectMonth and selectRange methods, the amount of code necessary is drastic.

This technique, combined with the use of macros, makes it easy to reduce the occurrence of copy duplication. Also, one of the major problems with the frontend design is that the contents of the class of the various elements may need to change throughout the entire application. This would mean performing a large find and replace operation, where changes are required to be made to HTML, such as changing class attributes. By creating an array of attributes, including class, for similar elements, changes made to the entire form can be performed simply by modifying the array that those elements use.

In a larger project, however, where parts of forms may be repeated throughout the application, the wise use of macros can easily reduce the amount of code necessary to be written. Not only this, but macros can isolate the code inside from changes that would require more than one block of code to be changed throughout multiple files. In the example, where the month, date, and year is to be selected, it is possible that this could be used up to 20 times in a large application. Any changes made to the desired block of HTML can be simply done to the macro and the result would be reflected in all of the elements that use it.

Ultimately, the choice of whether or not to use this package will reside with the developer and the designer. Since a designer who wants to use an alternative frontend design tool may not prefer, nor be competent, to work with the methods in the package, he or she may want to not use it.

Summary

The construction of the master template was explained and then the form components, such as the various form input types, were shown through examples.

Finally, the construction of a form for the room reservation, was explained, as well as a "do not repeat yourself" form macro creation technique.

Resources for Article:


Further resources on this subject:


You've been reading an excerpt of:

Mastering Laravel

Explore Title