kevinv's avatar

User relationships based on role

The debate over using a single user table vs a table for different types of user is something that I've been thinking about for my application for a long time.

If we use Airbnb as an example. A User could be a Customer and/or a Host. If the user is a host then they will need a relationship to Property to show which listings they own/manage. For a customer, this wouldn't be the case.

The relationship to properties is therefore conditional dependent on the role a specific User has. Does there need to be any sort of check on the relationship in the User model before returning the relationship or is it just a case of managing it at an earlier stage and checking the role before asking for the relationship?

I've seen a similar question where something like the following was suggested.

public function properties()
{
    if ($this->hasAnyRole(['host'])) {
        return $this->belongsToMany(Property::class);
    } 
}

I understand that there will have to be some level of frontend management with something like the following

@can('manage properties')
    foreach (Auth::user()->properties as $property) {
		//
	}
@endcan

I just want to understand if the relationship also needs to be checked on the model?

0 likes
14 replies
martinbean's avatar

@kevinv A user is a user. When you start creating tables for each “type” of user, you then have to create multiple authentication views, guards, etc for each type of user, and that work only grows exponentially the more types of users you add.

In your example, I wouldn’t even use roles. A user registers on the site, and can either book to stay at a property or create a property for listing. You can have different landing pages for saying or renting, but it would still just create a row in your users table. You can then optionally show property management-based links in the user’s account area if they have any properties. If they do, they see the links. If they don’t have any properties, they don’t see the links:

@if($user->properties()->exists())
    <a href="{{ route('admin.property.index') }}">Manage properties</a>
@endif

These are known as “multi-sided marketplaces” and I run one myself (a video on demand marketplace). Users can sign up to sell videos, and users can also sign up to watch these videos. But in the application, users are users. A single user can both create a channel to sell videos, as well as rent videos from other channels on the marketplace.

1 like
Neeraj1005's avatar

@martinbean Yes, I agreed.

But what about Role management? I think @kevinv can use this type of scenario for multiple users instead of creating different-2 tables.

kevinv's avatar

The reason I originally looked at the multiple tables option was because my Laravel app acts as a backend for a mobile app and users of that mobile app cannot login to the web side of things. Online dashboards are restricted to admins and, continuing the Airbnb example, hosts. The users of the app authorise themselves with Sanctum token. Separating the users who could login online from the users who couldn't seemed like the logical solution initially.

I'm looking at the single table model again as I think I should be able to check the user role before actually authenticating a login request.

I used the Airbnb example as that was the easiest way to explain what I was looking for an answer with. In reality there are more roles like various levels of admin as well as managers of properties rather than just the host who 'owns' the property.

I think from you example though you're saying that there doesn't need to any check on the relationship.

Snapey's avatar

Its just complicates things to think of your users as one or the other. Suppose in the AirBNB example, I rent a property and then realise I could rent my own property. Am I now to have two separate login accounts?

Authentication is about who is accessing your application, not what they are allowed to do.

Authorization covers what they can do, ie I can enquire about a property or I can list and edit my own property. I cannot edit someone else's property.

1 like
kevinv's avatar

I understand the concept of authentication vs authorisation. I was asking a more specific question regarding authorisation.

If a user cannot do something and therefore will not need access to a relationship, does that relationship need any further check within the model?

As I said in my original post, I know that when the relationship is called there will have been some previous check for a permission or ability but I wanted to know if checking the role within the relationship was necessary as I've seen others suggest it in this forum.

Snapey's avatar

The fact that you are (again using the Airbnb example) an agent for a property does not give you rights over other properties, so it still comes down to saying do I have rights to do this thing? Having multiple guards does not help that?

kevinv's avatar

Yeah I totally get the point you're making, my question is looking at using one guard now. In another reply I said I initially went that route of using multiple tables because my Laravel app is a backend to a mobile app and users of the app cannot login online. They can only register to use the app which handles authentication with Sanctum. Admins and managers/owners of properties can login online and we collect slightly different data. At the time, the multiple tables route seemed to make sense based on the response I received when I asked here.

Back to the question you raised. I have a many-to-many relationship between my Partner model who owns properties and the Property model. When it comes to authorisation, I check that the property id of the given property is contained within the array of id's returned from the relationship. If it is, I know that the user has the rights to update the property. If it's not, then they're denied.

If I want to combine my Partner model which has a ->properties relationship into my User model in order to move to a single User model, do I need to add a role check to the ->properties relationship in the model? That was my original question.

Normal users of the mobile app don't need the ->properties relationship and I would never try to return it for them but I just wanted to know if there was any need to check the role like I showed in my original post. I think I am probably overthinking the check on the relationship but as I saw it suggested on another question I wanted to understand if it was needed.

jlrdw's avatar

Here is a post with some good links https://laracasts.com/discuss/channels/laravel/default-authentication-from-multple-tables

Bottom line one table multiple roles.

A user logs in, that is Authentication

Now use authorization to determine what the logged in user can or cannot do.

And the from scratch series, which is free, has several videos covering authorization, also the code is available for free on GitHub to study.

So my advice would be to follow how @jeffreyway teaches this.

kevinv's avatar

I've watched the authorisation videos but I don't believe there is anything mentioned like my question.

The videos tend to show protection of routes and elements within views based on abilities but there is no mention of how to handle relationships on a model which can have multiple roles and the relationship change depending on role.

karim_aouaouda's avatar

hello, I have a similar scenario to your case so I was having a multi vendor website that have many types of users so multi users mean many roles so every user has a role like a vendor and admin a client and something like that, and every type of user has many relations I mean think of e-commerce website, that very very large actually that's means there is many many relationships and if I work with a user model so this means a tens of relationships in one model and if I need to call this relationship , okay I need to authorized the user or not authorize but check the type of the user, in this case I see that the guards are better than roll based auth system. in the other hand, there is an attributes will assign to the users depends on the types and their types for example admin user has many attributes and the clients have many attributes and the vendor have many attributes and for each type of users have their own attributes that the other type of ysers doesn't have in this case I just found that the guards are better than role based system. if you need more info how to do that reply

Snapey's avatar

@karim_aouaouda In the AirBNB scenario

$properties = User::properties()->get();

if($properties->count() == 0) {
	// this user is not a host. they have no properties
}

The presence of property records tells you if they are a host.

This just one possible way of doing it

Guards are NEVER the answer.

1 like
karim_aouaouda's avatar

@Snapey so if i had for example an admin, and vendor, client, ... and foreach role there is many attributes that must be added to table, and in the same time every role has it's own relationships (many relationships tens per role) how could i do that painless

karim_aouaouda's avatar

@Snapey 1 - i read the article and it was very helpfull for me, thank you very much, i understand that the attributes added in other table with relation , but what about relationships, do i have to check the user role before access to the relation ? 2 - i have another question sir, so what if I implement the guard Sollution, i mean it work, what could be a problem about it, right now, or in the futur

Please or to participate in this conversation.