Published on August 27, 2024By DeveloperBreeze

Tutorial: Building a Custom E-commerce Platform with Laravel and Vue.js

Introduction

Building a custom e-commerce platform can be a rewarding endeavor, offering flexibility, scalability, and control over the user experience. In this tutorial, we will guide you through the process of creating a custom e-commerce platform using Laravel for the backend and Vue.js for the frontend. By the end of this tutorial, you'll have a solid foundation for a fully functional e-commerce platform that you can expand and customize according to your needs.

Prerequisites

Before we begin, make sure you have the following:

    • Basic Knowledge of Laravel: Understanding Laravel’s MVC structure, routing, and Eloquent ORM is essential.

    • Basic Knowledge of Vue.js: Familiarity with Vue.js components, directives, and event handling is required.

    • Environment Setup: Ensure you have PHP, Composer, Node.js, and npm/yarn installed on your machine.

Part 1: Setting Up the Laravel Backend

Introduction

In this part, we will set up the backend for our custom e-commerce platform using Laravel. We'll focus on creating the necessary models, migrations, and API endpoints that will serve as the foundation for our platform. By the end of this section, you'll have a fully functional backend ready to communicate with the frontend.

Step 1: Creating a New Laravel Project

Start by setting up a new Laravel project. Laravel provides a robust framework with built-in tools that make it ideal for developing complex applications like an e-commerce platform.

Open your terminal and run the following command to create a new Laravel project:

composer create-project --prefer-dist laravel/laravel ecommerce-platform
cd ecommerce-platform

After the project is set up, navigate into the project directory.

Step 2: Configuring the Database

Next, you'll need to configure your database settings. Laravel uses environment variables to manage configuration, so you'll need to update the .env file in your project root.

Open .env and update the database configuration:

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=ecommerce
DB_USERNAME=root
DB_PASSWORD=secret

Make sure to replace DB_USERNAME and DB_PASSWORD with your actual database credentials.

After configuring the database, you can run the migration command to set up the default tables that Laravel provides, such as users, password_resets, etc.

php artisan migrate

Step 3: Defining the E-commerce Database Schema

Now let's define the core database schema for our e-commerce platform. We'll start by creating three essential models: Product, Category, and Order. These models will represent the main entities in our e-commerce platform.

Generate the models and migrations using the following commands:

php artisan make:model Product -m
php artisan make:model Category -m
php artisan make:model Order -m

This will create the model files in the app/Models directory and the corresponding migration files in the database/migrations directory.

Updating the Migrations

Next, update the migration files to define the database schema.

Products Table:

public function up()
{
    Schema::create('products', function (Blueprint $table) {
        $table->id();
        $table->string('name');
        $table->text('description');
        $table->decimal('price', 8, 2);
        $table->unsignedBigInteger('category_id');
        $table->foreign('category_id')->references('id')->on('categories')->onDelete('cascade');
        $table->timestamps();
    });
}

Categories Table:

public function up()
{
    Schema::create('categories', function (Blueprint $table) {
        $table->id();
        $table->string('name');
        $table->timestamps();
    });
}

Orders Table:

public function up()
{
    Schema::create('orders', function (Blueprint $table) {
        $table->id();
        $table->unsignedBigInteger('product_id');
        $table->foreign('product_id')->references('id')->on('products')->onDelete('cascade');
        $table->integer('quantity');
        $table->decimal('total_price', 8, 2);
        $table->timestamps();
    });
}

These tables will store the information needed for managing products, categories, and customer orders.

Run the migrations to create these tables in your database:

php artisan migrate

Step 4: Setting Up Eloquent Relationships

To link these tables together, you'll define relationships between the models in Laravel. Eloquent, Laravel's ORM, makes it easy to define these relationships.

Product Model:

In app/Models/Product.php, define the relationship between Product and Category as well as Product and Order:

public function category()
{
    return $this->belongsTo(Category::class);
}

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

Category Model:

In app/Models/Category.php, define the relationship between Category and Product:

public function products()
{
    return $this->hasMany(Product::class);
}

Order Model:

In app/Models/Order.php, define the relationship between Order and Product:

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

These relationships will allow you to easily query related data, such as fetching all products within a category or all orders for a product.

Step 5: Creating API Endpoints

To interact with the frontend, we'll create RESTful API endpoints. These endpoints will allow the frontend to retrieve, create, update, and delete products, categories, and orders.

