Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

kiasaty's avatar

Best way to handle different user roles with specific attributes

Hi.

I'm looking for the best way to handle multi user roles to use it on my projects. Currently, I'm working on a project with following roles: admin, doctor, secretary, and patient.

attributes like first_name, last_name, phone_number, password are the same. but followings are not:

  • for doctor: secretary_id, specialty.
  • for secretary: doctor_id.
  • for patient: medical_number.

how can I handle this? I want to have one User model. should I have sub tables for doctor and secretary and patient?

I also have challenges in defining relationships in the User model. for example:


    public function workingOurs()
    {
    if($this->isDoctor()) {
        return $this->hasMany('App\WorkingHours', 'doctor_id');
    }
    }

    public function doctor()
    {
    if($this->isDoctor()) {
            return $this->hasOne('App\User');
    }
    }

as you see, I have to check if the user has the right role to access the relationship, which is not a good way.

Whats is the best way to handle multi-role in Eloquent?

0 likes
29 replies
bugsysha's avatar
  • Every role can login and use system?
  • Every role has different UI/features?
jlrdw's avatar

Authentication just means someone is logged in. You need authorization to to determine what a logged in user can or cannot do.

I suggest you also look at Spatie: https://github.com/spatie/laravel-permission

If you are familiar with RBAC already you could also custom code authorization, but custom is for highly experienced programmers only. https://gist.github.com/jimgwhit/ed44a6c81815804f1ab910ce9eb88d84

// Bob has roles assigned admin and doctor 
// A method in controller Bob wants to access
// This method (function) requires the role to be doctor
// YOU NEED TO PROGRAM IN   (However you need to)

// Is doctor one of Bobs roles.

if doctor is not one of Bobs stored roles
    redirect where ever

rest of method since access is allowed

And use scopes to fine tune a query for a user seeing their own data.

Learn how to work with the

Auth::user()->id;

It is the key (main thing) to use to ensure a user is seing their own data. It has to match the FK in another table.

Like when building a query scope:

            $userid = Auth::user()->id;
            $query->where('ownerid', '=', $userid);

A spatie exampe:

spatie example:
public function update(Request $request, Post $post) {
    if ($post->author !== auth()->user()->id || auth()->user()->cannot('edit posts'))
        abort(404);// or some other 
    }
}

So no matter the RBAC, learn all about that auth()->user()->id.

Note

There is a fairly large learning curve with authorization, even Jeffrey in a video says it tricky at first but gets easier as you learn it.

bugsysha's avatar

@jlrdw why use that package when he has Policies out of the box which have more readable syntax than that package?

Also I think he is more asking about code and database architecture than which specific implementation to use for permissions.

Ofc, I might be wrong :D

1 like
cookie_good's avatar

What I do is I have column in users for roles. I assign a single letter like a for admin, u for user, etc. Then I use the Auth facade to pull the user information and I use logic like:

If user_role == a

then the user can see this

else

the user sees this

it works in my use case. I'm going to start implementing this in routes/web.php.

I don't think this rig would work in a large scale operation.

jlrdw's avatar

@bugsysha I only said "I suggest you also look at Spatie", as an option, what @kiasaty uses is their choice. I was just showing other options that a lot of folks here also mention.

bugsysha's avatar

@jlrdw my question was not well formed. I'm asking is there something special that package provides compared to builtin option?

jlrdw's avatar

@bugsysha I know higher level members like snapey has recommended it. https://laracasts.com/discuss/channels/laravel/roles-and-permissions-2

I have my own custom authorization. So I don't use it, but I have looked it over in detail and it seems to me easier than in built. But just my opinion.

I use other Frameworks as well so I don't write framework biased authorization I use authorization that works in other languages and Frameworks as well.

However I would never recommend to a newcomer to programming to attempt their own until they have learned it very well first.

Custom authorization is more for someone who's been programming quite a while and fully understands how to protect data.

See my gist it's just some simple examples but of course I have a lot more methods than that, it was just meant for simple examples only.

kiasaty's avatar

Thank you for answering,

but I don't have any problem with authorization.

Actually I'm asking about database design when different user roles have their own specific attributes.

When users share the same attributes, I create a user table with a "role_id" column.

But what about when some user roles also have their own additional attributes?

Username and password are shared attributes and I'm putting them in the user table. So there is no problem in authenticating or authorizing users.

I'm looking for the best database and system design to handle role specific attributes.

one approach is to create sub tables like doctor_user, secretary_user, patient_user for additional attributes. but I don't know if this fits good in Eloquent.

@jlrdw @bugsysha

jlrdw's avatar

Most folks in that case would setup a related roles table. Many past answers on roles and permissions and how others have set it up, if you copy and paste this in google, you will get several past articles on it:

site:laracasts.com roles and permissions setup
bugsysha's avatar

@kiasaty do the following:

  • if all doctors have same attributes then either put it in some separate table or config file
  • if all doctors have different attributes put it in json column on users table which you can call features, options, settings, or who knows how.

Also for the example from your initial post doctor: secretary_id, specialty it is possible to create polymorphic table which accepts those different models and relates them to the doctor. User with role doctor has many of those.

Apply all above to every role you have and that is it.

iabrar95's avatar

hello, did you solve the problem? because i am facing the same problem but i only have a doctor and an patient. when i read the comments below your question i did not quite find the answer. if you ever did find a solution it would be nice to share it.

kiasaty's avatar

Hi @iabrar95

