Notice: Trying to get property 'user_login' of non-object in /home/forge/jameswmontgomery.com/public/wp-content/plugins/wordpress-seo/frontend/schema/class-schema-utils.php on line 26

How to Add Referral Tracking System with Laravel

Referral affiliate tracking seemed like something that would be a pretty common development addition to Laravel, but I did not find much in the way of tutorials to guide me 100% of the way there.

This tutorial will walk you through creating functionality in Laravel that will allow your users to share an affiliate link that will give them credit for new users that register with your site. The principles described here should allow you to extend this tutorial to fit other referral needs.

Many thanks to Andy Brudtkuhl’s guide that brought me 85% of the way there; here is the extra 15%:

This tutorial was created with Laravel 5.5 on a fresh installation.

1. Setup the DB & Migrations

We’ll need to have at least the base auth scaffolding implemented

php artisan make:auth

Next, let’s generate a new migration that will add a new column to our User table.

php artisan make:migration AddReferredByToUsersTable

In this migration, just add a nullable integer called referred_by. We’ll just be setting the referrer’s user id as the referred_by value for simplicity’s sake.

Schema::table('users', function(Blueprint $table)
{
  $table->integer('referred_by')->nullable();
});

and make sure to drop it nicely

Schema::table('users', function(Blueprint $table)
{
  $table->dropColumn('referred_by');
});

Then, run your migration and check your DB to see that the columns are all set.

php artisan migrate

Protip: Make sure you add referred_by to the $fillable array in the User model (app/User.php), otherwise you’ll see some breakage.

2. Generate Referral Links

Easy peasy, just echo out the current logged-in user’s id appended to your url. The middleware we’ll build can catch it anywhere on the site, so feel free to customize this or build it into a helper function.

Stick this inside a view of your choosing.

@auth //only show for logged in users
  <input type="text" readonly="readonly"
         value="{{ url('/') . '/?ref=' . Auth::user()->id }}">
@endauth

The ?ref value will be what the middleware checks for.

3. Bake Cookies with Middleware

Now let’s create a new cookie if someone visits the referral link. We’ll later use this cookie to update the database during user registration.

For those that aren’t familiar with it, middleware basically runs as a filter for HTTP requests.

This means we can add new middleware that will check to see if we’ve added ?ref to the url and then set a cookie if we don’t have one already.

php artisan make:middleware CheckReferral

After we create our middleware, we need to register it in the kernel (app/Http/Kernel.php). We can specify certain routes or add it to a middleware group, but for this case a simple global registration is sufficient.

Add "\App\Http\Middleware\CheckReferral::class" to the end of the $middleware array.

Now, let’s create that cookie by adding some simple functionality to the new middleware file.

Replace the default handle() functionality with this:

$response = $next($request);

// Check that there is not already a cookie set and that we have 'ref' in the url
if (! $request->hasCookie('referral') && $request->query('ref') ) {
  // Add a cookie to the response that lasts 5 years (in minutes)
  $response->cookie( 'referral', encrypt( $request->query('ref') ), 525600 );
}

return $response;

Protip: By default, Laravel uses encryption in cookies. Use encrypt() to pass values to cookies, otherwise you won’t be able to retrieve them later on. Otherwise, you’ll need to add the cookie name to the $except array in the EncryptCookies (app/Http/Middleware/EncryptCookies.php) middleware.

Now, our registered middleware will check the url for ?ref and create a cookie.

Try it now: Navigate to a the referral url you generated in your view earlier. Then, check your developer tools to see if the cookie has been set. In Chrome, this is currently under the Application tab of the Developer Tools.

4. Assign the Cookie Value During Registration

So, now that our User model can have a referrer_id assigned to it and we have the referral cookie set, let’s finish this up by adding the referral cookie value to the referrer_id during user registration.

Fix up the default create() function in RegisterController (app/Http/Controllers/Auth/RegisterController.php) to look like this:

protected function create(array $data)
{
    $referred_by = Cookie::get('referral');

    return User::create([
        'name' => $data['name'],
        'email' => $data['email'],
        'password' => bcrypt($data['password']),
        'referred_by' => $referred_by,
    ]);
}

If you’re feeling protective, look into validating that $referred_by is a real user id. Remember, this value can be null and by default Cookie::get() will return null if no value is detected.

You’ll need to import the Cookie facade here to use that function.

use Illuminate\Support\Facades\Cookie;

Try it now: After navigating through a valid affiliate url (and confirmed the cookie is set properly), register a new user and confirm that the referrer_id has been set in the database for the new user.

Now what?

Well, that’s all for the sake of this article. Getting into awarding referrals tends to split dramatically from here on out depending on what you’re trying to accomplish.

At the very least, you have a foundation where you at least know who referred who.

Let me know if you have any questions or improvements in the comments below, particularly if you:

  1. Know how to register the Middleware to automatically encrypt cookies without the use of encrypt()
  2. Feel strongly that we should use a hashed user id for referral links.
  3. Got lost anywhere along the way.

15 comments

  1. This Code worked just perfect for me!

    I had to code a referral system for our website so that affiliates could get their 20% commision somehow.

    If somebody of you workes with the $fillable function, don´t forget to place
    protected $fillable = [
    'referred_by',
    ];

    P.S.: Big thank to the Author of this Blog! Keep up the good work!

  2. This code works, but the only issue that I see is if you refer a user then let’s say you sign out and try to signup without a referring a user with the route: localhost:8000/register. What I notice is that the cookie itself gets assigned to the referred_id instead of it being null. What’s the solution to that?

    1. Hey Mike, I’d recommend debugging in the create() method of RegisterController. It would appear that Cookie::get() is not returning null in the expected way.

    1. Is referred_by set in the database? If not, check the data connection there first. If it’s returning NULL, that’s where I would look. Otherwise, how are you retrieving the data?

    2. please logout and register with new account you can see refferd with the id of first account in the data-base! becz you are referring to another person so while he register and login he will be getting the refferal id of first account with which we have reated refferel link!

  3. I’m having a similar issue here, when I refer the first user it works just fine, but when the user I have referred uses their link, it shows the userId of the first referrer, not theirs, I think the problem is of the cookie, how do I fix that, anyone, who can assist.

    1. I would recommend creating a deleteReferralCookie method and using it within create(). Have you confirmed the issue is replicated in private browsing?

  4. Nice Post!
    What about adding some more info about multiple levels? like: who referred by your referral up to the 5th level 🙂

    This is some sort of a multi-level marketing, i guess…

    Anyways, thanks a lot for this!

    1. Hmm, you’d likely have all the data available right now with this data architecture. Write a method to getAllReferers() that accepts a user ID and returns an array of user objects that were referred by that user. You can then recursively use this method on each of the returned users, counting/listing all the way. This might not scale very well, but the principal is there if you wanted to run chron batch jobs or leverage a new table.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.