I have two tables :
User ->
id :
name :
role_id : ->refernces('id')->on('roles');
Roles ->
id :
role_name :
access :
I am trying to access roles details from user.
My user model has :
public function role(){
return $this->belongsTo('App\Role');
}
My role model has :
public function user(){
return $this->hasMany('App\User');
}
When I try to do following :
$user = User::find(1);
$details = [
'name' => $user->first_name,
'role' => $user->role->role_name
];
I get error :
Trying to get property of non-object
My roles table contains access columns containing array of permissions to different routes. So my user will have only one role. While a role can have multiple users.
How to do that?
In my recent project, I handled these requirement in that way..
First of All Database Table Structure/Migration
User Table
class CreateUserTable extends Migration {
public function up() {
Schema::create('user', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('email')->unique();
$table->string('password', 60);
$table->boolean('status')->default(0);
$table->boolean('is_admin')->default(0);
$table->boolean('notify')->default(0);
$table->rememberToken();
$table->timestamps();
});
}
public function down() {
Schema::drop('user');
}
}
Role Table
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateRoleTable extends Migration {
public function up()
{
Schema::create('role', function (Blueprint $table) {
$table->increments('id');
$table->string('name')->unique();
$table->string('display_name')->nullable();
$table->string('description')->nullable();
$table->boolean('status')->default(0);
$table->timestamps();
});
}
public function down()
{
Schema::drop('role');
}
}
Role And User Relation Table
class CreateRoleUserTable extends Migration {
public function up() {
// Create table for associating roles to users (Many-to-Many)
Schema::create('role_user', function (Blueprint $table) {
$table->integer('user_id')->unsigned();
$table->integer('role_id')->unsigned();
$table->foreign('user_id')->references('id')->on('user')
->onUpdate('cascade')->onDelete('cascade');
$table->foreign('role_id')->references('id')->on('role')
->onUpdate('cascade')->onDelete('cascade');
$table->primary(['user_id', 'role_id']);
});
}
public function down() {
Schema::drop('role_user');
}
}
After these table you have to handle permission by assigning to specific Role.
Permission
class Permission extends Migration {
public function up() {
Schema::create('permission', function (Blueprint $table) {
$table->increments('id');
$table->string('name')->unique();
$table->string('pattern');
$table->string('target');
$table->string('module');
$table->string('display_name')->nullable();
$table->boolean('status')->default(0);
$table->timestamps();
});
}
public function down() {
Schema::drop('permission');
}
}
Permission and Role Table Relation
class PermissionRole extends Migration {
public function up() {
// Create table for associating roles to permission (Many-to-Many)
Schema::create('permission_role', function (Blueprint $table) {
$table->integer('permission_id')->unsigned();
$table->integer('role_id')->unsigned();
$table->foreign('permission_id')->references('id')->on('permission')
->onUpdate('cascade')->onDelete('cascade');
$table->foreign('role_id')->references('id')->on('role')
->onUpdate('cascade')->onDelete('cascade');
$table->primary(['permission_id', 'role_id']);
});
}
public function down() {
Schema::drop('permission_role');
}
}
And Finally our model would look alike:
User Model
namespace App;
use Illuminate\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Auth\Passwords\CanResetPassword;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
class User extends Model implements AuthenticatableContract, CanResetPasswordContract {
use Authenticatable, CanResetPassword;
protected $table = 'user';
protected $fillable = ['name', 'email', 'password', 'is_admin'];
protected $hidden = ['password', 'remember_token'];
public function scopeActive($query) {
return $query->whereStatus('1');
}
public function scopeAdmin($query) {
return $query->whereIsAdmin('1');
}
public function scopeNotify($query) {
return $query->whereNotify('1');
}
public function roles() {
return $this->belongsToMany('App\Role', 'role_user', 'user_id', 'role_id');
}
public function attachRole($role) {
if (is_object($role)) {
$role = $role->getKey();
}
if (is_array($role)) {
$role = $role['id'];
}
$this->roles()->attach($role);
}
public function detachRole($role) {
if (is_object($role)) {
$role = $role->getKey();
}
if (is_array($role)) {
$role = $role['id'];
}
$this->roles()->detach($role);
}
public function attachRoles($roles) {
foreach ($roles as $role) {
$this->attachRole($role);
}
}
public function detachRoles($roles) {
foreach ($roles as $role) {
$this->detachRole($role);
}
}
public function isSuperUser() {
return (bool)$this->is_admin;
}
public function hasAccess($permissions, $all = true) {
if ($this->isSuperUser()) {
return true;
}
return $this->hasPermission($permissions, $all);
}
public function hasPermission($permissions) {
$mergedPermissions = $this->getMergedPermissions();
//dd($mergedPermissions);
if (!is_array($permissions)) {
$permissions = (array)$permissions;
}
foreach ($permissions as $permission) {
$matched = false;
// We will set a flag now for whether this permission was
// matched at all.
$founded_perms = find_in($mergedPermissions, "name", $permission);
if (!empty($founded_perms)) {
$matched = true;
}
}
if ($matched === false) {
return false;
}
return true;
}
public function getMergedPermissions() {
$permissions = array();
foreach ($this->getRoles() as $group) {
$permissions = array_merge($permissions, $group->permissions()->get()->toArray());
}
return $permissions;
}
public function getRoles() {
$roles = [];
if ($this->roles()) {
$roles = $this->roles()->get();
}
return $roles;
}
}
Role Model
namespace App;
use Illuminate\Database\Eloquent\Model;
class Role extends Model {
/**
* The database table used by the model.
*
* @var string
*/
protected $table = 'role';
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = ['name', 'display_name', 'description'];
public function scopeActive($query) {
return $query->whereStatus('1');
}
/**
* Many-to-Many relations with User.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function users() {
return $this->belongsToMany('App\User');
}
public function permissions() {
return $this->belongsToMany("App\Permission");
}
}
Permission Model
namespace App;
use Illuminate\Database\Eloquent\Model;
class Permission extends Model {
protected $table = 'permission';
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = ['name', 'pattern', 'target', 'module', 'display_name', 'status'];
public static function displayable() {
$prepared_array = [];
$temp = self::orderBy('module')->get()->toArray();
foreach ($temp as $sin) {
$prepared_array[$sin['module']][] = $sin;
}
return $prepared_array;
}
public function scopeActive($query) {
return $query->whereStatus('1');
}
public function roles() {
return $this->belongsToMany("App\Role");
}
}
Well, thats the basic structure helped to implement basic ACL and Auth with laravel 5.
Let me know if you have any further related question. Or If you need complete implementation I'll provide it to you.
For a one-to-many relationship you don't need a pivot table, so you can delete the user_roles
table. Then add a role_id
column to your users
table, that will reference the id
column in for your roles
table. Next define the relations as follows for each of your models:
// User.php
public function role()
{
return $this->belongsTo('App\Role');
}
and
// Role.php
public function users()
{
return $this->hasMany('App\User');
}
Now you can access your role via the relation like this:
$user->role->name;
I got the problem, i was having a role column in user table, so when i was doing
$user->role->role_name
it was fetching role
column instead of relationship.
i noticed you are not using the laravel default table naming conventions,
you are using user_roles whereass laravel naming conventions state you should use:
role_user (alphabetical and singular)
you could override the belongsToMany by stating your custom table name ofcource.
public function users() {
return $this->belongsToMany('App\User', 'user_roles');
}
on the second node there are also some good libraries to handle these kind of things, take a look at : https://github.com/romanbican/roles