Laravel for Beginners #2

In this article, we are going to talk about the MVC structure of Laravel. The MVC structure is a web design principle that consists of a model (M), which is in charge of communicating with the database, a controller (C), which is the place we store the logic of our application, and a view (V), which is the frontend interface of the app.

Typically, in a web application, there will be a router, which we’ve discussed before. The router will point to a controller, the controller will look for the required data in the database through the model, and then put the retrieved data to the corresponding location in a view, and finally return that view back to us.

The MVC structure

Views in Laravel, aka the Blade templates

Let’s start with views. Recall that in the previous article, we defined this route:

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

This route points to a view welcome, which is stored in the resources/views directory, and it has an extension .blade.php. This extension informs Laravel that we are using the Blade template system, which we’ll talk about later.

To create another view, we simply create another file with the same extension inside the views directory.

greetings.blade.php

1
2
3
4
5
6
7
<html>

<body>
  <h1>Hello!</h1>
</body>

</html>

We also talked about that it is possible to pass data from the route to the view like this:

1
2
3
Route::get('/', function () {
    return view('greeting', ['name' => 'James']);
});

In this case, we assigned the variable name with the value 'James', and passed it to the greeting.blade.php we just created. And in order to display this data in the view, we can use the double curly braces {{ }}.

greetings.blade.php

1
2
3
4
5
6
7
<html>

<body>
  <h1>Hello, {{ $name }}</h1>
</body>

</html>

For a large web application, it is very common for you to organize your view files in different folders. For example, your app could have an administration panel, and you probably want to store the corresponding views in a folder called admin. When pointing to view files in a nested structure from the router, remember to use . instead of /.

1
2
3
Route::get('/admin', function () {
    return view('admin.home');
});

This example points to the home.blade.php file in the views/admin/ directory.

Blade template syntax

By default, Laravel’s views use something called the Blade template. It is actually very similar to the components system in Vue.js, if you have been following my course on Vue.js. We know that views are just HTML documents, and the Blade template adds things like inheritance system, flow control, loops and other programming concepts. It allows us to create a more dynamic webpage while writing less code.

if Statements

We can build if statements using directives @if@elseif@else, and @endif. They work exactly the same as the if statements you see in other programming languages.

1
2
3
4
5
6
7
@if ($num == 1)
  <p>The number is one.</p>
@elseif ($num == 2)
  <p>The number is two.</p>
@else
  <p>The number is three.</p>
@endif

We can test this code with a route:

1
2
3
Route::get('/number/{num}', function ($num) {
    return view('view', ['num' => $num]);
});

Here, we assume num can only be 1, 2 or 3.

switch Statements

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
@switch($i)
  @case(1)
      First case...
      @break

  @case(2)
      Second case...
      @break

  @default
      Default case...
@endswitch

Loops

Loops could be a simple for loop:

1
2
3
@for ($i = 0; $i < 10; $i++)
  The current value is {{ $i }}
@endfor

Or a foreach loop. For every iteration, $user equals to the next element of $users.

1
2
3
@foreach ($users as $user)
  <p>This is user {{ $user->id }}</p>
@endforeach

Even a while loop:

1
2
3
@while (true)
  <p>I'm looping forever.</p>
@endwhile

These are very basic stuff, and they work exactly like their PHP counterparts. However, there is one thing special about these loops, which is the $loop variable.

For example, you have something that you only want displayed once, in the first iteration, then you can do this:

1
2
3
4
5
6
7
@foreach ($users as $user)
  @if ($loop->first)
      <p>This is the first iteration.</p>
  @endif
    
  <p>This is user {{ $user->id }}</p>
@endforeach

This time, the paragraph element <p>This is the first iteration.</p> will only be rendered once in the first iteration. There are other properties you can access, you can read about them here. Note that the $loop variable can only be accessed inside a loop.

Conditional Class

We know that class is the most commonly used way to assign styles to different HTML elements, and by changing the class of an element, we can easily change the look of that element. Laravel offers us a way to dynamically assign classes based on variables.

1
2
3
4
5
<span @class([
    'p-4',
    'font-bold' => $isActive,
    'bg-red' => $hasError,
])></span>

In this example, the existence of classes font-bold and bg-red will be based on the truthiness of variables $isActive and $hasError.

Building the layout

When we are building a web application, there are always some parts of the page that will appear on multiple pages. It would be a waste of time and resources if you write the same code over and over again, and it’s not good for maintenance. The smart thing to do here is to separate the part that will appear multiple times and put it into another file, and we’ll simply import it when we need it.

Laravel offers us two different ways to do this, template inheritance and components. For beginners, I think template inheritance is a lot easier to understand, so we’ll be using it to build our blog application later. But components do offer more features, so I’ll still introduce the basics of it. Let’s start with template inheritance.

