LaraClub - Create Roles

[0] Prep

Continue from previous article or download quickstart file.

[1] Create the Roles Table

[1.1] Create migration

Create a migration to generate the roles table:

php artisan make:migration create_roles_table --create=roles

Update the migration file (database/migrations/YYYY_MM_DD_HHmmss_create_roles_table.php):

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up()
    {
        Schema::create('roles', function (Blueprint $table) {
            $table->id();
            $table->string('name')->unique();
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('roles');
    }
};

Run the migration:

php artisan migrate

[1.2] Setup Model for Roles table

Create the Role model:

php artisan make:model Role

Define the relationship in the Role model (app/Models/Role.php):

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Role extends Model
{
    use HasFactory;

    protected $fillable = ['name'];    
}

Note: we add the "use HasFactory" statement and including the HasFactory trait in the Role model to enable the factory functionality for the model. Refer separate tutorial: (coming soon).

[1.3] Enter seed records for Roles

Using Tinker, enter records for the roles table.

Run the group of statements below (separated by empty lines) one after another:

php artisan tinker


use App\Models\Role;


$role1 = Role::create(['name' => 'Administrator']);
$role2 = Role::create(['name' => 'Manager']);
$role3 = Role::create(['name' => 'Coordinator']);
$role4 = Role::create(['name' => 'Moderator']);
$role5 = Role::create(['name' => 'Operator']);

Output:

[2] Create the User_Role Pivot Table

A pivot table is a database table used to establish a many-to-many relationship between the tables such as users and roles tables.

Pivot tables are typically used to establish relationships between two existing models, and they don't require their own dedicated model.

[2.1] Create migration for User_Role table

Create a new migration using the following command:

php artisan make:migration create_user_role_table --create=user_role

This command will generate a new migration file with a name like 2022_01_01_000000_create_user_role_table.php in the database/migrations directory.

Update the migration file database/migrations/YYYY_MM_DD_HHMMSS_create_user_role_table.php

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('user_role', function (Blueprint $table) {
            $table->id();
            $table->unsignedBigInteger('user_id');
            $table->unsignedBigInteger('role_id');
            $table->timestamps();

            $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
            $table->foreign('role_id')->references('id')->on('roles')->onDelete('cascade');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('user_role');
    }
};

Run the migration:

php artisan migrate

Outcome:

Update the User model (app/User.php) to reflect the many-to-many relationship with the Role model:

    /**
     * The roles that belong to the user.
     */
    public function roles()
    {
        return $this->belongsToMany(Role::class, 'user_role');
    }

The full code for the User model would be:

<?php

namespace App\Models;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

class User extends Authenticatable implements MustVerifyEmail
{
    use HasFactory, Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'name',
        'email',
        'password',
    ];

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var array<int, string>
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    /**
     * Get the attributes that should be cast.
     *
     * @return array<string, string>
     */
    protected function casts(): array
    {
        return [
            'email_verified_at' => 'datetime',
            'password' => 'hashed',
        ];
    }

    /**
     * The roles that belong to the user.
     */
    public function roles()
    {
        return $this->belongsToMany(Role::class, 'user_role');
    }
}

Update the Role model (app/Role.php) to reflect the many-to-many relationship with the User model:

    /**
     * The users that belong to the role.
     */
    public function users()
    {
        return $this->belongsToMany(User::class, 'user_role');
    }

The full code for the Role model would be:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Role extends Model
{
    use HasFactory;

    protected $fillable = ['name'];    

    /**
     * The users that belong to the role.
     */
    public function users()
    {
        return $this->belongsToMany(User::class, 'user_role');
    }
}

[2.3] Test

Assuming that you have a user "alpha" with id=1 and a role "Administrator" with id=1.

Open the tinker console:

php artisan tinker

[1] Create a new instance of the User model and retrieve the user with the ID of 1:

$user = new App\Models\User();
$user = $user->find(1);

Output:

[2] Create a new instance of the Role model and retrieve the role with the ID of 1:

$role = new App\Models\Role();
$role = $role->find(1);

Output:

[3] Use the roles() relationship method on the User model to attach the role to the user:

$user->roles()->attach($role->id);

The above code attaches the role to the user by creating a new record in the user_role pivot table.

Output:

[4] To test if the pivot relationship works, you can retrieve the roles associated with the user and verify that the role you attached is present.

Retrieve all the roles associated with the user:

$user->roles;

Output:

Retrieve all the users associated with the role:

$role->users;

Output:

[3] Create Page to output pivot table results

[3.1] Create UserController

Run the following command to generate the UserController:

php artisan make:controller UserController

[3.2] Update UserController

(file C:\laragon\www\laraclub\app\Http\Controllers\UserController.php)

<?php

namespace App\Http\Controllers;

use App\Models\User;

class UserController extends Controller
{
    public function showRoles($id)
    {
        $user = User::findOrFail($id);
        $roles = $user->roles;

        return view('user.roles', compact('user', 'roles'));
    }
}

[3.3] Create view file

Assuming that you are using the Breeze template, update the view file as follows:

(file C:\laragon\www\laraclub\resources\views\user\roles.blade.php)

<x-app-layout>
    <x-slot name="header">
        <h2 class="font-semibold text-xl text-gray-800 leading-tight">
            {{ __('User Roles') }}
        </h2>
    </x-slot>

    <div class="py-12">
        <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
            <div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
                <div class="p-6 text-gray-900">
                    <h1>Roles for {{ $user->name }}:</h1>

                    <ul>
                        @foreach ($roles as $role)
                        <li>{{ $role->name }}</li>
                        @endforeach
                    </ul>
                </div>
            </div>
        </div>
    </div>
</x-app-layout>

[3.4] Update Web Route

(file C:\laragon\www\laraclub\routes\web.php)


/* version 8.x++ style */
//use App\Http\Controllers\UserController;
//Route::get('/users/{id}/roles', [UserController::class, 'showRoles'])->name('user.roles');
/* version 5.x++ style */
Route::get('/users/{id}/roles', 'App\Http\Controllers\UserController@showRoles')->name('user.roles');

[3.5] Test

Browser Laravel (laraclub.test)