Xây dựng hệ thống referral trong Laravel

Bài viết được sự cho phép của tác giả Kien Dang Chung

Hiện nay, thương mại điện tử đang đạt đến đỉnh cao, rất nhiều các chương trình được đưa ra nhằm tăng doanh số bán hàng, quảng bá sản phẩm mạnh mẽ hơn. Một trong những cách thức đó là các chương trình giới thiệu bán hàng (referral system), nó giống như mô hình truyền thống trong việc mở rộng các đại lý phân phối sản phẩm, tuy nhiên có một điểm khác biệt là các đại lý truyền thống cần có sản phẩm để bán thì với chương trình này, đại lý chỉ việc giới thiệu các sản phẩm, dựa trên uy tín của mình, đại lý có thể giới thiệu được nhiều sản phẩm và có thu nhập tốt.

Tham khảo tuyển dụng Laravel mới nhất trong tháng này

  Các Laravel route tips giúp bạn cải thiện routing
  Cách sử dụng Laravel với Socket.IO

Hệ thống referral là cần thiết?

Các chương trình giới thiệu bán hàng đã tận dụng được lợi thế về quảng bá trên Internet giúp bạn nhanh chóng mở rộng được đại lý bán hàng, tuy nhiên bạn cũng phải thường xuyên tạo ra các chương trình chia sẻ lợi nhuận hấp dẫn, thiết kế các mẫu quảng bá thu hút người xem (hoặc do các đại lý thiết kế) và quan trọng hơn nữa là thường xuyên tạo ra các chương trình chi ân nhằm giữ chân khách hàng lâu dài. Như vậy, có thể thấy Referral là hệ thống quan trọng đối với một website bán hàng, với các website khác áp dụng hệ thống referral cũng là cách tốt để có được lượng traffict lớn ngoài các thành viên thường xuyên.

Xây dựng hệ thống referral trong Laravel

Trong mô hình trên, bạn có thể thấy tất cả các đối tượng tham gia đều được hưởng lợi:

  • Người giới thiệu (hay còn gọi là đại lý): sẽ nhận được hoa hồng khi giao dịch thành công.
  • Người được mời (người mua): nhận được các chương trình giảm giá, chương trình chi ân khách hàng từ hệ thống. Các hệ thống bán hàng có thể giảm giá tốt hơn với các khách hàng tiềm năng này.
  • Nhà phân phối (có hệ thống giới thiệu bán hàng – referral system): tăng doanh số bán hàng, tiếp cận được nhiều khách hàng tiềm năng.

Xây dựng hệ thống giới thiệu bán hàng – referral system trong ứng dụng Laravel

Bước 1: Tạo các bảng liên quan trong CSDL

Chúng ta sẽ tạo ra hai bảng referral_programs và referral_links:

c:\xampp\htdocs\laravel-test>php artisan make:migration create_referral_programs_table --create=referral_programs
Created Migration: 2017_04_24_075438_create_referral_programs_table

c:\xampp\htdocs\laravel-test>php artisan make:migration create_referral_links_table --create=referral_links
Created Migration: 2017_04_24_075503_create_referral_links_table

c:\xampp\htdocs\laravel-test>php artisan make:migration create_referral_relationships_table --create=referral_relationships
Created Migration: 2017_04_24_081042_create_referral_relationships_table

Thêm nội dung tạo các bảng liên quan, với file 2017_04_24_075438_create_referral_programs_table:

<?php

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

class CreateReferralProgramsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('referral_programs', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('uri');
            $table->integer('lifetime_minutes')->default(7 * 24 * 60);
            $table->timestamps();
        });
    }

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

Với file 2017_04_24_075503_create_referral_links_table:

<?php

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

class CreateReferralLinksTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('referral_links', function (Blueprint $table) {
            $table->increments('id');
            $table->integer('user_id')->unsigned();
            $table->integer('referral_program_id')->unsigned();
            $table->string('code', 36)->index();
            $table->unique(['referral_program_id', 'user_id']);
            $table->timestamps();
        });
    }

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

Thêm nội dung tạo referral_relationships:

<?php

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

class CreateReferralRelationshipsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('referral_relationships', function (Blueprint $table) {
            $table->increments('id');
            $table->integer('referral_link_id');
            $table->integer('user_id');
            $table->timestamps();
        });
    }

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

Tiếp theo thực hiện tạo hai bảng này bằng lệnh php artisan migrate

c:\xampp\htdocs\laravel-test>php artisan migrate
Migrated: 2017_04_24_075438_create_referral_programs_table
Migrated: 2017_04_24_075503_create_referral_links_table
Migrated: 2017_04_24_081042_create_referral_relationships_table

