Code review at scale is broken. Here’s how Augment Code is fixing it.

Building a User Invitation System with Laravel

Published on by

Building a User Invitation System with Laravel image

In the not too distant past at ubisend, we needed to build a solution to provide our users the ability to invite additional users to manage their account.

This is a pretty common problem.

Think about a CMS. It’s not much use if you are the only person able to create new content and you certainly don’t want to share your password to allow someone to proof an article.

Similarly, imagine you are building a multitenanted application and want to give your users the ability to add additional members to their team.

Sure, you could build out some CRUD methods for managing users, but wouldn’t it better if you could allow those users to set their own passwords rather than having to send it to them by email, insecurely, in plaintext?

This article will walk you through one approach to building this functionality.

Housekeeping

This task could be carried out in a multitude of ways. At its simplest, we could create a new user record (flagging it inactive) and store a token which is then used in a link to activate the account.

Here, though, we will tackle the problem in a different way using an additional invites table where we will store the user’s email address and activation token. Upon activation, we will use this data to create the new user.

We’ll be using a fresh install of Laravel 5.4, and you will need to have configured your database and mail settings.

Migrations

A default Laravel installation will give us a leg-up for the user migration as it ships by default with the framework.

For this tutorial, remove the name and password fields from the migration:

public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->string('email')->unique();
$table->rememberToken();
$table->timestamps();
});
}

We do need to create a migration for our invites. On the console from the root of your project, run php artisan make:migration create_invites_table.

This command will create a new migration file in the database/migrations directory. We’ll need to define an incrementing ID, a field to store the new user’s email address and a field for the unique token the new user will use when accepting the invite.

public function up()
{
Schema::create('invites', function (Blueprint $table) {
$table->increments('id');
$table->string('email');
$table->string('token', 16)->unique();
$table->timestamps();
});
}
 
public function down()
{
Schema::drop('invites');
}

Now, on the console from the root of your project, run php artisan migrate.

The database is good to go.

Models

We will need to create Eloquent models for managing both our team and user records.

As with migrations, Laravel ships with a default Eloquent user model so no need to create one.

For the invites model, head back to the console, and from the root of your project run php artisan make:model Invite.

Take a look in the app directory, and you will see your newly created invite model.

All we need to do here is define the fillable fields as follows. This will allow us to mass assign properties when creating updating models.

protected $fillable = [
'email', 'token',
];

Routes

For this tutorial we’ll need to define three routes for the following scenarios:

  • Show the form to invite a new user
  • Process the form submission
  • Accept the invitation

In the app/routes/web.php file, add the following routes:

Route::get('invite', 'InviteController@invite')->name('invite');
Route::post('invite', 'InviteController@process')->name('process');
// {token} is a required parameter that will be exposed to us in the controller method
Route::get('accept/{token}', 'InviteController@accept')->name('accept');

Controller

The eagle-eyed readers will have noticed when defining the routes above we referenced InviteController to process the requests.

That controller can, again, be created using artisan. On the console from the root of your project, run php artisan make:controller InviteController.

Open up app/Http/Controllers/InviteController.php and define the following methods:

public function invite()
{
// show the user a form with an email field to invite a new user
}
 
public function process()
{
// process the form submission and send the invite by email
}
 
public function accept($token)
{
// here we'll look up the user by the token sent provided in the URL
}

Great, everything is set up. We can now flesh out these methods and make our invitation system come to life.

Business Logic

Here, we will work through each of the methods stubbed out above.

invite()

This is nice and simple. We need to return a view which presents a form to the user where they can enter the email address of the invitee.

The method will look something like this:

public function invite()
{
return view('invite');
}

Create the file resources/views/invite.blade.php. This view will need to contain a form that posts to /invite with an input called email:

// make use of the named route so that if the URL ever changes,
// the form will not break #winning
<form action="{{ route('invite') }}" method="post">
{{ csrf_field() }}
<input type="email" name="email" />
<button type="submit">Send invite</button>
</form>

process()

This is where the bulk of our work will be carried out.

As part of this method, we need to notify the new user that someone has sent them an invitation. To do that, let’s make use of Laravel’s mailables.

To start, we need to create a mailable class. On the console from the root of your project, run php artisan make:mail InviteCreated.

This will generate a new class called, you guessed it, InviteCreated in your app/Mail directory. Open up this class and update the constructor to accept an invite model and assign this as a public property.

use App\Invite;
 
public function __construct(Invite $invite)
{
$this->invite = $invite;
}

Now, to send the email, define a build method. This will determine who to send the email from and which view to use to generate the email.

public function build()
{
return $this->from('you@example.com')
->view('emails.invite');
}

Next, we need to generate said view file. Create the file resources/views/emails/invite.blade.php. We’ll keep this super simple:

<p>Hi,</p>
 
<p>Someone has invited you to access their account.</p>
 
<a href="{{ route('accept', $invite->token) }}">Click here</a> to activate!

“How do we have access to the $invite variable in our view,” I hear you cry! Laravel will automatically make all public properties of your mailable class available to your views.

Now, back to our InviteController, we will need to generate a unique random token which can be used to identify the new user when they accept the invitation. We will store this record in the invites table along with the email address provided by the person carrying out the invite.

use App\Invite;
use App\Mail\InviteCreated;
use Illuminate\Support\Facades\Mail;
 
...
 
public function process(Request $request)
{
// validate the incoming request data
 
do {
//generate a random string using Laravel's str_random helper
$token = str_random();
} //check if the token already exists and if it does, try again
while (Invite::where('token', $token)->first());
 
//create a new invite record
$invite = Invite::create([
'email' => $request->get('email'),
'token' => $token
]);
 
// send the email
Mail::to($request->get('email'))->send(new InviteCreated($invite));
 
// redirect back where we came from
return redirect()
->back();
}