Template Inheritance

As an example, here we are trying to define a home page, and here is our route.

1
2
3
Route::get('/', function () {
    return view('home');
});

Typically, we’ll have a layout.blade.php, which imports the necessary CSS and JavaScript files, as well as the navbar and the footer, since they will appear on every page. And in our home.blade.php file, we’ll extend to layout.blade.php.

layout.blade.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<html>

<head>
    @yield('title')
</head>

<body>
    <div class="container">
        @yield('content')
    </div>
</body>

</html>

home.blade.php

1
2
3
4
5
6
7
8
9
@extends('layout')

@section('title')
    <title>Home Page</title>
@endsection

@section('content')
    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit...</p>
@endsection

Notice that @section('title') matches @yield('title'), and @section('content') matches @yield('content').

In this example, when the home page is being rendered, Laravel will first see the directive @extends('layout'), and it knows to go to layout.blade.php. And then, Laravel will locate the @yield('title') and replace it with the title section, and then find the @yield('content') directive and replace it with the content section.

Sometimes we’ll need to load another view from the current view. For example, we want to add a sidebar to our homepage. Since it is something that will be included in some pages but not the others, we don’t want to put it in the layout.blade.php. However, it will be very difficult to maintain if you create a sidebar for every page that requires it. In this scenario, we can create a sidebar.blade.php file, and then use the @include directive to import it into the home page.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
@extends('layout')

@section('title')
    <title>Home Page</title>
@endsection

@section('content')
    <p>...</p>
    
    @include('sidebar')

@endsection

Components

The component system in Laravel is actually a lot similar to the one in Vue.js, so if you followed the course on Vue components, this shouldn’t be too hard.

Again, we are building a home page, and we also need a layout, but this time we’ll define it as a component. All we need to do is create a components folder under resources/views/, and then create our layout.blade.php inside.

views/components/layout.blade.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<html>

<head>...</head>

<body>
    <div class="container">
        {{ $slot }}
    </div>
</body>

</html>

To use this component, we need to use the <x-layout> tag. Always remember the x- prefix.

1
2
3
4
<x-layout>
    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.
    Vestibulum eu elit semper ex varius vehicula.</p>
</x-layout>

The content inside this <x-layout> element will automatically be assigned to the variable $slot. However, what if we have multiple slots? Like our previous example, we have a title and a content section. To solve this problem, we can define a <x-slot> element.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<x-layout>
    <x-slot name="title">
        <title>Home Page</title>
    </x-slot>
    

    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.
    Vestibulum eu elit semper ex varius vehicula.</p>

</x-layout>

The content of this <x-slot name="title"> will be assigned to the variable $title, and the rest will still be assigned to the $slot variable.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<html>

<head>
    {{ $title }}
</head>


<body>
    <div class="container">
        {{ $slot }}
    </div>
</body>

</html>

Dealing with databases in Laravel project

Before we can talk about the model and the controller section of the MVC structure, we need to first discuss how to deal with databases in Laravel.

In our course on JavaScript and Node.js, we used a .txt file to store information. In a real-life web application, that is obviously not going to work. The common practice is to use a database for storing information. In this course, we are going to use SQLite. If you haven’t set it up already, as a reminder, here is how it’s done.

Create a file named database.sqlite under the directory database/, and go to the .env file, replace the database section (the ones with the prefix DB) with the following code.

1
2
3
DB_CONNECTION=sqlite
DB_DATABASE=/absolute/path/to/database.sqlite
DB_FOREIGN_KEYS=true

Migrations

Right now, this database is empty, in order to use it, we need to add some database tables. These tables will have columns, and the columns will have names and some special requirements. The process of setting this up is called running migrations.

In Laravel, all the migration files will be stored in the database/migrations directory. And we can create a new migration file with a simple command:

1
php artisan make:migration create_flights_table

We can apply the migration file like this:

1
php artisan migrate

If you want to roll back the previous migration:

1
php artisan migrate:rollback

Or reset the migrations completely:

1
php artisan migrate:reset

Create Table

However, before creating our own migration file, let’s take a look at an example. Open file database/migrations/2014_10_12_000000_create_users_table.php.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('users');
    }
} 

In this file, we have a class with two different methods. up() is used to create new tables and columns, while down() can be used to reverse the operations performed by up().

Inside the up() method, we used a the create() method inside Schema class to create a new table called 'users'. From line 17 to 23, each line creates a different column with a different name and type. For example, $table->string('name'); creates a column called name inside the users table and that column should store strings.

Here is a full list of column types available in Laravel. We are not going through all of them, and instead, we’ll why we choose the specific column type as we encounter specific problems in the future.