Define the routes in routes/api.php:

use App\Http\Controllers\ProductController;
use App\Http\Controllers\CategoryController;
use App\Http\Controllers\OrderController;

Route::get('/products', [ProductController::class, 'index']);
Route::get('/products/{id}', [ProductController::class, 'show']);
Route::post('/products', [ProductController::class, 'store']);
Route::put('/products/{id}', [ProductController::class, 'update']);
Route::delete('/products/{id}', [ProductController::class, 'destroy']);

Route::get('/categories', [CategoryController::class, 'index']);
Route::post('/categories', [CategoryController::class, 'store']);

Route::post('/orders', [OrderController::class, 'store']);

Next, implement the controller methods for handling these routes.

ProductController:

public function index()
{
    return Product::with('category')->get();
}

public function show($id)
{
    return Product::with('category')->findOrFail($id);
}

public function store(Request $request)
{
    $validated = $request->validate([
        'name' => 'required',
        'description' => 'required',
        'price' => 'required|numeric',
        'category_id' => 'required|exists:categories,id',
    ]);

    return Product::create($validated);
}

public function update(Request $request, $id)
{
    $product = Product::findOrFail($id);

    $validated = $request->validate([
        'name' => 'required',
        'description' => 'required',
        'price' => 'required|numeric',
        'category_id' => 'required|exists:categories,id',
    ]);

    $product->update($validated);

    return $product;
}

public function destroy($id)
{
    Product::findOrFail($id)->delete();

    return response()->noContent();
}

CategoryController:

public function index()
{
    return Category::all();
}

public function store(Request $request)
{
    $validated = $request->validate([
        'name' => 'required|unique:categories',
    ]);

    return Category::create($validated);
}

OrderController:

public function store(Request $request)
{
    $validated = $request->validate([
        'product_id' => 'required|exists:products,id',
        'quantity' => 'required|integer|min:1',
    ]);

    $product = Product::findOrFail($validated['product_id']);
    $totalPrice = $product->price * $validated['quantity'];

    $order = Order::create([
        'product_id' => $validated['product_id'],
        'quantity' => $validated['quantity'],
        'total_price' => $totalPrice,
    ]);

    return $order;
}

These methods will handle the API requests for products, categories, and orders, ensuring that the frontend can interact with the backend effectively.

Conclusion of Part 1

At this point, you've successfully set up the backend for your custom e-commerce platform using Laravel. You've defined the database schema, established relationships between models, and created API endpoints to interact with your data. In the next part of this tutorial, we'll focus on setting up the Vue.js frontend and connecting it to this backend.

---

Feel free to take a break here before moving on to Part 2, where we will dive into setting up the Vue.js frontend for our e-commerce platform.

Part 2: Setting Up the Vue.js Frontend

Introduction

In Part 2 of this tutorial, we'll focus on setting up the frontend for our e-commerce platform using Vue.js. We'll create a user-friendly interface that interacts with the Laravel backend we set up in Part 1. By the end of this section, you'll have a basic but functional frontend that can display products, view product details, and add items to a shopping cart.

Step 1: Setting Up Vue.js in Laravel

Laravel supports Vue.js out of the box, so integrating Vue into your Laravel project is straightforward. First, ensure that you have Node.js and npm installed on your system.

To begin, navigate to your Laravel project directory and install the necessary dependencies:

npm install vue@next vue-router@next vuex@next axios

Next, set up your Vue environment. Laravel provides a resources/js directory where you can store your Vue components and other frontend-related files.

Create a new vue.config.js file in the root directory of your project. This file is used to configure the Vue build process:

module.exports = {
  outputDir: '../public',
  publicPath: '/',
  filenameHashing: false,
};

This configuration ensures that your Vue assets are correctly output to the public directory, where Laravel can serve them.

Step 2: Organizing the Vue.js Project Structure

To keep your project organized, create the following directory structure inside resources/js:

resources/js/
├── components/
│   ├── ProductList.vue
│   ├── ProductDetail.vue
│   └── Cart.vue
├── store/
│   └── index.js
└── views/
    ├── Home.vue
    └── Product.vue

Here’s what each directory and file will be used for:

  • components/: This directory contains reusable Vue components such as ProductList.vue and ProductDetail.vue.

  • store/: This directory will contain the Vuex store, which manages the state of your application.

  • views/: This directory contains the main pages or views of your application, such as the Home and Product pages.

