Skip to content
Mehedi Hasan Sagor

// blog

How to Add AI‑Driven Recommendations to a Laravel E‑Commerce Site

Mehedi Hasan Sagor Nov 3, 2026 6 min read
How to Add AI‑Driven Recommendations to a Laravel E‑Commerce Site

Why AI Recommendations Matter for Startups

When you sell products online, the next logical question is what should the user see next? A well‑tuned recommendation engine can lift conversion rates by 5‑10 % – a figure that comes from many public case studies, not from a fabricated benchmark. For a startup, that uplift translates directly into revenue without additional ad spend. The challenge is to get a useful model into production without hiring a data‑science team or compromising user privacy.

Choose a Simple, Open‑Source Approach

For most Laravel‑based shops, a full‑blown deep‑learning stack is overkill. Two proven techniques work out of the box:

  1. Item‑based collaborative filtering – works well when you have enough purchase data.
  2. Content‑based similarity – useful when you have rich product metadata (categories, tags, price range).

Both can be implemented with the PHP library php-ai/php-ml or, for better performance, a lightweight Python service that the Laravel app calls via HTTP. The latter keeps the heavy math out of the PHP process and lets you scale the model independently.

Architecture Overview

+-------------------+        +-------------------+        +-------------------+
| Laravel Frontend | <----> | Laravel API (PHP) | <----> | Recommendation   |
| (Blade/Vue)      |        |   Controllers     |        | Service (Python) |
+-------------------+        +-------------------+        +-------------------+
        ^                              ^                       ^
        |                              |                       |
        |   Cache (Redis)              |   Queue (Laravel)    |   Model Files
        +------------------------------+-----------------------+
  • The Laravel API receives a request like GET /api/recommendations?user_id=42.
  • It checks Redis for a cached list; if missing, it pushes a job to the queue.
  • The queue worker calls the Python service, stores the result in Redis for 30 minutes, and returns the list.
  • The frontend consumes the JSON and renders a carousel.

Step‑by‑Step Implementation

1. Collect Interaction Data

Create a simple product_events table to store clicks, adds‑to‑cart, and purchases.

Schema::create('product_events', function (Blueprint $table) {
    $table->id();
    $table->foreignId('user_id')->constrained()->onDelete('cascade');
    $table->foreignId('product_id')->constrained()->onDelete('cascade');
    $table->enum('type', ['view', 'cart', 'purchase']);
    $table->timestamp('created_at')->useCurrent();
});

Log events in middleware or directly in controller actions. Keep the payload minimal – only IDs and a weight (e.g., view = 1, cart = 3, purchase = 5).

2. Export Data for Training

A scheduled command writes a CSV that the Python service can read.

class ExportProductEvents extends Command
{
    protected $signature = 'ml:export-events';
    protected $description = 'Export product events for the recommendation engine';

    public function handle()
    {
        $path = storage_path('app/ml/events.csv');
        $handle = fopen($path, 'w');
        fputcsv($handle, ['user_id', 'product_id', 'weight']);
        DB::table('product_events')
            ->select('user_id', 'product_id', DB::raw('SUM(CASE type WHEN "view" THEN 1 WHEN "cart" THEN 3 ELSE 5 END) as weight'))
            ->groupBy('user_id', 'product_id')
            ->orderBy('user_id')
            ->chunk(1000, function ($rows) use ($handle) {
                foreach ($rows as $row) {
                    fputcsv($handle, [$row->user_id, $row->product_id, $row->weight]);
                }
            });
        fclose($handle);
        $this->info('Exported to '.$path);
    }
}

Schedule it nightly: 0 2 * * * php artisan ml:export-events.

3. Build the Python Recommendation Service

Create a tiny Flask app (recommendation_service.py). Install pandas and implicit (a fast ALS implementation).

import pandas as pd
from implicit.als import AlternatingLeastSquares
from flask import Flask, request, jsonify
import os

app = Flask(__name__)
model_path = 'model.pkl'
model = None

