Export or Import of CSV or Excel file in Laravel 5.8 with MySQL

Export or Import of CSV or Excel file in Laravel 5.8 with MySQL

Do you know Import or Export CSV or Excel sheet data from MySQL Database is a primary needs of any admin level project. For this here we have make simple tutorial, in which we have step by step describe how to import CSV or Excel sheet data and Export CSV File or Excel sheet data in Laravel 5.8 application by using maatwebsite/excel version 3 package. There are many changes has been done in maatwebsite version 3 package. So, you have to need to know how to Export or Import of CSV or Excel file data in Laravel 5.8 with Mysql Database. This updated package is now very simple and easy way to use with Laravel latest version like 5.7 and 5.8 for import excel CSV file data from Mysql database.

Suppose We have to some time export or import thousand of records import or export from our database. Then Maatwebsite has been offer functionality for importing and exporting of large records in CSV file or Excel sheet. For learn this feature here we will make import data in CSV file and import into mysql in laravel 5.8 application. For this task here we will use maatwebsite/excel package for importing and exporting data task with laravel 5.8 framework. Because maatwebsite/excel package has provide very easy method for import and export data using database model. Below you can find step by step process for how to use maatwebsite/excel version for import and export csv file data in Laravel 5.8 application.

Export or Import of CSV or Excel file in Laravel 5.8 with MySQL

Table Content

  • Install Laravel 5.8 framework
  • Add Fake Records in Mysql Database
  • Install Maatwebsite Package
  • Create Import Class
  • Create Export Class
  • Create Controller
  • Create Blade View File
  • Add route

Install Laravel 5.8 framework

First We need to download Laravel 5.8 version, and install into our computer. For this we have to go command prompt, in which we have to already run composer command and write following command. It will download Laravel 5.8 version in define folder.

<pre class="ql-syntax" spellcheck="false"> composer create-project --prefer-dist laravel/laravel csv_file </pre>

After installing Laravel 5.8 framwork, after this we need to make database connection. For this we have to open .env file and under this file we have to define Mysql database configuration details which you can see below.

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=testing
DB_USERNAME=root
DB_PASSWORD=

After makiing database configuration, now in next step we will see how to make table from Laravel 5.8 application.

Add Fake Records in Mysql Database

For add fake record into Mysql database. First we want to make table in Mysql database from this Laravel 5.8 application. Here we will use user default model for migrate data from this Laravel 5.8 application. If you have download fresh Laravel 5.8 framework, then you can find User default model in app/User.php. And for migrate data, you have to go command prompt and write following command.

<pre class="ql-syntax" spellcheck="false">php artisan migrate </pre>

This command will migrate default data from database/migrations folder and it will make user table in define Mysql database. Now for add fake records, we have to write following command in command prompt.

php artisan tinker

factory(App\User::class, 20)->create();

This command will add 20 fake records in User table, so we can perform import export data operation with CSV file.

Install Maatwebsite Package

Now for import and export data to csv file, we need to download and install Maatwebsite Version 3.0 package, for this we have go to command prompt and write following command.

<pre class="ql-syntax" spellcheck="false">composer require maatwebsite/excel </pre>

This command will download maatwebsite package in Laravel 5.8 application. Now we want to add into Laravel 5.8 application. For this we have open config/app.php and following service provider and aliase.

config/app.php

'providers' => [

 ....

 Maatwebsite\Excel\ExcelServiceProvider::class,

],

'aliases' => [

 ....

 'Excel' => Maatwebsite\Excel\Facades\Excel::class,

],

So, this way we can add maatwebsite/excel package in Laravel 5.8 application.

Create Import Class

Now we want to create import class for import data using maatwebsite 3.0 package in Laravel 5.8 frameworl. For this we have to write following command in command prompt.

<pre class="ql-syntax" spellcheck="false">php artisan make:import CsvImport --model=User </pre>

This command will make CsvImport.php import class under Import. We have to open this class and under this we to define table column which data we want to get from CSV file. Below you can find complete source code of Import Class for import data from CSV file by using Maatwebsite/excel 3.0 package in Laravel 5.8.

Imports/CsvImport.php

<?php

namespace App\Imports;

use App\User;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Concerns\WithHeadingRow;

class CsvImport implements ToModel, WithHeadingRow
{
    /**
    * @param array $row
    *
    * @return \Illuminate\Database\Eloquent\Model|null
    */
    public function model(array $row)
    {
        return new User([
            'name'      =>  $row["name"],
            'email'     =>  $row["email"],
            'password'  =>  \Hash::make($row['password']),
        ]);
    }
}

Create Export Class

After this for export mysql data into CSV file or excel sheet, we need to create Export class of maatwebsite using Laravel model. For this we have to write following command in command prompt.

<pre class="ql-syntax" spellcheck="false">php artisan make:export CsvExport --model=User </pre>

This command will make CsvExport.php class under Exports folder. You can find source code of this class below. We need to add this class into controller header for export data into CSV or Excel file.

Exports/CsvExport.php

<?php

namespace App\Exports;

use App\User;
use Maatwebsite\Excel\Concerns\FromCollection;

class CsvExport implements FromCollection
{
    /**
    * @return \Illuminate\Support\Collection
    */
    public function collection()
    {
        return User::all();
    }
}

Create Controller

Now we have to create controller for handle http request for import and export data. For this we have to write following command in command prompt.

<pre class="ql-syntax" spellcheck="false">php artisan make:controller CsvFile </pre>

This command will create controller under app/Http/Controllers/CsvFile.php file. In this file first we need to add import class, export class, maatwebsite and User model class. In this controller we have make following method.

index() - This method has load all data of user data with pagination feature.

csv_export() - This method has been received data export to csv file request. This method will export all User table data into CSV file format using CsvExport class and CSV file download in computer.

csv_import() - This method has been used for import csv file data into Mysql database. This method has import data from csv file by using CsvImport class.

Create Blade View File

For display output in browser, we have to create blade file for display Laravel 5.8 application output on browser. You can find below two blade file has been used for display output on web page.

resources/views/csv_file.blade.php


<html>
 <head>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Laravel 5.8 - Import Export Data in CSV File</title>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" />
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
 </head>
 <body>
  <div class="container">    
     <br />
     <h3 align="center">Laravel 5.8 - Import Export Data in CSV File</h3>
     <br />
     <div class="panel panel-default">
          <div class="panel-heading">
           <h3 class="panel-title">Laravel 5.8 - Import Export Data in CSV File</h3>
          </div>
          <div class="panel-body">
           <form action="{{ route('import') }}" method="POST" enctype="multipart/form-data">
            @csrf
            <input type="file" name="file" accept=".csv">
                  <br>
                  <button class="btn btn-success">Import User Data</button>
                  <a class="btn btn-warning" href="{{ route('export') }}">Export User Data</a>
           </form>
              @yield('csv_data')
          </div>
      </div>
  </div>
 </body>
</html>


resources/views/csv_file_pagination.blade.php

@extends('csv_file')

@section('csv_data')

<table class="table table-bordered table-striped">
 <thead>
  <tr>
   <th>Name</th>
   <th>Email Address</th>
  </tr>
 </thead>
 <tbody>
 @foreach($data as $row)
  <tr>
   <td>{{ $row->name }}</td>
   <td>{{ $row->email }}</td>
  </tr>
 @endforeach
 </tbody>
</table>

{!! $data->links() !!}

@endsection

Add route

Lastly we need to add route for import and export operation. For this we have to open routes/web.php file. In this file you can define below route for import and export method.

Route::get('csv_file', '[email protected]');

Route::get('csv_file/export', '[email protected]_export')->name('export');

Route::post('csv_file/import', '[email protected]_import')->name('import');

Now we are ready for import and export data into CSV file in Laravel 5.8 by using maatwebsite 3.0 version. For this first we want to start Laravel 5.8 server, for this we have to write following command in command prompt.

<pre class="ql-syntax" spellcheck="false">php artisan serve </pre>

This command will start Laravel server and give you base url of Laravel application. For test import and export data operation we have to write following url in browser.

<pre class="ql-syntax" spellcheck="false">http://localhost:8000/csv_file </pre>

Now you can check your Laravel 5.8 import export data into CSV file. We hope it will help you to learn Laravel 5.8 with maatwebsite package.

Thanks for reading

If you liked this post, share it with all of your programming buddies!

Follow me on Facebook | Twitter

Learn More

PHP with Laravel for beginners - Become a Master in Laravel

Projects in Laravel: Learn Laravel Building 10 Projects

Laravel for RESTful: Build Your RESTful API with Laravel

Fullstack Web Development With Laravel and Vue.js

Laravel 5.8 Ajax CRUD tutorial using Datatable JS

Laravel 5.8 Tutorial from Scratch for Beginners

Build RESTful API In Laravel 5.8 Example

Login with Google in Laravel 5.8 App using Socialite Package

Laravel PHP Framework Tutorial - Full Course for Beginners (2019)

Go Top Programming Languages in 2020 from Authentic Surveys

Go Top Programming Languages in 2020 from Authentic Surveys

Comparing Programming Languages is a very complex thing and so there are many graphical illustration/jokes trying to symbolize Programming language. I found few and I am starting this article with those.

This is image title

In simple words, Programming Language empowers human to instruct and control machine. So, it is natural that there will be so many languages which try to make this process more powerful and simple. For this very reason there are hundreds of programming languages, many of those programming languages are now out of active use, few are going to be obsolete in coming years and then there are languages which is going to continue and prove its usage in coming years and then there are new programming language fighting for it acceptance.

This article going to present the trends of top Programming Languages which will continue in the coming year 2020. To predict the trend of the programming language in 2020 this article uses data from authentic surveys, various collected statistics, search results and salary trends according to programming languages. This article will help the new learner to pick a programming language to learn and for expert, it will help to decide either to switch to another language or to continue with his expertise language.

In the next section, I have prepared two tables which summarize the popularity trend of Programming Languages in the last five years (2015-19). The data is taken from Stackoverflow popularity survey 2015-2019. For a clear and accurate understanding, the programming languages are divided into two groups, first, languages which have origin before 2000 and the second group has languages which came after 2000. The selection of 2000 as the boundary is just random but very helpful to understand the programming trend under these two groups. The table also lists origin year and main or documented purpose of these programming/scripting languages.

This is image title
This is image title

Observations:

There is a decrease in the popularity of all languages from 2018 to 2019 except Python.

Python

Python is the only language continuously on rising since last five years. It is a general-purpose language, so someone wants to learn just one programming in 2020 and want to cover more area of software development then Python could be chosen**.**

Java

Java was on rising but fall in 2019, the reason could Kotlin gaining popularity on the Android platform. Java is a good choice for a programming language but now it is under Oracle and Google is promoting Kotlin so it is in the conflicted zone. As a matter of fact still, the large number of the company is using Java and going to continue with Java due to its developers base, framework, and legacy application.

C/C++

C and C++ are still holding with approx 20% and it will be there due to its inherent features and legacy system.

JavaScript

JavaScript popularity can be attributed to the growth of popular JavaScript library and framework like node.js, etc. JS is the language for the dynamic website and this going to be top for coming years because of its active development, support from Mozilla and penalty of libraries and frameworks. So, if someone wants to be web development, javascript is a must.

R

R is gaining popularity in recent years and reason would be growth and popularity of data analysis. It is used by data scientist but again much behind in comparison to Python which has established as general-purpose languages and enjoy active developers with lots of data science libraries and modules. So, one can prefer Python over R if they have to choose only one otherwise if wanted carrier in Data Sciences then learning both will a good option.

<div class="5tjAASx1"><script type="bfa163966612c2c2fba9ece2-text/javascript">(adsbygoogle = window.adsbygoogle || []).push({});</script></div>

Ruby