Step 3: Setting Up Vue Router

Vue Router is the official router for Vue.js, enabling navigation between different views of your application. To set it up, create a new file named router.js inside the resources/js directory:

// resources/js/router.js
import { createRouter, createWebHistory } from 'vue-router';
import Home from './views/Home.vue';
import Product from './views/Product.vue';

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home,
  },
  {
    path: '/product/:id',
    name: 'Product',
    component: Product,
  },
];

const router = createRouter({
  history: createWebHistory(),
  routes,
});

export default router;

This configuration defines two routes: the home page (/) and the product detail page (/product/:id). The createWebHistory() function enables HTML5 history mode, providing clean URLs without hash symbols.

Step 4: Setting Up the Vuex Store

Vuex is a state management library for Vue.js, which allows you to manage the state of your application in a centralized store. This is especially useful for managing data like the shopping cart.

Create a new file named index.js inside the store directory:

// resources/js/store/index.js
import { createStore } from 'vuex';

const store = createStore({
  state: {
    cart: [],
  },
  mutations: {
    addToCart(state, product) {
      const item = state.cart.find((item) => item.id === product.id);
      if (item) {
        item.quantity += 1;
      } else {
        state.cart.push({ ...product, quantity: 1 });
      }
    },
  },
  actions: {
    addToCart({ commit }, product) {
      commit('addToCart', product);
    },
  },
  getters: {
    cartItems(state) {
      return state.cart;
    },
    cartTotal(state) {
      return state.cart.reduce((total, item) => total + item.price * item.quantity, 0);
    },
  },
});

export default store;

This store manages the shopping cart state. It includes:

  • state: Contains the cart array, where each item represents a product added to the cart.

  • mutations: Methods to mutate the state. For example, addToCart adds a product to the cart or increases the quantity if it’s already in the cart.

  • actions: Functions that commit mutations. The addToCart action dispatches the mutation to add a product to the cart.

  • getters: Computed properties for the state. For instance, cartItems returns all items in the cart, and cartTotal calculates the total price of the items in the cart.

Step 5: Implementing Vue Components

Next, let's create the core Vue components for our e-commerce platform.

ProductList.vue:

<template>
  <div>
    <h1>Products</h1>
    <div v-for="product in products" :key="product.id">
      <h2>{{ product.name }}</h2>
      <p>{{ product.description }}</p>
      <p>{{ product.price }}</p>
      <router-link :to="`/product/${product.id}`">View Details</router-link>
    </div>
  </div>
</template>

<script>
import axios from 'axios';

export default {
  data() {
    return {
      products: [],
    };
  },
  created() {
    axios.get('/api/products').then((response) => {
      this.products = response.data;
    });
  },
};
</script>

ProductDetail.vue:

<template>
  <div>
    <h1>{{ product.name }}</h1>
    <p>{{ product.description }}</p>
    <p>{{ product.price }}</p>
    <button @click="addToCart">Add to Cart</button>
  </div>
</template>

<script>
import axios from 'axios';

export default {
  data() {
    return {
      product: null,
    };
  },
  created() {
    axios.get(`/api/products/${this.$route.params.id}`).then((response) => {
      this.product = response.data;
    });
  },
  methods: {
    addToCart() {
      this.$store.commit('addToCart', this.product);
    },
  },
};
</script>

Cart.vue:

<template>
  <div>
    <h1>Shopping Cart</h1>
    <div v-for="item in cart" :key="item.id">
      <h2>{{ item.name }}</h2>
      <p>Quantity: {{ item.quantity }}</p>
      <p>Total Price: {{ item.quantity * item.price }}</p>
    </div>
    <h3>Total: {{ cartTotal }}</h3>
  </div>
</template>

<script>
export default {
  computed: {
    cart() {
      return this.$store.getters.cartItems;
    },
    cartTotal() {
      return this.$store.getters.cartTotal;
    },
  },
};
</script>

These components provide a simple interface for browsing products, viewing product details, and managing the shopping cart.

Step 6: Integrating Vue with Laravel

Finally, you need to integrate your Vue frontend with the Laravel backend.

In your resources/js/app.js file, set up Vue, Vue Router, and Vuex:

import { createApp } from 'vue';
import router from './router';
import store from './store';
import App from './App.vue';