Bước 2: Tạo Eloquent Model

Sử dụng câu lệnh artisan make:model để tạo ba model là ReferralProgram, ReferralLink và ReferralRelationship

c:\xampp\htdocs\laravel-test>php artisan make:model ReferralProgram
Model created successfully.

c:\xampp\htdocs\laravel-test>php artisan make:model ReferralLink
Model created successfully.

c:\xampp\htdocs\laravel-test>php artisan make:model ReferralRelationship
Model created successfully.

Trong project này chúng ta sẽ sử dụng package ramsey/uuid là một thư viện làm việc với Universally Unique Identifiers (UUID) tương thích với tiêu chuẩn RFC 4122 phiên bản 1, 3, 4 và 5. Thực hiện cài đặt vào dự án bằng lệnh composer.

c:\xampp\htdocs\laravel-test>composer require ramsey/uuid
Using version ^3.6 for ramsey/uuid
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 0 installs, 1 update, 0 removals
  - Updating ramsey/uuid (3.6.0 => 3.6.1): Loading from cache
Writing lock file
Generating autoload files
> Illuminate\Foundation\ComposerScripts::postUpdate
> php artisan optimize
Generating optimized class loader
The compiled services file has been removed.

Ngoài ra chúng ta cần cài đặt thêm gói moontoast/math được sử dụng trong các thuật toán của ramsey/uuid:

c:\xampp\htdocs\laravel-test>composer require moontoast/math
Using version ^1.1 for moontoast/math
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 1 install, 0 updates, 0 removals
  - Installing moontoast/math (1.1.2): Downloading (100%)
Writing lock file
Generating autoload files
> Illuminate\Foundation\ComposerScripts::postUpdate
> php artisan optimize
Generating optimized class loader
The compiled services file has been removed.

Nội dung các model như sau, ReferralProgram:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class ReferralProgram extends Model
{
    protected $fillable = ['name', 'uri', 'lifetime_minutes'];
}

ReferralLink:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Ramsey\Uuid\Uuid;

class ReferralLink extends Model
{
    protected $fillable = ['user_id', 'referral_program_id'];

    protected static function boot()
    {
        static::creating(function (ReferralLink $model) {
            $model->generateCode();
        });
    }

    private function generateCode()
    {
        $this->code = (string)Uuid::uuid1();
    }

    public static function getReferral($user, $program)
    {
        return static::firstOrCreate([
            'user_id' => $user->id,
            'referral_program_id' => $program->id
        ]);
    }

    public function getLinkAttribute()
    {
        return url($this->program->uri) . '?ref=' . $this->code;
    }

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

    public function program()
    {
        // TODO: Check if second argument is requried
        return $this->belongsTo(ReferralProgram::class, 'referral_program_id');
    }

    public function relationships()
    {
        return $this->hasMany(ReferralRelationship::class);
    }
}

ReferralRelationship:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class ReferralRelationship extends Model
{
    protected $fillable = ['referral_link_id', 'user_id'];
}

Bước 3: Quản lý các đường link giới thiệu

Tiếp theo chúng ta cần giám sát các đường link có tham số ref và lưu chúng vào cookie, như vậy mỗi khi có một hành động nào đó của người dùng chúng ta có thể biết và tặng quà cho cả hai bên tham gia. Để thực hiện được việc này chúng ta tạo ra một middleware (Xem thêm Laravel middleware).

c:\xampp\htdocs\laravel-test>php artisan make:middleware StoreReferralCode
Middleware created successfully.

và thay đổi nội dung app\Middlewares\StoreReferralCode.php như sau:

<?php

namespace App\Http\Middleware;

use Closure;
use App\ReferralLink;
class StoreReferralCode
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $response = $next($request);

        if ($request->has('ref')){
            $referral = ReferralLink::whereCode($request->get('ref'))->first();
            $response->cookie('ref', $referral->id, $referral->lifetime_minutes);
        }

        return $response;
    }
}

Sau đó đăng ký middleware này như một global middleware bằng cách thêm vào trong file app\Http\Kernel.php:

'web' => [
    ...
    \App\Http\Middleware\StoreReferralCode::class,
]

Phương thức handle() của middleware StoreReferralCode sẽ thực hiện kiểm tra tất cả các request nếu có tham số ref sẽ tìm kiếm

Bước 4: Tạo Event và Listener quản lý các hành động người dùng