Nice work! That’s our new invite stored, and the new user notified.

accept()

Finally, we need to allow our new users to accept the invite. This is what will happen when the user clicks the activation email we sent above.

Usually, you would probably want to capture a password and any other user details you may need at this point, but to keep this simple, we’re just going to check for the existence of the token and create the user accordingly.

Remember, the token in the URL will be passed to us as a parameter from Laravel.

use App\User;
use App\Invite;
use App\Mail\InviteCreated;
use Illuminate\Support\Facades\Mail;
 
...
 
public function accept($token)
{
// Look up the invite
if (!$invite = Invite::where('token', $token)->first()) {
//if the invite doesn't exist do something more graceful than this
abort(404);
}
 
// create the user with the details from the invite
User::create(['email' => $invite->email]);
 
// delete the invite so it can't be used again
$invite->delete();
 
// here you would probably log the user in and show them the dashboard, but we'll just prove it worked
 
return 'Good job! Invite accepted!';
}

Give It a Go!

Hit the invite URL in your browser (e.g. http://example.com/invite), enter the email address of the person you are inviting and submit the form.

Take a look in the invites table of your database; you should see new record has been created with a new unique token. Now check your email; you should have received a message containing a link to activate the new user which contains the token stored in the database.

Finally, click the link and you will be greeted with “Good job! Invite accepted!” which is a good sign. To check everything worked as expected, the invite record should no longer be in the database, but instead, a new user record should have appeared in the users table.

That’s a Wrap

Nice work! You have successfully implemented a user invitation system.

Although this is a simple example, it provides a good base to build from.

It would be trivial to extend this example to capture new user details at the point they accept the invitation as well as allowing invites to be revoked and/or re-sent.

Additionally, you could modify the code to support multitenancy or team/user relationships—though this would be somewhat more complex.

Joe Dixon photo

Founder and CTO of ubisend. Proud Father to two tiny heroes, Husband, developer, occasional globetrotter.

Cube

Laravel Newsletter

Join 40k+ other developers and never miss out on new tips, tutorials, and more.

image
Tinkerwell

Enjoy coding and debugging in an editor designed for fast feedback and quick iterations. It's like a shell for your application – but with multi-line editing, code completion, and more.

Visit Tinkerwell
Curotec logo

Curotec

World class Laravel experts with GenAI dev skills. LATAM-based, embedded engineers that ship fast, communicate clearly, and elevate your product. No bloat, no BS.

Curotec
Bacancy logo

Bacancy

Supercharge your project with a seasoned Laravel developer with 4-6 years of experience for just $3200/month. Get 160 hours of dedicated expertise & a risk-free 15-day trial. Schedule a call now!

Bacancy
Tinkerwell logo

Tinkerwell

The must-have code runner for Laravel developers. Tinker with AI, autocompletion and instant feedback on local and production environments.

Tinkerwell
Cut PHP Code Review Time & Bugs into Half with CodeRabbit logo

Cut PHP Code Review Time & Bugs into Half with CodeRabbit

CodeRabbit is an AI-powered code review tool that specializes in PHP and Laravel, running PHPStan and offering automated PR analysis, security checks, and custom review features while remaining free for open-source projects.

Cut PHP Code Review Time & Bugs into Half with CodeRabbit
Get expert guidance in a few days with a Laravel code review logo

Get expert guidance in a few days with a Laravel code review

Expert code review! Get clear, practical feedback from two Laravel devs with 10+ years of experience helping teams build better apps.

Get expert guidance in a few days with a Laravel code review
Kirschbaum logo

Kirschbaum

Providing innovation and stability to ensure your web application succeeds.

Kirschbaum
Shift logo

Shift

Running an old Laravel version? Instant, automated Laravel upgrades and code modernization to keep your applications fresh.

Shift
Harpoon: Next generation time tracking and invoicing logo

Harpoon: Next generation time tracking and invoicing

The next generation time-tracking and billing software that helps your agency plan and forecast a profitable future.

Harpoon: Next generation time tracking and invoicing
Lucky Media logo

Lucky Media

Get Lucky Now - the ideal choice for Laravel Development, with over a decade of experience!

Lucky Media
Lunar: Laravel E-Commerce logo

Lunar: Laravel E-Commerce

E-Commerce for Laravel. An open-source package that brings the power of modern headless e-commerce functionality to Laravel.

Lunar: Laravel E-Commerce
SaaSykit: Laravel SaaS Starter Kit logo

SaaSykit: Laravel SaaS Starter Kit

SaaSykit is a Multi-tenant Laravel SaaS Starter Kit that comes with all features required to run a modern SaaS. Payments, Beautiful Checkout, Admin Panel, User dashboard, Auth, Ready Components, Stats, Blog, Docs and more.

SaaSykit: Laravel SaaS Starter Kit

The latest

View all →
JSON:API Resource in Laravel 12.45 image

JSON:API Resource in Laravel 12.45

Read article
Caching With MongoDB for Faster Laravel Apps image

Caching With MongoDB for Faster Laravel Apps

Read article
Laravel 12.44 Adds HTTP Client afterResponse() Callbacks image

Laravel 12.44 Adds HTTP Client afterResponse() Callbacks

Read article
Handle Nested Data Structures in PHP with the Data Block Package image

Handle Nested Data Structures in PHP with the Data Block Package

Read article
Detect and Clean Up Unchanged Vendor Files with Laravel Vendor Cleanup image

Detect and Clean Up Unchanged Vendor Files with Laravel Vendor Cleanup

Read article
Seamless PropelAuth Integration in Laravel with Earhart image

Seamless PropelAuth Integration in Laravel with Earhart

Read article