Building a simple Referral System API with Laravel 11
Tags: beginners, php, laravel, api
Published: 2024-05-28 08:24:49


With the use of special referral codes, referral systems are an effective marketing technique that encourages current customers to promote business offerings. For those who are new to using Laravel 11 and want to handle user referrals through APIs, this tutorial will show you how to create a basic referral system API.

**Let's get started.**

Prerequisites:

- Basic understanding of PHP, Laravel concepts, and RESTful APIs
- A local development environment with Laravel 11 installed
- A database (e.g., MySQL) configured with Laravel

## Step 1: Project Setup and Modify the User Migration Schema

Create a new Laravel project using the Artisan command and navigate to the project directory. We'll need a table to store user information. Laravel provides a User model by default.

Go to the users migration and modify accordingly.

```
 Schema::create('users', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->rememberToken();
            $table->string('referral_code')->index()->unique();
            $table->unsignedBigInteger('referred_by')->nullable();
            $table->integer('referral_count')->default(0);
            $table->string('password');    
            $table->timestamps();

            $table->foreign('referred_by')->references('id')->on('users')->onUpdate('cascade')->onDelete('set null');
        });
```

Then run the php artisan migrate to run your migrations.

## Step 2: Define API route

We'll create two main API endpoints to manage referrals:

- POST /users - This endpoint will handle user registration and potentially generate a referral code for the new user.

- GET /users/{user}/referrals - This endpoint will retrieve referral data for a specific user

Enable API routing using the `install:api` Artisan command and define the route in your `routes/api.php` file.

**Note:**

