Building a Laravel 8 Application: Routes and Views

In this post, we start building a Laravel 8 Application just created. You can follow the tutorial Create a Laravel 8 application from scratch to install and create your first project and the initial application structure. However, this can be applied to a project created using the Laravel Sail tools or Composer.

The main areas to work at the start of the project are:

  • Understanding the project directory structure
  • Adding/Modifying routes
  • Creating/Modifying views

Laravel 8 Project Directory Structure

The main folders used by the projects are:

  • app: contains the application code. It includes Controllers, Middleware, Models and Providers
  • bootstrap: includes the app.php file (main Application object). It contains the cache directory, used to cache generated files to improve performance.
  • config: contains all the configuration files, including app localization, timezone, application components, databases, filesystems, logs.
  • database: includes database scripts for schema definitions, migrations, and factories. You can use this directory to store an SQLite database.
  • public: all the public/static files to be served by the application, including the main page script (index.php), icons, and other compiled assets.
  • resources: contains the application views (PHP blade templates) as well as any un-compiled assets such as CSS or JavaScript. This directory also stores application language files for internationalization.
  • routes: this is the place to define routes for your application, console commands, REST APIs, and broadcasts.
  • tests: contains PHPUnit automated tests created to verify your application components, and generated reports of the test results.
  • vendor: this folder automatically stores all Laravel and third-party Composer components and the autoloader scripts.

A plain view of the main directory tree looks like this:

+-app
|---Console
|---Exceptions
|---Http
|---Models
|---Providers
+-bootstrap
|---cache
+-config
+-database
|---factories
|---migrations
|---seeders
+-public
+-resources
|---css
|---js
|---lang
|---views
+-routes
+-storage
|---app
|---framework
|---logs
+-tests
|---Feature
|---Unit
+-vendor

Laravel 8 application routes and views

