Writing your first Laravel app - Part five

Writing your first Laravel app - Part five

Rai Omido

Hi! It's Rai here!

I'm glad you are still following along.

# Introduction

This lesson continues where lesson four left off. We are going to learn how to write and use controllers and routes.

# Introduction to Controllers

Do you remember this code?

Route::get('/', function () { 
return view('index');
});

This type of implementation is called a Closure.

A closure in PHP is an anonymous function that can access variables imported from the outside scope without using any global variables.

~ PHP The Right Way

We don't have to keep responding to requests this way. This is where Controllers are our best friends.

Controllers are classes that contain logic for handling related requests. They are housed in the app/Http/Controllers directory.

The easiest way to create a Controller is by using the make:controller artisan command.

php artisan make:controller CategoriesController

//Create Resource (CRUD) controller
php artisan make:controller --resource CategoriesController

//Create Resource (CRUD) controller and specify model
php artisan make:controller CategoriesController --resource --model=Category

# Middleware

Middleware help you to filter Http requests that enter your application.

For instance, you may use middleware to restrict access to certain pages in your application to only authenticated users.

There are several middleware included in the Laravel framework by default. You also have the ability to create your own middleware using the make:middleware artisan command.

For this tutorial, the default middleware provided by Laravel will be sufficient.

~Assigning Middleware to a Controller

A middleware may be assigned to a controller in two ways;

  1. By assigning it to the controller's route in the route files.
  2. Specifying it within your controller's constructor.
~Assigning middleware via a controller's route

This may be done by calling the middleware() method of the Route facade in your route file.

