This tutorial is out of date and no longer maintained.
Views hold the presentation logic of a Laravel application. It is served separately from the application logic using Laravel’s blade templating engine.
Passing data from a controller to a view is as simple as declaring a variable and adding it as a parameter to the returned view helper method. There is no shortage of ways to do this.
We will create a SampleController
class that will handle our logic
- php artisan make:controller SampleController
Here is a sample controller in app/Http/Controllers/SampleController.php
class SampleController extends Controller
{
/**
* pass an array to the 'foo' view
* as a second parameter.
*/
public function foo()
{
return view('foo', [
'key' => 'The quick brown fox jumped over the lazy dog'
]);
}
/**
* Pass a key variable to the 'foo view
* using the compact method as
* the second parameter.
*/
public function bar()
{
$key = 'If a woodchuck could chuck wood,';
return view('foo', compact('key'));
}
/**
* Pass a key, value pair to the view
* using the with method.
*/
public function baz()
{
return view('foo')->with(
'key',
'How much wood would a woodchuck chuck.'
);
}
}
This is all fine and dandy. Well, it is until you try passing data to many views.
More often than not, we need to get some data on different pages. One such scenario would be information on the navigation bar or footer that will be available across all pages on your website, say, the most recent movie in theatres.
For this example, we will use an array of 5 movies to display the latest movie (the last item on the array) on the navigation bar.
For this, I will use a Boostrap template to set up the navigation bar in resources/views/app.blade.php
.
<nav class="navbar navbar-inverse">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<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="/">Movie Maniac</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li><a href="foo">Foo</a></li>
<li><a href="bar">Bar</a></li>
<li><a href="baz">Baz</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li><a href="#">latest movie title here</a></li>
</ul>
</div>
<!-- /.navbar-collapse -->
</div>
<!-- /.container-fluid -->
</nav>
The latest movie text on the far right will however be replaced with a title from our movie list to be created later on.
Let’s go ahead and create our movie list on the homepage.
View all the routes in app/Http/routes.php
Route::get('/', 'SampleController@index');
Route::get('/foo', 'SampleController@foo');
Route::get('/bar', 'SampleController@bar');
Route::get('/baz', 'SampleController@baz');
We are just creating four simple routes.
View the controller in app/Http/Controllers/SampleController.php
/**
* Return a list of the latest movies to the
* homepage
*
* @return View
*/
public function index()
{
$movieList = [
'Shawshank Redemption',
'Forrest Gump',
'The Matrix',
'Pirates of the Carribean',
'Back to the Future',
];
return view('welcome', compact('movieList'));
}
See latest movie views in resources/views/welcome.blade.php
@extends('app')
@section('content')
<h1>Latest Movies</h1>
<ul>
@foreach($movieList as $movie)
<li class="list-group-item"><h5>{{ $movie }}</h5></li>
@endforeach
</ul>
@endsection
It goes without saying that my idea of the latest movies is skewed, but we can overlook that for now. Here is what our homepage looks like now.
Awesome! We have our movie list. And now to the business of the day.
We will assume that Back to the future, being the last movie on our movie list, is the latest movie, and display it as such on the navigation bar.
/**
* Return a list of the latest movies to the
* homepage
*
* @return View
*/
public function index()
{
$movieList = [
'Shawshank Redemption',
'Forrest Gump',
'The Matrix',
'Pirates of the Carribean',
'Back to the Future',
];
$latestMovie = end($movieList);
return view('welcome', compact('movieList', 'latestMovie'));
}
We now have Back to the future as our latest movie, and rightfully so because Back to the Future 4 was released a week from now in 1985. I cannot make this stuff up.
This seems to work. Well until you try navigating to other pages (Try one of foo, bar, baz) created earlier on. This will throw an error.
This was expected and by now you must have figured out why this happened. We declared the latest movie variable on the index
method of the controller and passed it to the welcome
biew.
By extension, we made latestMovie
available to the navigation bar BUT only to views/welcome.blade.php
.
When we navigate to /foo
, our navigation bar still expects a latestMovie
variable to be passed to it from the foo
method but we have none to give.
There are three ways to fix this
Declare the latestMovie
value in every other method, and in this case, the movieList
too. It goes without saying we will not be doing this.
Place the movie information in a service provider’s boot method. You can place it on App/Providers/AppServiceProvider
or create one. This quickly becomes inefficient if we are sharing a lot of data.
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
view()->share('key', 'value');
}
View composers are callbacks or class methods that are called when a view is rendered. If you have data that you want to be bound to a view each time that view is rendered, a view composer can help you organize that logic into a single location.
-Laravel documentation
While it is possible to get the data in every controller method and pass it to the single view, this approach may be undesirable.
View composers, as described from the Laravel documentation, bind data to a view every time it is rendered. They clean our code by getting fetching data once and passing it to the view.
Since Laravel does not include a ViewComposers
directory in its application structure, we will have to create our own for better organization. Go ahead and create App\Http\ViewComposers
We will then proceed to create a new service provider to handle all our view composers using the artisan command
- php artisan make:provider ComposerServiceProvider
The service provider will be visible in app/Providers
Add the ComposerServiceProvider
class to config/app.php
array for providers
so that Laravel is able to identify it.
Modify the boot
method in the new Provider by adding a composer
method that extends view()
/**
* Bootstrap the application services.
*
* @return void
*/
public function boot()
{
view()->composer(
'app',
'App\Http\ViewComposers\MovieComposer'
);
}
Laravel will execute a MovieComposer@compose
method every time app.blade.php
is rendered. This means that every time our navigation bar is loaded, we will be ready to serve it with the latest movie from our view composer.
While MovieComposer@compose
is the default method to be called, you can overwrite it by specifying your own custom method name in the boot method.
view()->composer('app', 'App\Http\ViewComposers\MovieComposer@foobar');
Next, we will create our MovieComposer
class
<?php
namespace App\Http\ViewComposers;
use Illuminate\View\View;
class MovieComposer
{
public $movieList = [];
/**
* Create a movie composer.
*
* @return void
*/
public function __construct()
{
$this->movieList = [
'Shawshank Redemption',
'Forrest Gump',
'The Matrix',
'Pirates of the Carribean',
'Back to the Future',
];
}
/**
* Bind data to the view.
*
* @param View $view
* @return void
*/
public function compose(View $view)
{
$view->with('latestMovie', end($this->movieList));
}
}
The with
method will bind the latestMovies to the app
view when it is rendered. Notice that we added Illuminate\View\View
.
We can now get rid of the latestMovie
variable and actually remove it from the compact
helper method in SampleController@index
.
public function index()
{
$movieList = [
'Shawshank Redemption',
'Forrest Gump',
'The Matrix',
'Pirates of the Carribean',
'Back to the Future',
];
return view('welcome', compact('movieList'));
}
We can now access the latest movie on the navigation bar in all our routes.
While we seem to have solved one issue, we seem to have created another. we now have two movieList
arrays. one in the controller and one in the constructor method in MovieComposer
.
While this may have been caused by using an array as a simple data source, it may be a good idea to fix it, say, by creating a movie repository. Ideally, the latestMovie
value would be gotten from a database using Eloquent.
Check out the GitHub repo for this tutorial to see how I created a Movie Repository to get around the redundancy as shown below in MovieComposer
and SampleController
.
public $movieList = [];
/**
* Create a movie composer.
*
* @param MovieRepository $movie
*
* @return void
*/
public function __construct(MovieRepository $movies)
{
$this->movieList = $movies->getMovieList();
}
/**
* Bind data to the view.
*
* @param View $view
* @return void
*/
public function compose(View $view)
{
$view->with('latestMovie', end($this->movieList));
}
public function index(MovieRepository $movies)
{
$movieList = $movies->getMovieList();
return view('welcome', compact('movieList'));
}
It is possible to create a view composer that is executed when all views are rendered by replacing the view name with an asterisk wildcard
view()->composer('*', function (View $view) {
//logic goes here
});
Notice that instead of passing a string with the path to MovieComposer, you can also pass a closure.
You can also limit the view composer to a finite number of views, say, nav
and footer
view()->composer(
['nav', 'footer'],
'App\Http\ViewComposers\MovieComposer'
);
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!