User Roles One to Many

There are many ways to handle user roles in an application. One way is to create a role(s) that have many user(s) or a one to many relationship. In this scenario, we can create a role called admin and many users can have this role. We can create another role called staff and this role can belong to more than one user. Now a user cannot have more than one role so the inverse relatationship is many to one.

By default, Laravel comes with a migration file to create a users table. Running the migration file, you will get a table with the following structure.

In more recent Laravel versions, the primary key is now a BIGINT datatype. If you open the migration file, it uses $table->id();. Previously, it uses $table->increments(‘id’); and that creates a primary key that uses INT datatype. You can still use either one as of Laravel v8. Just make sure when creating foreign keys, both columns matches up. I have been using the id() to simplify creating migration files. I’m not too concerned with data size.

Next, we need to create a migration file for the roles table.

php artisan make:migration create_roles_table

This will create a migration file. The file name is prefixed with the current timestamp. The roles table has to be created prior to the users table. Migration files are run in alphabetical order. You can either rename the roles migration file to have a timestamp before the user migration file or create another migration to add the foreign after both users and roles table are created. For me, the quickest is to rename the roles migration file so that it appears “above” the user migration file when alphabetized.

Edit the roles migration class with the following code.

public function up()
{
    Schema::create('roles', function(Blueprint $table)
    {
        $table->id();
        $table->string('name', 50)->unique();
    });
}

I chose not to keep the timestamp columns but you can keep them if you want. The migration file will create the following table.

Next, edit the users migration file and add the role_id foreign key. Add the code below within the up(). I normally place my foreign keys under the primary key.

// CreateUsersTable class
public function up()
{
    Schema::create('users', function (Blueprint $table) {
        $table->id();
        $table->foreignId('agency_id')->constrained('agencies');
        $table->string('name');
        $table->string('email')->unique();
        $table->timestamp('email_verified_at')->nullable();
        $table->string('password');
        $table->rememberToken();
        $table->timestamps();
    });
}

This will create the foreign key constraint between the users and roles table.

It is a good idea to create the relationships in your model classes as well. Create a model class for your roles table.

php artisan make:model Role

Edit the Role.php model class file.

// app/Models/Role.php

class Role extends Model
{
    use HasFactory;

    protected $table = 'roles';              // defines table name
    public $timestamps = false;         // no created_at or updated_at columns

    /**
     * Has many users.
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function users()
    {
        return $this->hasMany(User::class);
    }
}

You will need to define the relationship in your User model class.

// app/Models/User.php

/**
 * Relationship to role.
 * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
 */
public function role()
{
        return $this->belongsTo(Role::class);
}

That’s all you need to do in terms of create users and roles relationship. The next step is to create a way to authorize users based on roles. You can either use Laravel’s Gates and Policies. Or you can create your own middleware.


Comments

One response to “User Roles One to Many”

  1. […] a previous post, I shared how to create user roles with one to many relationship. Another way to design user roles in your application is to create a many to many relationship. […]

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.