This three-part tutorial series shows how to build an e-commerce application with Laravel and Vue. It includes authentication using Passport, and a simple sqlite database. In part one, plan your app, create your database and install Passport.
In this article, we will cover how you can use Laravel to build a simple e-commerce application. After this tutorial, you should know how to use Laravel and Vue to make a web application and understand the basics of making an online store.
With the advent of the internet, many regular things became faster and easier. One of the things that improved is commerce. Commercial activities carried out on the web are known as e-commerce.
E-commerce applications make sales of goods and services over the internet possible. If you use the internet often, chances are you have come across one or even used one at some point.
When we are done we will have a store that looks like this:
As we mentioned earlier, an e-commerce application makes selling goods and services online very convenient. It provides a listing of the products the seller wishes to sell showing their prices, then a page where you can see all the details of the single product selected, and finally, where to pay for the product and set how you wish to receive it.
With this in mind, we know that to have a useful e-commerce application, we’d need to develop the following:
From our breakdown above, you may see that this application will have two user types:
Administrator – The owner of the store.
Buyer – The person who wants to buy a gift for a friend.
You definitely know that the application will also have products that need to be stored. These products will be ordered and you need a way to store and track those orders. These would form the basis of any e-commerce platform.
The basic details we need to store in the different parts:
Table | Fields |
---|---|
User | name, email, password, is_admin |
Product | name, description, price, units, image |
Order | product, user, quantity, address, is_delivered |
The basic operations we need the app to carry out, and where these are handled, are:
Operation | Controller |
---|---|
Login | UserController |
Register | UserController |
User profile | UserController |
View all orders by a single user | UserController |
View product listing | ProductController |
View a single product | ProductController |
Edit a product | ProductController |
Add a new product | ProductController |
Add more units to a product | ProductController |
Remove a product | ProductController |
Order product | OrderController |
View all orders | OrderController |
View a single order information | OrderController |
Deliver an order | OrderController |
Delete an order | OrderController |
If you want to go a little deeper, you can consider adding categories, tags, warehouse items, more user types and many other things. However, we will not be considering them for this guide, but you should definitely try it on your own.
? A good way to learn is by practicing. You can exercise your mind by doing the task in the previous paragraph.
We are going to use the Laravel CLI to make a new Laravel application. To create a new Laravel application, run the following command:
1$ laravel new bigStore
Then you can cd
into the project we just created. The Laravel related commands we run throughout the article needs to be run from the root of the Laravel project.
Models in Laravel provide a very convenient way to interact with the database. They provide methods for all the basic operations you need to run for your database table.
Let’s make the models that will interact with our database and hold business logic. New Laravel installations come with the User
model out of the box, which can be found in app
directory so let’s make the other two models:
1$ php artisan make:model Product -mr
? We have added the
-mr
flag to themake:model
command so that it will generate the accompanying migration and controller for the model.
1$ php artisan make:model Order -mr
Next open the app/User.php
file and replace the contents with the following:
1<?php 2 3 namespace App; 4 5 use Illuminate\Notifications\Notifiable; 6 use Illuminate\Database\Eloquent\SoftDeletes; 7 use Illuminate\Foundation\Auth\User as Authenticatable; 8 9 class User extends Authenticatable 10 { 11 use Notifiable, SoftDeletes; 12 13 protected $fillable = [ 14 'name', 'email', 'password', 15 ]; 16 17 protected $hidden = [ 18 'password', 'remember_token', 19 ]; 20 21 public function orders() 22 { 23 return $this->hasMany(Order::class); 24 } 25 }
We are using SoftDeletes
to allow us to mark a database record as deleted without actually deleting it completely. This is useful if you want to be able to restore the data.
We also have an array – $fillable
, with the column names we want to mass assign on the users table. Mass assignment happens when we call our User
model statically and pass an array to its create
method.
Open app/Product.php
file and edit as follows:
1<?php 2 namespace App; 3 4 use Illuminate\Database\Eloquent\Model; 5 use Illuminate\Database\Eloquent\SoftDeletes; 6 7 class Product extends Model 8 { 9 use SoftDeletes; 10 11 protected $fillable = [ 12 'name', 'price', 'units', 'description', 'image' 13 ]; 14 15 public function orders(){ 16 return $this->hasMany(Order::class); 17 } 18 }
The Product
model is quite similar to the User
model. It has the $fillable
array and also an orders
method for establishing a relationship with orders placed on the application.
Now, open app/Order.php
file and edit as follows:
1<?php 2 namespace App; 3 4 use Illuminate\Database\Eloquent\Model; 5 use Illuminate\Database\Eloquent\SoftDeletes; 6 7 class Order extends Model 8 { 9 use SoftDeletes; 10 11 protected $fillable = [ 12 'product_id', 'user_id', 'quantity', 'address' 13 ]; 14 15 public function user() 16 { 17 return $this->belongsTo(User::class, 'user_id'); 18 } 19 20 public function product() 21 { 22 return $this->belongsTo(Product::class, 'product_id'); 23 } 24 25 }
The Order
model looks slightly different from the other two but is essentially the same thing. We just established a different kind of relationship – belongsTo
that shows which user made an order or which product was ordered.
Migrations are a good way to create and maintain your application’s database. It essentially defines how tables should be created or modified.
Migrations are useful because they help you manage the database tables, columns and keys. You can share migration files instead of raw SQL, and because migration files are run chronologically, they make it easy to work with git, so it’s great for teams.
Open the create_users_table
migrations file in the database/migrations
directory and replace the content with the following:
1<?php 2 3 use Illuminate\Support\Facades\Schema; 4 use Illuminate\Database\Schema\Blueprint; 5 use Illuminate\Database\Migrations\Migration; 6 7 class CreateUsersTable extends Migration 8 { 9 public function up() 10 { 11 Schema::create('users', function (Blueprint $table) { 12 $table->increments('id'); 13 $table->string('name'); 14 $table->string('email')->unique(); 15 $table->boolean('is_admin')->default(false); 16 $table->string('password'); 17 $table->rememberToken(); 18 $table->timestamps(); 19 $table->softDeletes(); 20 }); 21 } 22 23 public function down() 24 { 25 Schema::dropIfExists('users'); 26 } 27 }
Laravel is quite readable and you can probably figure out what is going on by reading the code. We defined which columns should exist in the table and their attributes.
There are many methods in the Blueprint
class for migrations. You can read more here.
Next, open the create_products_table
migrations file in the database/migrations
directory and replace the code with the following:
1<?php 2 3 use Illuminate\Support\Facades\Schema; 4 use Illuminate\Database\Schema\Blueprint; 5 use Illuminate\Database\Migrations\Migration; 6 7 class CreateProductsTable extends Migration 8 { 9 public function up() 10 { 11 Schema::create('products', function (Blueprint $table) { 12 $table->increments('id'); 13 $table->string('name'); 14 $table->string('description'); 15 $table->unsignedInteger('units')->default(0); 16 $table->double('price'); 17 $table->string('image'); 18 $table->timestamps(); 19 $table->softDeletes(); 20 }); 21 } 22 23 public function down() 24 { 25 Schema::dropIfExists('products'); 26 } 27 }
Finally, open the create_orders_table
migrations file in the database/migrations
directory and replace the contents with the following:
1<?php 2 3 use Illuminate\Support\Facades\Schema; 4 use Illuminate\Database\Schema\Blueprint; 5 use Illuminate\Database\Migrations\Migration; 6 7 class CreateOrdersTable extends Migration 8 { 9 public function up() 10 { 11 Schema::create('orders', function (Blueprint $table) { 12 $table->increments('id'); 13 $table->unsignedInteger('product_id'); 14 $table->unsignedInteger('user_id'); 15 $table->unsignedInteger('quantity')->default(1); 16 $table->string('address')->nullable(); 17 $table->boolean('is_delivered')->default(false); 18 $table->timestamps(); 19 $table->softDeletes(); 20 }); 21 } 22 23 public function down() 24 { 25 Schema::dropIfExists('orders'); 26 } 27 }
Seeders are an excellent way to pre-populate our database with dummy data. We are going to use the seeder class to create the user account for our administration.
Create a seeder class by running the command below:
1$ php artisan make:seed UsersTableSeeder
Now, open the file UserTableSeeder.php
at the database/seeds
directory and replace the content with the following:
1<?php 2 3 use App\User; 4 use Illuminate\Database\Seeder; 5 6 class UsersTableSeeder extends Seeder 7 { 8 public function run() 9 { 10 $user = new User; 11 $user->name = "Admin"; 12 $user->email = "admin@devtest.com"; 13 $user->password = bcrypt('secret'); 14 $user->is_admin = true; 15 $user->save(); 16 } 17 }
The seeder class above will create a new admin user in the database.
? Recall that when we defined the
User
model, we did not includeis_admin
in the fillable column. The reason is that we do not want anyone who tries to spoof our application to create an administrator user. This is why we had to create a user instance here so we can access all the columns from the user table.
Make another seeder class for our products table:
1$ php artisan make:seed ProductsTableSeeder
Open the database/seeds/ProductTableSeeder.php
file and replace the contents with the following:
1<?php 2 3 use Illuminate\Database\Seeder; 4 5 class ProductsTableSeeder extends Seeder 6 { 7 public function run() 8 { 9 $products = [ 10 [ 11 'name' => "MEN'S BETTER THAN NAKED & JACKET", 12 'description' => 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod 13 tempor incididunt ut labore et dolore magna aliqua consequat.', 14 'units' => 21, 15 'price' => 200.10, 16 'image' => 'http://images.thenorthface.com/is/image/TheNorthFace/236x204_CLR/mens-better-than-naked-jacket-AVMH_LC9_hero.png', 17 'created_at' => new DateTime, 18 'updated_at' => null, 19 ], 20 [ 21 'name' => "WOMEN'S BETTER THAN NAKED™ JACKET", 22 'description' => 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod 23 tempor incididunt ut labore et dolore magna aliqua consequat.', 24 'units' => 400, 25 'price' => 1600.21, 26 'image' => 'http://images.thenorthface.com/is/image/TheNorthFace/236x204_CLR/womens-better-than-naked-jacket-AVKL_NN4_hero.png', 27 'created_at' => new DateTime, 28 'updated_at' => null, 29 ], 30 [ 31 'name' => "WOMEN'S SINGLE-TRACK SHOE", 32 'description' => 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod 33 tempor incididunt ut labore et dolore magna aliqua consequat.', 34 'units' => 37, 35 'price' => 378.00, 36 'image' => 'http://images.thenorthface.com/is/image/TheNorthFace/236x204_CLR/womens-single-track-shoe-ALQF_JM3_hero.png', 37 'created_at' => new DateTime, 38 'updated_at' => null, 39 ], 40 [ 41 'name' => 'Enduro Boa® Hydration Pack', 42 'description' => 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod 43 tempor incididunt ut labore et dolore magna aliqua consequat.', 44 'units' => 10, 45 'price' => 21.10, 46 'image' => 'http://images.thenorthface.com/is/image/TheNorthFace/236x204_CLR/enduro-boa-hydration-pack-AJQZ_JK3_hero.png', 47 'created_at' => new DateTime, 48 'updated_at' => null, 49 ] 50 ]; 51 52 DB::table('products')->insert($products); 53 } 54 }
The data we populated the database with is from here and is for sample educational purposes. You may require permissions to actually use the images.
When you are done making the seeder, you need to edit the database/seeds/DatabaseSeeder.php
, which actually invokes the seeders:
1<?php 2 3 use Illuminate\Database\Seeder; 4 5 class DatabaseSeeder extends Seeder 6 { 7 public function run() 8 { 9 $this->call([ 10 UsersTableSeeder::class, 11 ProductsTableSeeder::class, 12 ]); 13 } 14 }
Now, when we execute the command to seed our database the DatabaseSeeder
class is called and it calls the run
which in turn class the seeder classes we had setup to run.
? You can learn more about seeders here.
We have defined everything we need our database to have. Now, we need to actually define the database itself. We are going to use SQLite for this guide, but you can any database you like. You should use a non-file based database like MySQL if in a production environment.
Create a database file at database/database.sqlite
. Next, open your .env
file and replace the following lines:
1DB_CONNECTION=mysql 2 DB_DATABASE=homestead 3 DB_USERNAME=username 4 DB_PASSWORD=password
with
1DB_CONNECTION=sqlite 2 DB_DATABASE=/full/path/to/database/database.sqlite
That is all for our database setup. Run the migrations to create the database tables for our application and seed it:
1$ php artisan migrate --seed
Our application uses Laravel and Vue to create the best application experience. This means we would need to define APIs to provide our Vue components with data.
Laravel, by default, has support for web and API routes. Web routes handle routing for dynamically generated pages accessed from a web browser, while API routes handle requests from clients that need a response in mostly JSON
or XML
format.
Our application will have APIs for most requests. We need to secure our APIs to ensure only authorised users will access it. For this, we will use Laravel Passport.
To install passport, run the following command:
1$ composer require laravel/passport
Laravel Passport comes with it’s own migrations, run the migrations using the following command:
1$ php artisan migrate
Next, run the passport installation command to create the necessary keys for securing your application:
1$ php artisan passport:install
The above command will create encryption keys needed to generate secure access tokens plus “personal access” and “password grant” clients, which will be used to generate access tokens.
After the installation, you need to use
the Laravel Passport HasApiToken
trait in the User
model. This trait will provide a few helper methods to your model, which allow you to inspect the authenticated user’s token and scopes.
Open the app/User.php
file and edit as follows:
1<?php 2 3 namespace App; 4 5 [...] 6 7 use Laravel\Passport\HasApiTokens; 8 use Illuminate\Database\Eloquent\SoftDeletes; 9 10 class User extends Authenticatable 11 { 12 use Notifiable, SoftDeletes, HasApiTokens; 13 14 [...] 15 }
Next, call the Passport::routes
method within the boot
method of your AuthServiceProvider
. This method will register the routes necessary to issue the tokens your app will need:
In the app/Providers/AuthServiceProvider.php
file update as follows:
1<?php 2 3 [...] 4 5 use Laravel\Passport\Passport; 6 7 class AuthServiceProvider extends ServiceProvider 8 { 9 [...] 10 11 public function boot() 12 { 13 $this->registerPolicies(); 14 15 Passport::routes(); 16 } 17 18 [...] 19 }
Finally, in your config/auth.php
configuration file, you should set the driver
option of the api
authentication guard to passport
.
1[...] 2 3 'guards' => [ 4 5 [...] 6 7 'api' => [ 8 'driver' => 'passport', 9 'provider' => 'users', 10 ], 11 ], 12 13 [...]
That’s all for Laravel Passport.
So far, we have set up our application by defining all the models we need. We have also added migrations for our database, and lastly, we added Laravel Passport for handling authorization for our APIs.
The next thing we will look at is implementing the controllers to handle the different kinds of request we are going to make to the application and the views to interact with the application. Continue to part two.