How to use the Laravel invite codes package

Using invite codes for registrations with the Laravel invite codes package.

Having invite codes for your web application means you can limit who you want to register.

Laravel invite codes

The Laravel Invite Codes package can be installed with

composer require mateusjunges/laravel-invite-codes

Then migrate the invite codes database table with

php artisan migrate

Creating invite codes

The invite codes have several values to them, some are optional.

Firstly is the invite code itself in a string format, there is an expiry date, a max uses amount and restrict usage to a certain email address.

A Laravel CRUD for an admin to manage invite codes is straightforward, here are the create (store) parts

invites/create.blade.php

<form method="post" action="{{route('invites.store')}}">
    @csrf
    <div class="row mb-3">
        <div class="col-lg-4 col-md-4 col-12">
            <div class="input-group mb-3">
                <span class="input-group-text">Code</span>
                <input type="text" class="form-control" name="code"
                       minlength="3" maxlength="125" value="{{\Illuminate\Support\Str::random(12)}}" required>
            </div>
        </div>
        <div class="col-lg-4 col-md-4 col-12">
            <div class="input-group mb-3">
                <span class="input-group-text">Max Uses</span>
                <input type="number" class="form-control" name="uses"
                       min="1" max="99" value="1" step="1" required>
            </div>
        </div>
        <div class="col-lg-4 col-md-4 col-12">
            <div class="input-group mb-3">
                <span class="input-group-text">Expires</span>
                <input type="date" class="form-control" name="expires" value="">
            </div>
        </div>
    </div>
    <div class="row mb-3">
        <div class="col-lg-6 col-md-6 col-12">
            <div class="input-group mb-3">
                <span class="input-group-text">Valid only for email address</span>
                <input type="text" class="form-control" name="email" minlength="6" maxlength="125">
            </div>
        </div>
    </div>
    <input type="submit" value="Create" class="btn btn-light-success mt-2 px-5">
</form>

The Invites model:

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Invite extends Model
{
    use HasFactory;

    protected $table = 'invites';

    protected $fillable = ['code', 'max_usages', 'to', 'uses', 'expires_at'];

}

Here is the store method in the InvitesController where the invite code and parameters are inserted into the database:

public function store(Request $request)
    {
        $request->validate([
            'code' => 'string|required|min:3|max:125',
            'uses' => 'integer|required|min:1|max:99',
            'expires' => 'date|nullable',
            'email' => 'email|nullable|sometimes|min:6|max:125'
        ]);

        try {

            Invite::create([
                'code' => $request->code,
                'max_usages' => $request->uses,
                'to' => $request->to ?? null,
                'expires_at' => $request->expires ?? null
            ]);

        } catch (\Illuminate\Database\QueryException $e) {

            return redirect()->route('invites.create')
                ->withInput($request->input())
                ->with('error', 'Error creating invite code.');
        }

        return redirect()->route('invites.index')
            ->with('success', 'Invite code created successfully.');

}

When being validated an expires and/or email can be nullable (excluded).

Validating an invite code

Firstly an input is needed for an invite code to be submitted when registering this is edited in at views/auth/register.blade.php

<div class="col-12">
    <div class="mb-3">
        <label class="form-label">Invite code</label>
        <input id="invite" type="text"
               class="form-control @error('invite') is-invalid @enderror"
               name="invite"
               value="{{ old('invite') }}" required autocomplete="username"
               autofocus>
        @error('invite')
        <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
        @enderror
    </div>
</div>

In the RegisterController the validation needs to include the invite code being checked, this is done with redeem()in the validator method after the validation on name, email and password

try {
    InviteCodes::withoutEvents()->redeem($data['invite']);
} catch (\Exception) {
    throw ValidationException::withMessages(['invite' => 'Invite code is invalid']);
}

This above example catches any exception therefor if the code doesn’t exist, or it’s the wrong email or it has been used too many times it will all throw the same “Invite code is invalid” response.

To have a custom response go here and get the exception type an example being if the code has been used past its maximum amount:

} catch (\Junges\InviteCodes\Exceptions\SoldOutException) {
   throw ValidationException::withMessages(['invite' => 'Invite code has been used too many times']);
}

Lastly if you wanted to clear expired codes from the database there is a handy artisan command

\Illuminate\Support\Facades\Artisan::call('invite-codes:clear');