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.

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.

When we are done we will have a store that looks like this:

laravel ecommerce app preview

Prerequisites

  • Basic understanding of programming concepts.
  • Laravel CLI installed on your machine.
  • Knowledge of PHP.
  • Basic knowledge of the command line.
  • Basic knowledge of JavaScript and the VueJS framework.
  • Have a development environment setup. Check out this guide.

Introduction

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:

  • A listing page to view all our products.
  • A single page to view product information.
  • A checkout page where the buyer can enter a delivery address and pay.
  • A simple dashboard for a buyer to see all purchased products, and know if they have shipped.
  • A way for the seller to add products to sell.
  • A way for the seller to see which orders users placed.
  • A way for the seller to show a product has been shipped to the user.
  • A way for the seller to see and update all product information.

Planning the structure of the application

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.

Creating the application

We are going to use the Laravel CLI to make a new Laravel application. To create a new Laravel application, run the following command:

    $ 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.

Creating the models for the application

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:

    $ php artisan make:model Product -mr

💡 We have added the -mr flag to the make:model command so that it will generate the accompanying migration and controller for the model.

    $ php artisan make:model Order -mr

Next open the app/User.php file and replace the contents with the following:

    <?php

    namespace App;

    use Illuminate\Notifications\Notifiable;
    use Illuminate\Database\Eloquent\SoftDeletes;
    use Illuminate\Foundation\Auth\User as Authenticatable;

    class User extends Authenticatable
    {
        use Notifiable, SoftDeletes;

        protected $fillable = [
            'name', 'email', 'password',
        ];

        protected $hidden = [
            'password', 'remember_token',
        ];

        public function orders()
        {
            return $this->hasMany(Order::class);
        }
    }

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:

    <?php
    namespace App;

    use Illuminate\Database\Eloquent\Model;
    use Illuminate\Database\Eloquent\SoftDeletes;

    class Product extends Model
    {
        use SoftDeletes;

        protected $fillable = [
            'name', 'price', 'units', 'description', 'image'
        ];

        public function orders(){
            return $this->hasMany(Order::class);
        }
    }

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:

    <?php
    namespace App;

    use Illuminate\Database\Eloquent\Model;
    use Illuminate\Database\Eloquent\SoftDeletes;

    class Order extends Model
    {
        use SoftDeletes;

        protected $fillable = [
            'product_id', 'user_id', 'quantity', 'address' 
        ];

        public function user()
        {
            return $this->belongsTo(User::class, 'user_id');
        }

        public function product()
        {
            return $this->belongsTo(Product::class, 'product_id');
        }

    }

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.