Like PHP, Ruby is also facing tough competition from JavaScript and even Python to establish as back-end web development programming language. So, again for web development javascript and Python (server-side (Flask, Django, etc.) would be a good choice and will offer more domain flexibility than Ruby.

PHP

There is a sharp decline in PHP popularity in 2019 and it can be traced back to server-side acceptance of javascript and Python. So, if someone wants to go to server-side web development then still PHP is a good choice with a large number of popular framework like CakePHP, Codeigniter, etc. otherwise choosing a general-purpose programming language would be better.

Objective-C

Objective-C was the main language for Apple’s software such as macOS, iOS, etc. before Apple moved to Swift language. So this transition is reflected in the popularity of both languages, i.e. there is a fall in popularity for Objective-C and the popularity of Swift is rising. So, again if someone wants to be a developer for Apple’s products then Swift should be the language of choice.

This is image title

Observations:

Swift

Swift has replaced the Objective-C as the main language for Apple-related software and application. Since it is supported and promoted by Apple so there is an increase in popularity since its inception and as Apple is going to continue with it so if someone is looking for Apple-specific development platform then Swift is going to be a must-know programming language. This is mostly vendor and product-specific language with very less usage outside Apple’s eco-system.

Go

Go (Golang) is getting popularity as maintain, use and promoted by Google. The motivation of Go development was to address criticism of some of the popular languages and keeping the best of them in one place. Since 2017, Go is moving upward in popularity and with Google support, it is going to enjoy this in coming years. Google is also making Go as a primary language for new projects and replacing other languages with Go, this trend going to make useful and important to learn in coming years so one can pick Go as a new programming language.

Kotlin

Kotlin is being offered as an alternative of Java for Android development and again it is supported and promoted by Google so it is also picking up by developers and gaining popularity in recent years. So, with the growth of Android, support of Google and with clean and short syntax it is going to be a choice of Android application developers and is a good choice to learn for Android App developer. Kotlin going to be shine as a prominent programming environment for Android development.

Scala

Scala tries to establish as an alternative to Java but didn’t get very well among developers. It doesn’t have big support from any multi-national company, perceive as functional languages and dependency on JVM doesn’t provide much scope to rise in popularity. There could be steady growth but very slow and surely not a language to learn as a beginner.

Julia

Julia aims to bring the speed of ‘C’ and simplicity of Python but it is strange that didn’t found any popularity in Stackoverflow survey but gaining popularity among data science domain and being seen as a challenger for R and Python in long run. Surely, there will be growth in Julia but still, Python or R is better for job and growth.

C#

C# is the language for the .NET framework and developed by Microsoft. Its popularity is approx constant over past years and going to continue with a similar trend. This is vendor-specific language so one can pick this language if want to work in the Microsoft development environment. Recently, Microsoft has open-sourced the .NET so there would be some upward trend but again it is vendor-specific so there won’t be much affected.

Rust

Rust, Clojure, etc. are languages which have a user-base but not so popular so surely not going to have an upward swing in popularity.


A Picture Says a Thousand Words

To understand a clear trend and picture of top programming language growth let keep a picture of it by the various chart. The figure 1 and figure2 gives a very clear picture that in old language stack JavaScript is far ahead than others and credit goes to boom in web development, then C and C++ together competing very closer to Java. Python is moving upward in popularity and only language which popularity is constantly increasing in the last 5 years. New languages are getting popularity and most of them are supported by the multi-national company and bit IT giant like Microsoft, Google and Apple.

This is image title

This is image title

Loved and Wanted Languages

This is image title

This is image title

From above Table and Figure, few observations are very obvious, Love of Rust is growing in last five years whereas Swift is loosing love from developers and Python is in between these two and last two years have gain for Python. One more unique observation is that out of 5 loved languages 4 are from post 2000 group while only Python is the older language and Kotlin love started with addition of Kotlin for Android development post 2017.

This is image title

From above table, wish of developing in javascript and Python is growing in last years and this reflect in popularity and love for the language. There is a sharp decline in Java and this is due to the addition of Kotlin as alternative for Android app development and also change of policy by Oracle who own Java now.

This is image title

Technologies and Programming Languages

This is image title

In this figure, one can see that the largest cluster is for Web development and JavaScript and its various framework is dominating the cluster this is USP of JavaScript growth. The second-largest cluster is of Microsoft technologies and Python technologies which again clear the popularity and love for the language. Python cluster is linked with data science technologies which highlight the growth story of Python.

TIOBE Index

TIOBE index rank programming language based on search engine search result. The selection of search engines and programming language is defined in its page. The ratings are calculated by counting hits of the most popular search engines. The search query that is used is +”<language> programming”. In TIOBE index Java is dominating the ranking in the last two decades where C is holding the 1st and 2nd rank for the last 30 years. Python has come a long way in the last two decades i.e. 24th in 1999 to 3rd in 2019. If someone merges the C and C++ then it would hold the 1st positions forever.

This is image title

In the new languages (post-2000), Rust moved up in ranking i.e. from 33rd to 28th, and Julia from 50th to 39th. It is also interesting to note that Kotlin doesn’t seem to come closer to the top 20.

Popularity of Programming Language (PYPL) Index

The PYPL index is created by analyzing how often language tutorials are searched on Google. The more a language tutorial is searched, the more popular the language is assumed to be. It is a leading indicator. The raw data comes from Google Trends.

Below Figure verifies that the top 3 languages are Python, Java, and JavaScript. C#, PHP, C/C++ also secure top position, this trend is again similar to stack-overflow, and TIOBE index.

This is image title

Above Figure indicates that among new programming Language i.e. post 2000 Kotlin, Go, Rust, and Julia is moving up in the ranking.

This is image title

Job Market and Salary

Salary depends upon the geographical area and demand of the products, a programming language based salary comparison is just a tool to predict or estimate the salary trend. We have summarized salary based on programming language from popular survey i.e. Dice salary survey 2018 and Stack-overflow survey 2018 and 2019.

This is image title

From the above table, it is very clear from both survey that Go/Golang is a very high paid job in the market and even stands 1st rank in a high paid job in stack-overflow 2019 survey and Dice Salary Survey 2018.

Language Predictability

So, as closing remarks, It is easy to predict a language trend but choosing only one language to learn is a really difficult choice and totally depend upon the individual choice and their future plans, for example, if you want to work in Web Development you can’t afford neglecting Javascript, if you want to work with Apple’s products you can’t neglect Swift now, if your taste is in system-level programming then C and C++ is your friend, Python makes you run faster in many domains and currently darling in Data science. You see each language takes you on a different journey. Choose your destination and then drive with the language of that path.

You may also like: Programming Languages - Trend Predictions in 2020 and Next Years.

We’ll love to know your opinion. What is the Best Programming Language for you?

Thank for reading! If you enjoyed this article, please share it with others who may enjoy it as well.!

Build a CMS with Laravel and Vue

Build a CMS with Laravel and Vue

Build a CMS with Laravel and Vue - Part 1: Setting up

The birth of the internet has since redefined content accessibility for the better, causing a distinct rise in content consumption across the globe. The average user of the internet consumes and produces some form of content formally or informally.

An example of an effort at formal content creation is when an someone makes a blog post about their work so that a targeted demographic can easily find their website. This type of content is usually served and managed by a CMS (Content Management System). Some popular ones are WordPress, Drupal, and SilverStripe.

A CMS helps content creators produce content in an easily consumable format. In this tutorial series, we will consider how to build a simple CMS from scratch using Laravel and Vue.

Our CMS will be able to make new posts, update existing posts, delete posts that we do not need anymore, and also allow users make comments to posts which will be updated in realtime using Pusher. We will also be able to add featured images to posts to give them some visual appeal.

When we are done, we will be able to have a CMS that looks like this:


Prerequisites

To follow along with this series, a few things are required:

  • Basic knowledge of PHP.
  • Basic knowledge of the Laravel framework.
  • Basic knowledge of JavaScript (ES6 syntax).
  • Basic knowledge of Vue.
  • Postman installed on your machine.
The source code for this project is available here on GitHub.

Installing the Laravel CLI

If you already have the Laravel CLI installed on your machine, please skip this section.

The first thing we need to do is install the Laravel CLI, and the Laravel dependencies. The CLI will be instrumental in creating new Laravel projects whenever we need to create one. Laravel requires PHP and a few other tools and extensions, so we need to first install these first before installing the CLI.

Here’s a list of the dependencies as documented on the official Laravel documentation:

Let’s install them one at a time.


Installing PHP

An equivalent for Windows users could be to download and install XAMPP here. XAMPP comes with a UI for installing most of the other things you have to install manually below. Hence, Windows users may skip the next few steps until the Installing Composer sub-heading.

Open a fresh instance of the terminal and paste the following command:

<pre class="ql-syntax" spellcheck="false"> # Linux Users $ sudo apt-get install php7.2
# Mac users
$ brew install php72

</pre>

As at the time of writing this article, PHP 7.2 is the latest stable version of PHP so the command above installs it on your machine.

On completion, you can check that PHP has been installed to your machine with the following command:

<pre class="ql-syntax" spellcheck="false"> $ php -v

</pre>

Installing the Mbstring extension

To install the mbstring extension for PHP, paste the following command in the open terminal:

<pre class="ql-syntax" spellcheck="false"> # Linux users
$ sudo apt-get install php7.2-mbstring

# Mac users
# You don't have to do anything as it is installed automatically.

</pre>

To check if the mbstring extension has been installed successfully, you can run the command below:

<pre class="ql-syntax" spellcheck="false"> $ php -m | grep mbstring

</pre>

Installing the XML PHP extension

To install the XML extension for PHP, paste the following command in the open terminal:

<pre class="ql-syntax" spellcheck="false"> # Linux users
$ sudo apt-get install php-xml

# Mac users
# You don't have to do anything as it is installed automatically.

</pre>

To check if the xml extension has been installed successfully, you can run the command below:

<pre class="ql-syntax" spellcheck="false"> $ php -m | grep xml

</pre>

Installing the ZIP PHP extension

To install the zip extension for PHP, paste the following command in your terminal:

<pre class="ql-syntax" spellcheck="false"> # Linux users
$ sudo apt-get install php7.2-zip

# Mac users
# You don't have to do anything as it is installed automatically.

</pre>

To check if the zip extension has been installed successfully, you can run the command below:

<pre class="ql-syntax" spellcheck="false"> $ php -m | grep zip

</pre>

Installing curl

Windows users may need to download curl from here.

To install curl, paste the following command in your terminal:

<pre class="ql-syntax" spellcheck="false"> # Linux users
$ sudo apt-get install curl

# Mac users using Homebrew (https://brew.sh)
$ brew install curl

</pre>

To verify that curl has been installed successfully, run the following command:

<pre class="ql-syntax" spellcheck="false"> $ curl --version

</pre>

Installing Composer

Windows users can download and install Composer here. After the installation is complete, start a fresh instance of the command prompt as administrator and run this command anytime you need composer:
php composer.phar

Now that we have curl installed on our machine, let’s pull in Composer with this command:

<pre class="ql-syntax" spellcheck="false"> $ curl -sS https://getcomposer.org/installer | sudo php -- --install-dir=/usr/local/bin --filename=composer

</pre>

For us to run Composer in the future without calling sudo, we may need to change the permission, however you should only do this if you have problems installing packages:

<pre class="ql-syntax" spellcheck="false"> $ sudo chown -R $USER ~/.composer/

</pre>

Installing the Laravel installer

At this point, we can already create a new Laravel project using Composer’s create-project command, which looks like this:

<pre class="ql-syntax" spellcheck="false"> $ composer create-project --prefer-dist laravel/laravel project-name

</pre>

But we will go one step further and install the Laravel installer using composer:

<pre class="ql-syntax" spellcheck="false"> $ composer global require "laravel/installer"

</pre>

If you are on Windows, you may need to run the previous command in an advanced terminal such as PowerShell or the Gitbash terminal. Windows users can also skip the steps below.

After the installation, we will need to add the PATH to the bashrc file so that our terminal can recognize the laravel command:

<pre class="ql-syntax" spellcheck="false"> $ echo 'export PATH="$HOME/.composer/vendor/bin:$PATH"' >> ~/.bashrc
$ source ~/.bashrc

</pre>

Creating the CMS project

Now that we have the official Laravel CLI installed on our machine, let’s create our CMS project using the installer. In your terminal window, cd to the project directory you want to create the project in and run the following command:

<pre class="ql-syntax" spellcheck="false"> $ laravel new cms

</pre>

At the time of writing this article, the latest version of Laravel is 5.6

We will navigate into the project directory and serve the application using PHP’s web server:

<pre class="ql-syntax" spellcheck="false"> $ cd cms
$ php artisan serve

</pre>

Now, when we visit http://127.0.0.1:8000/, we will see the default Laravel template:


Setting up the database

In this series, we will be using MySQL as our database system so a prerequisite for this section is that you have MySQL installed on your machine.

You can follow the steps below to install and configure MySQL:

  • Linux users - check here for a detailed guide.
  • Mac users, you can just run the command brew install mysql.
  • Windows users who installed XAMPP, as suggested earlier, do not need to install MySQL as it comes preinstalled.

You will also need a special driver that makes it possible for PHP to work with MySQL, you can install it with this command:

<pre class="ql-syntax" spellcheck="false"> # Linux users
$ sudo apt-get install php7.2-mysql

# Mac Users
# You don't have to do anything as it is installed automatically.

</pre>

Load the project directory in your favorite text editor and there should be a .env file in the root of the folder. This is where Laravel stores its environment variables.

Create a new MySQL database and call it laravelcms. In the .env file, update the database configuration keys as seen below:

<pre class="ql-syntax" spellcheck="false"> DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravelcms
DB_USERNAME=YourUsername
DB_PASSWORD=YourPassword

</pre>

Replace the DB_USERNAME and DB_PASSWORD with your MySQL database credentials.

Setting up user roles

Like most content management systems, we are going to have a user role system so that our blog can have multiple types of users; the admin and regular user. The admin should be able to create a post and perform other CRUD operations on a post. The regular user, on the other hand, should be able to view and comment on a post.

For us to implement this functionality, we need to implement user authentication and add a simple role authorization system.


Setting up user authentication

Laravel provides user authentication out of the box, which is great, and we can key into the feature by running a single command:

<pre class="ql-syntax" spellcheck="false"> $ php artisan make:auth

</pre>

The above will create all that’s necessary for authentication in our application so we do not need to do anything extra.


Setting up role authorization

We need a model for the user roles so let’s create one and an associated migration file:

<pre class="ql-syntax" spellcheck="false"> $ php artisan make:model Role -m

</pre>

In the database/migrations folder, find the newly created migration file and update the CreateRolesTable class with this snippet:

<pre class="ql-syntax" spellcheck="false"> <?php // File: ./database/migrations/*_create_roles_table.php

// [...]

class CreateRolesTable extends Migration
{
    public function up()
    {
        Schema::create('roles', function (Blueprint $table) {
            $table-&gt;increments('id');
            $table-&gt;string('name');
            $table-&gt;string('description');
            $table-&gt;timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('roles');
    }
}

</pre>

We intend to create a many-to-many relationship between the User and Role models so let’s add a relationship method on both models.

Open the User model and add the following method:

<pre class="ql-syntax" spellcheck="false"> // File: ./app/User.php
public function roles()
{
return $this->belongsToMany(Role::class);
}
</pre>

Open the Role model and include the following method:

<pre class="ql-syntax" spellcheck="false"> // File: ./app/Role.php
public function users()
{
return $this->belongsToMany(User::class);
}
</pre>

We are also going to need a pivot table to associate each user with a matching role so let’s create a new migration file for the role_user table:

<pre class="ql-syntax" spellcheck="false"> $ php artisan make:migration create_role_user_table

</pre>

In the database/migrations folder, find the newly created migration file and update the CreateRoleUserTable class with this snippet:

<pre class="ql-syntax" spellcheck="false"> // File: ./database/migrations/*_create_role_user_table.php
<?php

// [...]

class CreateRoleUserTable extends Migration
{

    public function up()
    {
        Schema::create('role_user', function (Blueprint $table) {
            $table-&gt;increments('id');
            $table-&gt;integer('role_id')-&gt;unsigned();
            $table-&gt;integer('user_id')-&gt;unsigned();
        });
    }

    public function down()
    {
        Schema::dropIfExists('role_user');
    }
}

</pre>

Next, let’s create seeders that will populate the users and roles tables with some data. In your terminal, run the following command to create the database seeders:

<pre class="ql-syntax" spellcheck="false"> $ php artisan make:seeder RoleTableSeeder
$ php artisan make:seeder UserTableSeeder

</pre>

In the database/seeds folder, open the RoleTableSeeder.php file and replace the contents with the following code:

<pre class="ql-syntax" spellcheck="false"> // File: ./database/seeds/RoleTableSeeder.php
<?php

use App\Role;
use Illuminate\Database\Seeder;

class RoleTableSeeder extends Seeder
{
    public function run()
    {
        $role_regular_user = new Role;
        $role_regular_user-&gt;name = 'user';
        $role_regular_user-&gt;description = 'A regular user';
        $role_regular_user-&gt;save();

        $role_admin_user = new Role;
        $role_admin_user-&gt;name = 'admin';
        $role_admin_user-&gt;description = 'An admin user';
        $role_admin_user-&gt;save();
    }
}

</pre>

Open the UserTableSeeder.php file and replace the contents with the following code:

<pre class="ql-syntax" spellcheck="false"> // File: ./database/seeds/UserTableSeeder.php
<?php

use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Hash;
use App\User;
use App\Role;

class UserTableSeeder extends Seeder
{

    public function run()
    {
        $user = new User;
        $user-&gt;name = 'Samuel Jackson';
        $user-&gt;email = '[email protected]';
        $user-&gt;password = bcrypt('samuel1234');
        $user-&gt;save();
        $user-&gt;roles()-&gt;attach(Role::where('name', 'user')-&gt;first());

        $admin = new User;
        $admin-&gt;name = 'Neo Ighodaro';
        $admin-&gt;email = '[email protected]';
        $admin-&gt;password = bcrypt('neo1234');
        $admin-&gt;save();
        $admin-&gt;roles()-&gt;attach(Role::where('name', 'admin')-&gt;first());
    }
}

</pre>

We also need to update the DatabaseSeeder class. Open the file and update the run method as seen below:

<pre class="ql-syntax" spellcheck="false"> // File: ./database/seeds/DatabaseSeeder.php
<?php

// [...]

class DatabaseSeeder extends Seeder
{
    public function run()
    {
        $this-&gt;call([
            RoleTableSeeder::class, 
            UserTableSeeder::class,
        ]);
    }
}

</pre>

Next, let’s update the User model. We will be adding a checkRoles method that checks what role a user has. We will return a 404 page where a user doesn’t have the expected role for a page. Open the User model and add these methods:

<pre class="ql-syntax" spellcheck="false"> // File: ./app/User.php
public function checkRoles($roles)
{
if ( ! is_array($roles)) {
$roles = [$roles];
}

    if ( ! $this-&gt;hasAnyRole($roles)) {
        auth()-&gt;logout();
        abort(404);
    }
}

public function hasAnyRole($roles): bool
{
    return (bool) $this-&gt;roles()-&gt;whereIn('name', $roles)-&gt;first();
}

public function hasRole($role): bool
{
    return (bool) $this-&gt;roles()-&gt;where('name', $role)-&gt;first();
}

</pre>

Let’s modify the RegisterController.php file in the Controllers/Auth folder so that a default role, the user role, is always attached to a new user at registration.

Open the RegisterController and update the create action with the following code:

<pre class="ql-syntax" spellcheck="false"> // File: ./app/Http/Controllers/Auth/RegisterController.php
protected function create(array $data)
{
$user = User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
]);

    $user-&gt;roles()-&gt;attach(\App\Role::where('name', 'user')-&gt;first());

    return $user;
}

</pre>

Now let’s migrate and seed the database so that we can log in with the sample accounts. To do this, run the following command in your terminal:

<pre class="ql-syntax" spellcheck="false"> $ php artisan migrate:fresh --seed

</pre>

In order to test that our roles work as they should, we will make an update to the HomeController.php file. Open the HomeController and update the index method as seen below:

<pre class="ql-syntax" spellcheck="false"> // File: ./app/Http/Controllers/HomeController.php
public function index(Request $request)
{
$request->user()->checkRoles('admin');

    return view('home');
}

</pre>

Now, only administrators should be able to see the dashboard. In a more complex application, we would use a middleware to do this instead.

We can test that this works by serving the application and logging in both user accounts; Samuel Jackson and Neo Ighodaro.

Remember that in our UserTableSeeder.php file, we defined Samuel as a regular user and Neo as an admin, so Samuel should see a 404 error after logging in and Neo should be able to see the homepage.


Testing the application

Let’s serve the application with this command:

<pre class="ql-syntax" spellcheck="false"> $ php artisan serve

</pre>

When we try logging in with Samuel’s credentials, we should see this:

On the other hand, we will get logged in with Neo’s credentials because he has an admin account:

We will also confirm that whenever a new user registers, he is assigned a role and it is the role of a regular user. We will create a new user and call him Greg, he should see a 404 error right after:

It works just as we wanted it to, however, it doesn’t really make any sense for us to redirect a regular user to a 404 page. Instead, we will edit the HomeController so that it redirects users based on their roles, that is, it redirects a regular user to a regular homepage and an admin to an admin dashboard.

Open the HomeController.php file and update the index method as seen below:

<pre class="ql-syntax" spellcheck="false"> // File: ./app/Http/Controllers/HomeController.php
public function index(Request $request)
{
if ($request->user()->hasRole('user')) {
return redirect('/');
}

    if ($request-&gt;user()-&gt;hasRole('admin')){
        return redirect('/admin/dashboard');
    }
}

</pre>

If we serve our application and try to log in using the admin account, we will hit a 404 error because we do not have a controller or a view for the admin/dashboard route. In the next article, we will start building the basic views for the CMS.


Conclusion

In this tutorial, we learned how to install a fresh Laravel app on our machine and pulled in all the needed dependencies. We also learned how to configure the Laravel app to work with a MySQL database. We also created our models and migrations files and seeded the database using database seeders.

In the next part of this series, we will start building the views for the application.

The source code for this project is available on Github.


Build a CMS with Laravel and Vue - Part 2: Implementing posts

In the previous part of this series, we set up user authentication and role authorization but we didn’t create any views for the application yet. In this section, we will create the Post model and start building the frontend for the application.

Our application allows different levels of accessibility for two kinds of users; the regular user and admin. In this chapter, we will focus on building the view that the regular users are permitted to see.

Before we build any views, let’s create the Post model as it is imperative to rendering the view.

The source code for this project is available here on GitHub.

Prerequisites

To follow along with this series, a few things are required:

  • Basic knowledge of PHP.
  • Basic knowledge of the Laravel framework.
  • Basic knowledge of JavaScript (ES6 syntax).
  • Basic knowledge of Vue.
  • Postman installed on your machine.

Setting up the Post model

We will create the Post model with an associated resource controller and a migration file using this command:

<pre class="ql-syntax" spellcheck="false"> $ php artisan make:model Post -mr

</pre>

We added the r flag because we want the controller to be a resource controller. The m flag will generate a migration for the model.

Let’s navigate into the database/migrations folder and update the CreatePostsTable class that was generated for us:

<pre class="ql-syntax" spellcheck="false"> // File: ./app/database/migrations/*_create_posts_table.php
<?php

// [...]

class CreatePostsTable extends Migration
{
    public function up()
    {
        Schema::create('posts', function (Blueprint $table) {
            $table-&gt;increments('id');
            $table-&gt;integer('user_id')-&gt;unsigned();
            $table-&gt;string('title');
            $table-&gt;text('body');
            $table-&gt;binary('image')-&gt;nullable();
            $table-&gt;timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('posts');
    }
}

</pre>

We included a user_id property because we want to create a relationship between the User and Post models. A Post also has an image field, which is where its associated image’s address will be stored.


Creating a database seeder for the Post table

We will create a new seeder file for the posts table using this command:

<pre class="ql-syntax" spellcheck="false"> $ php artisan make:seeder PostTableSeeder

</pre>

Let’s navigate into the database/seeds folder and update the PostTableSeeder.php file:

<pre class="ql-syntax" spellcheck="false"> // File: ./app/database/seeds/PostsTableSeeder.php
<?php

use App\Post;
use Illuminate\Database\Seeder;

class PostTableSeeder extends Seeder
{
    public function run()
    {
        $post = new Post;
        $post-&gt;user_id = 2;
        $post-&gt;title = "Using Laravel Seeders";
        $post-&gt;body = "Laravel includes a simple method of seeding your database with test data using seed classes. All seed classes are stored in the database/seeds directory. Seed classes may have any name you wish, but probably should follow some sensible convention, such as UsersTableSeeder, etc. By default, a DatabaseSeeder class is defined for you. From this class, you may use the  call method to run other seed classes, allowing you to control the seeding order.";
        $post-&gt;save();

        $post = new Post;
        $post-&gt;user_id = 2;
        $post-&gt;title = "Database: Migrations";
        $post-&gt;body = "Migrations are like version control for your database, allowing your team to easily modify and share the application's database schema. Migrations are typically paired with Laravel's schema builder to easily build your application's database schema. If you have ever had to tell a teammate to manually add a column to their local database schema, you've faced the problem that database migrations solve.";
        $post-&gt;save();
    }
}

</pre>

When we run this seeder, it will create two new posts and assign both of them to the admin user whose ID is 2. We are attaching both posts to the admin user because the regular users are only allowed to view posts and make comments; they can’t create a post.

Let’s open the DatabaseSeeder and update it with the following code:

<pre class="ql-syntax" spellcheck="false"> // File: ./app/database/seeds/DatabaseSeeder.php
<?php

use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    public function run()
    {
        $this-&gt;call([
            RoleTableSeeder::class,
            UserTableSeeder::class,
            PostTableSeeder::class,
        ]);
    }
}

</pre>

We created the RoleTableSeeder and UserTableSeeder files in the previous chapter.

We will use this command to migrate our tables and seed the database:

<pre class="ql-syntax" spellcheck="false"> $ php artisan migrate:fresh --seed

</pre>

Defining the relationships

Just as we previously created a many-to-many relationship between the User and Role models, we need to create a different kind of relationship between the Post and User models.

We will define the relationship as a one-to-many relationship because a user will have many posts but a post will only ever belong to one user.

Open the User model and include the method below:

<pre class="ql-syntax" spellcheck="false"> // File: ./app/User.php
public function posts()
{
return $this->hasMany(Post::class);
}
</pre>

Open the Post model and include the method below:

<pre class="ql-syntax" spellcheck="false"> // File: ./app/Post.php
public function user()
{
return $this->belongsTo(User::class);
}
</pre>

Setting up the routes

At this point in our application, we do not have a front page with all the posts listed. Let’s create so anyone can see all of the created posts. Asides from the front page, we also need a single post page in case a user needs to read a specific post.

Let’s include two new routes to our routes/web.php file:

  • The first route will match requests to the root of our application and will be handled by the [email protected] action:
<pre class="ql-syntax" spellcheck="false"> Route::get('/', '[email protected]');
</pre>
In the routes/web.php file, there will already be a route definition for the / address, you will have to replace it with the new route definition above.
  • The second route will handle requests for specific Post items and will be handled by the [email protected] action:
<pre class="ql-syntax" spellcheck="false"> Route::get('/posts/{post}', '[email protected]');
</pre>

With these two new routes added, here’s what the routes/web.php file should look like this:

<pre class="ql-syntax" spellcheck="false"> // File: ./routes/web.php
<?php

Auth::routes();
Route::get('/posts/{post}', '[email protected]');
Route::get('/home', '[email protected]')-&gt;name('home');
Route::get('/', '[email protected]');

</pre>

Setting up the Post controller

In this section, we want to define the handler action methods that we registered in the routes/web.php file so that our application know how to render the matching views.

First, let’s add the all() method:

<pre class="ql-syntax" spellcheck="false"> // File: ./app/Http/Controllers/PostController.php
public function all()
{
return view('landing', [
'posts' => Post::latest()->paginate(5)
]);
}
</pre>

Here, we want to retrieve five created posts per page and send to the landing view. We will create this view shortly.

Next, let’s add the single() method to the controller:

<pre class="ql-syntax" spellcheck="false"> // File: ./app/Http/Controllers/PostController.php
public function single(Post $post)
{
return view('single', compact('post'));
}
</pre>

In the method above, we used a feature of Laravel named route model binding to map the URL parameter to a Post instance with the same ID. We are returning a single view, which we will create shortly. This will be the view for the single post page.


Building our views

Laravel uses a templating engine called Blade for its frontend. We will use Blade to build these parts of the frontend before switching to Vue in the next chapter.

Navigate to the resources/views folder and create two new Blade files:

  1. landing.blade.php
  2. single.blade.php

These are the files that will load the views for the landing page and single post page. Before we start writing any code in these files, we want to create a simple layout template that our page views can use as a base.

In the resources/views/layouts folder, create a Blade template file and call it master.blade.php. This is where we will define the inheritable template for our single and landing pages.

Open the master.blade.php file and update it with this code:

<pre class="ql-syntax" spellcheck="false"> <!-- File: ./resources/views/layouts/master.blade.php -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="Neo Ighodaro">
<title>LaravelCMS</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
<style>
body {
padding-top: 54px;
}
@media (min-width: 992px) {
body {
padding-top: 56px;
}
}
</style>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark fixed-top">
<div class="container">
<a class="navbar-brand" href="/">LaravelCMS</a>
<div class="collapse navbar-collapse" id="navbarResponsive">
<ul class="navbar-nav ml-auto">
@if (Route::has('login'))
@auth
<li class="nav-item">
<a class="nav-link" href="{{ url('/home') }}">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ route('logout') }}"
onclick="event.preventDefault();
document.getElementById('logout-form').submit();">
Log out
</a>
<form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
@csrf
</form>
</li>
@else
<li class="nav-item">
<a class="nav-link" href="{{ route('login') }}">Login</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ route('register') }}">Register</a>
</li>
@endauth
@endif
</ul>
</div>
</div>
</nav>

    &lt;div id="app"&gt;
        @yield('content')
    &lt;/div&gt;

    &lt;footer class="py-5 bg-dark"&gt;
      &lt;div class="container"&gt;
        &lt;p class="m-0 text-center text-white"&gt;Copyright &amp;copy; LaravelCMS 2018&lt;/p&gt;
      &lt;/div&gt;
    &lt;/footer&gt;
  &lt;/body&gt;
&lt;/html&gt;

</pre>

Now we can inherit this template in the landing.blade.php file, open it and update it with this code:

<pre class="ql-syntax" spellcheck="false"> {{-- File: ./resources/views/landing.blade.php --}}
@extends('layouts.master')

@section('content')
&lt;div class="container"&gt;
  &lt;div class="row align-items-center"&gt;
    &lt;div class="col-md-8 mx-auto"&gt;
      &lt;h1 class="my-4 text-center"&gt;Welcome to the Blog &lt;/h1&gt;

      @foreach ($posts as $post)
      &lt;div class="card mb-4"&gt;
        &lt;img class="card-img-top" src=" {!! !empty($post-&gt;image) ? '/uploads/posts/' . $post-&gt;image :  'http://placehold.it/750x300' !!} " alt="Card image cap"&gt;
        &lt;div class="card-body"&gt;
          &lt;h2 class="card-title text-center"&gt;{{ $post-&gt;title }}&lt;/h2&gt;
          &lt;p class="card-text"&gt; {{ str_limit($post-&gt;body, $limit = 280, $end = '...') }} &lt;/p&gt;
          &lt;a href="/posts/{{ $post-&gt;id }}" class="btn btn-primary"&gt;Read More &amp;rarr;&lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="card-footer text-muted"&gt;
          Posted {{ $post-&gt;created_at-&gt;diffForHumans() }} by
          &lt;a href="#"&gt;{{ $post-&gt;user-&gt;name }} &lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;
      @endforeach

    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
@endsection

</pre>

Let’s do the same with the single.blade.php file, open it and update it with this code:

<pre class="ql-syntax" spellcheck="false"> {{-- File: ./resources/views/single.blade.php --}}
@extends('layouts.master')

