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

Veltix's avatar

Tenant roles and permissions

I have issue with tenant roles.

I have created new table to database: tenant_roles

My roles and permissions looks like this:

CREATE TABLE `permissions` (
  `id` int(10) UNSIGNED NOT NULL,
  `module_id` int(10) UNSIGNED NOT NULL,
  `name` varchar(191) COLLATE utf8_unicode_ci NOT NULL,
  `display_name` varchar(191) COLLATE utf8_unicode_ci DEFAULT NULL,
  `description` varchar(191) COLLATE utf8_unicode_ci DEFAULT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

-- --------------------------------------------------------

--
-- Table structure for table `permission_role`
--

CREATE TABLE `permission_role` (
  `permission_id` int(10) UNSIGNED NOT NULL,
  `role_id` int(10) UNSIGNED NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

-- --------------------------------------------------------

--
-- Table structure for table `permission_user`
--

CREATE TABLE `permission_user` (
  `permission_id` int(10) UNSIGNED NOT NULL,
  `user_id` int(10) UNSIGNED NOT NULL,
  `user_type` varchar(191) COLLATE utf8_unicode_ci NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

-- --------------------------------------------------------

--
-- Table structure for table `roles`
--

CREATE TABLE `roles` (
  `id` int(10) UNSIGNED NOT NULL,
  `name` varchar(191) COLLATE utf8_unicode_ci NOT NULL,
  `display_name` varchar(191) COLLATE utf8_unicode_ci DEFAULT NULL,
  `description` varchar(191) COLLATE utf8_unicode_ci DEFAULT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

-- --------------------------------------------------------

--
-- Table structure for table `role_user`
--

CREATE TABLE `role_user` (
  `role_id` int(10) UNSIGNED NOT NULL,
  `user_id` int(10) UNSIGNED NOT NULL,
  `user_type` varchar(191) COLLATE utf8_unicode_ci NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

--
-- Table structure for table `tenant_roles`
--

CREATE TABLE `tenant_roles` (
  `id` int(11) NOT NULL,
  `tenant_id` bigint(20) UNSIGNED NOT NULL,
  `user_id` bigint(20) UNSIGNED NOT NULL,
  `role_id` bigint(20) UNSIGNED NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

How I can create relationship with tenants?

Schenario:

Partner registers in our platform account (it automatically creates tenant ID for his business and for his user) and his business has different roles and permission + he can add new roles for his business. So do I have to create new table for tenant roles (Done: tenants_roles)?? and what that table should contain (I added tenant_id, user_id, role_id)?

if I create relationships I have to change user model and role models?

Users model:

 public function role()
 {
        return $this->belongsToMany(Role::class);
 }

public function scopeAllAdministrators() {
    return $this->whereHas('roles', function ($query) {
        $query->where('name', 'administrator');
    });
}

public function scopeAllCustomers() {
    return $this->whereHas('roles', function ($query) {
        $query->where('name', 'customer')->withoutGlobalScopes();
    });
}

public function scopeOtherThanCustomers() {
    return $this->whereHas('roles', function ($query) {
        $query->where('name', '<>', 'customer');
    });
}

public function scopeAllEmployees() {
    return $this->whereHas('roles', function ($query) {
        $query->where('name', 'employee');
    });
}

public function getIsAdminAttribute() {
    return $this->hasRole('administrator');
}

public function getIsOwnerAttribute() {
    return $this->hasRole('owner');
}

public function getIsManagerAttribute() {
    return $this->hasRole('manager');
}

public function getIsEmployeeAttribute() {
    return $this->hasRole('employee');
}

public function getIsCustomerAttribute() {
    if ($this->roles()->withoutGlobalScopes()->where('roles.name', 'customer')->count() > 0) {
        return true;
    }
    return false;
}

Roles model:

protected static function boot() {
    parent::boot();
    static::addGlobalScope('withoutCustomerRole', function (Builder $builder) {
        $builder->where('name', '<>', 'customer');
    });
}

//------------------------------------ Relations ----------------------------

public function getRoleCount() {
    return $this->hasMany(User::class);
}

//------------------------------------ Accessors ----------------------------

public function getMemberCountAttribute() {
    return $this->users->count();
}

public function employee()
{
    return $this->belongsToMany(User::class);
}

public function manager()
{
    return $this->belongsToMany(User::class);
}

Users observer:

namespace App\Observers;

use App\Helper\SearchLog;
use App\Models\User;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Storage;

class UserObserver
{
    public function roleAttached(User $user, $role, $team)
    {
        if (!$user->is_admin) {
            if ($user->is_owner) {
                $type = 'Owner';
                $route = 'admin.owner.edit';
            }

            if ($user->is_manager) {
                $type = 'Manager';
                $route = 'admin.manager.edit';
            }

            if ($user->is_employee) {
                $type = 'Employee';
                $route = 'admin.employee.edit';
            }

            if ($user->is_customer) {
                $type = 'Customer';
                $route = 'admin.customers.show';
            }

            SearchLog::createSearchEntry($user->id, $type, $user->first_name . " " . $user->last_name, $route);
            SearchLog::createSearchEntry($user->id, $type, $user->email, $route);
        }
    }

    public function updating(User $user)
    {
        if (!$user->is_admin) {
            if ($user->is_owner) {
                $type = 'Owner';
                $route = 'admin.owner.edit';
            }

            if ($user->is_manager) {
                $type = 'Manager';
                $route = 'admin.manager.edit';
            }

            if ($user->is_employee) {
                $type = 'Employee';
                $route = 'admin.employee.edit';
            }

            if ($user->is_customer) {
                $type = 'Customer';
                $route = 'admin.customers.show';
            }

            if ($user->isDirty('first_name')) {
                $original = $user->getOriginal('first_name');
                SearchLog::updateSearchEntry($user->id, $type, $user->first_name . " " . $user->last_name, $route, ['first_name' => $original]);
            }

            if ($user->isDirty('email')) {
                $original = $user->getOriginal('email');
                SearchLog::updateSearchEntry($user->id, $type, $user->email, $route, ['email' => $original]);
            }

            if ($user->isDirty('image')) {
                if(!is_null($user->getOriginal('image'))){
                    $path = public_path('user-uploads/avatar/'.$user->getOriginal('image'));
                    if($path){
                        File::delete($path);
                    }
                }
            }
        }
    }

    public function deleted(User $user)
    {
        if(!is_null($user->getOriginal('image')))
        {
            $path = public_path('user-uploads/avatar/'.$user->getOriginal('image'));
            if($path){
                File::delete($path);
            }
        }

        if (!$user->is_admin) {
            if ($user->is_owner) {
                $route = 'admin.owner.edit';
            }

            if ($user->is_manager) {
                $route = 'admin.manager.edit';
            }

            if ($user->is_employee) {
                $route = 'admin.employee.edit';
            }

            if ($user->is_customer) {
                $route = 'admin.customers.show';
            }

            SearchLog::deleteSearchEntry($user->id, $route);
        }
    }
}

So tenants means like company/team. In my system it is company. I give each user who is from this company I give tenant_id. So Company should have own roles and permissions. Do I need change users observer also?

I've watched multi tenancy single database course already and it has basic roles in users table.

0 likes
4 replies
bugsysha's avatar

Could you make it more complicated? You are going to hate yourself in 6 months. 😆

Veltix's avatar

Can you tell me how to make it all easier then. Some tips for me?

bugsysha's avatar

You would have to explain what is the business logic for this app before anyone can help.

Please or to participate in this conversation.