Notice line 19, $table->string('email')->unique();, there is something else here, after we’ve declared the column name and type. The unique() is called a column modifier, it adds extra constraints to that column, in this case, it makes sure that the column cannot contain repeating values, each email the user provides must be unique.

Here is a full list of column modifiers available in Laravel.

Make Changes to Table

Besides creating tables, it is also possible for us to update a table using the table() method:

1
2
3
Schema::table('users', function (Blueprint $table) {
    $table->integer('votes');
});

This code adds a new column votes to the existing table user.

Rename a table:

1
Schema::rename($from, $to);

Or drop a table completely:

1
2
3
Schema::drop('users');

Schema::dropIfExists('users');

Seeding

Seeder is used to generate dummy data to your database, that way it is easier for you to test things. To generate a seeder, run the following command:

1
php artisan make:seeder UserSeeder

This command will generate a seeder for the user table, which will be placed in the database/seeders directory.

UserSeeder.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;

class UserSeeder extends Seeder
{
    /**
     * Run the database seeders.
     *
     * @return void
     */
    public function run()
    {
        DB::table('users')->insert([
            'name' => Str::random(10),
            'email' => Str::random(10).'@gmail.com',
            'password' => Hash::make('password'),
        ]);
    }
}

To execute this seeder, run the following command:

1
php artisan db:seed UserSeeder

Query builder

The query builder is an interface that allows us to interact with the database. For example, we can use the table() method provided by the DB class to retrieve all rows in a table.

1
2
3
use Illuminate\Support\Facades\DB;

$users = DB::table('users')->get();

We can add more constraints by chaining a where() method.

1
$users = DB::table('users')->where('age', '>=', 18)->get();

This code will get all the rows where the value in the column age is greater than or equal to 18.

There are a lot of other methods besides where and get, we’ll talk about them later when we encounter specific problems in the future.

Models and controllers

Model is a very important concept in modern web design. It is in charge of interacting with the database of our web application. The model is in fact a part of Laravel’s Eloquent ORM (Object-Relational Mapper) system. Think of it as a query builder, with some extra features. We can use the make:model command to generate a new model.

1
php artisan make:model Post

If you’d like to generate a corresponding migration file, use the --migration option.

1
php artisan make:model Post --migration

app/Models/Post.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    ...
}

By default, this model assumes that there is a posts table in the database. If you wish to change that setting, set a $table property like this:

1
2
3
4
5
6
7
8
9
class Post extends Model
{
    /**
     * The table associated with the model.
     *
     * @var string
     */
    protected $table = 'my_posts';
}

The controller is the place where we interact with the database through the models. There are four basic operations we can perform using the models, such as getting data, creating data, updating data, and deleting data. In this section, we are going to introduce all of them.

We can use the following command to generate a new controller:

1
php artisan make:controller NewController

This will create a new controller file app/Http/Controllers/NewController.php.

Retrieving model

The eloquent model is a powerful query builder that allows us to communicate with the database. For example, we can use the all() method to retrieve all the records in the table.

1
2
3
use App\Models\Post;

$posts = Post::all();

Notice here we imported the Post model, instead of the DB class. The Post model will automatically connect with the posts table.

Since models are query builders, that means all the query builder methods can be accessed here.

1
2
3
4
$posts = Post::where('published', 1)
               ->orderBy('title')
               ->take(10)
               ->get();

When you are using all() or get() to retrieve data, the value that is returned is not a simple array or object, but instead, it is an instance of Illuminate\Database\Eloquent\Collection. This Collection provides a few more powerful methods than a simple object.

For example, the find() method can be used to locate a record with a certain primary key (id).

1
2
3
$posts = Post::all();

$post = $posts->find(1);

In this example, $post will be the post with id==1.

Inserting & updating model

We can also insert or update now date using the model, but before we do that, we need to make sure the corresponding model has a $fillable property, and make sure you list all the columns that you wish to make fillable.

1
2
3
4
5
6
7
8
9
class Post extends Model
{
    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = ['title', 'content'];
}

After that, we can use the create() method to create a new record.

1
2
3
4
$flight = Post::create([
    'title' => '...',
    'content' => '...',
]);

Or, you can update existing records with the update() method.

1
Flight::where('published', 1)->update(['published' => 0]);

Deleting model

There are two methods that allow you to delete records. If you wish to delete a single record, use the delete() method.

1
2
3
4
5
$posts = Post::all();

$post = $posts->find(1);

$post->delete();

If you want to mass delete records, use the truncate() method.

1
2
3
4
5
6
$posts = Post::where('published', 1)
               ->orderBy('title')
               ->take(10)
               ->get();

$posts->truncate();

comments powered by Disqus