@section('content')
&lt;div class="container"&gt;
  &lt;div class="row"&gt;
    &lt;div class="col-lg-10 mx-auto"&gt;
      &lt;h3 class="mt-4"&gt;{{ $post-&gt;title }} &lt;span class="lead"&gt; by &lt;a href="#"&gt; {{ $post-&gt;user-&gt;name }} &lt;/a&gt;&lt;/span&gt; &lt;/h3&gt;
      &lt;hr&gt;
      &lt;p&gt;Posted {{ $post-&gt;created_at-&gt;diffForHumans() }} &lt;/p&gt;
      &lt;hr&gt;
      &lt;img class="img-fluid rounded" src=" {!! !empty($post-&gt;image) ? '/uploads/posts/' . $post-&gt;image :  'http://placehold.it/750x300' !!} " alt=""&gt;
      &lt;hr&gt;
      &lt;p class="lead"&gt;{{ $post-&gt;body }}&lt;/p&gt;
      &lt;hr&gt;
      &lt;div class="card my-4"&gt;
        &lt;h5 class="card-header"&gt;Leave a Comment:&lt;/h5&gt;
        &lt;div class="card-body"&gt;
          &lt;form&gt;
            &lt;div class="form-group"&gt;
              &lt;textarea class="form-control" rows="3"&gt;&lt;/textarea&gt;
            &lt;/div&gt;
            &lt;button type="submit" class="btn btn-primary"&gt;Submit&lt;/button&gt;
          &lt;/form&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
@endsection

</pre>

Testing the application

We can test the application to see that things work as we expect. When we serve the application, we expect to see a landing page and a single post page. We also expect to see two posts because that’s the number of posts we seeded into the database.

We will serve the application using this command:

<pre class="ql-syntax" spellcheck="false"> $ php artisan serve

</pre>

We can visit this address to see the application:

We have used simple placeholder images here because we haven’t built the admin dashboard that allows CRUD operations to be performed on posts.

In the coming chapters, we will add the ability for an admin to include a custom image when creating a new post.


Conclusion

In this chapter, we created the Post model and defined a relationship on it to the User model. We also built the landing page and single page.

In the next part of this series, we will develop the API that will be the medium for communication between the admin user and the post items.

The source code for this project is available here on Github.


Build a CMS with Laravel and Vue - Part 3: Building an API

In the previous part of this series, we initialized the posts resource and started building the frontend of the CMS. We designed the front page that shows all the posts and the single post page using Laravel’s templating engine, Blade.

In this part of the series, we will start building the API for the application. We will create an API for CRUD operations that an admin will perform on posts and we will test the endpoints using Postman.

The source code for this project is available here on GitHub.

Prerequisites

To follow along with this series, a few things are required:

  • Basic knowledge of PHP.
  • Basic knowledge of the Laravel framework.
  • Basic knowledge of JavaScript (ES6 syntax).
  • Basic knowledge of Vue.
  • Postman installed on your machine.

Building the API using Laravel’s API resources

The Laravel framework makes it very easy to build APIs. It has an API resources feature that we can easily adopt in our project. You can think of API resources as a transformation layer between Eloquent models and the JSON responses that will be sent back by our API.


Allowing mass assignment on specified fields

Since we are going to be performing CRUD operations on the posts in the application, we have to explicitly specify that it’s permitted for some fields to be mass-assigned data. For security reasons, Laravel prevents mass assignment of data to model fields by default.

Open the Post.php file and include this line of code:

<pre class="ql-syntax" spellcheck="false"> // File: ./app/Post.php
protected $fillable = ['user_id', 'title', 'body', 'image'];
</pre>

Defining API routes

We will use the apiResource()method to generate only API routes. Open the routes/api.php file and add the following code:

<pre class="ql-syntax" spellcheck="false"> // File: ./routes/api.php
Route::apiResource('posts', 'PostController');

</pre>

Because we will be handling the API requests on the /posts URL using the PostController, we will have to include some additional action methods in our post controller.

Creating the Post resource

At the beginning of this section, we already talked about what Laravel’s API resources are. Here, we create a resource class for our Post model. This will enable us to retrieve Post data and return formatted JSON format.

To create a resource class for our Post model run the following command in your terminal:

<pre class="ql-syntax" spellcheck="false"> $ php artisan make:resource PostResource

</pre>

A new PostResource.php file will be available in the app/Http/Resources directory of our application. Open up the PostResource.php file and replace the toArray() method with the following:

<pre class="ql-syntax" spellcheck="false"> // File: ./app/Http/Resources/PostResource.php
public function toArray($request)
{
return [
'id' => $this->id,
'title' => $this->title,
'body' => $this->body,
'image' => $this->image,
'created_at' => (string) $this->created_at,
'updated_at' => (string) $this->updated_at,
];
}
</pre>

The job of this toArray() method is to convert our P``ost resource into an array. As seen above, we have specified the fields on our Post model, which we want to be returned as JSON when we make a request for posts.

We are also explicitly casting the dates, created_at and update_at, to strings so that they would be returned as date strings. The dates are normally an instance of Carbon.

Now that we have created a resource class for our Post model, we can start building the API’s action methods in our PostController and return instances of the PostResource where we want.


Adding the action methods to the Post controller

The usual actions performed on a post include the following:

  1. Create - the process of creating a new post.
  2. Read - the process of reading one or more posts.
  3. Update - the process of updating an already published post.
  4. Delete - the process of deleting a post.

In the last article, we already implemented a kind of ‘Read’ functionality when we defined the all and single methods. These methods allow users to browse through posts on the homepage.

In this section, we will define the methods that will resolve our API requests for creating, reading, updating and deleting posts.

The first thing we want to do is import the PostResource class at the top of the PostController.php file:

<pre class="ql-syntax" spellcheck="false"> // File: ./app/Http/Controllers/PostController.php
use App\Http\Resources\PostResource;
</pre>
Because we created the PostController as a resource controller, we already have the resource action methods included for us in the PostController.php file, we will be updating them with fitting snippets of code.

Building the handler action for the create operation

In the PostController update the store() action method with the code snippet below. It will allow us to validate and create a new post:

<pre class="ql-syntax" spellcheck="false"> // File: ./app/Http/Controllers/PostController.php
public function store(Request $request)
{
$this->validate($request, [
'title' => 'required',
'body' => 'required',
'user_id' => 'required',
'image' => 'required|mimes:jpeg,png,jpg,gif,svg',
]);

    $post = new Post;

    if ($request-&gt;hasFile('image')) {
        $image = $request-&gt;file('image');
        $name = str_slug($request-&gt;title).'.'.$image-&gt;getClientOriginalExtension();
        $destinationPath = public_path('/uploads/posts');
        $imagePath = $destinationPath . "/" . $name;
        $image-&gt;move($destinationPath, $name);
        $post-&gt;image = $name;
    }

    $post-&gt;user_id = $request-&gt;user_id;
    $post-&gt;title = $request-&gt;title;
    $post-&gt;body = $request-&gt;body;
    $post-&gt;save();

    return new PostResource($post);
}

</pre>

Here’s a breakdown of what this method does:

  1. Receives a new request.
  2. Validates the request.
  3. Creates a new post.
  4. Returns the post as a PostResource, which in turn returns a JSON formatted response.

Building the handler action for the read operations

What we want here is to be able to read all the created posts or a single post. This is possible because the apiResource() method defines the API routes using standard REST rules.

This means that a GET request to this address, http://127.0.0.1:800/api/posts, should be resolved by the index() action method. Let’s update the index method with the following code:

<pre class="ql-syntax" spellcheck="false"> // File: ./app/Http/Controllers/PostController.php
public function index()
{
return PostResource::collection(Post::latest()->paginate(5));
}
</pre>

This method will allow us to return a JSON formatted collection of all of the stored posts. We also want to paginate the response as this will allow us to create a better view on the admin dashboard.

Following the RESTful conventions as we discussed above, a GET request to this address, http://127.0.0.1:800/api/posts/id, should be resolved by the show() action method. Let’s update the method with the fitting snippet:

<pre class="ql-syntax" spellcheck="false"> // File: ./app/Http/Controllers/PostController.php
public function show(Post $post)
{
return new PostResource($post);
}
</pre>

Awesome, now this method will return a single instance of a post resource upon API query.


Building the handler action for the update operation

Next, let’s update the update() method in the PostController class. It will allow us to modify an existing post:

<pre class="ql-syntax" spellcheck="false"> // File: ./app/Http/Controllers/PostController.php
public function update(Request $request, Post $post)
{
$this->validate($request, [
'title' => 'required',
'body' => 'required',
]);

    $post-&gt;update($request-&gt;only(['title', 'body']));

    return new PostResource($post);
}

</pre>

This method receives a request and a post id as parameters, then we use route model binding to resolve the id into an instance of a Post. First, we validate the $request attributes, then we update the title and body fields of the resolved post.


Building the handler action for the delete operation

Let’s update the destroy() method in the PostController class. This method will allow us to remove an existing post:

<pre class="ql-syntax" spellcheck="false"> // File: ./app/Http/Controllers/PostController.php
public function destroy(Post $post)
{
$post->delete();

    return response()-&gt;json(null, 204);
}

</pre>

In this method, we resolve the Post instance, then delete it and return a 204 response code.

Our methods are complete. We have a method to handle our CRUD operations, however, we haven’t built the frontend for the admin dashboard.

At the end of the second article, we defined the [email protected]() action method like this:

<pre class="ql-syntax" spellcheck="false"> public function index(Request $request)
{
if ($request->user()->hasRole('user')) {
return view('home');
}

    if ($request-&gt;user()-&gt;hasRole('admin')) {
        return redirect('/admin/dashboard');
    }
}

</pre>

This allowed us to redirect regular users to the view home, and admin users to the URL /admin/dashboard. At this point in this series, a visit to /admin/dashboard will fail because we have neither defined it as a route with a handler Controller nor built a view for it.

Let’s create the AdminController with this command:

<pre class="ql-syntax" spellcheck="false"> $ php artisan make:controller AdminController

</pre>

We will add the /admin/ route to our routes/web.php file:

<pre class="ql-syntax" spellcheck="false"> Route::get('/admin/{any}', '[email protected]')->where('any', '.*');
</pre>
Note that we wrote /admin/{any} here because we intend to serve every page of the admin dashboard using the Vue router. When we start building the admin dashboard in the next article, we will let Vue handle all the routes of the /admin pages.

Let’s update the AdminController.php file to use the auth middleware and include an index() action method:

<pre class="ql-syntax" spellcheck="false"> // File: ./app/Http/Controllers/AdminController.php
<?php

namespace App\Http\Controllers;

class AdminController extends Controller
{
    public function __construct()
    {
        $this-&gt;middleware('auth');
    }

    public function index()
    {
        if (request()-&gt;user()-&gt;hasRole('admin')) {
            return view('admin.dashboard');
        }

        if (request()-&gt;user()-&gt;hasRole('user')) {
            return redirect('/home');
        }
    }
}

</pre>

In the index()action method, we included a snippet that will ensure that only admin users can visit the admin dashboard and perform CRUD operations on posts.

We will not start building the admin dashboard in this article but will test that our API works properly. We will use Postman to make requests to the application.


Testing the application

Let’s test that our API works as expected. We will, first of all, serve the application using this command:

<pre class="ql-syntax" spellcheck="false"> $ php artisan serve

</pre>

We can visit http://localhost:8000 to see our application and there should be exactly two posts available; these are the posts we seeded into the database during the migration:

When testing with Postman always set the Content-Type header to application/json.

Now let’s create a new post over the API interface using Postman. Send a POST request as seen below:

Now let’s update this post we just created. In Postman, we will pass only the title and body fields to a PUT request.

To make it easy, you can just copy the payload below and use the raw request data type for the Body:

<pre class="ql-syntax" spellcheck="false"> {
"title": "We made an edit to the Post on APIs",
"body": "To a developer, 'What's an API?' might be a straightforward - if not exactly simple - question. But to anyone who doesn't have experience with code. APIs can come across as confusing or downright intimidating."
}

</pre>

We could have used the PATCH method to make this update, the PUT and PATCH HTTP verbs both work well for editing an already existing item.

Finally, let’s delete the post using Postman:

We are sure the post is deleted because the response status is 204 No Content as we specified in the PostController.


Conclusion

In this chapter, we learned about Laravel’s API resources and we created a resource class for the Post model. We also used the apiResources() method to generate API only routes for our application. We wrote the methods to handle the API operations and tested them using Postman.

In the next part, we will build the admin dashboard and develop the logic that will enable the admin user to manage posts over the API.

The source code for this project is available here on Github.


Build a CMS with Laravel and Vue - Part 4: Building the dashboard

In the last article of this series, we built the API interface and used Laravel API resources to return neatly formatted JSON responses. We tested that the API works as we defined it to using Postman.

In this part of the series, we will start building the admin frontend of the CMS. This is the first part of the series where we will integrate Vue and explore Vue’s magical abilities.

When we are done with this part, our application will have some added functionalities as seen below:

The source code for this project is available here on GitHub.

Prerequisites

To follow along with this series, a few things are required:

  • Basic knowledge of PHP.
  • Basic knowledge of the Laravel framework.
  • Basic knowledge of JavaScript (ES6 syntax).
  • Basic knowledge of Vue.

Building the frontend

Laravel ships with Vue out of the box so we do not need to use the Vue-CLI or reference Vue from a CDN. This makes it possible for us to have all of our application, the frontend, and backend, in a single codebase.

Every newly created instance of a Laravel installation has some Vue files included by default, we can see these files when we navigate into the resources/assets/js/components folder.


Setting up Vue and VueRouter

Before we can start using Vue in our application, we need to first install some dependencies using NPM. To install the dependencies that come by default with Laravel, run the command below:

<pre class="ql-syntax" spellcheck="false"> $ npm install

</pre>

We will be managing all of the routes for the admin dashboard using vue-router so let’s pull it in:

<pre class="ql-syntax" spellcheck="false"> $ npm install --save vue-router

</pre>

When the installation is complete, the next thing we want to do is open the resources/assets/js/app.js file and replace its contents with the code below:

<pre class="ql-syntax" spellcheck="false"> // File: ./resources/assets/js/app.js
require('./bootstrap');

import Vue from 'vue'
import VueRouter from 'vue-router'
import Homepage from './components/Homepage'
import Read from './components/Read'

Vue.use(VueRouter)

const router = new VueRouter({
    mode: 'history',
    routes: [
        {
            path: '/admin/dashboard',
            name: 'read',
            component: Read,
            props: true
        },
    ],
});

const app = new Vue({
    el: '#app',
    router,
    components: { Homepage },
});

</pre>

In the snippet above, we imported the VueRouter and added it to the Vue application. We also imported a Homepage and a Read component. These are the components where we will write our markup so let’s create both files.

Open the resources/assets/js/components folder and create four files:

  1. Homepage.vue - this will be the parent component for the admin dashboard frontend.
  2. Read.vue - this will be component that displays all the available posts on the admin dashboard.
  3. Create.vue - this will be the component where an admin user can create a new post.
  4. Update.vue - this will be the component that displays the view where an admin user can update an existing post.
Note that we didn’t create a component file for the delete operation, this is because it is going to be possible to delete a post from the Read component. There is no need for a view.

In the resources/assets/js/app.js file, we defined a routes array and in it, we registered a read route. During render time, this route’s path will be mapped to the Read component.

In the previous article, we specified that admin users should be shown an admin.dashboard view in the index method, however, we didn’t create this view. Let’s create the view. Open the resources/views folder and create a new folder called admin. Within the new resources/views/admin folder, create a new file and called dashboard.blade.php. This is going to be the entry point to the admin dashboard, further from this route, we will let the VueRouter handle everything else.

Open the resources/views/admin/dashboard.blade.php file and paste in the following code:

<pre class="ql-syntax" spellcheck="false"> <!-- File: ./resources/views/admin/dashboard.blade.php -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title> Welcome to the Admin dashboard </title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
<style>
html, body {
background-color: #202B33;
color: #738491;
font-family: "Open Sans";
font-size: 16px;
font-smoothing: antialiased;
overflow: hidden;
}
</style>
</head>
<body>

  &lt;script src="{{ asset('js/app.js') }}"&gt;&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;

</pre>

Our goal here is to integrate Vue into the application, so we included the resources/assets/js/app.js file with this line of code:

<pre class="ql-syntax" spellcheck="false"> <script src="{{ asset('js/app.js') }}"></script>

</pre>

For our app to work, we need a root element to bind our Vue instance unto. Before the <script> tag, add this snippet of code:

<pre class="ql-syntax" spellcheck="false"> <div id="app">
<Homepage
:user-name='@json(auth()->user()->name)'
:user-id='@json(auth()->user()->id)'
></Homepage>
</div>
</pre>

We earlier defined the Homepage component as the wrapping component, that’s why we pulled it in here as the root component. For some of the frontend components to work correctly, we require some details of the logged in admin user to perform CRUD operations. This is why we passed down the userName and userId props to the Homepage component.

We need to prevent the CSRF error from occurring in our Vue frontend, so include this snippet of code just before the <title> tag:

<pre class="ql-syntax" spellcheck="false"> <meta name="csrf-token" content="{{ csrf_token() }}">
<script> window.Laravel = { csrfToken: 'csrf_token() ' } </script>
</pre>

This snippet will ensure that the correct token is always included in our frontend, Laravel provides the CSRF protection for us out of the box.

At this point, this should be the contents of your resources/views/admin/dashboard.blade.php file:

<pre class="ql-syntax" spellcheck="false"> <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<meta name="csrf-token" content="{{ csrf_token() }}">
<script> window.Laravel = { csrfToken: 'csrf_token() ' } </script>
<title> Welcome to the Admin dashboard </title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
<style>
html, body {
background-color: #202B33;
color: #738491;
font-family: "Open Sans";
font-size: 16px;
font-smoothing: antialiased;
overflow: hidden;
}
</style>
</head>
<body>
<div id="app">
<Homepage
:user-name='@json(auth()->user()->name)'
:user-id='@json(auth()->user()->id)'>
</Homepage>
</div>
<script src="{{ asset('js/app.js') }}"></script>
</body>
</html>
</pre>

Setting up the Homepage view

Open the Homepage.vue file that we created some time ago and include this markup template:

<pre class="ql-syntax" spellcheck="false"> <!-- File: ./resources/app/js/components/Homepage.vue -->
<template>
<div>
<nav>
<section>
<a style="color: white" href="/admin/dashboard">Laravel-CMS</a> &nbsp; || &nbsp;
<a style="color: white" href="/">HOME</a>
<hr>
<ul>
<li>
<router-link :to="{ name: 'create', params: { userId } }">
NEW POST
</router-link>
</li>
</ul>
</section>
</nav>
<article>
<header>
<header class="d-inline">Welcome, {{ userName }}</header>
<p @click="logout" class="float-right mr-3" style="cursor: pointer">Logout</p>
</header>
<div>
<router-view></router-view>
</div>
</article>
</div>
</template>
</pre>

We added a router-link in this template, which routes to the Create component.

We are passing the userId data to the create component because a userId is required during Post creation.

Let’s include some styles so that the page looks good. Below the closing template tag, paste the following code:

