Laravel 5.4: Form Request Data and CSRF

For this tutorial, we carry on from the last but start from scratch in creating a bootstrap blog:

Ultimate render should look the same as that on bootstrap!


So, now we've got the bootstrap blog theme loaded and working we can add the Form capability!

First, we need to add the ability to create a post. So, we add the create method to /app/Http/Controllers/PostsController.php:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

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');
	}
}

Adjust the route /routes/web.php for the create method:

<?php

Route::get('/', 'PostsController@index');

Route::get('/posts/create', 'PostsController@create');

//Route::get('/posts/{post}', 'PostsController@show');

And create the /resources/views/posts/create.blade.php view:

@extends('layouts.master')

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

*note use of the div with class col-md-8

Next, we insert our form. First go to https://getbootstrap.com/docs/4.0/components/forms/ to save time and get the form code:

<form>
  <div class="form-group">
    <label for="exampleInputEmail1">Email address</label>
    <input type="email" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp" placeholder="Enter email">
    <small id="emailHelp" class="form-text text-muted">We'll never share your email with anyone else.</small>
  </div>
  <div class="form-group">
    <label for="exampleInputPassword1">Password</label>
    <input type="password" class="form-control" id="exampleInputPassword1" placeholder="Password">
  </div>
  <div class="form-check">
    <input type="checkbox" class="form-check-input" id="exampleCheck1">
    <label class="form-check-label" for="exampleCheck1">Check me out</label>
  </div>
  <button type="submit" class="btn btn-primary">Submit</button>
</form>

and paste this in /resources/views/posts/create.blade.php to give:

@extends('layouts.master')

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

		<form>
		  <div class="form-group">
		    <label for="exampleInputEmail1">Email address</label>
		    <input type="email" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp" placeholder="Enter email">
		    <small id="emailHelp" class="form-text text-muted">We'll never share your email with anyone else.</small>
		  </div>
		  <div class="form-group">
		    <label for="exampleInputPassword1">Password</label>
		    <input type="password" class="form-control" id="exampleInputPassword1" placeholder="Password">
		  </div>
		  <div class="form-check">
		    <input type="checkbox" class="form-check-input" id="exampleCheck1">
		    <label class="form-check-label" for="exampleCheck1">Check me out</label>
		  </div>
		  <button type="submit" class="btn btn-primary">Submit</button>
		</form>
		
	</div>
@endsection

adjust pasted text to end up with:

@extends('layouts.master')

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

		<form method="Post" action="/posts">
		  <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>
		  <button type="submit" class="btn btn-primary">Publish</button>
		</form>

	</div>
@endsection

Notice the form's method and action have now been added.

Now, we add the store method to /routes/web.php:

<?php

Route::get('/', 'PostsController@index');

Route::get('/posts/create', 'PostsController@create');

Route::post('/posts', 'PostsController@store');

//Route::get('/posts/{post}', 'PostsController@show');

and set up the store method in /app/Http/Controllers/PostsController.php to create a new post using the request data, save it to the database, and then redirect to the home page:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

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()
	{
		dd(request()->all());
	}
}

*note, line 23 use of dd just for testing purposes...

Now, run /posts/create/ and enter some test data.

This should fail, due to TokenMismatchException!

Which is due to Laravel's inbuilt protection against Cross-Site Request Forgery:  {{ csrf_field() }}.

ALL forms should include this call:  {{ csrf_field() }}.

Quickly test to see the submitted json string in the browser.

Next , adjust /app/Http/Controllers/PostsController.php to store the submitted post:

<?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()
	{
		//Create a new post using the request data
		$post = new Post;

		$post->title = request('title');
		$post->body = request('body');

		// Save it to the database
		$post->save();

		// And then redirect to the home page
		return redirect('/');

	}
}

Upon submission of the new post, the user is taken back to the home page, but you'll notice that the newly submitted post has not been rendered. However, a quick hop into Tinker, shows the data has been submitted to the database:

php artisan tinker
Psy Shell v0.9.3 (PHP 7.2.4-1+ubuntu16.04.1+deb.sury.org+1 â cli) by Justin Hileman
>>> App\Post::all();
=> Illuminate\Database\Eloquent\Collection {#2308
     all: [
       App\Post {#2309
         id: 1,
         title: "My test title",
         body: "My test body",
         created_at: "2018-04-24 18:01:33",
         updated_at: "2018-04-24 18:01:33",
       },
     ],
   }
>>>

Next, we can tidy up the store method using a create method and passing in an array of data:

<?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()
	{
		Post::create(request(['title', 'body']));

		return redirect('/');
	}
}

However, this will fail due to a MassAssignmentException error, as a result of Laravel's inbuilt security features. To overcome this we can add a fillable property to /app/Post.php:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    protected $fillable = ['title', 'body'];
}

Again, checking Tinker, we can see the data has been submitted to the database:

php artisan tinker
Psy Shell v0.9.3 (PHP 7.2.4-1+ubuntu16.04.1+deb.sury.org+1 â cli) by Justin Hileman
>>> App\Post::all();
=> Illuminate\Database\Eloquent\Collection {#2308
     all: [
       App\Post {#2309
         id: 1,
         title: "My test title",
         body: "My test body",
         created_at: "2018-04-24 18:01:33",
         updated_at: "2018-04-24 18:01:33",
       },
       App\Post {#2310
         id: 2,
         title: "A brand new title",
         body: "With a brand new body",
         created_at: "2018-04-24 18:04:43",
         updated_at: "2018-04-24 18:04:43",
       },
     ],
   }
>>>

 

Next, we'll look at form validation before we move on to rendering the post into the view...