The `install:api` command installs Laravel Sanctum, which provides a robust, yet simple API token authentication guard which can be used to authenticate third-party API consumers, SPAs, or mobile applications. In addition, the `install:api` command creates the `routes/api.php` file: read more [https://laravel.com/docs/11.x/routing](https://laravel.com/docs/11.x/routing).

```
Route::prefix('/users')->group(function () {
    Route::post('/', [UserReferralProgramController::class, 'store']);
    Route::get('/{user}/referrals', [UserReferralProgramController::class, 'fetchUserReferral']);
});
```
Consider using versioning in your API URL structure (e.g., /api/v1/users) to maintain flexibility for future changes. You may change the prefix by modifying your application's **bootstrap/app.php** file.

```
->withRouting(
        web: __DIR__ . '/../routes/web.php',
        api: __DIR__ . '/../routes/api.php',
        commands: __DIR__ . '/../routes/console.php',
        health: '/up',
        apiPrefix: 'api/v1',
    )
```

## Step 3: Define Methods in your Controller

Run the command `php artisan make:controller Api/UserReferralProgramController`
Goto **app\Http\Controllers\Api\UserReferralProgramController.php** and define the methods. Later, we shall return to the controller.

```
class UserReferralProgramController extends Controller
{
    /**
     * Store a newly created resource in storage.
     */
    public function store(UserStoreRequest $request): void
    {
    }

    /**
     * fetch user referrals.
     */
    public function fetchUserReferral(User $user): void
    {
    }
}
```

## Step 4: Utilize the service class(if applicable).
run the command `php artisan make:class Service/ReferralCodeGenerator` to create the class

```
<?php

namespace App\Service;

use App\Models\User;

class ReferralCodeGenerator
{
    /**
     * Generates a random referral code.
     *
     * This method generates a random string of specified length using the provided character set.
     * It then checks for existing codes in the database and regenerates if necessary.
     *
     * @return string The generated referral code.
     * 
     * @throws \Exception If an error occurs during code generation or validation.
     */
    public function generate(): string
    {
        $codeLength = 10;
        $characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
        $code = '';

        $code = substr(str_shuffle(str_repeat($characters, 5)), 0, $codeLength);

        return $this->checkAndRegenerate($code);
    }

    /**
     * Checks if a referral code already exists in the database and regenerates if necessary.
     *
     * This method takes a generated referral code as input and checks if it exists in the database.
     * If the code already exists, it regenerates a new code using the `generate` method and calls itself recursively to check again.
     * This process continues until a unique, non-existing code is found.
     *
     * @param string $code The referral code to be checked.
     * 
     * @return string A unique, non-existing referral code.
     * 
     */
    private function checkAndRegenerate(string $code): string
    {
        if (User::where('referral_code', $code)->exists()) {
            return $this->checkAndRegenerate($this->generate());
        }

        return $code;
    }

    /**
     * Finds a user by referral code and increments their referral count in a single database query.
     *
     * This method takes a referral code as input and attempts to find a matching user in the database.
     * If a user is found, it atomically increments their `referral_count` by 1 and retrieves the user's ID.
     * 
     * This method utilizes a single database query with Laravel's `update` method and raw expressions for efficiency.
     *
     * @param string $referralCode The referral code to be checked.
     * 
     * @return int|null The user ID if found, otherwise null.
     *
     */
    public function getUserIdByReferralCodeAndIncrementCount(string $referralCode): int|null
    {
        $userId = User::where('referral_code', $referralCode)->value('id');

        if ($userId) {
            $this->incrementReferralCount($userId);
            return $userId;
        }
        return null;
    }

    private function incrementReferralCount(int $userId): void
    {
        User::where('id', $userId)->increment('referral_count');
    }
}

```
**Code Explanation:**

The **ReferralCodeGenerator** class provides functionality to generate unique referral codes and manage user referral counts. Here's a summary:

1. **generate** Method: Creates a random 10-character referral code using a specified character set and ensures its uniqueness by checking against the database.
2. **checkAndRegenerate** Method: Recursively verifies the uniqueness of a referral code and regenerates it if necessary.
3. **getUserIdByReferralCodeAndIncrementCount** Method: Finds a user by referral code, increments their referral count atomically, and returns the user's ID.
4. **incrementReferralCount** Method: Increments the referral_count of a user by their ID.

This class ensures that generated referral codes are unique and facilitates updating user referral counts efficiently.

## Step 8: Modify our existing Controller

Let’s go back to our controller **app\Http\Controllers\Api\UserReferralProgramController.php** and make some modifications.

```
class UserReferralProgramController extends Controller
{

    /**
     * Create a new class instance.
     */
    public function __construct(public ReferralCodeGenerator $referralCodeGenerator)
    {
        //
    }

    /**
     * Store a newly created resource in storage.
     */
    public function store(UserStoreRequest $request): JsonResponse
    {

        $validatedData = $request->validated();

        $referredBy = null;

        if ($request->has('referral_code')) {
            $referredBy = $this->referralCodeGenerator->getUserIdByReferralCodeAndIncrementCount($request->referral_code);
        }

        $requestData = array_merge(
            $validatedData,
            [
                'referred_by' => $referredBy,
                'referral_code' => $this->referralCodeGenerator->generate()
            ]
        );

        $user  = User::create($requestData);

        return response()->json([
            'message' => 'user created',
            'data' => $user
        ], Response::HTTP_CREATED);
    }

    /**
     * fetch user referrals.
     */
    public function fetchUserReferral(User $user): JsonResponse
    {
        $referrals = User::where('referred_by', $user->id)->get();

        return response()->json([
            'message' => 'success',
            'data' => [
                'referrals_count' => $user->referral_count,
                'referrals' => $referrals
            ]
        ], Response::HTTP_OK);
    }
}

```
In our controller method:

- I Initialized the controller with a ReferralCodeGenerator instance, which is used for generating and validating referral codes.
- Also validated the incoming request data (name, email, password, etc.).
- If a referral_code parameter is present in the request, use it to find the referring user.
- Created a new User model instance with the provided data and generated code.
- Saved the user to the database and potentially update the referring user's referral count 
- Returned a successful response with relevant user information (excluding password) or an error response in case of validation issues or database errors.

**Remember**

This example has been simplified. Ensure that your system is production-ready by integrating _strong authentication methods_, _Database transaction_, _resource to transform the result_, _extensive error handling_, and extra features depending on your requirements.

Using Laravel 11, you can create a robust referral system API that facilitates user growth and acquisition for your application by following these instructions and experimenting with advanced features.

**Test our Endpoint on POSTMAN**

-  `{{url}}/api/v1/users` to store users

![register user with referal code](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wc825a7sa3snn7fe25jt.png)

- `{{url}}/api/v1/users/:userId/referrals` to fetch user referrals

![fetch user with their referrals](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zxicg54cgwxcxj6fty3f.png)

You can get the complete code from [https://github.com/quitenoisemaker/import_excel_file](https://github.com/quitenoisemaker/import_excel_file)