<pre class="ql-syntax" spellcheck="false"> <style scoped>
@import url(https://fonts.googleapis.com/css?family=Dosis:300|Lato:300,400,600,700|Roboto+Condensed:300,700|Open+Sans+Condensed:300,600|Open+Sans:400,300,600,700|Maven+Pro:400,700);
@import url("https://netdna.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.css");
* {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
header {
color: #d3d3d3;
}
nav {
position: absolute;
top: 0;
bottom: 0;
right: 82%;
left: 0;
padding: 22px;
border-right: 2px solid #161e23;
}
nav > header {
font-weight: 700;
font-size: 0.8rem;
text-transform: uppercase;
}
nav section {
font-weight: 600;
}
nav section header {
padding-top: 30px;
}
nav section ul {
list-style: none;
padding: 0px;
}
nav section ul a {
color: white;
text-decoration: none;
font-weight: bold;
}
article {
position: absolute;
top: 0;
bottom: 0;
right: 0;
left: 18%;
overflow: auto;
border-left: 2px solid #2a3843;
padding: 20px;
}
article > header {
height: 60px;
border-bottom: 1px solid #2a3843;
}
</style>
</pre>
We are using the scoped attribute on the <style> tag because we want the CSS to only be applied on the Homepage component.

Next, let’s add the <script> section that will use the props we passed down from the parent component. We will also define the method that controls the log out feature here. Below the closing style tag, paste the following code:

<pre class="ql-syntax" spellcheck="false"> <script>
export default {
props: {
userId: {
type: Number,
required: true
},
userName: {
type: String,
required: true
}
},
data() {
return {};
},
methods: {
logout() {
axios.post("/logout").then(() => {
window.location = "/";
});
}
}
};
</script>
</pre>

Setting up the Read view

In the resources/assets/js/app.js file, we defined the path of the read component as /admin/dashboard, which is the same address as the Homepage component. This will make sure the Read component always loads by default.

In the Read component, we want to load all of the available posts. We are also going to add Update and Delete options to each post. Clicking on these options will lead to the update and delete views respectively.

Open the Read.vue file and paste the following:

<pre class="ql-syntax" spellcheck="false"> <!-- File: ./resources/app/js/components/Read.vue -->
<template>
<div id="posts">
<p class="border p-3" v-for="post in posts">
{{ post.title }}
<router-link :to="{ name: 'update', params: { postId : post.id } }">
<button type="button" class="p-1 mx-3 float-right btn btn-light">
Update
</button>
</router-link>
<button
type="button"
@click="deletePost(post.id)"
class="p-1 mx-3 float-right btn btn-danger"
>
Delete
</button>
</p>
<div>
<button
v-if="next"
type="button"
@click="navigate(next)"
class="m-3 btn btn-primary"
>
Next
</button>
<button
v-if="prev"
type="button"
@click="navigate(prev)"
class="m-3 btn btn-primary"
>
Previous
</button>
</div>
</div>
</template>
</pre>

Above, we have the template to handle the posts that are loaded from the API. Next, paste the following below the closing template tag:

<pre class="ql-syntax" spellcheck="false"> <script>
export default {
mounted() {
this.getPosts();
},
data() {
return {
posts: {},
next: null,
prev: null
};
},
methods: {
getPosts(address) {
axios.get(address ? address : "/api/posts").then(response => {
this.posts = response.data.data;
this.prev = response.data.links.prev;
this.next = response.data.links.next;
});
},
deletePost(id) {
axios.delete("/api/posts/" + id).then(response => this.getPosts())
},
navigate(address) {
this.getPosts(address)
}
}
};
</script>
</pre>

In the script above, we defined a getPosts() method that requests a list of posts from the backend server. We also defined a posts object as a data property. This object will be populated whenever posts are received from the backend server.

We defined next and prev data string properties to store pagination links and only display the pagination options where it is available.

Lastly, we defined a deletePost() method that takes the id of a post as a parameter and sends a DELETE request to the API interface using Axios.


Testing the application

Now that we have completed the first few components, we can serve the application using this command:

<pre class="ql-syntax" spellcheck="false"> $ php artisan serve

</pre>

We will also build the assets so that our JavaScript is compiled for us. To do this, will run the command below in the root of the project folder:

<pre class="ql-syntax" spellcheck="false"> $ npm run dev

</pre>

We can visit the application’s URL http://localhost:8000 and log in as an admin user, and delete a post:


Conclusion

In this part of the series, we started building the admin dashboard using Vue. We installed VueRouter to make the admin dashboard a SPA. We added the homepage view of the admin dashboard and included read and delete functionalities.

We are not done with the dashboard just yet. In the next part, we will add the views that lets us create and update posts.

The source code for this project is available here on Github.


Build a CMS with Laravel and Vue - Part 5: Completing our dashboards

In the previous part of this series, we built the first parts of the admin dashboard using Vue. We also made it into an SPA with the VueRouter, this means that visiting the pages does not cause a reload to the web browser.

We only built the wrapper component and the Read component that retrieves the posts to be loaded so an admin can manage them.

Here’s a recording of what we ended up with, in the last article:

In this article, we will build the view that will allow users to create and update posts. We will start writing code in the Update.vue and Create.vue files that we created in the previous article.

When we are done with this part, we will have additional functionalities like create and updating:

The source code for this project is available here on Github.

Prerequisites

To follow along with this series, a few things are required:

  • Basic knowledge of PHP.
  • Basic knowledge of the Laravel framework.
  • Basic knowledge of JavaScript (ES6 syntax).
  • Basic knowledge of Vue.

Including the new routes in VueRouter

In the previous article, we only defined the route for the Read component, we need to include the route configuration for the new components that we are about to build; Update and Create.

Open the resources/assets/js/app.js file and replace the contents with the code below:

<pre class="ql-syntax" spellcheck="false"> require('./bootstrap');

import Vue from 'vue'
import VueRouter from 'vue-router'
import Homepage from './components/Homepage'
import Create from './components/Create'
import Read from './components/Read'
import Update from './components/Update'

Vue.use(VueRouter)

const router = new VueRouter({
    mode: 'history',
    routes: [
        {
            path: '/admin/dashboard',
            name: 'read',
            component: Read,
            props: true
        },
        {
            path: '/admin/create',
            name: 'create',
            component: Create,
            props: true
        },
        {
            path: '/admin/update',
            name: 'update',
            component: Update,
            props: true
        },
    ],
});

const app = new Vue({
    el: '#app',
    router,
    components: { Homepage },
});

</pre>

Above, we have added two new components to the JavaScript file. We have the Create and Read components. We also added them to the router so that they can be loaded using the specified URLs.


Building the create view

Open the Create.vue file and update it with this markup template:

<pre class="ql-syntax" spellcheck="false"> <!-- File: ./resources/app/js/components/Create.vue -->
<template>
<div class="container">
<form>
<div :class="['form-group m-1 p-3', (successful ? 'alert-success' : '')]">
<span v-if="successful" class="label label-sucess">Published!</span>
</div>
<div :class="['form-group m-1 p-3', error ? 'alert-danger' : '']">
<span v-if="errors.title" class="label label-danger">
{{ errors.title[0] }}
</span>
<span v-if="errors.body" class="label label-danger">
{{ errors.body[0] }}
</span>
<span v-if="errors.image" class="label label-danger">
{{ errors.image[0] }}
</span>
</div>

      &lt;div class="form-group"&gt;
        &lt;input type="title" ref="title" class="form-control" id="title" placeholder="Enter title" required&gt;
      &lt;/div&gt;

      &lt;div class="form-group"&gt;
        &lt;textarea class="form-control" ref="body" id="body" placeholder="Enter a body" rows="8" required&gt;&lt;/textarea&gt;
      &lt;/div&gt;

      &lt;div class="custom-file mb-3"&gt;
        &lt;input type="file" ref="image" name="image" class="custom-file-input" id="image" required&gt;
        &lt;label class="custom-file-label" &gt;Choose file...&lt;/label&gt;
      &lt;/div&gt;

      &lt;button type="submit" @click.prevent="create" class="btn btn-primary block"&gt;
        Submit
      &lt;/button&gt;
    &lt;/form&gt;
  &lt;/div&gt;
&lt;/template&gt;

</pre>

Above we have the template for the Create component. If there is an error during post creation, there will be a field indicating the specific error. When a post is successfully published, there will also a message saying it was successful.

Let’s include the script logic that will perform the sending of posts to our backend server and read back the response.

After the closing template tag add this:

<pre class="ql-syntax" spellcheck="false"> <script>
export default {
props: {
userId: {
type: Number,
required: true
}
},
data() {
return {
error: false,
successful: false,
errors: []
};
},
methods: {
create() {
const formData = new FormData();
formData.append("title", this.$refs.title.value);
formData.append("body", this.$refs.body.value);
formData.append("user_id", this.userId);
formData.append("image", this.$refs.image.files[0]);

      axios
        .post("/api/posts", formData)
        .then(response =&gt; {
          this.successful = true;
          this.error = false;
          this.errors = [];
        })
        .catch(error =&gt; {
          if (!_.isEmpty(error.response)) {
            if ((error.response.status = 422)) {
              this.errors = error.response.data.errors;
              this.successful = false;
              this.error = true;
            }
          }
        });

      this.$refs.title.value = "";
      this.$refs.body.value = "";
    }
  }
};
&lt;/script&gt;

</pre>

In the script above, we defined a create() method that takes the values of the input fields and uses the Axios library to send them to the API interface on the backend server. Within this method, we also update the status of the operation, so that an admin user can know when a post is created successfully or not.


Building the update view

Let’s start building the Update component. Open the Update.vue file and update it with this markup template:

<pre class="ql-syntax" spellcheck="false"> <!-- File: ./resources/app/js/components/Update.vue -->
<template>
<div class="container">
<form>
<div :class="['form-group m-1 p-3', successful ? 'alert-success' : '']">
<span v-if="successful" class="label label-sucess">Updated!</span>
</div>

      &lt;div :class="['form-group m-1 p-3', error ? 'alert-danger' : '']"&gt;
        &lt;span v-if="errors.title" class="label label-danger"&gt;
          {{ errors.title[0] }}
        &lt;/span&gt;
        &lt;span v-if="errors.body" class="label label-danger"&gt;
          {{ errors.body[0] }}
        &lt;/span&gt;
      &lt;/div&gt;

      &lt;div class="form-group"&gt;
        &lt;input type="title" ref="title" class="form-control" id="title" placeholder="Enter title" required&gt;
      &lt;/div&gt;

      &lt;div class="form-group"&gt;
        &lt;textarea class="form-control" ref="body" id="body" placeholder="Enter a body" rows="8" required&gt;&lt;/textarea&gt;
      &lt;/div&gt;

      &lt;button type="submit" @click.prevent="update" class="btn btn-primary block"&gt;
        Submit
      &lt;/button&gt;
    &lt;/form&gt;
  &lt;/div&gt;
&lt;/template&gt;

</pre>

This template is similar to the one in the Create component. Let’s add the script for the component.

Below the closing template tag, paste the following:

<pre class="ql-syntax" spellcheck="false"> <script>
export default {
mounted() {
this.getPost();
},
props: {
postId: {
type: Number,
required: true
}
},
data() {
return {
error: false,
successful: false,
errors: []
};
},
methods: {
update() {
let title = this.$refs.title.value;
let body = this.$refs.body.value;

      axios
        .put("/api/posts/" + this.postId, { title, body })
        .then(response =&gt; {
          this.successful = true;
          this.error = false;
          this.errors = [];
        })
        .catch(error =&gt; {
          if (!_.isEmpty(error.response)) {
            if ((error.response.status = 422)) {
              this.errors = error.response.data.errors;
              this.successful = false;
              this.error = true;
            }
          }
        });
    },
    getPost() {
      axios.get("/api/posts/" + this.postId).then(response =&gt; {
        this.$refs.title.value = response.data.data.title;
        this.$refs.body.value = response.data.data.body;
      });
    }
  }
};
&lt;/script&gt;

</pre>

In the script above, we make a call to the getPosts() method as soon as the component is mounted. The getPosts() method fetches the data of a single post from the backend server, using the postId.

When Axios sends back the data for the post, we update the input fields in this component so they can be updated.

Finally, the update() method takes the values of the fields in the components and attempts to send them to the backend server for an update. In a situation where the fails, we get instant feedback.


Testing the application

To test that our changes work, we want to refresh the database and restore it back to a fresh state. To do this, run the following command in your terminal:

<pre class="ql-syntax" spellcheck="false"> $ php artisan migrate:fresh --seed

</pre>

Next, let’s compile our JavaScript files and assets. This will make sure all the changes we made in the Vue component and the app.js file gets built. To recompile, run the command below in your terminal:

<pre class="ql-syntax" spellcheck="false"> $ npm run dev

</pre>

Lastly, we need to serve the application. To do this, run the following command in your terminal window:

<pre class="ql-syntax" spellcheck="false"> $ php artisan serve

</pre>

If you had the serve command running before, then you might need to restart it.

We will visit the application’s http://localhost:8000 and log in as an admin user. From the dashboard, you can test the create and update feature:


Conclusion

In this part of the series, we updated the dashboard to include the Create and Update component so the administrator can add and update posts.

In the next article, we will build the views that allow for the creation and updating of a post.

The source code for this project is available here on Github.


Build a CMS with Laravel and Vue - Part 6: Adding Realtime Comments

In the previous part of this series, we finished building the backend of the application using Vue. We were able to add the create and update component, which is used for creating a new post and updating an existing post.

Here’s a screen recording of what we have been able to achieve:

In this final part of the series, we will be adding support for comments. We will also ensure that the comments on each post are updated in realtime, so a user doesn’t have to refresh the page to see new comments.

When we are done, our application will have new features and will work like this:

The source code for this project is available here on Github.

Prerequisites

To follow along with this series, a few things are required:

  • A Pusher account. Sign up here.
  • Basic knowledge of PHP.
  • Basic knowledge of the Laravel framework.
  • Basic knowledge of JavaScript (ES6 syntax).
  • Basic knowledge of Vue.

Adding comments to the backend

When we were creating the API, we did not add the support for comments to the post resource, so we will have to do so now. Open the API project in your text editor as we will be modifying the project a little.

The first thing we want to do is create a model, controller, and a migration for the comment resource. To do this, open your terminal and cd to the project directory and run the following command:

<pre class="ql-syntax" spellcheck="false"> $ php artisan make:model Comment -mc

</pre>

The command above will create a model called Comment, a controller called CommentController, and a migration file in the database/migrations directory.


Updating the comments migration file

To update the comments migration navigate to the database/migrations folder and find the newly created migration file for the Comment model. Let’s update the up() method in the file:

<pre class="ql-syntax" spellcheck="false"> // File: ./database/migrations/*_create_comments_table.php
public function up()
{
Schema::create('comments', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->integer('user_id')->unsigned();
$table->integer('post_id')->unsigned();
$table->text('body');
});
}
</pre>

We included user_id and post_id fields because we intend to create a link between the comments, users, and posts. The body field will contain the actual comment.


Defining the relationships among the Comment, User, and Post models

In this application, a comment will belong to a user and a post because a user can make a comment on a specific post, so we need to define the relationship that ties everything up.

Open the User model and include this method:

<pre class="ql-syntax" spellcheck="false"> // File: ./app/User.php
public function comments()
{
return $this->hasMany(Comment::class);
}
</pre>

This is a relationship that simply says that a user can have many comments. Now let’s define the same relationship on the Post model. Open the Post.php file and include this method:

<pre class="ql-syntax" spellcheck="false"> // File: ./app/Post.php
public function comments()
{
return $this->hasMany(Comment::class);
}
</pre>

Finally, we will include two methods in the Comment model to complete the second half of the relationships we defined in the User and Post models.

Open the app/Comment.php file and include these methods:

<pre class="ql-syntax" spellcheck="false"> // File: ./app/Comment.php
public function user()
{
return $this->belongsTo(User::class);
}

public function post()
{
    return $this-&gt;belongsTo(Post::class);
}

</pre>

Since we want to be able to mass assign data to specific fields of a comment instance during comment creation, we will include this array of permitted assignments in the app/Comment.php file:

<pre class="ql-syntax" spellcheck="false"> protected $fillable = ['user_id', 'post_id', 'body'];
</pre>

We can now run our database migration for our comments:

<pre class="ql-syntax" spellcheck="false"> $ php artisan migrate

</pre>

Configuring Laravel to broadcast events using Pusher

We already said that the comments will have a realtime functionality and we will be building this using Pusher, so we need to enable Laravel’s event broadcasting feature.

Open the config/app.php file and uncomment the following line in the providers array:

<pre class="ql-syntax" spellcheck="false"> App\Providers\BroadcastServiceProvider

</pre>

Next, we need to configure the broadcast driver in the .env file:

<pre class="ql-syntax" spellcheck="false"> BROADCAST_DRIVER=pusher

</pre>

Let’s pull in the Pusher PHP SDK using composer:

<pre class="ql-syntax" spellcheck="false"> $ composer require pusher/pusher-php-server

</pre>

Configuring Pusher

For us to use Pusher in this application, it is a prerequisite that you have a Pusher account. You can create a free Pusher account here then login to your dashboard and create an app.

Once you have created an app, we will use the app details to configure pusher in the .env file:

<pre class="ql-syntax" spellcheck="false"> PUSHER_APP_ID=xxxxxx
PUSHER_APP_KEY=xxxxxxxxxxxxxxxxxxxx
PUSHER_APP_SECRET=xxxxxxxxxxxxxxxxxxxx
PUSHER_APP_CLUSTER=xx

</pre>

Update the Pusher keys with the app credentials provided for you under the Keys section on the Overview tab on the Pusher dashboard.


Broadcasting an event for when a new comment is sent

To make the comment update realtime, we have to broadcast an event based on the comment creation activity. We will create a new event and call it CommentSent. It is to be fired when there is a successful creation of a new comment.

Run command in your terminal:

<pre class="ql-syntax" spellcheck="false"> php artisan make:event CommentSent

</pre>

There will be a newly created file in the app\Events directory, open the CommentSent.php file and ensure that it implements the ShouldBroadcast interface.

Open and replace the file with the following code:

<pre class="ql-syntax" spellcheck="false"> // File: ./app/Events/CommentSent.php
<?php

namespace App\Events;

use App\Comment;
use App\User;
use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;

class CommentSent implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public $user;

    public $comment;

    public function __construct(User $user, Comment $comment)
    {
        $this-&gt;user = $user;

        $this-&gt;comment = $comment;
    }

    public function broadcastOn()
    {
        return new PrivateChannel('comment');
    }
}

</pre>

In the code above, we created two public properties, user and comment, to hold the data that will be passed to the channel we are broadcasting on. We also created a private channel called comment. We are using a private channel so that only authenticated clients can subscribe to the channel.


Defining the routes for handling operations on a comment

We created a controller for the comment model earlier but we haven’t defined the web routes that will redirect requests to be handled by that controller.

Open the routes/web.php file and include the code below:

<pre class="ql-syntax" spellcheck="false"> // File: ./routes/web.php
Route::get('/{post}/comments', '[email protected]');
Route::post('/{post}/comments', '[email protected]');
</pre>

Setting up the action methods in the CommentController

We need to include two methods in the CommentController.php file, these methods will be responsible for storing and retrieving methods. In the store() method, we will also be broadcasting an event when a new comment is created.

Open the CommentController.php file and replace its contents with the code below:

<pre class="ql-syntax" spellcheck="false"> // File: ./app/Http/Controllers/CommentController.php
<?php

namespace App\Http\Controllers;

use App\Comment;
use App\Events\CommentSent;
use App\Post;
use Illuminate\Http\Request;

class CommentController extends Controller
{
    public function store(Post $post)
    {
        $this-&gt;validate(request(), [
            'body' =&gt; 'required',
        ]);

        $user = auth()-&gt;user();

        $comment = Comment::create([
            'user_id' =&gt; $user-&gt;id,
            'post_id' =&gt; $post-&gt;id,
            'body' =&gt; request('body'),
        ]);

        broadcast(new CommentSent($user, $comment))-&gt;toOthers();

        return ['status' =&gt; 'Message Sent!'];
    }

    public function index(Post $post)
    {
        return $post-&gt;comments()-&gt;with('user')-&gt;get();
    }
}

</pre>

In the store method above, we are validating then creating a new post comment. After the comment has been created, we broadcast the CommentSent event to other clients so they can update their comments list in realtime.

In the index method we just return the comments belonging to a post along with the user that made the comment.


Adding a layer of authentication

Let’s add a layer of authentication that ensures that only authenticated users can listen on the private comment channel we created.

Add the following code to the routes/channels.php file:

<pre class="ql-syntax" spellcheck="false"> // File: ./routes/channels.php
Broadcast::channel('comment', function ($user) {
return auth()->check();
});
</pre>

Adding comments to the frontend

In the second article of this series, we created the view for the single post landing page in the single.blade.php file, but we didn’t add the comments functionality. We are going to add it now. We will be using Vue to build the comments for this application so the first thing we will do is include Vue in the frontend of our application.

Open the master layout template and include Vue to its <head> tag. Just before the <title> tag appears in the master.blade.php file, include this snippet:

<pre class="ql-syntax" spellcheck="false"> <!-- File: ./resources/views/layouts/master.blade.php -->
<meta name="csrf-token" content="{{ csrf_token() }}">
<script src="{{ asset('js/app.js') }}" defer></script>
</pre>

The csrf_token() is there so that users cannot forge requests in our application. All our requests will pick the randomly generated csrf-token and use that to make requests.

Related: CSRF in Laravel: how VerifyCsrfToken works and how to prevent attacks

Now the next thing we want to do is update the resources/assets/js/app.js file so that it includes a template for the comments view.

Open the file and replace its contents with the code below:

<pre class="ql-syntax" spellcheck="false"> require('./bootstrap');

import Vue          from 'vue'
import VueRouter    from 'vue-router'
import Homepage from './components/Homepage'
import Create   from './components/Create'
import Read     from './components/Read'
import Update   from './components/Update'
import Comments from './components/Comments'

Vue.use(VueRouter)

const router = new VueRouter({
    mode: 'history',
    routes: [
        {
            path: '/admin/dashboard',
            name: 'read',
            component: Read,
            props: true
        },
        {
            path: '/admin/create',
            name: 'create',
            component: Create,
            props: true
        },
        {
            path: '/admin/update',
            name: 'update',
            component: Update,
            props: true
        },
    ],
});

const app = new Vue({
    el: '#app',
    components: { Homepage, Comments },
    router,
});

</pre>

Above we imported the Comment component and then we added it to the list of components in the applications Vue instance.

Now create a Comments.vue file in the resources/assets/js/components directory. This is where all the code for our comment view will go. We will populate this file later on.


Installing Pusher and Laravel Echo

For us to be able to use Pusher and subscribe to events on the frontend, we need to pull in both Pusher and Laravel Echo. We will do so by running this command:

<pre class="ql-syntax" spellcheck="false"> $ npm install --save laravel-echo pusher-js

</pre>

Laravel Echo is a JavaScript library that makes it easy to subscribe to channels and listen for events broadcast by Laravel.

Now let’s configure Laravel Echo to work in our application. In the resources/assets/js/bootstrap.js file, find and uncomment this snippet of code:

<pre class="ql-syntax" spellcheck="false"> import Echo from 'laravel-echo'

window.Pusher = require('pusher-js');

window.Echo = new Echo({
     broadcaster: 'pusher',
     key: process.env.MIX_PUSHER_APP_KEY,
     cluster: process.env.MIX_PUSHER_APP_CLUSTER,
     encrypted: true
});

</pre>

The key and cluster will pull the keys from your .env file so no need to enter them manually again.

Now let’s import the Comments component into the single.blade.php file and pass along the required the props.

Open the single.blade.php file and replace its contents with the code below:

<pre class="ql-syntax" spellcheck="false"> {{-- File: ./resources/views/single.blade.php --}}
@extends('layouts.master')

@section('content')
&lt;div class="container"&gt;
  &lt;div class="row"&gt;
    &lt;div class="col-lg-10 mx-auto"&gt;
      &lt;br&gt;
      &lt;h3 class="mt-4"&gt;
        {{ $post-&gt;title }} 
        &lt;span class="lead"&gt;by &lt;a href="#"&gt;{{ $post-&gt;user-&gt;name }}&lt;/a&gt;&lt;/span&gt;
      &lt;/h3&gt;
      &lt;hr&gt;
      &lt;p&gt;Posted {{ $post-&gt;created_at-&gt;diffForHumans() }}&lt;/p&gt;
      &lt;hr&gt;
      &lt;img class="img-fluid rounded" src="{!! !empty($post-&gt;image) ? '/uploads/posts/' . $post-&gt;image : 'http://placehold.it/750x300' !!}" alt=""&gt;
      &lt;hr&gt;
      &lt;div&gt;
        &lt;p&gt;{{ $post-&gt;body }}&lt;/p&gt;
        &lt;hr&gt;
        &lt;br&gt;
      &lt;/div&gt;

      @auth
      &lt;Comments
          :post-id='@json($post-&gt;id)' 
          :user-name='@json(auth()-&gt;user()-&gt;name)'&gt;
      &lt;/Comments&gt;
      @endauth
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
@endsection

</pre>

Building the comments view

Open the Comments.vue file and add the following markup template below:

