Since we know we’re going to need to retrieve and store tasks, let’s create aTaskControllerusing the Artisan CLI, which will place the new controller in theapp/Http/Controllersdirectory:
1 php artisan make:controller TaskController
Now that the controller has been generated, let’s go ahead and stub out some routes in our app/Http/routes.phpfile to point to the controller:
1 Route::get('/tasks', 'TaskController@index'); 2 Route::post('/task', 'TaskController@store');
3 Route::delete('/task/{task}', 'TaskController@destroy');
Authenticating All Task Routes
For this application, we want all of our task routes to require an authenticated user. In other words, the user must be “logged into” the application in order to create a task. So, we need to restrict access to our task routes to only authenticated users. Laravel makes this a cinch usingmiddleware. To require an authenticated users for all actions on the controller, we can add a call to the middlewaremethod from the controller’s constructor. All available route middleware are defined in theapp/Http/Kernel.phpfile. In this case, we want to assign theauthmiddleware to all actions on the controller: 1 <?php 2 3 namespace App\Http\Controllers; 4 5 use App\Http\Requests; 6 use Illuminate\Http\Request; 7 use App\Http\Controllers\Controller; 8
9 class TaskController extends Controller
10 {
11 /**
12 * Create a new controller instance.
13 *
14 * @return void
15 */
16 public function __construct()
17 {
18 $this->middleware('auth');
19 }
Building Layouts & Views
The primary part of this application only has a single view which contains a form for adding new tasks as well as a listing of all current tasks. To help you visualize the view, here is a screenshot of the finished application with basic Bootstrap CSS styling applied:
Application Image
Defining The Layout
Almost all web applications share the same layout across pages. For example, this application has a top navigation bar that would be typically present on every page (if we had more than one). Laravel makes it easy to share these common features across every page using Blade layouts.
As we discussed earlier, all Laravel views are stored in resources/views. So, let’s define a new layout view inresources/views/layouts/app.blade.php. The.blade.phpextension instructs the framework to use the Blade templating engine to render the view. Of course, you may use plain PHP templates with Laravel. However, Blade provides convenient short-cuts for writing cleaner, terse templates.
Ourapp.blade.phpview should look like the following: 1 // resources/views/layouts/app.blade.php
2
3 <!DOCTYPE html>
4 <html lang="en"> 5 <head>
6 <title>Laravel Quickstart - Intermediate</title> 7
8 <!-- CSS And JavaScript --> 9 </head>
10
11 <body>
12 <div class="container"> 13 <nav class="navbar navbar-default"> 14 <!-- Navbar Contents --> 15 </nav> 16 </div> 17 18 @yield('content') 19 </body>
20 </html>
Note the @yield('content') portion of the layout. This is a special Blade directive that specifies where all child pages that extend the layout can inject their own content. Next, let’s define the child view that will use this layout and provide its primary content.
Defining The Child View
Great, our application layout is finished. Next, we need to define a view that contains a form to create a new task as well as a table that lists all existing tasks. Let’s define this view in resources/views/tasks/index.blade.php, which will correspond to the index method in our TaskController.
We’ll skip over some of the Bootstrap CSS boilerplate and only focus on the things that matter. Remember, you can download the full source for this application onGitHub⁵²:
1 // resources/views/tasks/index.blade.php 2 3 @extends('layouts.app') 4 5 @section('content') 6 7 <!-- Bootstrap Boilerplate... --> 8 9 <div class="panel-body">
10 <!-- Display Validation Errors --> 11 @include('common.errors') 12
13 <!-- New Task Form -->
14 <form action="{{ url('task') }}" method="POST" class="form-horizontal"> 15 {!! csrf_field() !!}
16
17 <!-- Task Name --> 18 <div class="form-group">
19 <label for="task-name" class="col-sm-3 control-label">Task</label> 20
21 <div class="col-sm-6">
22 <input type="text" name="name" id="task-name" class="form-control"> 23 </div>
24 </div>
25
26 <!-- Add Task Button --> 27 <div class="form-group">
28 <div class="col-sm-offset-3 col-sm-6">
29 <button type="submit" class="btn btn-default"> 30 <i class="fa fa-plus"></i> Add Task
31 </button> 32 </div> 33 </div> 34 </form> 35 </div> 36
37 <!-- TODO: Current Tasks --> 38 @endsection
A Few Notes Of Explanation
Before moving on, let’s talk about this template a bit. First, the @extendsdirective informs Blade that we are using the layout we defined at resources/views/layouts/app.blade.php. All of the content between @section('content')and @endsection will be injected into the location of the @yield('content')directive within theapp.blade.phplayout.
The@include('common.errors')directive will load the template located atresources/views/com- mon/errors.blade.php. We haven’t defined this template, but we will soon!
Now we have defined a basic layout and view for our application. Let’s go ahead and return this view from theindexmethod of ourTaskController:
1 /**
2 * Display a list of all of the user's task.
3 *
4 * @param Request $request 5 * @return Response
6 */
7 public function index(Request $request)
8 {
9 return view('tasks.index');
10 }
Next, we’re ready to add code to ourPOST /taskroute’s controller method to handle the incoming form input and add a new task to the database.
Adding Tasks
Validation
Now that we have a form in our view, we need to add code to ourTaskController@storemethod to validate the incoming form input and create a new task. First, let’s validate the input.
For this form, we will make the name field required and state that it must contain less than 255 characters. If the validation fails, we want to redirect the user back to the/tasksURL, as well as flash the old input and errors into thesession:
1 /**
2 * Create a new task.
3 *
4 * @param Request $request 5 * @return Response
6 */
7 public function store(Request $request)
8 {
9 $this->validate($request, [
10 'name' => 'required|max:255',
11 ]);
12
13 // Create The Task...
14 }
If you followed along with thebasic quickstart, you’ll notice this validation code looks quite a bit different! Since we are in a controller, we can leverage the convenience of theValidatesRequests trait that is included in the base Laravel controller. This trait exposes a simplevalidate method which accepts a request and an array of validation rules.
We don’t even have to manually determine if the validation failed or do manual redirection. If the validation fails for the given rules, the user will automatically be redirected back to where they came from and the errors will automatically be flashed to the session. Nice!
The$errorsVariable
Remember that we used the@include('common.errors')directive within our view to render the form’s validation errors. The common.errors will allow us to easily show validation errors in the same format across all of our pages. Let’s define the contents of this view now:
1 // resources/views/common/errors.blade.php 2
3 @if (count($errors) > 0) 4 <!-- Form Error List -->
5 <div class="alert alert-danger">
6 <strong>Whoops! Something went wrong!</strong> 7
8 <br><br> 9
10 <ul>
11 @foreach ($errors->all() as $error) 12 <li>{{ $error }}</li>
13 @endforeach
14 </ul> 15 </div> 16 @endif
Note: The$errorsvariable is available in every Laravel view. It will simply be an empty
instance ofViewErrorBagif no validation errors are present.
Creating The Task
Now that input validation is handled, let’s actually create a new task by continuing to fill out our route. Once the new task has been created, we will redirect the user back to the/tasks URL. To create the task, we are going to leverage the power of Eloquent’s relationships.
Most of Laravel’s relationships expose acreatemethod, which accepts an array of attributes and will automatically set the foreign key value on the related model before storing it in the database. In this case, thecreatemethod will automatically set theuser_idproperty of the given task to the ID of the currently authenticated user, which we are accessing using$request->user():
1 /**
2 * Create a new task.
3 *
4 * @param Request $request 5 * @return Response
6 */
7 public function store(Request $request)
8 { 9 $this->validate($request, [ 10 'name' => 'required|max:255', 11 ]); 12 13 $request->user()->tasks()->create([ 14 'name' => $request->name, 15 ]); 16 17 return redirect('/tasks'); 18 }
Great! We can now successfully create tasks. Next, let’s continue adding to our view by building a list of all existing tasks.
Displaying Existing Tasks
First, we need to edit ourTaskController@indexmethod to pass all of the existing tasks to the view. Theviewfunction accepts a second argument which is an array of data that will be made available to the view, where each key in the array will become a variable within the view. For example, we could do this:
1 /**
2 * Display a list of all of the user's task.
3 *
4 * @param Request $request 5 * @return Response
6 */
7 public function index(Request $request)
8 {
9 $tasks = Task::where('user_id', $request->user()->id)->get(); 10
11 return view('tasks.index', [
13 ]);
14 }
However, let’s explore some of the dependency injection capabilities of Laravel to inject aTaskRepos- itoryinto ourTaskController, which we will use for all of our data access.