Actually I haven't found a super good solution. It sits in a corner of my mind to find one of the best solutions.

But I'll share with you what I have found.

There are two senarios:

A. you don't have different table columns for different roles, but the different roles have different relationships. If this is the scenario, you can create a Doctor and a Patient model which extend the user model, and put the relationships in them.

B. different roles have their own particular columns in addition to the identical columns. in this scenario I have came to two solutions.

  • use "table inheritance" feature in Postgresql.

Postgres has a feature that some tables can inherit other tables. kinda like OOP. so your tables should be like this:

CREATE TABLE users (
  name       text,
  username text,
  password  text,
);

CREATE TABLE doctors (
  specialty      text
) INHERITS (users);


CREATE TABLE patients (
  medical_number      text
) INHERITS (users);

read more: https://www.postgresql.org/docs/10/tutorial-inheritance.html

  • use eloquent Polymorphic relationship.
CREATE TABLE users (
  userable_type       varchar,
  userable_id    id
  username varchar,
  password  varchar,
);

CREATE TABLE doctors (
  specialty      varchar
);


CREATE TABLE patients (
  medical_number      varchar
);

read more: https://laravel.com/docs/7.x/eloquent-relationships#polymorphic-relationships

these are the two solutions that I have found to this day. I don't know what is the best solution, and I'm still in search of it, but not actively.

I hope this helps you :)

If you found any other solutions, please share it here on this page.

Thanks,

2 likes
copyandpaste's avatar

Option #1

hmmm.. how about to just have tables like doctor_details and patient_details

then have a relationship called details

public function details(){
 if($this->role === 'doctor'){
  return $this->hasOne(DoctorDetails::class);
 } else if ($this->role === 'patient') {
  return $this->hasOne(PatientDetails::class);
 }
}

The problem with this is that when you're going to use $user->details->specialty which is only available in the doctor_details table but you have a patient-user then you'd probably get an error or a null value

Option #2

An alternative solution is to just have 2 relationship methods called DoctorDetails and PatientDetails now of course on your view, it would be easy to know that you're accessing the doctor_details table since that is the name of the relationship... therefore $user-> DoctorDetails->specialty should have a value.

The problem with this is that YOU know that the PatientDetails method should not exists since the user is not a patient why should it have a relationship to the patient_details table?

the problem now is in our head, why? it's how we think about the system that is the problem...

the user doesn't really care if it is a doctor as long as it has a relationship to the doctor_details then it will have data for doctor_details. the user doesn't care if it's a patient as long as it has a relationship to the patient_details table then it will have data for patient_details.

so let's say in the future your system would allow multiple roles... then maybe let's say...

a doctor from that hospital has suffered an accident, he can still use his account and add the role as a patient...

the user is now a doctor and at the same time a patient...

Notes

you don't connect relationships specific from the doctor directly to the user's table.

example doctor has many patients that relationship should be found on the DoctorDetails and PatientDetails table.

$user->DoctorDetails->patients OR $user->PatientDetails->doctor

bugsysha's avatar

That sounds like a pretty overcomplicated solution that will probably produce bunch of issues at larger scale and once requirements complexity rises.

kiasaty's avatar

@bugsysha which solution? Postgres table inheritance or eloquent polymorphic relationship?

what do you think is the best way to tackle this challenge? because I'm exactly looking for a solution which can survive the rise of requirements complexity.

bugsysha's avatar

In my mind proposed A and B are both overcomplicated solutions. Easiest one is single responsibility principle translated to database means to have a single column to describe the type of the user on the users table.

kiasaty's avatar

@bugsysha But where should the doctor specific column and patient specific column be stored?

doctors have a column named "specialty", and patients have a column named "medical_number". this is an example, they could have more particular columns.

it is not a good idea to put the "specialty" and "medical_number" columns in users table and leave "specialty" column for patients null and vice-versa.

It is not a good idea either to create two tables named "doctors" and "patients" and repeat all the shared columns in them.

So what is the solution?

1 like
Long-Blade's avatar

@snapey That was very exiting!! Thank you for that sir! :)

This also can work with many to may relationship! Like a user can have many type_profile/s! Lets say we have a platform providing 2 services a blog and an eshop ! So we would like the same user profile to have both a 'customer_profile' & a 'author_profile'!

bugsysha's avatar

@kiasaty if your data is totally different for different types of users then I would go with JSON profile column on users table and use custom casts.

https://laravel.com/docs/7.x/eloquent-mutators#custom-casts

The reason why I would use custom casts over something like what @snapey provided is that I have a feeling that you will never start a query from your doctors or patients table and it is easier if you have all the data inside one table.

Also the queries are simpler since you will not have to provide a callback to see the content of related table, instead you would use whereJsonContains.

chaima67's avatar

Hey hope you're doing well .Please did you find a solution for this case

chaima67's avatar

@martinbean i saw that it's polymorphic one to one relationship but i found it hard to deal with such a case

kiasaty's avatar

@chaima67 I would say just keep it simple and create separate tables with one-to-one relationship.

For example in case of doctors table the fields would be: id, user_id, secretary_id, specialty

1 like
chaima67's avatar

@kiasaty I start doing this but i'm facing real problems with using inheritance I found it very difficult Can you please give me some advices about how it works

kiasaty's avatar

@chaima67 extend all the models from the main eloquent model. In other words Create standalone models, Do NOT do something like Doctor extends User for example.

Behave them like totally different models, with just a one-to-one relationship.

1 like

Please or to participate in this conversation.