<pre class="ql-syntax" spellcheck="false"> <template>
<div class="card my-4">
<h5 class="card-header">Leave a Comment:</h5>
<div class="card-body">
<form>
<div class="form-group">
<textarea ref="body" class="form-control" rows="3"></textarea>
</div>
<button type="submit" @click.prevent="addComment" class="btn btn-primary">
Submit
</button>
</form>
</div>
<p class="border p-3" v-for="comment in comments">
<strong>{{ comment.user.name }}</strong>:
<span>{{ comment.body }}</span>
</p>
</div>
</template>
</pre>

Now, we’ll add a script that defines two methods:

  1. fetchComments() - this will fetch all the existing comments when the component is created.
  2. addComment() - this will add a new comment by hitting the backend server. It will also trigger a new event that will be broadcast so all clients receive them in realtime.

In the same file, add the following below the closing template tag:

<pre class="ql-syntax" spellcheck="false"> <script>
export default {
props: {
userName: {
type: String,
required: true
},
postId: {
type: Number,
required: true
}
},
data() {
return {
comments: []
};
},

  created() {
    this.fetchComments();

    Echo.private("comment").listen("CommentSent", e =&gt; {
        this.comments.push({
          user: {name: e.user.name},
          body: e.comment.body,
        });
    });
  },

  methods: {
    fetchComments() {
      axios.get("/" + this.postId + "/comments").then(response =&gt; {
        this.comments = response.data;
      });
    },

    addComment() {
      let body = this.$refs.body.value;
      axios.post("/" + this.postId + "/comments", { body }).then(response =&gt; {
        this.comments.push({
          user: {name: this.userName},
          body: this.$refs.body.value
        });
        this.$refs.body.value = "";
      });
    }
  }
};
&lt;/script&gt;

</pre>

In the created() method above, we first made a call to the fetchComments() method, then we created a listener to the private comment channel using Laravel Echo. Once this listener is triggered, the comments property is updated.


Testing the application

Now let’s test the application to see if it is working as intended. Before running the application, we need to refresh our database so as to revert any changes. To do this, run the command below in your terminal:

<pre class="ql-syntax" spellcheck="false"> $ php artisan migrate:fresh --seed

</pre>

Next, let’s build the application so that all the changes will be compiled and included as a part of the JavaScript file. To do this, run the following command on your terminal:

<pre class="ql-syntax" spellcheck="false"> $ npm run dev

</pre>

Finally, let’s serve the application using this command:

<pre class="ql-syntax" spellcheck="false"> $ php artisan serve

</pre>

To test that our application works visit the application URL http://localhost:8000 on two separate browser windows, we will log in to our application on each of the windows as a different user.

We will finally make a comment on the same post on each of the browser windows and check that it updates in realtime on the other window:


Conclusion

In this final tutorial of this series, we created the comments feature of the CMS and also made it realtime. We were able to accomplish the realtime functionality using Pusher.

In this entire series, we learned how to build a CMS using Laravel and Vue.

The source code for this article series is available here on Github.


Learn More

Build a Basic CRUD App with Laravel and Vue

Fullstack Vue App with Node, Express and MongoDB

Build a Simple CRUD App with Spring Boot and Vue.js

Build a Basic CRUD App with Laravel and Angular

Build a Basic CRUD App with Laravel and React

PHP Programming Language - PHP Tutorial for Beginners

Vuejs 2 Authentication Tutorial

Vue Authentication And Route Handling Using Vue-router

Vue JS 2 - The Complete Guide (incl. Vue Router & Vuex)

Nuxt.js - Vue.js on Steroids

MEVP Stack Vue JS 2 Course: MySQL + Express.js + Vue.js +PHP

Build Web Apps with Vue JS 2 & Firebase

Originally published by Neo Ighodaro at https://pusher.com

Build RESTful API In Laravel 5.8 Example

Build RESTful API In Laravel 5.8 Example


If you want to create web services with php than i will must suggest to use laravel 5.8 to create apis because laravel provide structure with authentication using passport. Based on structure it will become a very easily way to create rest apis.

Just Few days ago, laravel released it's new version as laravel 5.8. As we know laravel is a more popular because of security feature. So many of the developer choose laravel to create rest api for mobile app developing. Yes Web services is a very important when you create web and mobile developing, because you can create same database and work with same data.

Follow bellow few steps to create restful api example in laravel 5.8 app.

Step 1: Download Laravel 5.8

I am going to explain step by step from scratch so, we need to get fresh Laravel 5.8 application using bellow command, So open your terminal OR command prompt and run bellow command:

<pre class="ql-syntax" spellcheck="false">composer create-project --prefer-dist laravel/laravel blog </pre>

Step 2: Install Passport

In this step we need to install passport via the Composer package manager, so one your terminal and fire bellow command:

<pre class="ql-syntax" spellcheck="false">composer require laravel/passport </pre>

After successfully install package, we require to get default migration for create new passport tables in our database. so let's run bellow command.

<pre class="ql-syntax" spellcheck="false">php artisan migrate </pre>

Next, we need to install passport using command, Using passport:install command, it will create token keys for security. So let's run bellow command:

<pre class="ql-syntax" spellcheck="false">php artisan passport:install </pre>

Step 3: Passport Configuration

In this step, we have to configuration on three place model, service provider and auth config file. So you have to just following change on that file.

In model we added HasApiTokens class of Passport,

In AuthServiceProvider we added "Passport::routes()",

In auth.php, we added api auth configuration.

app/User.php

<pre class="ql-syntax" spellcheck="false"><?php

namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Laravel\Passport\HasApiTokens;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable implements MustVerifyEmail
{
use HasApiTokens, Notifiable;

/**
 * The attributes that are mass assignable.
 *
 * @var array
 */
protected $fillable = [
    'name', 'email', 'password',
];

/**
 * The attributes that should be hidden for arrays.
 *
 * @var array
 */
protected $hidden = [
    'password', 'remember_token',
];

}
</pre>

app/Providers/AuthServiceProvider.php

<pre class="ql-syntax" spellcheck="false"><?php

namespace App\Providers;

use Laravel\Passport\Passport;
use Illuminate\Support\Facades\Gate;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;

class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* @var array
*/
protected $policies = [
'App\Model' => 'App\Policies\ModelPolicy',
];

/**
 * Register any authentication / authorization services.
 *
 * @return void
 */
public function boot()
{
    $this-&gt;registerPolicies();

    Passport::routes();
}

}
</pre>

config/auth.php

<pre class="ql-syntax" spellcheck="false"><?php

return [
.....
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
],
.....
]
</pre>

Step 4: Add Product Table and Model

next, we require to create migration for posts table using Laravel 5.8 php artisan command, so first fire bellow command:

<pre class="ql-syntax" spellcheck="false">php artisan make:migration create_products_table
</pre>

After this command you will find one file in following path database/migrations and you have to put bellow code in your migration file for create products table.

<pre class="ql-syntax" spellcheck="false"><?php

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

class CreateProductsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('products', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->text('detail');
$table->timestamps();
});
}

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

}
</pre>

After create migration we need to run above migration by following command:

<pre class="ql-syntax" spellcheck="false">php artisan migrate
</pre>

After create "products" table you should create Product model for products, so first create file in this path app/Product.php and put bellow content in item.php file:


app/Product.php

<pre class="ql-syntax" spellcheck="false"><?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Product extends Model
{
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'name', 'detail'
];
}
</pre>

Step 5: Create API Routes

In this step, we will create api routes. Laravel provide api.php file for write web services route. So, let's add new route on that file.

routes/api.php

<pre class="ql-syntax" spellcheck="false"><?php

/*
|--------------------------------------------------------------------------

API Routes
Here is where you can register API routes for your application. These
routes are loaded by the RouteServiceProvider within a group which
is assigned the "api" middleware group. Enjoy building your API!

*/

Route::post('register', 'API\[email protected]');

Route::middleware('auth:api')->group( function () {
Route::resource('products', 'API\ProductController');
});
</pre>

Step 6: Create Controller Files

in next step, now we have create new controller as BaseController, ProductController and RegisterController, i created new folder "API" in Controllers folder because we will make alone APIs controller, So let's create both controller:

app/Http/Controllers/API/BaseController.php

<pre class="ql-syntax" spellcheck="false"><?php

namespace App\Http\Controllers\API;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller as Controller;

class BaseController extends Controller
{
/**
* success response method.
*
* @return \Illuminate\Http\Response
*/
public function sendResponse($result, $message)
{
$response = [
'success' => true,
'data' => $result,
'message' => $message,
];

    return response()-&gt;json($response, 200);
}

/**
 * return error response.
 *
 * @return \Illuminate\Http\Response
 */
public function sendError($error, $errorMessages = [], $code = 404)
{
	$response = [
        'success' =&gt; false,
        'message' =&gt; $error,
    ];

    if(!empty($errorMessages)){
        $response['data'] = $errorMessages;
    }

    return response()-&gt;json($response, $code);
}

}
</pre>

app/Http/Controllers/API/ProductController.php

<pre class="ql-syntax" spellcheck="false"><?php

namespace App\Http\Controllers\API;

use Illuminate\Http\Request;
use App\Http\Controllers\API\BaseController as BaseController;
use App\Product;
use Validator;

class ProductController extends BaseController
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
$products = Product::all();

    return $this-&gt;sendResponse($products-&gt;toArray(), 'Products retrieved successfully.');
}

/**
 * Store a newly created resource in storage.
 *
 * @param  \Illuminate\Http\Request  $request
 * @return \Illuminate\Http\Response
 */
public function store(Request $request)
{
    $input = $request-&gt;all();

    $validator = Validator::make($input, [
        'name' =&gt; 'required',
        'detail' =&gt; 'required'
    ]);

    if($validator-&gt;fails()){
        return $this-&gt;sendError('Validation Error.', $validator-&gt;errors());       
    }

    $product = Product::create($input);

    return $this-&gt;sendResponse($product-&gt;toArray(), 'Product created successfully.');
}

/**
 * Display the specified resource.
 *
 * @param  int  $id
 * @return \Illuminate\Http\Response
 */
public function show($id)
{
    $product = Product::find($id);

    if (is_null($product)) {
        return $this-&gt;sendError('Product not found.');
    }

    return $this-&gt;sendResponse($product-&gt;toArray(), 'Product retrieved successfully.');
}

/**
 * Update the specified resource in storage.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  int  $id
 * @return \Illuminate\Http\Response
 */
public function update(Request $request, Product $product)
{
    $input = $request-&gt;all();

    $validator = Validator::make($input, [
        'name' =&gt; 'required',
        'detail' =&gt; 'required'
    ]);

    if($validator-&gt;fails()){
        return $this-&gt;sendError('Validation Error.', $validator-&gt;errors());       
    }

    $product-&gt;name = $input['name'];
    $product-&gt;detail = $input['detail'];
    $product-&gt;save();

    return $this-&gt;sendResponse($product-&gt;toArray(), 'Product updated successfully.');
}

/**
 * Remove the specified resource from storage.
 *
 * @param  int  $id
 * @return \Illuminate\Http\Response
 */
public function destroy(Product $product)
{
    $product-&gt;delete();

    return $this-&gt;sendResponse($product-&gt;toArray(), 'Product deleted successfully.');
}

}
</pre>

app/Http/Controllers/API/RegisterController.php

<pre class="ql-syntax" spellcheck="false"><?php

namespace App\Http\Controllers\API;

use Illuminate\Http\Request;
use App\Http\Controllers\API\BaseController as BaseController;
use App\User;
use Illuminate\Support\Facades\Auth;
use Validator;

class RegisterController extends BaseController
{
/**
* Register api
*
* @return \Illuminate\Http\Response
*/
public function register(Request $request)
{
$validator = Validator::make($request->all(), [
'name' => 'required',
'email' => 'required|email',
'password' => 'required',
'c_password' => 'required|same:password',
]);

    if($validator-&gt;fails()){
        return $this-&gt;sendError('Validation Error.', $validator-&gt;errors());       
    }

    $input = $request-&gt;all();
    $input['password'] = bcrypt($input['password']);
    $user = User::create($input);
    $success['token'] =  $user-&gt;createToken('MyApp')-&gt;accessToken;
    $success['name'] =  $user-&gt;name;

    return $this-&gt;sendResponse($success, 'User register successfully.');
}

}
</pre>

Now we are ready to to run full restful api and also passport api in laravel. so let's run our example so run bellow command for quick run:

<pre class="ql-syntax" spellcheck="false">php artisan serve
</pre>

make sure in details api we will use following headers as listed bellow:

<pre class="ql-syntax" spellcheck="false">'headers' => [
'Accept' => 'application/json',
'Authorization' => 'Bearer '.$accessToken,
]
</pre>

Here is Routes URL with Verb:

1) Login: Verb:GET, URL:http://localhost:8000/oauth/token

2) Register: Verb:GET, URL:http://localhost:8000/api/register

3) List: Verb:GET, URL:http://localhost:8000/api/products

4) Create: Verb:POST, URL:http://localhost:8000/api/products

5) Show: Verb:GET, URL:http://localhost:8000/api/products/{id}

6) Update: Verb:PUT, URL:http://localhost:8000/api/products/{id}

7) Delete: Verb:DELETE, URL:http://localhost:8000/api/products/{id}

Now simply you can run above listed url like as bellow screen shot:

Login API:

Register API:

Product List API:

Product Create API:

Product Show API:

Product Update API:

Product Delete API:

I hope it can help you...

Thanks for reading ❤

If you liked this post, share it with all of your programming buddies!

Follow me on Facebook | Twitter

Learn More

PHP with Laravel for beginners - Become a Master in Laravel

Projects in Laravel: Learn Laravel Building 10 Projects

Laravel for RESTful: Build Your RESTful API with Laravel

Fullstack Web Development With Laravel and Vue.js

Creating RESTful APIs with NodeJS and MongoDB Tutorial

Developing RESTful APIs with Lumen (A PHP Micro-framework)

Build a Simple REST API in PHP

Node.js and Express Tutorial: Building and Securing RESTful APIs

Building a Vue SPA With Laravel

Build a CMS with Laravel and Vue

Laravel 5.8 Tutorial - How to build user roles and permissions on Laravel 5.8 App

Laravel 5.8 Tutorial - How to build user roles and permissions on Laravel 5.8 App

Spatie role permission composer package provide way to create acl in Laravel 5.8. They provide how to assign role to user, how to assign permission to user and how to assign permission assign to roles. I will write step by step creating roles and permissions in Laravel 5.8 application.

Roles and Permissions through you can create several types of users with different role and permission, i mean some user have only see listing of items module, some user can also edit items modules, for delete and etc.

In this examples i created three modules as listed bellow:

  • User Management
  • Role Management
  • Product Management

After register user, you don't have any roles, so you can edit your details and assign admin role to you from User Management. After that you can create your own role with permission like role-list, role-create, role-edit, role-delete, product-list, product-create, product-edit, product-delete. You can check with assign new user and check that.

Step 1: Laravel 5.8 Installation

We are going from scratch so, If you haven't installed Laravel in your system then you can run bellow command and get fresh Laravel project.

<pre class="ql-syntax" spellcheck="false">composer create-project --prefer-dist laravel/laravel blog </pre>

Step 2: Install Composer Packages

Now we require to install Spatie package for ACL, that way we can use it's method. Also we will install form collection package. So Open your terminal and run bellow command.

<pre class="ql-syntax" spellcheck="false">composer require spatie/laravel-permission

composer require laravelcollective/html
</pre>

Now open config/app.php file and add service provider and aliase.

config/app.php

<pre class="ql-syntax" spellcheck="false">'providers' => [
....
Spatie\Permission\PermissionServiceProvider::class,
Collective\Html\HtmlServiceProvider::class,
],
'aliases' => [
....
'Form' => Collective\Html\FormFacade::class,
'Html' => Collective\Html\HtmlFacade::class,
],
</pre>

We can also custom changes on Spatie package, so if you also want to changes then you can fire bellow command and get config file in config/permission.php.

<pre class="ql-syntax" spellcheck="false">php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider" --tag="config"
</pre>

Step 3: Create Migrations

In this step we have to create three migrations for as listed bellow tables:

1. users

2. products

3. roles

4. permissions

5. model_has_permissions

6. model_has_roles

7. role_has_permissions

So, if you install fresh project then you have already users table migration but if you don't have products table, so can create manually and other table can create using Spatie package command, so run bellow command and check migration file also.

<pre class="ql-syntax" spellcheck="false">php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider" --tag="migrations"
php artisan make:migration create_products_table
</pre>

users table:

<pre class="ql-syntax" spellcheck="false"><?php

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

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

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

}
</pre>

products table:

<pre class="ql-syntax" spellcheck="false"><?php

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

class CreateProductsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('products', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->text('detail');
$table->timestamps();
});
}

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

}
</pre>

Spatie tables:

<pre class="ql-syntax" spellcheck="false"><?php

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

class CreatePermissionTables extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
$tableNames = config('permission.table_names');

    Schema::create($tableNames['permissions'], function (Blueprint $table) {
        $table-&gt;increments('id');
        $table-&gt;string('name');
        $table-&gt;string('guard_name');
        $table-&gt;timestamps();
    });

    Schema::create($tableNames['roles'], function (Blueprint $table) {
        $table-&gt;increments('id');
        $table-&gt;string('name');
        $table-&gt;string('guard_name');
        $table-&gt;timestamps();
    });

    Schema::create($tableNames['model_has_permissions'], function (Blueprint $table) use ($tableNames) {
        $table-&gt;integer('permission_id')-&gt;unsigned();
        $table-&gt;morphs('model');

        $table-&gt;foreign('permission_id')
            -&gt;references('id')
            -&gt;on($tableNames['permissions'])
            -&gt;onDelete('cascade');

        $table-&gt;primary(['permission_id', 'model_id', 'model_type']);
    });

    Schema::create($tableNames['model_has_roles'], function (Blueprint $table) use ($tableNames) {
        $table-&gt;integer('role_id')-&gt;unsigned();
        $table-&gt;morphs('model');

        $table-&gt;foreign('role_id')
            -&gt;references('id')
            -&gt;on($tableNames['roles'])
            -&gt;onDelete('cascade');

        $table-&gt;primary(['role_id', 'model_id', 'model_type']);
    });

    Schema::create($tableNames['role_has_permissions'], function (Blueprint $table) use ($tableNames) {
        $table-&gt;integer('permission_id')-&gt;unsigned();
        $table-&gt;integer('role_id')-&gt;unsigned();

        $table-&gt;foreign('permission_id')
            -&gt;references('id')
            -&gt;on($tableNames['permissions'])
            -&gt;onDelete('cascade');

        $table-&gt;foreign('role_id')
            -&gt;references('id')
            -&gt;on($tableNames['roles'])
            -&gt;onDelete('cascade');

        $table-&gt;primary(['permission_id', 'role_id']);

        app('cache')-&gt;forget('spatie.permission.cache');
    });
}

/**
 * Reverse the migrations.
 *
 * @return void
 */
public function down()
{
    $tableNames = config('permission.table_names');

    Schema::drop($tableNames['role_has_permissions']);
    Schema::drop($tableNames['model_has_roles']);
    Schema::drop($tableNames['model_has_permissions']);
    Schema::drop($tableNames['roles']);
    Schema::drop($tableNames['permissions']);
}

}
</pre>

Now run migration:

<pre class="ql-syntax" spellcheck="false">php artisan migrate
</pre>

Step 4: Create Models

In this step we have to create model for User and Product table, so if you get fresh project then you have User Model have so just replace code and other you should create.

app/User.php

<pre class="ql-syntax" spellcheck="false"><?php

namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Spatie\Permission\Traits\HasRoles;

class User extends Authenticatable
{
use Notifiable;
use HasRoles;

/**
 * The attributes that are mass assignable.
 *
 * @var array
 */
protected $fillable = [
    'name', 'email', 'password',
];

/**
 * The attributes that should be hidden for arrays.
 *
 * @var array
 */
protected $hidden = [
    'password', 'remember_token',
];

/**
 * The attributes that should be cast to native types.
 *
 * @var array
 */
protected $casts = [
    'email_verified_at' =&gt; 'datetime',
];

}
</pre>

app/Product.php

<pre class="ql-syntax" spellcheck="false"><?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Product extends Model
{
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'name', 'detail'
];
}
</pre>

Step 5: Add Middleware

Spatie package provide it's in-built middleware that way we can use it simply and that is display as bellow:

role

permission

So, we have to add middleware in Kernel.php file this way :

app/Http/Kernel.php

<pre class="ql-syntax" spellcheck="false">....
protected $routeMiddleware = [
....
'role' => \Spatie\Permission\Middlewares\RoleMiddleware::class,
'permission' => \Spatie\Permission\Middlewares\PermissionMiddleware::class,
]
....
</pre>

Step 6: Create Authentication

In this step we require to create authentication of Laravel 5.8, so laravel provide artisan command to create authentication that way we don't require to create route and controller for login and registration. so run bellow command:

<pre class="ql-syntax" spellcheck="false">php artisan make:auth
</pre>

Step 7: Create Routes

We require to add number of route for users module, products module and roles module. In this this route i also use middleware with permission for roles and products route, so add route this way:

routes/web.php

<pre class="ql-syntax" spellcheck="false">Auth::routes();

Route::get('/home', '[email protected]')->name('home');

Route::group(['middleware' => ['auth']], function() {
Route::resource('roles','RoleController');
Route::resource('users','UserController');
Route::resource('products','ProductController');
});
</pre>

Step 8: Add Controllers

In this step we have add three controller for users module, products module and roles module so you can create three controller like as bellow:

app/Http/Controllers/UserController.php

<pre class="ql-syntax" spellcheck="false"><?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\User;
use Spatie\Permission\Models\Role;
use DB;
use Hash;

class UserController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index(Request $request)
{
$data = User::orderBy('id','DESC')->paginate(5);
return view('users.index',compact('data'))
->with('i', ($request->input('page', 1) - 1) * 5);
}

/**
 * Show the form for creating a new resource.
 *
 * @return \Illuminate\Http\Response
 */
public function create()
{
    $roles = Role::pluck('name','name')-&gt;all();
    return view('users.create',compact('roles'));
}

/**
 * Store a newly created resource in storage.
 *
 * @param  \Illuminate\Http\Request  $request
 * @return \Illuminate\Http\Response
 */