Defining the migrations for the application

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:

    <?php

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

    class CreateUsersTable extends Migration
    {
        public function up()
        {
            Schema::create('users', function (Blueprint $table) {
                $table->increments('id');
                $table->string('name');
                $table->string('email')->unique();
                $table->boolean('is_admin')->default(false);
                $table->string('password');
                $table->rememberToken();
                $table->timestamps();
                $table->softDeletes();
            });
        }

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

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:

    <?php

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

    class CreateProductsTable extends Migration
    {
        public function up()
        {
            Schema::create('products', function (Blueprint $table) {
                $table->increments('id');
                $table->string('name');
                $table->string('description');
                $table->unsignedInteger('units')->default(0);
                $table->double('price');
                $table->string('image');
                $table->timestamps();
                $table->softDeletes();
            });
        }

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

Finally, open the create_orders_table migrations file in the database/migrations directory and replace the contents with the following:

    <?php

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

    class CreateOrdersTable extends Migration
    {
        public function up()
        {
            Schema::create('orders', function (Blueprint $table) {
                $table->increments('id');
                $table->unsignedInteger('product_id');
                $table->unsignedInteger('user_id');
                $table->unsignedInteger('quantity')->default(1);
                $table->string('address')->nullable();
                $table->boolean('is_delivered')->default(false);
                $table->timestamps();
                $table->softDeletes();
            });
        }

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

Creating seeders for the application

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:

    $ php artisan make:seed UsersTableSeeder

Now, open the file UserTableSeeder.php at the database/seeds directory and replace the content with the following:

    <?php

    use App\User;
    use Illuminate\Database\Seeder;

    class UsersTableSeeder extends Seeder
    {
        public function run()
        {
            $user = new User;
            $user->name = "Admin";
            $user->email = "admin@devtest.com";
            $user->password = bcrypt('secret');
            $user->is_admin = true;
            $user->save();
        }
    }

The seeder class above will create a new admin user in the database.

💡 Recall that when we defined the User model, we did not include is_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:

    $ php artisan make:seed ProductsTableSeeder

Open the database/seeds/ProductTableSeeder.php file and replace the contents with the following:

    <?php

    use Illuminate\Database\Seeder;

    class ProductsTableSeeder extends Seeder
    {
        public function run()
        {
            $products = [
                [
                    'name' => "MEN'S BETTER THAN NAKED & JACKET",
                    'description' => 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
                    tempor incididunt ut labore et dolore magna aliqua consequat.',
                    'units' => 21,
                    'price' => 200.10,
                    'image' => 'http://images.thenorthface.com/is/image/TheNorthFace/236x204_CLR/mens-better-than-naked-jacket-AVMH_LC9_hero.png',
                    'created_at' => new DateTime,
                    'updated_at' => null,
                ],
                [
                    'name' => "WOMEN'S BETTER THAN NAKED™ JACKET",
                    'description' => 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
                    tempor incididunt ut labore et dolore magna aliqua consequat.',
                    'units' => 400,
                    'price' => 1600.21,
                    'image' => 'http://images.thenorthface.com/is/image/TheNorthFace/236x204_CLR/womens-better-than-naked-jacket-AVKL_NN4_hero.png',
                    'created_at' => new DateTime,
                    'updated_at' => null,
                ],
                [
                    'name' => "WOMEN'S SINGLE-TRACK SHOE",
                    'description' => 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
                    tempor incididunt ut labore et dolore magna aliqua consequat.',
                    'units' => 37,
                    'price' => 378.00,
                    'image' => 'http://images.thenorthface.com/is/image/TheNorthFace/236x204_CLR/womens-single-track-shoe-ALQF_JM3_hero.png',
                    'created_at' => new DateTime,
                    'updated_at' => null,
                ],
                [
                    'name' => 'Enduro Boa® Hydration Pack',
                    'description' => 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
                    tempor incididunt ut labore et dolore magna aliqua consequat.',
                    'units' => 10,
                    'price' => 21.10,
                    'image' => 'http://images.thenorthface.com/is/image/TheNorthFace/236x204_CLR/enduro-boa-hydration-pack-AJQZ_JK3_hero.png',
                    'created_at' => new DateTime,
                    'updated_at' => null,
                ]
            ];

            DB::table('products')->insert($products);
        }
    }

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:

    <?php

    use Illuminate\Database\Seeder;

    class DatabaseSeeder extends Seeder
    {
        public function run()
        {
            $this->call([
              UsersTableSeeder::class,
              ProductsTableSeeder::class,
            ]);
        }
    }

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.

Creating the database for our application

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:

    DB_CONNECTION=mysql
    DB_DATABASE=homestead
    DB_USERNAME=username
    DB_PASSWORD=password

with

    DB_CONNECTION=sqlite
    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:

    $ php artisan migrate --seed

Defining and securing our endpoints

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.

Install passport

To install passport, run the following command:

    $ composer require laravel/passport

Laravel Passport comes with it’s own migrations, run the migrations using the following command:

    $ php artisan migrate

Laravel migrate command

Next, run the passport installation command to create the necessary keys for securing your application:

    $ php artisan passport:install

Laravel Passport Installtion

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:

    <?php

    namespace App;

    [...]

    use Laravel\Passport\HasApiTokens;
    use Illuminate\Database\Eloquent\SoftDeletes;

    class User extends Authenticatable
    {
        use Notifiable, SoftDeletes, HasApiTokens;

        [...]
    }

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:

    <?php

    [...]

    use Laravel\Passport\Passport;

    class AuthServiceProvider extends ServiceProvider
    {
        [...]

        public function boot()
        {
            $this->registerPolicies();

            Passport::routes();
        }

        [...]
    }

Finally, in your config/auth.php configuration file, you should set the driver option of the api authentication guard to passport.

    [...]

    'guards' => [

        [...]

        'api' => [
            'driver' => 'passport',
            'provider' => 'users',
        ],
    ],

    [...]

That’s all for Laravel Passport.

Conclusion

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 authorisation 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.