Khi hành động như mong muốn được người dùng thực hiện (ở đây là đăng ký tài khoản mới), chúng ta thực hiện tạo ra một event và dùng listener để bắt sự kiện đó, trong listener sẽ thực hiện tặng thưởng cho người dùng. Thêm cấu hình vào EventServiceProvider.php trong thư mục app\Providers:

    protected $listen = [
        ...
        'App\Events\UserReferred' => [
            'App\Listeners\RewardUser',
        ],
    ];

Sau đó sử dụng lệnh artisan event:generate để tự động tạo ra Event và Listener (Xem thêm Quản lý sự kiện với Laravel Event để hiểu hơn về câu lệnh này):

c:\xampp\htdocs\laravel-test>php artisan event:generate
Events and listeners generated successfully!

Thay đổi nội dung của Event app\Events\UserReferred.php:

<?php

namespace App\Events;

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 UserReferred
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public $referralId;
    public $user;
    /**
     * Create a new event instance.
     *
     * @return void
     */
    public function __construct($referralId, $user)
    {
        $this->referralId = $referralId;
        $this->user = $user;
    }

    /**
     * Get the channels the event should broadcast on.
     *
     * @return Channel|array
     */
    public function broadcastOn()
    {
        return new PrivateChannel('channel-name');
    }
}

Thêm nội dung của app\Listener\RewardUser.php:

<?php

namespace App\Listeners;

use App\Events\UserReferred;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;

class RewardUser
{
    /**
     * Create the event listener.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Handle the event.
     *
     * @param  UserReferred  $event
     * @return void
     */
    public function handle(UserReferred $event)
    {
        $referral = \App\ReferralLink::find($event->referralId);
        if (!is_null($referral)) {
            \App\ReferralRelationship::create(['referral_link_id' => $referral->id, 'user_id' => $event->user->id]);
            // Tặng thưởng cho cả người share link và người sử dụng link
            if ($referral->program->name === 'Bonus Credits') {
                // Người chia sẻ link
                $provider = $referral->user;
                $provider->addCredits(15);
                // Người sử dụng link
                $user = $event->user;
                $user->addCredits(20);
            }
        }
    }
}

Ok, tiếp theo chúng ta sẽ tạo ra một event app\Events\UserReferred.php khi người dùng đăng ký bằng cách can thiệp vào phương thức create() của RegisterController.php nằm trong app\Http\Controllers\Auth:

/**
     * Create a new user instance after a valid registration.
     *
     * @param  array  $data
     * @return User
     */
    protected function create(array $data)
    {
        $user = User::create([
            'name' => $data['name'],
            'email' => $data['email'],
            'password' => bcrypt($data['password'])
        ]);

        event(new \App\Events\UserReferred(request()->cookie('ref'), $user));
        return $user;
    }

Bước 5: Thêm view và route cho referral system

Thêm route vào routes/web.php:

Route::get('/referral', function(){
    return view('fontend.referral');
});

Tạo view referral.blade.php trong resources/views/fontend:

@extends('layouts.default')

@section('title', 'Referral system - Allaravel.com')

@section('content')
    @forelse(auth()->user()->getReferrals() as $referral)
        <h4>
            Chương trình: {{ $referral->program->name }}
        </h4>
        <code>
            Referral link: {{ $referral->link }}
        </code>
        <p>
            Số user sử dụng referral link: {{ $referral->relationships()->count() }}
        </p>
    @empty
        Không có chương trình referral nào!
    @endforelse 
@endsection

Thêm menu Referral System vào resources/views/layouts/menu.php:

<!-- Fixed navbar -->
<nav class="navbar navbar-default navbar-fixed-top">
    <div class="container">
        <div class="navbar-header">
            <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="/">All Laravel TEnv</a>
        </div>
        <div id="navbar" class="collapse navbar-collapse">
            <ul class="nav navbar-nav">
                <li class="active"><a href="/">Trang chủ</a></li>
                <li class="dropdown">
                    <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Ví dụ <span class="caret"></span></a>
                    <ul class="dropdown-menu">
                        <li><a href="/first-blade-example">Ví dụ Blade 1</a></li>
                        <li><a href="/second-blade-example">Ví dụ Blade 2</a></li>
                        <li><a href="{{ URL::to('/referral') }}">Referral System</a></li>
                    </ul>
                </li>
                {!! Form::open(array('url' => '/search', 'class' => 'navbar-form navbar-left', 'method' => 'get')) !!}
                    <div class="form-group">
                        {!! Form::text('keyword', '', array('class' => 'form-control', 'placeholder' => 'Nhập từ khóa...')) !!}
                    </div>
                    {!! Form::submit('Tìm kiếm', array('class' => 'btn btn-default')) !!}
                {!! Form::close() !!}
                <li><a href="/contact">Liên hệ</a></li>
                @if(Session::has('login') && Session::get('login') == true)
                <li class="dropdown">
                    <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Xin chào {{ Session::get('name') }} <span class="caret"></span></a>
                    <ul class="dropdown-menu">
                        <li><a href="/logout">Đăng xuất</a></li>
                    </ul>
                </li>
                @endif
            </ul>
        </div><!--/.nav-collapse -->
    </div>