public function store(Request $request)
{
    $this-&gt;validate($request, [
        'name' =&gt; 'required',
        'email' =&gt; 'required|email|unique:users,email',
        'password' =&gt; 'required|same:confirm-password',
        'roles' =&gt; 'required'
    ]);

    $input = $request-&gt;all();
    $input['password'] = Hash::make($input['password']);

    $user = User::create($input);
    $user-&gt;assignRole($request-&gt;input('roles'));

    return redirect()-&gt;route('users.index')
                    -&gt;with('success','User created successfully');
}

/**
 * Display the specified resource.
 *
 * @param  int  $id
 * @return \Illuminate\Http\Response
 */
public function show($id)
{
    $user = User::find($id);
    return view('users.show',compact('user'));
}

/**
 * Show the form for editing the specified resource.
 *
 * @param  int  $id
 * @return \Illuminate\Http\Response
 */
public function edit($id)
{
    $user = User::find($id);
    $roles = Role::pluck('name','name')-&gt;all();
    $userRole = $user-&gt;roles-&gt;pluck('name','name')-&gt;all();

    return view('users.edit',compact('user','roles','userRole'));
}

/**
 * Update the specified resource in storage.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  int  $id
 * @return \Illuminate\Http\Response
 */
public function update(Request $request, $id)
{
    $this-&gt;validate($request, [
        'name' =&gt; 'required',
        'email' =&gt; 'required|email|unique:users,email,'.$id,
        'password' =&gt; 'same:confirm-password',
        'roles' =&gt; 'required'
    ]);

    $input = $request-&gt;all();
    if(!empty($input['password'])){ 
        $input['password'] = Hash::make($input['password']);
    }else{
        $input = array_except($input,array('password'));    
    }

    $user = User::find($id);
    $user-&gt;update($input);
    DB::table('model_has_roles')-&gt;where('model_id',$id)-&gt;delete();

    $user-&gt;assignRole($request-&gt;input('roles'));

    return redirect()-&gt;route('users.index')
                    -&gt;with('success','User updated successfully');
}

/**
 * Remove the specified resource from storage.
 *
 * @param  int  $id
 * @return \Illuminate\Http\Response
 */
public function destroy($id)
{
    User::find($id)-&gt;delete();
    return redirect()-&gt;route('users.index')
                    -&gt;with('success','User deleted successfully');
}

}
</pre>

app/Http/Controllers/ProductController.php

<pre class="ql-syntax" spellcheck="false"><?php

namespace App\Http\Controllers;

use App\Product;
use Illuminate\Http\Request;

class ProductController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
/
function __construct()
{
$this->middleware('permission:product-list|product-create|product-edit|product-delete', ['only' => ['index','show']]);
$this->middleware('permission:product-create', ['only' => ['create','store']]);
$this->middleware('permission:product-edit', ['only' => ['edit','update']]);
$this->middleware('permission:product-delete', ['only' => ['destroy']]);
}
/
*
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
$products = Product::latest()->paginate(5);
return view('products.index',compact('products'))
->with('i', (request()->input('page', 1) - 1) * 5);
}

/**
 * Show the form for creating a new resource.
 *
 * @return \Illuminate\Http\Response
 */
public function create()
{
    return view('products.create');
}

/**
 * Store a newly created resource in storage.
 *
 * @param  \Illuminate\Http\Request  $request
 * @return \Illuminate\Http\Response
 */
public function store(Request $request)
{
    request()-&gt;validate([
        'name' =&gt; 'required',
        'detail' =&gt; 'required',
    ]);

    Product::create($request-&gt;all());

    return redirect()-&gt;route('products.index')
                    -&gt;with('success','Product created successfully.');
}

/**
 * Display the specified resource.
 *
 * @param  \App\Product  $product
 * @return \Illuminate\Http\Response
 */
public function show(Product $product)
{
    return view('products.show',compact('product'));
}

/**
 * Show the form for editing the specified resource.
 *
 * @param  \App\Product  $product
 * @return \Illuminate\Http\Response
 */
public function edit(Product $product)
{
    return view('products.edit',compact('product'));
}

/**
 * Update the specified resource in storage.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  \App\Product  $product
 * @return \Illuminate\Http\Response
 */
public function update(Request $request, Product $product)
{
     request()-&gt;validate([
        'name' =&gt; 'required',
        'detail' =&gt; 'required',
    ]);

    $product-&gt;update($request-&gt;all());

    return redirect()-&gt;route('products.index')
                    -&gt;with('success','Product updated successfully');
}

/**
 * Remove the specified resource from storage.
 *
 * @param  \App\Product  $product
 * @return \Illuminate\Http\Response
 */
public function destroy(Product $product)
{
    $product-&gt;delete();

    return redirect()-&gt;route('products.index')
                    -&gt;with('success','Product deleted successfully');
}

}
</pre>

app/Http/Controllers/RoleController.php

<pre class="ql-syntax" spellcheck="false"><?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;
use DB;

class RoleController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
function __construct()
{
$this->middleware('permission:role-list|role-create|role-edit|role-delete', ['only' => ['index','store']]);
$this->middleware('permission:role-create', ['only' => ['create','store']]);
$this->middleware('permission:role-edit', ['only' => ['edit','update']]);
$this->middleware('permission:role-delete', ['only' => ['destroy']]);
}

/**
 * Display a listing of the resource.
 *
 * @return \Illuminate\Http\Response
 */
public function index(Request $request)
{
    $roles = Role::orderBy('id','DESC')-&gt;paginate(5);
    return view('roles.index',compact('roles'))
        -&gt;with('i', ($request-&gt;input('page', 1) - 1) * 5);
}

/**
 * Show the form for creating a new resource.
 *
 * @return \Illuminate\Http\Response
 */
public function create()
{
    $permission = Permission::get();
    return view('roles.create',compact('permission'));
}

/**
 * Store a newly created resource in storage.
 *
 * @param  \Illuminate\Http\Request  $request
 * @return \Illuminate\Http\Response
 */
public function store(Request $request)
{
    $this-&gt;validate($request, [
        'name' =&gt; 'required|unique:roles,name',
        'permission' =&gt; 'required',
    ]);

    $role = Role::create(['name' =&gt; $request-&gt;input('name')]);
    $role-&gt;syncPermissions($request-&gt;input('permission'));

    return redirect()-&gt;route('roles.index')
                    -&gt;with('success','Role created successfully');
}
/**
 * Display the specified resource.
 *
 * @param  int  $id
 * @return \Illuminate\Http\Response
 */
public function show($id)
{
    $role = Role::find($id);
    $rolePermissions = Permission::join("role_has_permissions","role_has_permissions.permission_id","=","permissions.id")
        -&gt;where("role_has_permissions.role_id",$id)
        -&gt;get();

    return view('roles.show',compact('role','rolePermissions'));
}

/**
 * Show the form for editing the specified resource.
 *
 * @param  int  $id
 * @return \Illuminate\Http\Response
 */
public function edit($id)
{
    $role = Role::find($id);
    $permission = Permission::get();
    $rolePermissions = DB::table("role_has_permissions")-&gt;where("role_has_permissions.role_id",$id)
        -&gt;pluck('role_has_permissions.permission_id','role_has_permissions.permission_id')
        -&gt;all();

    return view('roles.edit',compact('role','permission','rolePermissions'));
}

/**
 * Update the specified resource in storage.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  int  $id
 * @return \Illuminate\Http\Response
 */
public function update(Request $request, $id)
{
    $this-&gt;validate($request, [
        'name' =&gt; 'required',
        'permission' =&gt; 'required',
    ]);

    $role = Role::find($id);
    $role-&gt;name = $request-&gt;input('name');
    $role-&gt;save();

    $role-&gt;syncPermissions($request-&gt;input('permission'));

    return redirect()-&gt;route('roles.index')
                    -&gt;with('success','Role updated successfully');
}
/**
 * Remove the specified resource from storage.
 *
 * @param  int  $id
 * @return \Illuminate\Http\Response
 */
public function destroy($id)
{
    DB::table("roles")-&gt;where('id',$id)-&gt;delete();
    return redirect()-&gt;route('roles.index')
                    -&gt;with('success','Role deleted successfully');
}

}
</pre>

Step 9: Add Blade Files

This is last step we have to add numbers view for layouts, users module, roles module, products modules and errors page, so create number of view like as bellow:

resources/views/layouts/app.blade.php

<pre class="ql-syntax" spellcheck="false"><html lang="{{ app()->getLocale() }}">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- CSRF Token -->
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>{{ config('app.name', 'Laravel 5.8 User Roles and Permissions Tutorial') }}</title>
<!-- Scripts -->
<script src="{{ asset('js/app.js') }}" defer></script>
<!-- Fonts -->
<link rel="dns-prefetch" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css?family=Raleway:300,400,600" rel="stylesheet" type="text/css">
<!-- Styles -->
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
</head>
<body>
<div id="app">
<nav class="navbar navbar-expand-md navbar-light navbar-laravel">
<div class="container">
<a class="navbar-brand" href="{{ url('/') }}">
Laravel 5.8 User Roles and Permissions - ItSolutionStuff.com
</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>

            &lt;div class="collapse navbar-collapse" id="navbarSupportedContent"&gt;
                &lt;!-- Left Side Of Navbar --&gt;
                &lt;ul class="navbar-nav mr-auto"&gt;&lt;/ul&gt;

                &lt;!-- Right Side Of Navbar --&gt;
                &lt;ul class="navbar-nav ml-auto"&gt;
                    &lt;!-- Authentication Links --&gt;
                    @guest
                        &lt;li&gt;&lt;a class="nav-link" href="{{ route('login') }}"&gt;{{ __('Login') }}&lt;/a&gt;&lt;/li&gt;
                        &lt;li&gt;&lt;a class="nav-link" href="{{ route('register') }}"&gt;{{ __('Register') }}&lt;/a&gt;&lt;/li&gt;
                    @else
                        &lt;li&gt;&lt;a class="nav-link" href="{{ route('users.index') }}"&gt;Manage Users&lt;/a&gt;&lt;/li&gt;
                        &lt;li&gt;&lt;a class="nav-link" href="{{ route('roles.index') }}"&gt;Manage Role&lt;/a&gt;&lt;/li&gt;
                        &lt;li&gt;&lt;a class="nav-link" href="{{ route('products.index') }}"&gt;Manage Product&lt;/a&gt;&lt;/li&gt;
                        &lt;li class="nav-item dropdown"&gt;
                            &lt;a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre&gt;
                                {{ Auth::user()-&gt;name }} &lt;span class="caret"&gt;&lt;/span&gt;
                            &lt;/a&gt;

                            &lt;div class="dropdown-menu" aria-labelledby="navbarDropdown"&gt;
                                &lt;a class="dropdown-item" href="{{ route('logout') }}"
                                   onclick="event.preventDefault();
                                                 document.getElementById('logout-form').submit();"&gt;
                                    {{ __('Logout') }}
                                &lt;/a&gt;

                                &lt;form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;"&gt;
                                    @csrf
                                &lt;/form&gt;
                            &lt;/div&gt;
                        &lt;/li&gt;
                    @endguest
                &lt;/ul&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/nav&gt;

    &lt;main class="py-4"&gt;
        &lt;div class="container"&gt;
        @yield('content')
        &lt;/div&gt;
    &lt;/main&gt;
&lt;/div&gt;

</body>
</html>
</pre>

resources/views/users/index.blade.php

<pre class="ql-syntax" spellcheck="false">@extends('layouts.app')

@section('content')
<div class="row">
<div class="col-lg-12 margin-tb">
<div class="pull-left">
<h2>Users Management</h2>
</div>
<div class="pull-right">
<a class="btn btn-success" href="{{ route('users.create') }}"> Create New User</a>
</div>
</div>
</div>

@if ($message = Session::get('success'))
<div class="alert alert-success">
<p>{{ $message }}</p>
</div>
@endif

<table class="table table-bordered">
<tr>
<th>No</th>
<th>Name</th>
<th>Email</th>
<th>Roles</th>
<th width="280px">Action</th>
</tr>
@foreach ($data as $key => $user)
<tr>
<td>{{ ++$i }}</td>
<td>{{ $user->name }}</td>
<td>{{ $user->email }}</td>
<td>
@if(!empty($user->getRoleNames()))
@foreach($user->getRoleNames() as $v)
<label class="badge badge-success">{{ $v }}</label>
@endforeach
@endif
</td>
<td>
<a class="btn btn-info" href="{{ route('users.show',$user->id) }}">Show</a>
<a class="btn btn-primary" href="{{ route('users.edit',$user->id) }}">Edit</a>
{!! Form::open(['method' => 'DELETE','route' => ['users.destroy', $user->id],'style'=>'display:inline']) !!}
{!! Form::submit('Delete', ['class' => 'btn btn-danger']) !!}
{!! Form::close() !!}
</td>
</tr>
@endforeach
</table>

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

<p class="text-center text-primary"><small>Tutorial by ItSolutionStuff.com</small></p>
@endsection
</pre>

resources/views/users/create.blade.php

<pre class="ql-syntax" spellcheck="false">@extends('layouts.app')

@section('content')
<div class="row">
<div class="col-lg-12 margin-tb">
<div class="pull-left">
<h2>Create New User</h2>
</div>
<div class="pull-right">
<a class="btn btn-primary" href="{{ route('users.index') }}"> Back</a>
</div>
</div>
</div>

@if (count($errors) > 0)
<div class="alert alert-danger">
<strong>Whoops!</strong> There were some problems with your input.<br><br>
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif

{!! Form::open(array('route' => 'users.store','method'=>'POST')) !!}
<div class="row">
<div class="col-xs-12 col-sm-12 col-md-12">
<div class="form-group">
<strong>Name:</strong>
{!! Form::text('name', null, array('placeholder' => 'Name','class' => 'form-control')) !!}
</div>
</div>
<div class="col-xs-12 col-sm-12 col-md-12">
<div class="form-group">
<strong>Email:</strong>
{!! Form::text('email', null, array('placeholder' => 'Email','class' => 'form-control')) !!}
</div>
</div>
<div class="col-xs-12 col-sm-12 col-md-12">
<div class="form-group">
<strong>Password:</strong>
{!! Form::password('password', array('placeholder' => 'Password','class' => 'form-control')) !!}
</div>
</div>
<div class="col-xs-12 col-sm-12 col-md-12">
<div class="form-group">
<strong>Confirm Password:</strong>
{!! Form::password('confirm-password', array('placeholder' => 'Confirm Password','class' => 'form-control')) !!}
</div>
</div>
<div class="col-xs-12 col-sm-12 col-md-12">
<div class="form-group">
<strong>Role:</strong>
{!! Form::select('roles[]', $roles,[], array('class' => 'form-control','multiple')) !!}
</div>
</div>
<div class="col-xs-12 col-sm-12 col-md-12 text-center">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</div>
{!! Form::close() !!}

<p class="text-center text-primary"><small>Tutorial by ItSolutionStuff.com</small></p>
@endsection
</pre>

resources/views/users/edit.blade.php

<pre class="ql-syntax" spellcheck="false">@extends('layouts.app')

@section('content')
<div class="row">
<div class="col-lg-12 margin-tb">
<div class="pull-left">
<h2>Edit New User</h2>
</div>
<div class="pull-right">
<a class="btn btn-primary" href="{{ route('users.index') }}"> Back</a>
</div>
</div>
</div>

@if (count($errors) > 0)
<div class="alert alert-danger">
<strong>Whoops!</strong> There were some problems with your input.<br><br>
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif

{!! Form::model($user, ['method' => 'PATCH','route' => ['users.update', $user->id]]) !!}
<div class="row">
<div class="col-xs-12 col-sm-12 col-md-12">
<div class="form-group">
<strong>Name:</strong>
{!! Form::text('name', null, array('placeholder' => 'Name','class' => 'form-control')) !!}
</div>
</div>
<div class="col-xs-12 col-sm-12 col-md-12">
<div class="form-group">
<strong>Email:</strong>
{!! Form::text('email', null, array('placeholder' => 'Email','class' => 'form-control')) !!}
</div>
</div>
<div class="col-xs-12 col-sm-12 col-md-12">
<div class="form-group">
<strong>Password:</strong>
{!! Form::password('password', array('placeholder' => 'Password','class' => 'form-control')) !!}
</div>
</div>
<div class="col-xs-12 col-sm-12 col-md-12">
<div class="form-group">
<strong>Confirm Password:</strong>
{!! Form::password('confirm-password', array('placeholder' => 'Confirm Password','class' => 'form-control')) !!}
</div>
</div>
<div class="col-xs-12 col-sm-12 col-md-12">
<div class="form-group">
<strong>Role:</strong>
{!! Form::select('roles[]', $roles,$userRole, array('class' => 'form-control','multiple')) !!}
</div>
</div>
<div class="col-xs-12 col-sm-12 col-md-12 text-center">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</div>
{!! Form::close() !!}

<p class="text-center text-primary"><small>Tutorial by ItSolutionStuff.com</small></p>
@endsection
</pre>

resources/views/users/show.blade.php

<pre class="ql-syntax" spellcheck="false">@extends('layouts.app')

@section('content')
<div class="row">
<div class="col-lg-12 margin-tb">
<div class="pull-left">
<h2> Show User</h2>
</div>
<div class="pull-right">
<a class="btn btn-primary" href="{{ route('users.index') }}"> Back</a>
</div>
</div>
</div>

<div class="row">
<div class="col-xs-12 col-sm-12 col-md-12">
<div class="form-group">
<strong>Name:</strong>
{{ $user->name }}
</div>
</div>
<div class="col-xs-12 col-sm-12 col-md-12">
<div class="form-group">
<strong>Email:</strong>
{{ $user->email }}
</div>
</div>
<div class="col-xs-12 col-sm-12 col-md-12">
<div class="form-group">
<strong>Roles:</strong>
@if(!empty($user->getRoleNames()))
@foreach($user->getRoleNames() as $v)
<label class="badge badge-success">{{ $v }}</label>
@endforeach
@endif
</div>
</div>
</div>
@endsection
</pre>

resources/views/roles/index.blade.php

<pre class="ql-syntax" spellcheck="false">@extends('layouts.app')

@section('content')
<div class="row">
<div class="col-lg-12 margin-tb">
<div class="pull-left">
<h2>Role Management</h2>
</div>
<div class="pull-right">
@can('role-create')
<a class="btn btn-success" href="{{ route('roles.create') }}"> Create New Role</a>
@endcan
</div>
</div>
</div>

@if ($message = Session::get('success'))
<div class="alert alert-success">
<p>{{ $message }}</p>
</div>
@endif

<table class="table table-bordered">
<tr>
<th>No</th>
<th>Name</th>
<th width="280px">Action</th>
</tr>
@foreach ($roles as $key => $role)
<tr>
<td>{{ ++$i }}</td>
<td>{{ $role->name }}</td>
<td>
<a class="btn btn-info" href="{{ route('roles.show',$role->id) }}">Show</a>
@can('role-edit')
<a class="btn btn-primary" href="{{ route('roles.edit',$role->id) }}">Edit</a>
@endcan
@can('role-delete')
{!! Form::open(['method' => 'DELETE','route' => ['roles.destroy', $role->id],'style'=>'display:inline']) !!}
{!! Form::submit('Delete', ['class' => 'btn btn-danger']) !!}
{!! Form::close() !!}
@endcan
</td>
</tr>
@endforeach
</table>

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

<p class="text-center text-primary"><small>Tutorial by ItSolutionStuff.com</small></p>
@endsection
</pre>

resources/views/roles/create.blade.php

<pre class="ql-syntax" spellcheck="false">@extends('layouts.app')

@section('content')
<div class="row">
<div class="col-lg-12 margin-tb">
<div class="pull-left">
<h2>Create New Role</h2>
</div>
<div class="pull-right">
<a class="btn btn-primary" href="{{ route('roles.index') }}"> Back</a>
</div>
</div>
</div>

@if (count($errors) > 0)
<div class="alert alert-danger">
<strong>Whoops!</strong> There were some problems with your input.<br><br>
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif

{!! Form::open(array('route' => 'roles.store','method'=>'POST')) !!}
<div class="row">
<div class="col-xs-12 col-sm-12 col-md-12">
<div class="form-group">
<strong>Name:</strong>
{!! Form::text('name', null, array('placeholder' => 'Name','class' => 'form-control')) !!}
</div>
</div>
<div class="col-xs-12 col-sm-12 col-md-12">
<div class="form-group">
<strong>Permission:</strong>
<br/>
@foreach($permission as $value)
<label>{{ Form::checkbox('permission[]', $value->id, false, array('class' => 'name')) }}
{{ $value->name }}</label>
<br/>
@endforeach
</div>
</div>
<div class="col-xs-12 col-sm-12 col-md-12 text-center">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</div>
{!! Form::close() !!}

<p class="text-center text-primary"><small>Tutorial by ItSolutionStuff.com</small></p>
@endsection
</pre>

resources/views/roles/edit.blade.php

<pre class="ql-syntax" spellcheck="false">@extends('layouts.app')

@section('content')
<div class="row">
<div class="col-lg-12 margin-tb">
<div class="pull-left">
<h2>Edit Role</h2>
</div>
<div class="pull-right">
<a class="btn btn-primary" href="{{ route('roles.index') }}"> Back</a>
</div>
</div>
</div>

@if (count($errors) > 0)
<div class="alert alert-danger">
<strong>Whoops!</strong> There were some problems with your input.<br><br>
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif

{!! Form::model($role, ['method' => 'PATCH','route' => ['roles.update', $role->id]]) !!}
<div class="row">
<div class="col-xs-12 col-sm-12 col-md-12">
<div class="form-group">
<strong>Name:</strong>
{!! Form::text('name', null, array('placeholder' => 'Name','class' => 'form-control')) !!}
</div>
</div>
<div class="col-xs-12 col-sm-12 col-md-12">
<div class="form-group">
<strong>Permission:</strong>
<br/>
@foreach($permission as $value)
<label>{{ Form::checkbox('permission[]', $value->id, in_array($value->id, $rolePermissions) ? true : false, array('class' => 'name')) }}
{{ $value->name }}</label>
<br/>
@endforeach
</div>
</div>
<div class="col-xs-12 col-sm-12 col-md-12 text-center">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</div>
{!! Form::close() !!}