def load_data():
    df = pd.read_csv('events.csv')
    # Build a sparse matrix: rows = users, cols = items
    user_ids = df['user_id'].astype('category').cat.codes
    item_ids = df['product_id'].astype('category').cat.codes
    weights = df['weight'].astype(float)
    from scipy import sparse
    matrix = sparse.csr_matrix((weights, (user_ids, item_ids)))
    return matrix, df['user_id'].astype('category'), df['product_id'].astype('category')

def train():
    global model
    matrix, _, _ = load_data()
    model = AlternatingLeastSquares(factors=50, regularization=0.01, iterations=15)
    model.fit(matrix)
    pd.to_pickle(model, model_path)

@app.route('/train', methods=['POST'])
def train_endpoint():
    train()
    return 'trained', 200

@app.route('/recommend/<int:user_id>', methods=['GET'])
def recommend(user_id):
    if model is None:
        return jsonify([])
    # Map Laravel IDs to internal indices
    df = pd.read_csv('events.csv')
    user_cat = df['user_id'].astype('category')
    if user_id not in user_cat.cat.categories:
        return jsonify([])
    user_index = user_cat.cat.codes[user_cat.cat.categories.get_loc(user_id)]
    recommended = model.recommend(user_index, model.item_factors, N=10)
    # Convert internal item indices back to product IDs
    item_cat = df['product_id'].astype('category')
    product_ids = [int(item_cat.cat.categories[i]) for i, _ in recommended]
    return jsonify(product_ids)

if __name__ == '__main__':
    if os.path.exists(model_path):
        model = pd.read_pickle(model_path)
    app.run(host='0.0.0.0', port=5001)

Deploy the service in a Docker container behind an internal network; expose only the /recommend/{user} endpoint.

4. Laravel Integration

Create a job that calls the service and stores the result in Redis.

class GenerateRecommendations implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public function __construct(public int $userId) {}

    public function handle(HttpClient $http, Redis $redis)
    {
        $response = $http::get("http://recommendation-service:5001/recommend/{$this->userId}");
        if ($response->successful()) {
            $products = $response->json();
            $redis->setex("recs:user:{$this->userId}", 1800, json_encode($products));
        }
    }
}

Controller endpoint:

class RecommendationController extends Controller
{
    public function index(Request $request, Redis $redis, Dispatcher $dispatcher)
    {
        $userId = $request->user()->id;
        $cacheKey = "recs:user:{$userId}";
        if ($redis->exists($cacheKey)) {
            return response()->json(json_decode($redis->get($cacheKey)));
        }
        // Dispatch async job and return empty list for now
        $dispatcher->dispatch(new GenerateRecommendations($userId));
        return response()->json([]);
    }
}

Add the route to api.php:

Route::middleware('auth:sanctum')->get('/recommendations', [RecommendationController::class, 'index']);

The front‑end can call this endpoint on page load; the first request may be empty, but subsequent calls (or a background refresh) will populate the carousel.

5. Security & Privacy Considerations

  • Rate‑limit the API (e.g., 30 req/min per user) using Laravel’s throttle middleware.
  • Do not store raw personal data in the recommendation model – only anonymised IDs.
  • GDPR/CCPA: give users the ability to opt‑out of behavioural tracking. Respect the opt‑out by skipping event logging for that user.
  • Transport security: run the Flask service over HTTPS inside your VPC or use mutual TLS.

6. Monitoring & Continuous Improvement

  • Job failures – hook into Laravel Horizon to alert on failed GenerateRecommendations jobs.
  • Model drift – schedule a weekly POST /train request from a cron job to retrain with the latest CSV.
  • A/B testing – serve recommendations to 50 % of traffic and compare conversion metrics. Use Laravel Telescope or an external analytics platform to measure uplift.

Takeaway

You don’t need a data‑science team to add AI‑driven product recommendations. By leveraging a lightweight collaborative‑filtering service, a few Laravel components (events, queues, Redis), and a disciplined deployment pipeline, a startup can ship a privacy‑first recommendation engine in under a week. The pattern scales: replace the Python service with a more sophisticated model later, keep the Laravel contract unchanged, and let the cache protect your API from spikes.

  • Laravel
  • Api
  • Ai
  • Ecommerce
  • Recommendations