Multiple user models with Laravel Jetstream

Published in Programming on Apr 1, 2021

How to use multiple user models with Laravel Jetstream using Caleb Porzio's parental package.

When I was developing StackJobs, I wanted to use Laravel Jetstream, but also multiple User models. Developer for developers, and Recruiter for recruiters.

Each one would have its own relationships, and the relationships from the other models (e.g. Skill and Stack) would reference these specific models, not generic User.

That's tough, because Jetstream assumes the existence of a single model — User. (Or User and Team if you're also using teams.)

Luckily, Caleb Porzio created the perfect package for this: parental.

Implementation

Migrations

Add a type string column to your users table.

$table->string('type');

Models

Then, create your custom models, and implement the HasParent trait on them:

class Developer extends User
{
    use HasParent;
  
    // developer relations...
}

class Recruiter extends User
{
    use HasParent;
  
    // recruiter relations...
}

Then, make these changes to your User model:

class User extends Authenticable
{
    use HasChildren;

    protected $childTypes = [
        'developer' => Developer::class,
        'recruiter' => Recruiter::class,
    ];
  
    public function isDeveloper(): bool
    {
        return $this->type === 'developer';
    }

    public function isRecruiter(): bool
    {
        return $this->type === 'recruiter';
    }
}

The traits add parental support, and the methods are useful helpers for doing checks like auth()->user()->isDeveloper().

Helpers

You may also want to create a global helper.

I like to use developer() to fetch the current developer. Same with recruiter().

If the current is not a developer, the developer() helper will return null. This means that we can do nice things like:

<nav>
	@if(developer())
		<div>Some item</div>
		<div>Some item</div>
    @elseif(recruiter())
		<div>Some item</div>
		<div>Some item</div>
    @else
        ...
    @endif
</nav>

You can also use the helpers in your code, such as:

abort_unless(recruiter(), 403);

To add these helpers, create a src/helpers.php file:

function developer(): ?Developer
{
    $user = auth()->user();

    if ($user instanceof Developer) {
        return $user;
    }

    return null;
}

function recruiter(): ?Recruiter
{
    $user = auth()->user();

    if ($user instanceof Recruiter) {
        return $user;
    }

    return null;
}

And add the path to your autoload.files section in composer.json.

"autoload": {
    "psr-4": {...},
    "files": [
      	"src/helpers.php"
    ],
}

Registering

You'll also want to customize the registration page.

For example, only create a team if the user is of a certain type, and accept the user type in a <select>.

In that case you would validate it and store it in the App\Actions\Fortify\CreateNewUser class:

'type' => ['required', Rule::in(['developer', 'recruiter'])],

Closing

And that's it. Parental will automatically return the correct class from auth()->user() calls, and you can add relations to your specific user models.

Hope this is useful :)

Newsletter

You will be notified whenever I publish an article about the topics you selected. Unsubscribe anytime.

Comments

Your comment will appear once it's approved (to fight spam).