@endsection
<p class="text-center text-primary"><small>Tutorial by ItSolutionStuff.com</small></p>
</pre>

resources/views/roles/show.blade.php

<pre class="ql-syntax" spellcheck="false">@extends('layouts.app')

@section('content')
<div class="row">
<div class="col-lg-12 margin-tb">
<div class="pull-left">
<h2> Show Role</h2>
</div>
<div class="pull-right">
<a class="btn btn-primary" href="{{ route('roles.index') }}"> Back</a>
</div>
</div>
</div>

<div class="row">
<div class="col-xs-12 col-sm-12 col-md-12">
<div class="form-group">
<strong>Name:</strong>
{{ $role->name }}
</div>
</div>
<div class="col-xs-12 col-sm-12 col-md-12">
<div class="form-group">
<strong>Permissions:</strong>
@if(!empty($rolePermissions))
@foreach($rolePermissions as $v)
<label class="label label-success">{{ $v->name }},</label>
@endforeach
@endif
</div>
</div>
</div>
@endsection
</pre>

resources/views/products/index.blade.php

<pre class="ql-syntax" spellcheck="false">@extends('layouts.app')

@section('content')
<div class="row">
<div class="col-lg-12 margin-tb">
<div class="pull-left">
<h2>Products</h2>
</div>
<div class="pull-right">
@can('product-create')
<a class="btn btn-success" href="{{ route('products.create') }}"> Create New Product</a>
@endcan
</div>
</div>
</div>

@if ($message = Session::get('success'))
    &lt;div class="alert alert-success"&gt;
        &lt;p&gt;{{ $message }}&lt;/p&gt;
    &lt;/div&gt;
@endif

&lt;table class="table table-bordered"&gt;
    &lt;tr&gt;
        &lt;th&gt;No&lt;/th&gt;
        &lt;th&gt;Name&lt;/th&gt;
        &lt;th&gt;Details&lt;/th&gt;
        &lt;th width="280px"&gt;Action&lt;/th&gt;
    &lt;/tr&gt;
    @foreach ($products as $product)
    &lt;tr&gt;
        &lt;td&gt;{{ ++$i }}&lt;/td&gt;
        &lt;td&gt;{{ $product-&gt;name }}&lt;/td&gt;
        &lt;td&gt;{{ $product-&gt;detail }}&lt;/td&gt;
        &lt;td&gt;
            &lt;form action="{{ route('products.destroy',$product-&gt;id) }}" method="POST"&gt;
                &lt;a class="btn btn-info" href="{{ route('products.show',$product-&gt;id) }}"&gt;Show&lt;/a&gt;
                @can('product-edit')
                &lt;a class="btn btn-primary" href="{{ route('products.edit',$product-&gt;id) }}"&gt;Edit&lt;/a&gt;
                @endcan

                @csrf
                @method('DELETE')
                @can('product-delete')
                &lt;button type="submit" class="btn btn-danger"&gt;Delete&lt;/button&gt;
                @endcan
            &lt;/form&gt;
        &lt;/td&gt;
    &lt;/tr&gt;
    @endforeach
&lt;/table&gt;

{!! $products-&gt;links() !!}

<p class="text-center text-primary"><small>Tutorial by ItSolutionStuff.com</small></p>
@endsection
</pre>

resources/views/products/create.blade.php

<pre class="ql-syntax" spellcheck="false">@extends('layouts.app')

@section('content')
<div class="row">
<div class="col-lg-12 margin-tb">
<div class="pull-left">
<h2>Add New Product</h2>
</div>
<div class="pull-right">
<a class="btn btn-primary" href="{{ route('products.index') }}"> Back</a>
</div>
</div>
</div>

@if ($errors-&gt;any())
    &lt;div class="alert alert-danger"&gt;
        &lt;strong&gt;Whoops!&lt;/strong&gt; There were some problems with your input.&lt;br&gt;&lt;br&gt;
        &lt;ul&gt;
            @foreach ($errors-&gt;all() as $error)
                &lt;li&gt;{{ $error }}&lt;/li&gt;
            @endforeach
        &lt;/ul&gt;
    &lt;/div&gt;
@endif

&lt;form action="{{ route('products.store') }}" method="POST"&gt;
	@csrf

     &lt;div class="row"&gt;
	    &lt;div class="col-xs-12 col-sm-12 col-md-12"&gt;
	        &lt;div class="form-group"&gt;
	            &lt;strong&gt;Name:&lt;/strong&gt;
	            &lt;input type="text" name="name" class="form-control" placeholder="Name"&gt;
	        &lt;/div&gt;
	    &lt;/div&gt;
	    &lt;div class="col-xs-12 col-sm-12 col-md-12"&gt;
	        &lt;div class="form-group"&gt;
	            &lt;strong&gt;Detail:&lt;/strong&gt;
	            &lt;textarea class="form-control" style="height:150px" name="detail" placeholder="Detail"&gt;&lt;/textarea&gt;
	        &lt;/div&gt;
	    &lt;/div&gt;
	    &lt;div class="col-xs-12 col-sm-12 col-md-12 text-center"&gt;
	            &lt;button type="submit" class="btn btn-primary"&gt;Submit&lt;/button&gt;
	    &lt;/div&gt;
	&lt;/div&gt;

&lt;/form&gt;

<p class="text-center text-primary"><small>Tutorial by ItSolutionStuff.com</small></p>
@endsection
</pre>

resources/views/products/edit.blade.php

<pre class="ql-syntax" spellcheck="false">@extends('layouts.app')

@section('content')
<div class="row">
<div class="col-lg-12 margin-tb">
<div class="pull-left">
<h2>Edit Product</h2>
</div>
<div class="pull-right">
<a class="btn btn-primary" href="{{ route('products.index') }}"> Back</a>
</div>
</div>
</div>

@if ($errors-&gt;any())
    &lt;div class="alert alert-danger"&gt;
        &lt;strong&gt;Whoops!&lt;/strong&gt; There were some problems with your input.&lt;br&gt;&lt;br&gt;
        &lt;ul&gt;
            @foreach ($errors-&gt;all() as $error)
                &lt;li&gt;{{ $error }}&lt;/li&gt;
            @endforeach
        &lt;/ul&gt;
    &lt;/div&gt;
@endif

&lt;form action="{{ route('products.update',$product-&gt;id) }}" method="POST"&gt;
	@csrf
    @method('PUT')

     &lt;div class="row"&gt;
	    &lt;div class="col-xs-12 col-sm-12 col-md-12"&gt;
	        &lt;div class="form-group"&gt;
	            &lt;strong&gt;Name:&lt;/strong&gt;
	            &lt;input type="text" name="name" value="{{ $product-&gt;name }}" class="form-control" placeholder="Name"&gt;
	        &lt;/div&gt;
	    &lt;/div&gt;
	    &lt;div class="col-xs-12 col-sm-12 col-md-12"&gt;
	        &lt;div class="form-group"&gt;
	            &lt;strong&gt;Detail:&lt;/strong&gt;
	            &lt;textarea class="form-control" style="height:150px" name="detail" placeholder="Detail"&gt;{{ $product-&gt;detail }}&lt;/textarea&gt;
	        &lt;/div&gt;
	    &lt;/div&gt;
	    &lt;div class="col-xs-12 col-sm-12 col-md-12 text-center"&gt;
	      &lt;button type="submit" class="btn btn-primary"&gt;Submit&lt;/button&gt;
	    &lt;/div&gt;
	&lt;/div&gt;

&lt;/form&gt;

<p class="text-center text-primary"><small>Tutorial by ItSolutionStuff.com</small></p>
@endsection
</pre>

resources/views/products/show.blade.php

<pre class="ql-syntax" spellcheck="false">@extends('layouts.app')

@section('content')
<div class="row">
<div class="col-lg-12 margin-tb">
<div class="pull-left">
<h2> Show Product</h2>
</div>
<div class="pull-right">
<a class="btn btn-primary" href="{{ route('products.index') }}"> Back</a>
</div>
</div>
</div>

&lt;div class="row"&gt;
    &lt;div class="col-xs-12 col-sm-12 col-md-12"&gt;
        &lt;div class="form-group"&gt;
            &lt;strong&gt;Name:&lt;/strong&gt;
            {{ $product-&gt;name }}
        &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="col-xs-12 col-sm-12 col-md-12"&gt;
        &lt;div class="form-group"&gt;
            &lt;strong&gt;Details:&lt;/strong&gt;
            {{ $product-&gt;detail }}
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

@endsection
<p class="text-center text-primary"><small>Tutorial by ItSolutionStuff.com</small></p>
</pre>

Step 10: Handle Exertion Error

Now, in this step we will handle exertion. if you don't have a permission and try to access that page using browser url then you can give message as like bellow:

add/Exceptions/Handler.php

<pre class="ql-syntax" spellcheck="false">......
public function render($request, Exception $exception)
{
if ($exception instanceof \Spatie\Permission\Exceptions\UnauthorizedException) {
return response()->json(['User have not permission for this page access.']);
}

return parent::render($request, $exception);

}
....
</pre>

Step 11: Create Seeder For Permissions and AdminUser

In this step we will create seeder for permissions, Right now we have fixed permission so we create using seeder as listed bellow, but if you can add more permission as you want:

1.role-list

2.role-create

3.role-edit

4.role-delete

5.product-list

6.product-create

7.product-edit

8.product-delete

So, first create seeder using bellow command:

<pre class="ql-syntax" spellcheck="false">php artisan make:seeder PermissionTableSeeder
</pre>

And put bellow code in PermissionTableSeeder seeder this way:

database/seeds/PermissionTableSeeder.php

<pre class="ql-syntax" spellcheck="false"><?php

use Illuminate\Database\Seeder;
use Spatie\Permission\Models\Permission;

class PermissionTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
$permissions = [
'role-list',
'role-create',
'role-edit',
'role-delete',
'product-list',
'product-create',
'product-edit',
'product-delete'
];

    foreach ($permissions as $permission) {
         Permission::create(['name' =&gt; $permission]);
    }
}

}
</pre>

After this we have to run bellow command for run PermissionTableSeeder seeder:

<pre class="ql-syntax" spellcheck="false">php artisan db:seed --class=PermissionTableSeeder
</pre>

Now let's create new seeder for creating admin user.

<pre class="ql-syntax" spellcheck="false">php artisan make:seeder CreateAdminUserSeeder
</pre>

database/seeds/PermissionTableSeeder.php

<pre class="ql-syntax" spellcheck="false"><?php

use Illuminate\Database\Seeder;
use App\User;
use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;

class CreateAdminUserSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
$user = User::create([
'name' => 'Hardik Savani',
'email' => '[email protected]',
'password' => bcrypt('123456')
]);

    $role = Role::create(['name' =&gt; 'Admin']);

    $permissions = Permission::pluck('id','id')-&gt;all();

    $role-&gt;syncPermissions($permissions);

    $user-&gt;assignRole([$role-&gt;id]);
}

}
php artisan db:seed --class=CreateAdminUserSeeder
</pre>

Now we are ready to to run full example of ACL. so let's run our example so run bellow command for quick run:

<pre class="ql-syntax" spellcheck="false">php artisan serve
</pre>

Access By

<pre class="ql-syntax" spellcheck="false">http://localhost:8000/
</pre>

Now you can login with following credential:

<pre class="ql-syntax" spellcheck="false">Email: [email protected]
Password: 123456
</pre>

You can see bellow screenshots:

You can download code from GitHub

Thanks for reading

If you liked this post, share it with all of your programming buddies!

Follow me on Facebook | Twitter

Learn More about

PHP with Laravel for beginners - Become a Master in Laravel

Projects in Laravel: Learn Laravel Building 10 Projects

Laravel for RESTful: Build Your RESTful API with Laravel

Fullstack Web Development With Laravel and Vue.js

Laravel 5.8 Ajax CRUD tutorial using Datatable JS

Laravel 5.8 Tutorial from Scratch for Beginners

Build RESTful API In Laravel 5.8 Example

Login with Google in Laravel 5.8 App using Socialite Package

Laravel PHP Framework Tutorial - Full Course for Beginners (2019)

Laravel 5.8 Tutorial from Scratch for Beginners

Laravel 5.8 Tutorial from Scratch for Beginners

Let’s get started.


Laravel 5.8 From Scratch: Intro, Setup , MVC Basics, and Views.

What You Will Learn In This Series

  • Installing and setting up Laravel locally.
  • Models, Views, and Controllers (MVC).
  • Database, Migrations, and Eloquent ORM.
  • Authentication.
  • Email verification and Password reset.

Introduction

Laravel is the most popular PHP framework built by Taylor Otwell and community. It uses MVC architecture pattern. It provides a lot of features like a complete Authentication System, Database Migrations , A Powerful ORM, Pagination, and so on. Before creating the application, you will need to have PHP 7.2 and MySQL (we are not using apache in this series) and composer installed. I use xampp which is a package that comes with PHP, MySQL, and Apache. Composer is a dependency manager for PHP. It’s similar to npm which is a dependency manager for Javascript. Also, read the server requirements for laravel if you run into installation errors.


Installation

Laravel provides an installer which can be used for creating Laravel projects. It can be install with composer:

<pre class="ql-syntax" spellcheck="false">composer global require "laravel/installer" </pre>

Now we can create our laravel project with:

<pre class="ql-syntax" spellcheck="false">laravel new myapp </pre>

Above command will create our project. We can also use composer to create a laravel project (which I do):

<pre class="ql-syntax" spellcheck="false">composer create-project laravel/laravel myapp </pre>

MVC

A Model is used to interact with the database and of course it does not have to be a database. It could be a JSON File or some other resource. A Controller contains the logic e.g how to validate form data and save a resource to the database by interacting through Model. View is the User Interface (UI) of the application that contains HTML or the presentation markup. It can also have logic e.g loops and conditionals. Template engines are used to embed logic in Views. Laravel has Blade template engine that is used for adding logic inside the views. you can read more about MVC at wikipedia.


Directory Structure

Let’s look at the basic folder structure of laravel application e.g where are Views, Models, and Controllers etc.


Routes

In laravel, there are two routes file web.php and api.php. web.php file is used for registering all the web routes like mywebsite.com/about or mywebsite.com/contact. api.php is used for registering all the routes related to an api. We are only using web routes so don’t worry about any api routes.


Controllers and Models

Models are stored in app directory and there is a default user model that comes with laravel for authentication purposes. app\Http\Controllers has all the controllers and there are some default controllers for authentication.


Views

Views can be found in resources/views and welcome.blade.php is a default view provided by laravel. Laravel views have a .blade.php extension so whenever you create a view don’t forget to add the blade extension. resources/sass has all sass files and js has vuejs components and bootstrap files (we won’t talk about that).


Other Directories

You can put your css, js, and all other static assets in public directory. Config directory has all the configuration files related to our application like database, session, and other configurations (we will talk about config in the next tutorial). There are other directories but you don’t need to worry about that.


Starting the Development Server

Since the root directory of the project is public, we will have to create a virtual host but instead of creating a virtual host, I want to make it simple by using Laravel development server with command (you should be inside the root of your project directory):

<pre class="ql-syntax" spellcheck="false">php artisan serve </pre>

This will start the development server on port 8000. By any chance, if you can’t use that port then you can use **— **port flag to specify a different port:

<pre class="ql-syntax" spellcheck="false">php artisan serve --port=8080 </pre>

Routes and Controllers

Routing is a mechanism by which requests (as specified by a URL and HTTP method) are routed to the code that handles them and that code in MVC is a controller method. e.g / will be your home and /about will be your about page. If you open up the web.php file, you will see just one route:

<pre class="ql-syntax" spellcheck="false"><?php

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

We have a ‘/’ route that uses a callback/closure making a GET request that returns a view named welcome. In a route, the first parameter is the url and the second parameter can be a callback or controller action. Let’s create a controller named PagesController and update our route. In laravel, we have the ability to name our routes which comes in handy when you change the url later on. We will define two routes for our index page and about page:

<pre class="ql-syntax" spellcheck="false"><?php

// syntax: '[email protected]'

Route::get('/','[email protected]')->name('pages.index');
Route::get('/about','[email protected]')->name('pages.about');
</pre>

GET method is used for retrieving data from the server. In our case we will always use GET routes for displaying a plain view or a view with data. With GET method we can pass data to the query string e.g search term or date range for a report but you should not insert sensitive data like email or password since it’s visible in the URL. For passing sensitive data to the server (when we submit a form), we use POST method because the data is sent in the request body. There are other request methods but the most commonly used are GET and POST. PUT method is used for updating data and DELETE for deleting data.

We need to create that PagesController and we can do that by typing this command:

<pre class="ql-syntax" spellcheck="false">php artisan make:controller PagesController
</pre>

Laravel uses autoloading and all the classes are namespaced so if you want to create a controller inside a sub-directory then you should use artisan(It will create the directory if it does not exist):

<pre class="ql-syntax" spellcheck="false">php artisan make:controller Admin/LoginController
</pre>

Above command will create a LoginController inside a sub-directory called Admin. Most of the stuff can be done through artisan which is a command line tool that comes with laravel. You can create models, controllers, migrations and other files with artisan.

Let’s open the PagesController located in app\Http\Controllers:

<pre class="ql-syntax" spellcheck="false"><?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class PagesController extends Controller
{
//
}
</pre>

It has a class PagesController that’s extending Controller class which will provide us all the controller functionality. It also has a Request class imported with “use” statement which is used to access form data and validation. In this controller, we need two methods namely index and about, both will return views. let’s add them:

<pre class="ql-syntax" spellcheck="false">public function index(){
return view('pages.index');
}

public function about(){
return view('pages.about');
}
</pre>

Index method is returning index view stored inside the pages folder and about method is returning a view named about. In laravel, you don’t have to specify view extension and if you have a view inside a folder e.g index view inside posts folder then use dots like view(‘posts.index’).

Also If you want to see the list of all the registered routes in your application then type the below artisan command:

<pre class="ql-syntax" spellcheck="false">php artisan route:list
</pre>

Views

First of all, we will create a layout file which will have all the markup that gets repeated by views e.g doctype, head, and body tags. Usually, layout files are stored in layouts folder in views directory, named app (you can name it whatever you want). let’s create an app.blade.php file after creating layouts folder and add this markup:

<pre class="ql-syntax" spellcheck="false"><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">

&lt;link rel="stylesheet" href="{{asset('css/app.css')}}"&gt; {{-- &lt;- bootstrap css --}}

&lt;title&gt;@yield('title','Laravel 5.8 Basics')&lt;/title&gt;

</head>
<body>

