Multiple user models with Laravel Jetstream
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 :)
Comments
MD. Rezaul Islam:
Same type of Issue! I'm implementing a project where Intake & Trainee [extends User model using parental] models are many2many in relationship. I can access intakes from trainees but when I try to access trainees from intakes it shows that intake_trainee table is not present. I need some light here!
MD. Rezaul Islam:
Saved me! I bored with that package!
nerdroid23:
That's a very good tip, thank you very much! I am trying to implement this in a project and every was running smoothly until i had to writes tests. The issue is that whenever the tests run, for some reason, the helper methods always return null. Have you encountered this issue? Would you happen to have any idea on how to handle this, if yes would you be willing to share it with me? Thank you for your time!