createApp(App)
  .use(router)
  .use(store)
  .mount('#app');

Update your resources/views/welcome.blade.php to include the root element for Vue:

<div id="app"></div>
<script src="{{ mix('js/app.js') }}"></script>

Build your assets:

npm run dev

Now, when you visit your Laravel application, you should see the Vue.js frontend integrated and functioning.

Conclusion of Part 2

You've successfully set up the Vue.js frontend for your e-commerce platform and connected it to your Laravel backend. You now have a basic but functional interface where users can browse products, view product details, and manage their shopping cart.

In the next part of this tutorial, we'll enhance the platform by adding features such as user authentication, order processing, and improving the UI/UX with more advanced Vue.js features.

---

You can now proceed to Part 3, where we will continue building on the foundation we've created so far.

Part 3: Enhancing the E-commerce Platform

Introduction

In this final part of the tutorial, we'll focus on adding advanced features to our e-commerce platform, such as user authentication, order processing, and improving the overall user interface and user experience. By the end of this part, you'll have a more complete and polished e-commerce application that can be further customized and scaled according to your needs.

Step 1: Implementing User Authentication

To add user authentication, we will use Laravel's built-in authentication system, which provides a straightforward way to handle user registration, login, and logout.

Setting Up Authentication in Laravel

Run the following command to create authentication scaffolding:

composer require laravel/ui
php artisan ui vue --auth
npm install && npm run dev

This command sets up authentication routes, controllers, and views. It also installs Vue.js and integrates the necessary frontend components.

Run the migrations to create the users table:

php artisan migrate
Modifying the Frontend for Authentication

Ensure that the login and registration views are correctly integrated into your Vue.js application.

You can modify the resources/views/layouts/app.blade.php to include Vue components for login and registration if you prefer a more Vue-centric approach. However, for simplicity, Laravel’s default blade templates can be used.

To protect routes that require authentication, you can modify your Vue Router configuration in router.js:

import { createRouter, createWebHistory } from 'vue-router';
import Home from './views/Home.vue';
import Product from './views/Product.vue';
import Login from './views/Login.vue';
import Register from './views/Register.vue';
import Cart from './components/Cart.vue';

const routes = [
  { path: '/', name: 'Home', component: Home },
  { path: '/product/:id', name: 'Product', component: Product },
  { path: '/login', name: 'Login', component: Login },
  { path: '/register', name: 'Register', component: Register },
  { path: '/cart', name: 'Cart', component: Cart, meta: { requiresAuth: true } },
];

const router = createRouter({
  history: createWebHistory(),
  routes,
});

router.beforeEach((to, from, next) => {
  const requiresAuth = to.matched.some(record => record.meta.requiresAuth);
  const isAuthenticated = !!localStorage.getItem('auth_token'); // Assuming you're storing the auth token in localStorage

  if (requiresAuth && !isAuthenticated) {
    next('/login');
  } else {
    next();
  }
});

export default router;

This ensures that certain routes, like the cart page, are only accessible to authenticated users.

Step 2: Adding Order Processing

Now that we have user authentication in place, we can implement order processing. This involves creating a checkout flow where users can review their cart, provide payment details, and place orders.

Extending the Order Model

Update the Order model to include user relationships and payment details:

// In app/Models/Order.php
public function user()
{
    return $this->belongsTo(User::class);
}

Update the orders migration file to include user_id and status fields:

public function up()
{
    Schema::table('orders', function (Blueprint $table) {
        $table->unsignedBigInteger('user_id');
        $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
        $table->string('status')->default('pending');
    });
}

Run the migration to update the database schema:

php artisan migrate
Creating the Checkout Component

Create a Checkout.vue component in resources/js/components/Checkout.vue:

<template>
  <div>
    <h1>Checkout</h1>
    <div v-for="item in cart" :key="item.id">
      <h2>{{ item.name }}</h2>
      <p>Quantity: {{ item.quantity }}</p>
      <p>Total Price: {{ item.quantity * item.price }}</p>
    </div>
    <h3>Total: {{ cartTotal }}</h3>
    <button @click="placeOrder">Place Order</button>
  </div>
</template>