{{-- That's how you write a comment in blade --}}

@include('inc.navbar')

&lt;main class="container mt-4"&gt;
    @yield('content')
&lt;/main&gt;

@include('inc.footer')

&lt;script src="{{asset('js/app.js')}}"&gt;&lt;/script&gt; {{-- &lt;- bootstrap and jquery --}}

</body>
</html>
</pre>

Blade directives start with “@” symbol. @include() is used for including a separate view file. As you can see inc is the name of the folder and navbar is the name of the view. @yield() will output the content of the view section that’s extending this layout. Double curly braces “{{}}” are used for echoing a variable or calling a helper function. Asset() helper is used for generating URLs to public assets stored in public directory like CSS, Images, and Javascript. Note that laravel comes with bootstrap 4 and that’s what we are including.

Let’s create navbar.blade.php in inc folder and add the markup:

<pre class="ql-syntax" spellcheck="false"><nav class="navbar navbar-expand-md navbar-light navbar-laravel">
<div class="container">
<a class="navbar-brand" href="{{ url('/') }}">
{{ config('app.name', 'Laravel') }}
</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="{{ __('Toggle navigation') }}">
<span class="navbar-toggler-icon"></span>
</button>

    &lt;div class="collapse navbar-collapse" id="navbarSupportedContent"&gt;
        &lt;!-- Left Side Of Navbar --&gt;
        &lt;ul class="navbar-nav mr-auto"&gt;
            &lt;li class="nav-item"&gt;
                &lt;a href="{{route('pages.index')}}" class="nav-link"&gt;Home&lt;/a&gt;
            &lt;/li&gt;
            &lt;li class="nav-item"&gt;
                &lt;a href="{{route('pages.about')}}" class="nav-link"&gt;About&lt;/a&gt;
            &lt;/li&gt;
        &lt;/ul&gt;

        &lt;!-- Right Side Of Navbar --&gt;
        &lt;ul class="navbar-nav ml-auto"&gt;
        &lt;/ul&gt;
    &lt;/div&gt;
&lt;/div&gt;

</nav>
</pre>

Inside the navbar we are using url() helper which is used for generating application urls. route() function is used to generate urls of named routes. config() is used for retrieving values from files inside the config folder, app.name will return “name” value from the app.php config file, second parameter is a fallback value or else it will return null. You can use dot syntax for accessing values inside of an array e.g config(‘file.array.string’,’fallback value’).

Now whenever you create a view just add the below lines of code. All the content of your page will be inside the @section directive.

<pre class="ql-syntax" spellcheck="false">@extends('layouts.app')
@section('content')
My Page Content
@endsection
</pre>

Index.blade.php view inside the pages folder will have the following markup:

<pre class="ql-syntax" spellcheck="false">@extends('layouts.app')
@section('content')
<h2 class="mt-5 mb-3 text-center">Laravel 5.8 For Beginners!</h2>
<p class="text-center">Lorem ipsum dolor sit amet consectetur adipisicing elit Aut.</p>
@endsection
</pre>

And for the about.blade.php view:

<pre class="ql-syntax" spellcheck="false">@extends('layouts.app')

@section('title')
Laravel 5.8 Basics | About Page
@endsection

@section('content')
<h3>About Page</h3>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit.</p>
@endsection
</pre>

As you have noticed, we have a @yield directive inside the title html tag and we are defining the section inside our about view so when you will access the about page from the browser you will see that the title on the browser tab is changed.

In the next tutorial, we will talk about accessing and updating config values from .env file then we will create, read, update, and delete todos with MySQL database.

Source code till this tutorial HERE | complete project HERE


Laravel 5.8 From Scratch: Config, ENV, Migrations, and Todos CRUD

In this tutorial, we will talk about Models, Database Migrations and make CRUD (CRUD means Create, Read, Update, and Delete from a database) operations on todos table with MySQL.


.ENV File

Before creating migrations, We will need to setup our database, assuming you know how to create a database using phpmyadmin. After creating the database, we will add the database credentials in our application. Laravel has a **.**env environment file which will have all the sensitive data like database details, mail driver details, etc because it’s not recommended to store such information directly inside the code (environment files are not limited to PHP. They are used in all other major frameworks). Values inside the **.**env files are loaded inside the files from the config directory. .env file is located at the root of our application. Let’s take a look at the file (Below is not the complete env file. I have just paste the variables that are important for beginners to know):

<pre class="ql-syntax" spellcheck="false">#Use double quotes if you have spaces between values e.g APP_NAME="My First Application"
APP_NAME=Laravel #Application name
APP_ENV=local #Application environment, can change to "production"
APP_KEY=your_application_key #Application encryption key
APP_DEBUG=true #Display errors
APP_URL=http://localhost #Application URL
#Supports: MySQL, SQL Server, SQLite, and PostgreSQL.
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=your_database_name
DB_USERNAME=your_db_username
DB_PASSWORD=your_db_password
</pre>

All APP values are loaded in config/app.php config file and DB values in config/database.php.

Note: Whenever you making changes to .env file then don’t forget to restart the server ( if you are using laravel dev server) and if you are using a virtual host and changes don’t seem to take effect then just run php artisan config:clear (This command will clear the configuration cache) in your terminal.

Migrations

Since we have setup our database, now let’s take a look at database migrations. Migration are used to store the details about a database table and it’s properties so you don’t have to manually create all the tables by going to the database interface or something like phpmyadmin. We can create migrations using artisan with “make:migration” command:

<pre class="ql-syntax" spellcheck="false">php artisan make:migration create_todos_table
</pre>

In laravel, the name of the model has to be singular and the name of the migration should be plural so it can automatically find the table name. You can find these migration files in database/migrations directory. Laravel by default comes with two migrations namely users and password_resets (all migration files are prepended with a timestamp), which are used for authentication. If you want to create a migration but have a different table name in mind then you can explicitly define the table name with “ — create” flag:

<pre class="ql-syntax" spellcheck="false">php artisan make:migration create_todos_table --create=todos
</pre>

When you open the create_todos_table.php. You will see two methods, up() and down(). up() is used for creating/updating tables, columns, and indexes. The down() method is used for reversing the operation done by up() method.

<pre class="ql-syntax" spellcheck="false"><?php

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

class CreateTodosTable extends Migration
{
public function up()
{
Schema::create('todos', function (Blueprint $table) {
$table->bigIncrements('id');
$table->timestamps();
});
}

public function down()
{
    Schema::dropIfExists('todos');
}

}
</pre>

Inside up() method, We have Schema:create(‘table_name’,callback) method which will be used for creating a new table. Inside the callback, we have $table->bigIncrements(‘id’) which will create an auto_increment integer column with a primary key and argument ‘id’ is the name of the column. Second one is $table->timestamps() which will create two timestamp columns created_at and updated_at. created_at will be filled when a row is created and updated_at when a row is updated. We will add two columns title and body (for the sake of simplicity) but if you want to know the available columns types then see the list.

<pre class="ql-syntax" spellcheck="false">Schema::create('todos', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('title')->unique(); //unique varchar equivalent column
$table->text('body'); //text equivalent column
$table->timestamps();
});
</pre>

You can also specify length to a string column as the second argument:

<pre class="ql-syntax" spellcheck="false">$table->string('title',255);
</pre>

Before running any migration commands we need to add a line of code inside the AppServiceProvider.php which can be found in app/Providers directory. Open the file and inside the boot() method, add this line:

<pre class="ql-syntax" spellcheck="false">\Schema::defaultStringLength(191);
</pre>

Above code will set the default length of a string column to 191. Since laravel comes with utf8mb4 charset as default which supports emojis and it’s max length for a unique key is 191. If it exceeds the limit then laravel will generate an error and won’t run migrations. If you want to specify a different charset then you can set it in database.php config file.

To run migrations, We can use “migrate” command:

<pre class="ql-syntax" spellcheck="false">php artisan migrate
</pre>

Above command will run the migrations and create all the tables. It will execute the up() method. If you want to reverse the migrations, you can use migrate:rollback command which will execute down() method:

<pre class="ql-syntax" spellcheck="false">php artisan migrate:rollback
</pre>

Models

After creating the tables, We will create model that will interact with the database resource (table) e.g Post model for posts table. To create a model we simply need to run make:model command:

<pre class="ql-syntax" spellcheck="false">php artisan make:model Todo
</pre>

We can also add -m flag which will create a Model as well as a migration for it.

<pre class="ql-syntax" spellcheck="false">php artisan make:model Todo -m
</pre>

Above command will also create a migrating named create_todos_table. You can find all the Models inside the app directory. Let’s open the Todo model.

<pre class="ql-syntax" spellcheck="false"><?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Todo extends Model
{
//
}
</pre>

We can specify properties to modify the behavior of a model. Let’s start with $table property which is used to specify the name of the table that this model will interact with. By default, It will define the table name as the plural of the model name e.g todos table for Todo model and users table for User model. When you don’t want to use timestamps on your table then you will also have to specify $timestamps property and set it to false in your Model because Laravel expects your table to have created_at and updated_at timestamp columns.

<pre class="ql-syntax" spellcheck="false">class Todo extends Model
{
protected $table = 'todos';

public $timestamps = false;

}
</pre>

Above is just an example, so you don’t need to put it in your code to follow this series.


Todos CRUD

Now all the basic stuff is done, let’s start creating views for our todos and add functionality to our TodosController. We will have 4 views Index, Create, Edit, and Show. So let’s create a folder named todos inside the views directory and create all those four views. After that, we will first create the controller:

<pre class="ql-syntax" spellcheck="false">php artisan make:controller TodosController --resource
</pre>

Note that we have also added a — resource flag which will define six methods inside the TodosController namely:

  1. Index (used for displaying a list of Todos)
  2. Create (will show a view with form for creating a Todo)
  3. Store (used for creating a Todo inside the database. Note: create method submits to store method)
  4. Show (will display a specified Todo)
  5. Edit (will show a form for editing a Todo. Form will be filled with the existing Todo data)
  6. Update (Used for updating a Todo inside the database. Note: edit submits to update method)
  7. Destroy (used for deleting a specified Todo)
These methods are not specific to TodosController. It can be used for PostsController, CommentsController or any other resource. (A resource is an object e.g user, todo or anything else)

Let’s add the routes to our web.php files:

<pre class="ql-syntax" spellcheck="false">Route::get('/todos','[email protected]')->name('todos.index');
Route::get('/todos/create','[email protected]')->name('todos.create');
Route::post('/todos','[email protected]')->name('todos.store'); // making a post request
Route::get('/todos/{id}','[email protected]')->name('todos.show');
Route::get('/todos/{id}/edit','[email protected]')->name('todos.edit');
Route::put('/todos/{id}','[email protected]')->name('todos.update'); // making a put request
Route::delete('/todos/{id}','[email protected]')->name('todos.destroy'); // making a delete request
</pre>

We can pass dynamic parameters with {} brackets and you might have noticed that show, update, and destroy has the same url but different methods so it’s legit. Just like — resource flag, laravel has a method called resource() that will generate all the above routes. You can also use that method instead of specifying them individually like above:

<pre class="ql-syntax" spellcheck="false">Route::resource('/todos','TodosController');
</pre>

After defining routes, we need to add the links to our navbar so open the navbar.blade.php view inside the inc folder and update the

<pre class="ql-syntax" spellcheck="false">that has the links for the right side of our navbar:<!-- Right Side Of Navbar -->
<ul class="navbar-nav ml-auto">
<li class="nav-item">
<a href="{{route('todos.index')}}" class="nav-link">Todos</a>
</li>
<li class="nav-item">
<a href="{{route('todos.create')}}" class="nav-link">New Todo</a>
</li>
</ul>
</pre>

Displaying Todos

  • Now open the TodosController so we can modify our Index method:
<pre class="ql-syntax" spellcheck="false">public function index()
{
$todos = Todo::orderBy('created_at','desc')->paginate(8);
return view('todos.index',[
'todos' => $todos,
]);
}
</pre>
  • We are retrieving all the todos with Todo model and ordering by created_at column in descending order then we are chaining it with paginate() which will create pagination links and by default the items displayed per page is 15. After retrieving todos, We are returning a view with $todos array. We can pass data to a view by passing an array of data as a second parameter to the view() method.
  • Some helpful methods for retreiving models:
<pre class="ql-syntax" spellcheck="false">Todo::where('completed',true)->take(8)->get(); // only take 8 todos from the database. <- just an example, we don't have completed column
Todo::where('title','LIKE',"{%}$search_keyword{%}")->get(); // with LIKE operator.
Todo::where('title','LIKE',"{%}$search_keyword{%}")->take(8)->get(); //retrieve only 8 results.
Todo::select(['title','body'])->findOrFail(id); // for specific columns, you can also chain where() with select().
Todo::where('title','value')->whereOr('body','value')->firstOrFail(); // with SQL OR operator
Todo::where('title','value')->whereAnd('body','value')->firstOrFail(); // with SQL AND operator.
</pre>
  • Now, we can add the markup to our Index view inside the todos folder:
<pre class="ql-syntax" spellcheck="false">@extends('layouts.app')
@section('content')
<h2 class="text-center">All Todos</h2>
<ul class="list-group py-3 mb-3">
@forelse($todos as $todo)
<li class="list-group-item my-2">
<h5>{{$todo->title}}</h5>
<p>{{str_limit($todo->body,20)}}</p>
<small class="float-right">{{$todo->created_at->diffForHumans()}}</small>
<a href="{{route('todos.show',$todo->id)}}">Read More</a>
</li>
@empty
<h4 class="text-center">No Todos Found!</h4>
@endforelse
</ul>
<div class="d-flex justify-content-center">
{{$todos->links()}}
</div>
@endsection
</pre>
  • Inside the content section, we are using @forelse() loop to iterate todos and content inside the @empty works as an else statement if an array or collection is empty. Blade has @for() , @foreach(), and @while() loops too. Of course, you need to end this @forelse with @endforelse(). Inside @forelse(), We have some HTML markup and you can see that we are echoing $todo object properties. str_limit() is a helper function used to limit the number of string characters and diffForHumans() is used for displaying date in a more readable format e.g 1 day ago or 1 months ago. We are displaying the pagination links with $todos->links(). By default, Laravel supports bootstrap pagination but if you are using a different framework like materialize-css then you can specify custom pagination view inside the links() method. Laravel already comes with some default pagination views. You can publish these pagination views with below command:
<pre class="ql-syntax" spellcheck="false">php artisan vendor:publish --tag=laravel-pagination
</pre>
  • Above command will copy the pagination views to our views/vendor/pagination directory. Then you can specify the view like this:
<pre class="ql-syntax" spellcheck="false">{{$todos->links('vendor.pagination.bootstrap-4')}} // bootstrap-4 is the name of the pagination view
</pre>
  • There will be more than one pagination views. You can customize the view however you like. Now, when you navigate to todos page, you will see “Nothing found!” since there are not todos in the database.(we will create them now)

Creating A Todo

  • Let’s modify the create method of TodosController which will simply return a view with a form for creating a todo:
<pre class="ql-syntax" spellcheck="false">public function create()
{
return view('todos.create');
}
</pre>
  • Adding the markup to create view inside the todos folder:
<pre class="ql-syntax" spellcheck="false">@extends('layouts.app')
@section('content')
<h3 class="text-center">Create Todo</h3>
<form action="{{route('todos.store')}}" method="post">
@csrf
<div class="form-group">
<label for="title">Todo Title</label>
<input type="text" name="title" id="title" class="form-control {{$errors->has('title') ? 'is-invalid' : '' }}" value="{{old('title')}}" placeholder="Enter Title">
@if($errors->has('title'))
<span class="invalid-feedback">
{{$errors->first('title')}}
</span>
@endif
</div>
<div class="form-group">
<label for="body">Todo Description</label>
<textarea name="body" id="body" rows="4" class="form-control {{$errors->has('body') ? 'is-invalid' : ''}}" placeholder="Enter Todo Description">{{old('body')}}</textarea>
@if($errors->has('body')) {{-- <-check if we have a validation error --}}
<span class="invalid-feedback">
{{$errors->first('body')}} {{-- <- Display the First validation error --}}
</span>
@endif
</div>
<button type="submit" class="btn btn-primary">Create</button>
</form>
@endsection
</pre>
  • We have a form with POST method submitting to a name route “todos.store”. Inside the form, we have @csrf will generate a hidden input named csrf_token which is important for security and without that you will not be able to submit a form (You will see a page with “Page expired” text after form submission or a 419 error page). old(‘field_name’) is a helper function used for echoing the old values from a submitted form e.g if you failed the validation and got redirected back to the form then you will need those values that were previously filled. Validation errors can be accessed by $errors object. To check for validation failure, We can use $errors->has(‘field_name’) and $errors->first(‘field_name’) will echo the first validation error.
“field_name” is the value of the “name” attribute defined in our input fields. In our form, we have “title” and “body” fields.
  • Let’s modify the Store method of our TodosController which will store the todo to the database:
<pre class="ql-syntax" spellcheck="false">public function store(Request $request)
{
//validation rules
$rules = [
'title' => 'required|string|unique:todos,title|min:2|max:191',
'body' => 'required|string|min:5|max:1000',
];
//custom validation error messages
$messages = [
'title.unique' => 'Todo title should be unique', //syntax: field_name.rule
];
//First Validate the form data
$request->validate($rules,$messages);
//Create a Todo
$todo = new Todo;
$todo->title = $request->title;
$todo->body = $request->body;
$todo->user_id = Auth::id();
$todo->save(); // save it to the database.
//Redirect to a specified route with flash message.
return redirect()
->route('todos.index')
->with('status','Created a new Todo!');
}
</pre>
  • Store method has $request object as a parameter which will be used to access form data. First thing you want to do is validate the form data. We can use the $request->validate() method for validation, which will receive an array of validation rules. Validation rules is an associative array. Key will be the field_name and value with be the validation rules. Second parameter is an optional array for custom validation messages. Rules are separated with pipe sign “|”. We are using the most basic validation rules. First is “required” which means the field_name should not be empty (“nullable” rule is vice versa), “string” means it should be a string value, “min” is the limit of minimum characters for a string in an input field and “max” is the maximum characters. “unique:table,column” with see if the same value does not exists in the database (comes handy for storing emails or any other unique data). If validation fails then it will redirect us back. After validation, we are create a new object of the Todo model. Since Todo and Request are both objects, We can access their properties with arrow “->” operator. After specifying the values to the model properties, We can call the save() method which will save the Todo to the database. When a Todo is successfully created then we are redirecting the user to a specific route and chaining it with with() method which will make the data available for the next request so we can use it as a flash message. Since this will be stored in session, We can access the value with session(‘status’) as well as check if the a value exists.
  • Some Methods for accessing form values that are useful:
<pre class="ql-syntax" spellcheck="false">$request->input('field_name'); // access an input field
$request->has('field_name'); // check if field exists
$request->title; // dynamically access input fields
request('key') // you can use this global helper if needed inside a view
</pre>
  • Helpful redirecting methods:
<pre class="ql-syntax" spellcheck="false">return redirect('/todos'); // to a specific url
return redirect(url('/todos')); // to a specific url with url helper
return redirect(url()->previous()); // to a previous url
return redirect()->back(); // redirect back (same as above)
</pre>
  • We also need to display the message as bootstrap alert so open the app.blade.php layout file (so we can make this alert available in every view) and add the below code right after the app.js script:
<pre class="ql-syntax" spellcheck="false">@if(session('status')) {{-- <- If session key exists --}}
<div class="alert alert-success alert-dismissible fade show" role="alert">
{{session('status')}} {{-- <- Display the session value --}}
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
@endif
<script>
//close the alert after 3 seconds.
$(document).ready(function(){
setTimeout(function() {
$(".alert").alert('close');
}, 3000);
});
</script>
</pre>
  • Let’s add a little css in the tag for our alert:
<pre class="ql-syntax" spellcheck="false"><style>
.alert{
z-index: 99;
top: 60px;
right:18px;
min-width:30%;
position: fixed;
animation: slide 0.5s forwards;
}
@keyframes slide {
100% { top: 30px; }
}
@media screen and (max-width: 668px) {
.alert{ /* center the alert on small screens */
left: 10px;
right: 10px;
}
}
</style>
</pre>

Displaying a Single Todo

  • Let’s open the show.blade.php and add the following markup:
<pre class="ql-syntax" spellcheck="false">@extends('layouts.app')
@section('content')
<h3 class="text-center">{{$todo->title}}</h3>
<p>{{$todo->body}}</p>
<br>
<a href="{{route('todos.edit',$todo->id)}}" class="btn btn-primary float-left">Update</a>
<a href="#" class="btn btn-danger float-right" data-toggle="modal" data-target="#delete-modal">Delete</a>
<div class="clearfix"></div>
<div class="modal fade" id="delete-modal">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Delete Todo</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<p>Are you sure!</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger" onclick="document.querySelector('#delete-form').submit()">Proceed</button>
<button type="button" class="btn btn-primary" data-dismiss="modal">Cancel</button>
</div>
</div>
</div>
</div>
<form method="POST" id="delete-form" action="{{route('todos.destroy',$todo->id)}}">
@csrf
@method('DELETE')
</form>
@endsection
</pre>
  • All that’s different in our view is that this time we have a single Todo and since it’s a single object we don’t need a loop. There’s an Update button that with open the edit todo view and there’s also a Delete button that will open the modal for confirmation and when you will click the Proceed button it will submit a hidden form having an id of “delete-form” (we will work on destroy method after show method). We are making a delete request when deleting a specific resource so simple anchor tag will not work and a form is needed. Since HTML forms only support GET and POST methods, We will need to add @method(‘DELETE’) which will generate an input field with a value of DELETE ( PUT for updating a resource). Let’s modify the show method in TodosController.
<pre class="ql-syntax" spellcheck="false">public function show($id)
{
$todo = Todo::findOrFail($id);
return view('todos.show',[
'todo' => $todo,
]);
}
</pre>
  • To get a specific resource with an id column, We can use findOrFail() method this method will throw a 404 error if it fails to find the resource. If you want to find a resource by a different column then you can use where(‘column’,’value’) and chain it to firstOrFail() method. For example:
<pre class="ql-syntax" spellcheck="false">$todo = Todo::where('title','this is title')->firstOrFail();
</pre>

Delete a Todo

  • We have already created a button in the show view that submits to this method. Now we can modify the destroy method that is responsible for deleting the Todo and all we need to do in this method is fetch the specified Todo and call delete() method on it.
<pre class="ql-syntax" spellcheck="false">public function destroy($id)
{
//Delete the Todo
$todo = Todo::findOrFail($id);
$todo->delete();
//Redirect to a specified route with flash message.
return redirect()
->route('todos.index')
->with('status','Deleted the selected Todo!');
}
</pre>

Updating the Todo

  • Edit method is same as show method just views are different because we will display the values of our todo object in an html form:
<pre class="ql-syntax" spellcheck="false">public function edit($id)
{
//Find a Todo by it's ID
$todo = Todo::findOrFail($id);
return view('todos.edit',[
'todo' => $todo,
]);
}
</pre>
  • Let’s add the markup for our last view edit.blade.php:
<pre class="ql-syntax" spellcheck="false">@extends('layouts.app')
@section('content')
<h3 class="text-center">Edit Todo</h3>
<form action="{{route('todos.update',$todo->id)}}" method="post">
@csrf
@method('PUT')
<div class="form-group">
<label for="title">Todo Title</label>
<input type="text" name="title" id="title" class="form-control {{ $errors->has('title') ? 'is-invalid' : '' }}" value="{{ old('title') ? : $todo->title }}" placeholder="Enter Title">
@if($errors->has('title')) {{-- <-check if we have a validation error --}}
<span class="invalid-feedback">
{{$errors->first('title')}} {{-- <- Display the First validation error --}}
</span>
@endif
</div>
<div class="form-group">
<label for="body">Todo Description</label>
<textarea name="body" id="body" rows="4" class="form-control {{ $errors->has('body') ? 'is-invalid' : '' }}" placeholder="Enter Todo Description">{{ old('body') ? : $todo->body }}</textarea>
@if($errors->has('body')) {{-- <-check if we have a validation error --}}
<span class="invalid-feedback">
{{$errors->first('body')}} {{-- <- Display the First validation error --}}
</span>
@endif
</div>
<button type="submit" class="btn btn-primary">Update</button>
</form>
@endsection
</pre>
  • This view is the same as the create.blade.php, the difference is that it’s submitting to update method of TodosController having a @method(‘PUT’) inside the form. We are also echoing the values from the $post in the input field with a ternary operator and if validation fails then we will have old() value instead of $post values. Lastly the update method of our TodosController which is same as store but we are updating an existing resource:
<pre class="ql-syntax" spellcheck="false">public function update(Request $request, $id)
{
//validation rules
$rules = [
'title' => "required|string|unique:todos,title,{$id}|min:2|max:191", //Using double quotes
'body' => 'required|string|min:5|max:1000',
];
//custom validation error messages
$messages = [
'title.unique' => 'Todo title should be unique',
];
//First Validate the form data
$request->validate($rules,$messages);
//Update the Todo
$todo = Todo::findOrFail($id);
$todo->title = $request->title;
$todo->body = $request->body;
$todo->save(); //Can be used for both creating and updating
//Redirect to a specified route with flash message.
return redirect()
->route('todos.show',$id)
->with('status','Updated the selected Todo!');
}
</pre>
  • There is a scenario when you want to update the todo body but not title then you will get a validation error saying the title should be unique. In these cases we can specify the id of that model to ignore.
  • Now we can create, display all todos, display a single todo, update them, and lastly delete them, Next tutorial will be on authentication and middleware.
  • Next tutorial will soon be published within this week.
  • Source code till this tutorial HERE | complete project HERE

Learn More