Route::get('admin/posts', [PostsController::class', 'index'])->middleware('auth');

This line of code means that if a user visits the /admin/posts URL in our app without being logged in, they will be redirected to the login page.

~Assigning Middleware in a Controller's Constructor

To assign middleware using this method, you may call your controller's middleware() method from within the constructor like so;

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class PostsController extends Controller
{
public function __construct() {
$this->middleware('auth');
}
}

You can also restrict middleware to only specific methods like so;

# Only the index method
$this->middleware('auth')->only('delete');

# All the methods except index.
$this->middleware('auth')->except('index');

The delete and index are methods within our controller, in this case, the PostsController.

...

class PostsController extends Controller
{
...
    public function index() {
          //
  }
...

...
    public function delete() {
          //
  }
...
}

Also, you may register middleware using a closure. This comes in handy when you want to assign middleware without having to write an entire middleware class.

 public function __construct() {
      $this->middleware(function($request, $next) {
if($request->lang == 'es')
return redirect('es');
return $next($request);
});
}

This code checks if the user has selected the Spanish language and redirects them to the Spanish translation. Otherwise, it allows the request to be processed.

# Introduction to Routing

Routing is simply mapping Http requests to code that handles them.

In Laravel, the most basic form of a route may be defined as follows;

Route::get('/', function () {
return view('index');
});

By default, Laravel's routes are classified into four categories as we saw in part one of this tutorial.

# Defining Routes

For the most part, you will define your application's routes in the routes/web.php file. The routes may be accessed by typing their defined URLs in the browser.

For instance, you may define a route like;

Route::get('blog', [BlogController::class, 'index');

Then to access the route in the browser;

yoursite.test/blog
Resource Routes

Resource routes are used to assign CRUD routes to a controller.

Route::resource('posts', PostsController::class);

This will assign the following routes to the controller;

Verb URI Action Route Name
GET posts index posts.index
GET posts/create create posts.create
POST posts store posts.store
GET posts/{post} show posts.show
GET posts/{post}/edit edit posts.edit
PUT/PATCH posts/{post} update posts.update
DELETE posts/{post} destroy posts.destroy

 

You may also assign multiple resource controllers by using the resources() method;

Route::resources([
  'categories' => CategoriesController::class,
  'posts' => PostsController::class,
]);
~The API routes

You may define your API routes in the routes/api.php. By default, routes written in this file are accessed by prefixing all defined URLs with api/.

For instance, an API route defined as;

Route::get('users', [UsersController::class, 'index');

Will be accessible at;

yoursite.test/api/users
API resource routes

Just like the web routes, you can also assign CRUD routes to your API controllers, using the apiResource method;

Route::apiResource('posts', PostsController::class);

You may use the apiResources method to register many resource Controllers.

Route::apiResources([
  'categories' => CategoriesController::class,
  'posts' => PostsController::class,
]);

Note

For the api, the create and edit routes are excluded as these are only required in web routes to present HTML templates.

~Altering Default Routes Behavior

Just like with may other Laravel features, you have the ability to change the default behaviour of your application's routes. This includes modifying route prefixes, route group actions, and even adding other route groups.

You can achieve this by modifying your application's RouteServiceProvider class.

~Using HTTP verbs

The Route facade provides methods that correspond to all HTTP verbs.

Route::get('blah', /**/);

Route::post('blah', /**/);

Route::put('blah', /**/);

Route::patch('blah', /**/);

Route::get('options', /**/);

Route::delete('blah', /**/);

You have the ability to define routes that respond to several verbs. You can do this by using the match() method.

Route::match(['get', 'post'], 'blah', function() {
      //
});

You can also define a route that responds to all HTTP verbs using the any() method.

Route::any('blah', function() {
      //
});

# Route Parameters

Defining route parameters helps us to capture segments of a URI. The parameters may be required or optional. For example, if you would like to capture the id of a post, you would define your route as;

Route::get('posts/{id}', function ($id) {
  return "The id is {$id}";
});

In this case, the id parameter is mandatory.

To capture an optional parameter, you may suffix the parameter name with a question mark (?), like so;

Route::get('users/{name}', function ($name = null) {
  return "The name is {$name}";
});

# Other route actions

There are several other actions we can perform on routes. Let's go over these next up.

~Naming Routes

Route naming allows for the convenient generation of URLs.

To name a route, you should chain a name() method onto a route definition like so;

Route:get('blog', [BlogController::class, 'index'])->name('blog.index');

We can then generate our URLs by using any of the following methods;

Using the route() helper method;

$url = route('blog.index'); 
// -> yoursite.test/blog

Generating a redirect;

public function store() {
...
  return redirect()->route('blog.index');
}

In case the route definition requires parameters, you might have an implementation like this;

# Define your route
Route:get('blog/{slug}', [BlogController::class, 'show'])->name('blog.show');

# Generate your URL
$url = route('blog.show', ['slug' => 'how-to-bake-pancakes']);

//-> yoursite.test/blog/how-to-bake-pancakes
~Route Groups

Grouping routes allows you to share route parameters across several routes.

These parameters may be specified in array form as the first parameter of the group() method of the Route facade.

Route::group(['middleware' => ['auth', 'verified'], 'prefix' => 'admin/posts', 'as' => 'admin.posts.'], function() {

Route::get('/', [PostsControlller::class, 'index'])->name('index');
# $url = route('admin.posts.index')
# -> yoursite.test/admin/posts

  Route::get('{slug}', [PostsControlller::class, 'show'])->name('show');
# $url = route('admin.posts.show', ['slug' => 'how-to-bake-pancakes'])
# -> yoursite.test/admin/posts/how-to-bake-pancakes
});

You may also chain methods instead of using the array parameters above;

Route::middleware(['auth', 'verified'])->prefix('admin/posts')->name('blog.')->group(function() {
  //
});
~Form Method Spoofing

HTML forms do not support the PUT, PATCH, and DELETE HTTP methods. If you would like to use routes defined for these methods, you need to do the following;

  1. Define your form method as POST.
  2. Provide a hidden _method field alongside your form payload whose value is any of the HTTP methods. This can be achieved by adding a hidden field named _method in your form, with the desired method as the value. When the form is submitted, the value for the _method field will be used as the HTTP  method.
<form method="POST">
...
<input type="hidden" name="_method" value="PUT">

#Or using the @method blade directive
@method('PUT')
...
</form>
~CSRF protection

By default, if you try to submit forms using any of the POST, PUT, PATCH, and DELETE verbs for routes defined in the web.php file, they will be rejected. This is because Laravel implements CSRF protection for web routes out of the box for the security of your app.

As a result, you need to send a field named _token with a CSRF token as its value, alongside your form data. This can be achieved by creating a hidden input named _token in your form. This will be submitted alongside your data and its value used as the CSRF token.

<form method="POST">
<input type="hidden" name="_token" value="{{csrf_token()}}">
</form>

Or using the blade directive @csrf

<form method="POST">
@csrf
</form>

# What's next?

At this point, you should be a little well versed in Laravel's controllers and routing. Next up, we shall be exploring view and blade templates. See you in the next lesson.