Laravel 5.4: Form Validation

First off, we'll use HTML5's Browser Validation 'required' field in /resources/views/layouts/posts/create.blade.php:

@extends('layouts.master')

@section('content')
	<div class="col-md-8 blog-main">
		<h1>Publish a Post</h1>

		<form method="Post" action="/posts">
			{{ csrf_field() }}
		  <div class="form-group">
		    <label for="title">Title</label>
		    <input type="text" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp" name="title" required>
		  </div>
		  <div class="form-group">
		    <label for="exampleInputPassword1">Body</label>
		    <textarea id="body" name="body" class="form-control" required></textarea>
		  </div>
		  <button type="submit" class="btn btn-primary">Publish</button>
		</form>

	</div>
@endsection

*note 'required' on lines  11 & 15

However, not all browsers support this, especially older ones.

So, as an exercise we remove the above and continue with server side validation in /app/Http/Controllers/PostsController.php:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use App\Post;

class PostsController extends Controller
{
	public function index()
	{
		return view('posts.index');
	}
	public function show()
	{
		return view('posts.show');
	}
	public function create()
	{
		return view('posts.create');
	}
	public function store()
	{
		$this->validate(request(), [
			'title' => 'required',
			'body' => 'required'
		]);

		Post::create(request(['title', 'body']));

		return redirect('/');
	}
}

Notice Laravel's builtin validate() method on line 25, to which we pass in the array of fields to be validated against the required rules, in the above those being 'require' on lines 26 & 26.

Laravel's validation rules are quite in depth, so it's a good idea to read up on those: https://laravel.com/docs/5.6/validation

If you were to now try to submit the form, Laravel would return you back to the home page, as expected in line 32 above.

However, Laravel also provides a populated $errors variable (for every view), which we can leverage in /resources/views/layouts/posts/create.blade.php:

@extends('layouts.master')

@section('content')
	<div class="col-md-8 blog-main">
		<h1>Publish a Post</h1>

		<form method="Post" action="/posts">
			{{ csrf_field() }}
			<div class="form-group">
				<label for="title">Title</label>
				<input type="text" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp" name="title">
			</div>
			<div class="form-group">
				<label for="exampleInputPassword1">Body</label>
				<textarea id="body" name="body" class="form-control"></textarea>
			</div>
			<div class="form-group">
				<button type="submit" class="btn btn-primary">Publish</button>
			</div>
			<div class="form-group">
				<div class="alert alert-danger">
					<ul>
						@foreach ($errors->all() as $error)
							<li> {{ $error }} </li>
						@endforeach
					</ul>
				</div>
			</div>
		</form>
	</div>
@endsection

*note div form-groups have been added for layout

However, this now shows the alert area even if there are no errors. We therefore set a conditional (line 20) to check if there are any errors:

@extends('layouts.master')

@section('content')
	<div class="col-md-8 blog-main">
		<h1>Publish a Post</h1>

		<form method="Post" action="/posts">
			{{ csrf_field() }}
			<div class="form-group">
				<label for="title">Title</label>
				<input type="text" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp" name="title">
			</div>
			<div class="form-group">
				<label for="exampleInputPassword1">Body</label>
				<textarea id="body" name="body" class="form-control"></textarea>
			</div>
			<div class="form-group">
				<button type="submit" class="btn btn-primary">Publish</button>
			</div>
			@if(count($errors))
				<div class="form-group">
					<div class="alert alert-danger">
						<ul>
							@foreach ($errors->all() as $error)
								<li> {{ $error }} </li>
							@endforeach
						</ul>
					</div>
				</div>
			@endif
		</form>
	</div>
@endsection

Since there could be a number of forms, it makes sense to take this errors section and put into its own partial, /resources/views/layouts/errors.blade.php:

@if(count($errors))
	<div class="form-group">
		<div class="alert alert-danger">
			<ul>
				@foreach ($errors->all() as $error)
					<li> {{ $error }} </li>
				@endforeach
			</ul>
		</div>
	</div>
@endif

and simply use an include within /resources/views/layouts/posts/create.blade.php in its place, on line 20:

@extends('layouts.master')

@section('content')
	<div class="col-md-8 blog-main">
		<h1>Publish a Post</h1>

		<form method="Post" action="/posts">
			{{ csrf_field() }}
			<div class="form-group">
				<label for="title">Title</label>
				<input type="text" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp" name="title">
			</div>
			<div class="form-group">
				<label for="exampleInputPassword1">Body</label>
				<textarea id="body" name="body" class="form-control"></textarea>
			</div>
			<div class="form-group">
				<button type="submit" class="btn btn-primary">Publish</button>
			</div>
			@include('layouts.errors')
		</form>
	</div>
@endsection