The first step is to learn about the Laravel routing configuration. Routing is the mechanism to map a URL path to a specific resource of the application. The main configuration files for routes are stored in the /routes/ folder:

  • web.php : routes for the web application.
  • api.php : routes for a REST API interface
  • console.php : commands to run using artisan console commands
  • channels.php : used to “broadcast” your server-side Laravel events over a WebSocket connection (see https://laravel.com/docs/8.x/broadcasting)

Web routing

The main application route configuration is stored at routes/web.php. This file already contains the main route (the root path / ) pointing to a welcome page view.

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

Views are stored in the /resources/views/ folder. Normally, you can use blade templates to work with PHP views adding additional templating features like value formatting, loops, conditions, sub-views. In this example calling to view(‘welcome’) will route the root path to the file /resources/views/welcome.blade.php (see more details in the views section)

Each route has a method, a URI expression and the request callback to manage requests to this route.

Route methods

A route can be defined using any common HTTP method, matching the Route class method name:

Route::get($uri, $callback);
Route::post($uri, $callback);
Route::put($uri, $callback);
Route::patch($uri, $callback);
Route::delete($uri, $callback);
Route::options($uri, $callback);

Also, you can use Route::match() to match more than one method, and Route::any() to match any method.

Route::match(['get', 'post'], '/', function () {
    // controller code for both GET and POST requests
});

Route::any('/', function () {
    // controller code for any request method
});

Route URIs

The $uri parameter is a literal path or a path expression containing parameters surrounded by brackets (eg: {parameter}).

// literal route, exact match
Route::post('/register', function (Request $request) {
    return view('register');
});
// parametrized route , example: /notification/1234
Route::get('/notification/{id}', function ($id) {
    return view('notification',['notification_id' => $id]);
});

Route callbacks

Callbacks could be inline functions, like in the previous examples, or calls to Controller methods:

use App\Http\Controllers\LoginController;

Route::post('/login', [LoginController::class,'authenticate']);

In some cases, the controller class could be defined dynamically, using the full namespace:

Route::post('/login', ['uses'=>'App\\Http\\Controllers\\LoginController@authenticate']);

Route Controllers

Controllers can be defined in the app/Http/Controllers directory, using the name of the controller class as the filename (LoginController.php):

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class LoginController extends Controller
{
    /**
     * Handle an authentication attempt.
     *
     * @param  \Illuminate\Http\Request $request
     * @return \Illuminate\Http\Response
     */
    public function authenticate(Request $request)
    {
        // user authentication
    }
}

Requests and responses

Finally, you can use the Request object (passed to your controller function before any route parameter) to get information and available methods to manage the $request and the final response(). For example:

Route::patch('/user/{id}', function(Request $request, $id){
    $resource = User::findOrFail($id);
    $resource->fill($request->only(['firstName','lastName']));
    $resource->save();
    if ($request->expectsJson()){
        return response()->json([
           'status' => 'success'
        ]); 
    }else{
        redirect('/user/'.$id);
    }
}

This code will retrieve the user $id from the route path ({id}) and specific data from the user using $request->only([keys]), in order to replace this information in the user model data. Additionally, it will return a JSON response if the request expects JSON (from an Ajax request, for example) or simply redirect to a specific URL path.

By default, Laravel will convert any literal response to a text response using the text/html mime-type. If you want to respond with a different response code or adding headers, you can use the response(content, status) function:

return response('Text content', 200)
       ->header('Content-Type', 'text/plain');

You can redirect your response using redirect('/path'). Also, you can redirect the response to a specific Controller/method, also passing parameters:

return redirect()->action(
    [PaymentController::class, 'thankyou'], ['transaction_id' => $id]
);

One common scenario is to process data and the return a view (using a Laravel blade template file or a plain PHP view):

Route::get('/terms-and-conditions', function () {
    return view('terms');
});

In the next section we explain how views works.

Laravel 8 application views

By default, Laravel views are built using the Blade template language, which is a mix of literal HTML code, directives starting with @ (example: @include, @if and @foreach) and PHP expressions surrounded by double brackets, for example:

@if( count($users)>0 )
    <span >{{ count($users) }} users</span>
@endisset

To differentiate plain PHP views from blade templates, use the .blade.php suffix to process a file as a blade template.

Displaying data

Blade uses the double curly brackets ( {{ expression }} ) to echo information to the PHP output. By default, all the content generated by blade expressions is automatically encoded/escaped to prevent XSS attacks (using raw HTML to break your code). You can use any valid PHP variable, expression, or function call as an expression:

<div>Number of items: {{ count($items) }}.</div>

Displaying raw HTML

Sometimes you may want to display HTML generated code in your template (warning: do this only from a trusted generated code, not directly from the user input), in this case, use the double exclamation mark between curly brackets ( {!! expression !!}) to display raw HTML

{!! $component->render() !!}

Displaying JSON data

You may need to embed JSON code inside your template when you need some javascript code to process your data (optionally, you can add the JSON_PRETTY_PRINT option to format the JSON output:

<script>
    var appdata = @json($appdata, JSON_PRETTY_PRINT);
    var app = new Vue({
        el: '#app',
        data: appdata
    })
</script>

Blade Directives

Laravel Blade directives help to build complex views using programmatic logic found in most programming languages. Most common directives are:

@if directive

@if - @endif Is the common way to conditionally display a block based on conditions:

@if (count($results) === 1)
    <span>1 result</span>
@elseif (count($results) > 1)
    <span>{{ count($results) }} results</span>
@else
    <span>No results</span>
@endif

@isset and @empty directives

@isset checks if a variable is defined and not null. @empty checks if the variable has an ’empty’ value: null, false, '', [] empty array and others (see empty())

@isset($errorMessage)
    <span class='error-message'>{{ $errorMessage }}</span>
@endisset

@empty($records)
    <span>No matches found</span>
@endempty

Loop directives

You can loop over data using @for @foreach @forelse and @while

// regular for
@for ($i = 0; $i < count($results); $i++)
    <div>Name: {{ $results[$i]["firstName"] }}</div>
@endfor

// for each element of a list
@foreach ($results as $result)
    <div>Name: {{ $result["firstName"] }}</div>
@endforeach

// for each element, fallback if empty 
@forelse ($results as $result)
    <div>{{ $result["firstName"] }}</div>
@empty
    <div>No results</div>
@endforelse

// loop while a condition is true 
@while ($dataSource->hasData())
    <div>{{ $dataSource->data['id'] }}</div>
@endwhile

As in regular loops, you can use the @break to stop the loop, and @continue directive to skip/pass the current loop. Also, you can check the value of $loop->index (0-based) attribute to get the current loop index or $loop->iteration (1-based) to get the current loop count (see more options in the Laravel Blade documentation).

@foreach ($users as $user)
    @if ($user->isDeleted())
        @continue
    @endif

    <li>{{ $user->name }}</li>

    @if ($loop->iteration == 10)
        @break
    @endif
@endforeach

Adding sub-views using @include

You can include other sub-views inside a blade template using @include( view, parameters). For example, you can create your views using a mix of local content and reuse of common components (the same header/footer for each page, for example):

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    @include('head',['title'=>'My Page'))
<body>
    @include('components/toolbar')
    <div id="content">
      My content ...
    </div>
    @include('components/footer')
</body>
</html>

This structure will use a sub-view stored in /resources/views/head.blade.php to fill the <head></head> section and 2 components to create the header/toolbar and the footer of the application (both created at /resources/views/components/). However, all relative paths when calling @include will be relative to /resources/views/.

Create a Laravel 8 application from scratch

In this tutorial, we will explore how to create a Laravel 8 application from scratch and then prepare your application for development. However, the overall process changed from the previous version (Laravel 7), improving the developer experience with a fresh set of tools. Now, the new Laravel 8 provides Sail, a built-in solution for running your Laravel project using Docker.

Continue reading “Create a Laravel 8 application from scratch”

How to explain Javascript .filter() .map() and .reduce() to SQL users

I found using .filter() .map() and .reduce() Array methods could help to reduce code complexity and simplify many typical array data processing tasks. Looking at http://kangax.github.io/compat-table/es5/#test-Array_methods, you can confirm these methods are compatible with almost any modern desktop/mobile browser.

A good approach to understand how to use .filter() .map() and .reduce() is to compare each one to SQL data operations in SELECT statements.

Continue reading “How to explain Javascript .filter() .map() and .reduce() to SQL users”

Google SEO Tools to improve your site rank

There are many useful Google SEO tools, created by Google, to improve your SEO and site rank. These free tools could help you improve your search position:

  • Webmaster Tools (Google Search Console)
  • Google Analytics
  • Keyword Planner
  • Google Page Speed
  • Structured Data tools
    • Markup Helper
    • Structured Data Testing

Continue reading “Google SEO Tools to improve your site rank”

Configure PHP and Laravel Framework

In this guide we configure PHP and Laravel Framework to build web applications and REST APIs. As a result, you can prepare the environment to start the development process in a Laravel Project.

Note: for the newest Laravel 8, you can follow the instructions in this new post: Create a Laravel 8 application from scratch

After this process, you will get installed the following components:

  • PHP (PHP 7 or newer)
  • MySQL/MariaDB server engine and client
  • PEAR (PHP repository of common reusable components)
  • Composer (PHP package and dependency manager)
  • XDebug (PHP debugger used by unit tests tools like PHPUnit)
  • Laravel command line tools (laravel and php artisan)

Continue reading “Configure PHP and Laravel Framework”

GitHub is now offering Unlimited private repos for free

GitHub is now offering Unlimited private repos for free. From a recent update on January 7th, 2019, Github announced a big change in their main offerings for developer and enterprise accounts.

Continue reading “GitHub is now offering Unlimited private repos for free”

Statically typed Javascript : Why and How

In the last months I have found good experiences using Statically typed Javascript. For example, using React with Typescript. It helps a lot to build a robust Web development, with statically typed Javascript support, ready for a complex and enterprise-level projects.

TypeScript is maintained by Microsoft, but it’s not the only option for statically typed Javascript. Flow, from Facebook and Dart from Google are similar options among a long list of implementations.

Why so many “big companies” are doing really big efforts to introduce static typing in Javascript development in the last years?

Continue reading “Statically typed Javascript : Why and How”

Build a REST API with Node.js SQLite and Express JS

In this guide you can build a REST API with Node.js SQLite and Express.js. Then, in a next part, we will be building automated Unit tests using Mocha and Chai, ready for a Test-Driven Development (TDD).

The main components of this REST service are:

  • A Node.js base project, created as a NPM module
  • An Express.js web server to manage the API endpoints, requests and responses
  • A SQLite database for application storage

Continue reading “Build a REST API with Node.js SQLite and Express JS”