Inlining flash messages with the redirect() call in Laravel
Say you're creating an admin dashboard for a blog. When the user saves a post, you want to redirect to the posts index and display a success message to the user. Wouldn't it be cool if you could do all of that in one line?
First lets go through some ways to achieve this functionality.
TL;DR: go to the final solution.
Quick & dirty
We put this code in the PostsController
:
public function store(Request $request)
{
Post::create($request->only(['title', 'body']));
session()->flash('message', 'Post was created!');
return redirect()->route('posts.index');
}
We create the post in the database. Then we store the message Post was created!
in the session. We use the flash()
method so that the message
key will only be available during the next HTTP request and then it will be deleted. Then we redirect to the posts index page.
We put this code somewhere in the app.blade.php
layout file:
@if (session()->has('message'))
<div class="alert alert-info">
{{ session('message') }}
</div>
@endif
We use the global session()
helper and its has()
method to determine whether there is a message
stored in the session. If so, we display it to the user, using the standard Bootstrap alert component.
It works fine, but we can do better.
Using the laracasts/flash package
This functionality is so common that Jeffrey Way from Laracasts created a package for it. It's super simple to use. For installation details, follow the readme at the Github repository.
Here's how we can change our PostsController
to make use of the package:
public function store(Request $request)
{
Post::create($request->only(['title', 'body']));
flash('Post was created!');
return redirect()->route('posts.index');
}
We also need to change the app.blade.php
file:
@if (session()->has('flash_notification.message'))
<div class="alert alert-{{ session('flash_notification.level') }}">
{!! session('flash_notification.message') !!}
</div>
@endif
The cool thing is that we can set the contextual class of the alert. By default it's set as info
, but if you change the middle line of our PostsController@store
method to flash('Post was created!', 'success')
the alert will get a green background indicating successful action.
Ok, this is nice, but wouldn't it be cool if we could inline the flash()
call with the return redirect()
call?
Using the RedirectResponse
Lets go back to using what Laravel gives us by default.
Lets change the PostsController
to this:
public function store(Request $request)
{
Post::create($request->only(['title', 'body']));
return redirect()->route('posts.index')->with('message', 'Post was created!');
}
When we call the redirect()
global helper, we get back an instance of RedirectResponse
. We can flash data to the session by chaining the with()
method to the redirect()
call.
Lets change our app.blade.php
file back to this:
@if (session()->has('message'))
<div class="alert alert-info">
{{ session('message') }}
</div>
@endif
The controller method looks better, but we lost the ability to set the class of the alert. Or have we? We can chain more with()
methods to the redirect()
call. Lets change the last line of the PostsController@store
method to this:
return redirect()->route('posts.index')->with('flash.message', 'Post was created!')->with('flash.class', 'success');
And our app.blade.php
file to this:
@if (session()->has('flash.message'))
<div class="alert alert-{{ session('flash.class') }}">
{{ session('flash.message') }}
</div>
@endif
Ok, lets recap. We shortened the controller method and we were able to retain the ability to set the css class of the alert. Cool, but this line has become rather long and untidy:
return redirect()->route('posts.index')->with('flash.message', 'Post was created!')->with('flash.class', 'success');
What if instead we could write something like:
return redirect()->route('posts.index')->withSuccessMessage('Post was created!');
Feels better, right?
Extending the RedirectResponse
The RedirectResponse
class that we get when we use the redirect()
helper is macroable. What that means is that we can easily add our custom withSuccessMessage
method to it.
Lets add this to our AppServiceProvider
:
public function boot()
{
RedirectResponse::macro('withSuccessMessage', function ($message) {
return RedirectResponse::with('flash.message', $message)->with('flash.class', 'success');
});
}
That's it. Now our controller method can look like this:
public function store(Request $request)
{
Post::create($request->only(['title', 'body']));
return redirect()->route('posts.index')->withSuccessMessage('Post was created!');
}