<script>
export default {
  computed: {
    cart() {
      return this.$store.getters.cartItems;
    },
    cartTotal() {
      return this.$store.getters.cartTotal;
    },
  },
  methods: {
    placeOrder() {
      axios.post('/api/orders', {
        user_id: this.$store.state.user.id, // Assuming user info is stored in Vuex
        items: this.cart,
        total_price: this.cartTotal,
      }).then(() => {
        this.$router.push('/orders');
      });
    },
  },
};
</script>

This component allows users to review their cart and place an order. Upon clicking "Place Order," the order is submitted to the backend.

Updating the OrderController

In OrderController, update the store method to handle the new order structure:

public function store(Request $request)
{
    $validated = $request->validate([
        'user_id' => 'required|exists:users,id',
        'items' => 'required|array',
        'total_price' => 'required|numeric',
    ]);

    $order = Order::create([
        'user_id' => $validated['user_id'],
        'total_price' => $validated['total_price'],
        'status' => 'pending',
    ]);

    foreach ($validated['items'] as $item) {
        // Assuming OrderItem is a pivot table or related model
        $order->items()->create([
            'product_id' => $item['id'],
            'quantity' => $item['quantity'],
            'price' => $item['price'],
        ]);
    }

    return $order;
}

This method creates an order and associates it with the authenticated user and the items in their cart.

Step 3: Improving UI/UX

To provide a better user experience, let's add some final touches to the UI using Vue.js components and animations.

Adding Notifications

Integrate a notification system using Vue Toasted:

npm install vue-toasted

In resources/js/app.js, register Vue Toasted:

import Toasted from 'vue-toasted';

createApp(App)
  .use(router)
  .use(store)
  .use(Toasted, { duration: 3000 })
  .mount('#app');

Now, you can add notifications for actions like adding items to the cart or placing an order:

methods: {
  addToCart() {
    this.$store.commit('addToCart', this.product);
    this.$toasted.show('Product added to cart!', { type: 'success' });
  },
  placeOrder() {
    axios.post('/api/orders', {
      user_id: this.$store.state.user.id,
      items: this.cart,
      total_price: this.cartTotal,
    }).then(() => {
      this.$router.push('/orders');
      this.$toasted.show('Order placed successfully!', { type: 'success' });
    });
  },
},
Adding Animations

To make the UI more interactive, add animations using Vue's built-in transition component:

<template>
  <transition name="fade">
    <div v-if="showCart">
      <Cart />
    </div>
  </transition>
</template>

<style>
.fade-enter-active, .fade-leave-active {
  transition: opacity 0.5s;
}
.fade-enter, .fade-leave-to /* .fade-leave-active in <2.1.8 */ {
  opacity: 0;
}
</style>

These simple animations can greatly enhance the user experience by making the interface feel more responsive.

Step 4: Deploying the Application

Finally, it's time to deploy your application. Ensure that your environment is ready by doing the following:

  • Production Build: Create a production build of your Vue.js application:

npm run build
  

  • Laravel Configuration: Ensure that your .env file is set up for production, including database, caching, and queue settings.

  • Server Setup: Deploy the Laravel application to a server (e.g., using Laravel Forge, DigitalOcean, or another hosting service). Make sure to configure the web server (like Nginx) to serve the Laravel application correctly.

  • Deploying Static Assets: Ensure that your built Vue.js assets are served correctly by your web server.

Conclusion of Part 3

Congratulations! You've successfully built and enhanced a custom e-commerce platform using Laravel and Vue.js. This platform includes essential features like user authentication, product management, a shopping cart, and order processing, all wrapped in a responsive and interactive UI.

While the application is now functional, there is always room for improvement. You could extend this platform by adding more features like payment gateways, inventory management, product reviews, and more.

---

Comments

Please log in to leave a comment.

Continue Reading:

Tailwind Browser Mockup

Published on January 26, 2024

Simple and Clean Tailwind Buttons

Published on January 26, 2024

Tailwind Buttons with Arrow Icon

Published on January 26, 2024

AI Interactive Chat Interface

Published on January 26, 2024

AI Chat Interface with Online Assistant

Published on January 26, 2024

CSS Grid and Flexbox: Mastering Modern Layouts

Published on August 03, 2024

csshtml

Creating a Simple REST API with Flask

Published on August 03, 2024

python

Building a Real-Time Chat Application with WebSockets in Node.js

Published on August 03, 2024

javascriptcsshtml

JavaScript Code Snippet: Fetch and Display Data from an API

Published on August 04, 2024

javascriptjson