</nav>

Kiểm tra kết quả hoạt động hệ thống referral

Trước khi đi vào kiểm tra hoạt động hệ thống referral, chúng ta cùng xem qua luồng hoạt động của nó.

Xây dựng hệ thống referral trong Laravel

Các bước thực hiện kiểm thử cũng sẽ tuân thủ theo hoạt động mô tả ở luồng hoạt động hệ thống ở trên: Đầu tiên, đăng nhập vào hệ thống laravel.dev tại http://laravel.dev/login, sau khi đăng nhập chuyển sang menu Ví dụ->Referral System. Nhưng trước tiên chúng ta cần tạo ra một chương trình giới thiệu bán hàng trước đã do hiện tại bảng referral_programs đang chưa có bản ghi nào. Sử dụng Laravel Tinker để nhập nhanh một bảng ghi (Nếu bạn chưa biết Tinker là gì xem thêm Công cụ debug và kiểm thử trong Laravel):

c:\xampp\htdocs\laravel-test>php artisan tinker
Psy Shell v0.8.3 (PHP 5.6.20 ΓÇö cli) by Justin Hileman
>>> App\ReferralProgram::create(['name'=>'register', 'uri' => 'register']);
=> App\ReferralProgram {#714
     name: "register",
     uri: "register",
     updated_at: "2017-04-24 08:24:42",
     created_at: "2017-04-24 08:24:42",
     id: 1,
   }
>>> exit
Exit:  Goodbye.

Xây dựng hệ thống referral trong Laravel

Như vậy, thành viên này đã có referral link, thành viên này muốn kiếm tiền có thể đưa đường dẫn này vào bất kỳ đâu: – Mạng xã hội Facebook, Google Plus, Twitter, Github…

  • Website: Blog, trang tin tức…
  • Email: Gửi email trong nội dung có đường dẫn này.

Bất kỳ ai khi thực hiện click vào đường dẫn này, hệ thống sẽ ghi xuống máy tính của họ một cookie chứa id bản ghi trong referral_links, cookie này có thời gian hoạt động mặc định là 7 ngày, do trong phần tạo bảng referral_programs chúng ta để là:

$table->integer('lifetime_minutes')->default(7 * 24 * 60);

Trong khoảng thời gian này, nếu người dùng trên thực hiện đăng ký tài khoản, referral system sẽ biết do đã cài đặt các Event và Listener. Khi đó tùy thuộc vào chương trình giới thiệu bán hàng chúng ta sẽ xử lý trả thưởng cho cả người chia sẻ link và người dùng link trong phương thức hanlde() của Listener.

/**
 * Handle the event.
 *
 * @param  UserReferred  $event
 * @return void
 */
public function handle(UserReferred $event)
{
    $referral = \App\ReferralLink::find($event->referralId);
    if (!is_null($referral)) {
        \App\ReferralRelationship::create(['referral_link_id' => $referral->id, 'user_id' => $event->user->id]);
        // Tặng thưởng cho cả người share link và người sử dụng link
        if ($referral->program->name === 'Bonus Credits') {
            // Người chia sẻ link
            $provider = $referral->user;
            $provider->addCredits(15);
            // Người sử dụng link
            $user = $event->user;
            $user->addCredits(20);
        }
    }
}

Để kiểm tra các công đoạn này, chúng ta mở một trình duyệt khác và dán vào đường dẫn referral ở trên (Trong bài viết này, người chia sẻ link dùng Chrome, người dùng link sử dụng Firefox):

Xây dựng hệ thống referral trong Laravel

Khi thực hiện đăng ký xong, quay trở lại với người chia sẻ link chúng ta sẽ thấy số lượng người sử dụng link tăng lên:

Xây dựng hệ thống referral trong Laravel

Ok, như vậy referral system của chúng ta đã hoạt động tốt. Với khung ứng dụng này, chúng ta hoàn toàn có thể thực hiện các hệ thống referral với các sự kiện phức tạp hơn.

Bài viết gốc được đăng tải tại allaravel.com

Có thể bạn quan tâm:

Xem thêm Việc làm laravel hấp dẫn trên TopDev