How to make user, and roles relationship in Laravel 5 - laravel

I have two tables :
User ->
id :
name :
role_id : ->references('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

Related

Laravel - I want to get all the messages between the auth user and the receiver

I have a users table, a message table and a user-message table.
i want to get all the messages between the authenticated user and the receiver.
here are the tables and the models:
the users table and model are obvious.
Message model :
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Message extends Model
{
protected $fillable = [
'parent_id', 'message', 'type','status',
];
public function user_messages(){
return $this->hasMany('App\UserMessage');
}
public function users(){
return $this->belongsToMany('App\User','user_messages','message_id','sender_id')->withTimestamps();
}
}
User_message model :
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class UserMessage extends Model
{
protected $fillable = [
'message_id', 'sender_id', 'receiver_id','type','seen_status','deliver_status',
];
public function message(){
return $this->belongsTo('App\Message');
}
}
Message table :
* #return void
*/
public function up()
{
Schema::create('messages', function (Blueprint $table) {
$table->id();
$table->integer('parent_id')->nullable();
$table->longText('message')->nullable();
$table->integer('type')->default(1)->comment('1 : message , 2 : file');
$table->integer('status')->default(1);
$table->timestamps();
});
}
User_message table :
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('user_messages', function (Blueprint $table) {
$table->id();
$table->integer('message_id');
$table->integer('sender_id');
$table->integer('receiver_id');
$table->integer('type')->default(0)->comment('1 : groupe message , 0 : personal message');
$table->integer('seen_status')->default(0)->comment('1 : seen');
$table->integer('deliver_status')->default(0)->comment('1 : delivered');
$table->timestamps();
});
}
so all i want to know is how the logic inside the controller function will be.
I found the solution :
public function getMessages($id, $ids){
$final_messages_list = array();
$auth_id = $id;
$friend_id = $ids;
$messages = UserMessage::where('sender_id',$auth_id)->where('receiver_id',$friend_id)->orderby('created_at','desc')->get();
foreach($messages as $message){
$final_messages_list[] = $message->message;
}
return response()->json([
'status' => 200,
'messages' => $final_messages_list,
]);
}

Deleted_by is not changing while i delete a data using observers;

I want to have fields created_by,updated_by and deleted_by and want to update these fields automatically. created_by,updated_by is working but deleted_by is not working.i am using an observer to perform this task. When i use dd($model) in deleting function in observer it shows the collection or values but the deleted_by is not updating.
My model
namespace App\Model;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use App\Observers\UserIdFinderObserver;
class supplier extends Model
{
use SoftDeletes;
protected $fillable = [
'name', 'address', 'contact','contact_person_name','country','email'
];
public static function boot()
{
parent::boot();
$class = get_called_class();
$class::observe(new UserIdFinderObserver());
}
}
my migration file
public function up()
{
Schema::create('suppliers', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('contact_person_name');
$table->string('country')->nullable();
$table->text('address')->nullable();
$table->string('contact')->nullable();
$table->string('email')->nullable();
$table->string('created_by')->nullable();
$table->string('updated_by')->nullable();
$table->string('deleted_by')->nullable();
$table->softDeletes();
$table->timestamps();
});
}
My delete function in controller
public function destroy(supplier $supplier)
{
try{
$supplier->delete();
return redirect()->route('supplier.index')->with('success', 'Supplier is deleted successfully');
}catch(Exception $e){
return redirect()->back()->withErrors($e->getMessage());
}
}
My Observer
namespace App\Observers;
class UserIdFinderObserver
{
private $userID;
public function __construct(){
$this->userID = auth()->id();
}
public function saving($model)
{
$model->created_by = $this->userID;
}
public function updating($model){
$model->updated_by = $this->userID;
}
public function creating($model)
{
$model->created_by = $this->userID;
}
public function deleting($model)
{
$model->deleted_by = $this->userID;
}
}

Completion system with Laravel + VueJS

I've been tring for hours to define my relations for a completion system but I failed.
I have a table Users and a table Episodes, and I would like to get in my views if the User has completed an episode.
I created a "completions" table with user_id and episode_id and a boolean field called "completed"
Is it a manytomany relation ? I'd like to have something like $episode->completed which gave me True if the logged in user finished the course, but I can't find my way... I just wanna know how to define my relations, not a whole work done.
Thank you very much !!!!
I believe you can tell Laravel what table to use and also query the pivot column.
//user model
public function episodes(){
return $this->belongsToMany( 'App\Episode', 'completions', 'user_id', 'episode_id' );
}
public function completedEpisodes(){
return $this->belongsToMany( 'App\Episode', 'completions', 'user_id', 'episode_id' )
->wherePivot('completed','=', true)->get();
}
//episode model
public function users(){
return $this->belongsToMany( 'App\User', 'completions', 'episode_id', 'user_id' );
}
The alternative would be to create your pivot as episode_user and laravel will auto detect it as the pivot, add a completed boolean to that table and it would function with just:
//user model
public function episodes(){
return $this->belongsToMany('App\Episode');
}
public function completedEpisodes(){
return $this->belongsToMany('App\Episode')
->wherePivot('completed','=', true)->get();
}
//episode model
public function users(){
return $this->belongsToMany('App\User');
}
Query if episode is complete:
//maybe, haven't tried this
//user
public function hasCompletedEpisode($id){
return $this->belongsToMany('App\Episode')->wherePivot('episode_id','=', $id)
->wherePivot('completed','=', true)->get();
}
//episode
public function hasCompletedEpisode($id){
$user_id = Auth::id();
return $this->belongsToMany('App\User')->wherePivot('user_id','=', $user_id)
->wherePivot('completed', true)->get();
}
If I was you, I would use a custom intermediate table. You can implement this as follows:
Migrations
Schema::create('users', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->timestamps();
});
Schema::create('episodes', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->timestamps();
});
Schema::create('watches', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('user_id');
$table->unsignedBigInteger('epsiode_id');
$table->boolean('completed');
$table->timestamps();
});
Models
class User extends Model
{
protected $guarded = [];
public function watchedEpisodes()
{
return $this->hasMany(Episode::class)->using(Watch::class);
}
public function watches()
{
return $this->hasMany(Watch::class);
}
}
class Episode extends Model
{
protected $guarded = [];
public function users()
{
return $this->hasMany(User::class)->using(Watch::class);
}
}
class Watch extends \Illuminate\Database\Eloquent\Relations\Pivot
{
protected $table = 'watches';
protected $guarded = [];
public static function create(User $user, Episode $episode, bool $completed = false)
{
$watch = new self();
$watch->user_id = $user->id;
$watch->epsiode_id = $episode->id;
$watch->completed = $completed;
$watch->save();
return $watch;
}
public function user()
{
return $this->hasOne(User::class);
}
public function episode()
{
return $this->hasOne(User::class);
}
}
Example Use
$user = User::create(['name' => 'Big Watcher']);
$episode1 = Episode::create(['name' => 'Episode 1']);
$episode2 = Episode::create(['name' => 'Episode 2']);
$episode3 = Episode::create(['name' => 'Episode 3']);
$episode4 = Episode::create(['name' => 'Episode 4']);
Watch::create($user, $episode1);
Watch::create($user, $episode2);
Watch::create($user, $episode3);
return $user->watchedEpisodes;

My hasMany in laravel response null, my model its correct

I have problem my has many relation response null.
this is my model
class Diskusi extends Model
{
protected $table = 'tbl_diskusi';
protected $hidden = [
'topic'
];
public function user(){
return $this->belongsTo(User::class,'id_user');
}
public function category(){
return $this->belongsTo(Category::class);
}
public function pets_category(){
return $this->belongsTo(PetsCategory::class);
}
}
this is my another model
class PetsCategory extends Model
{
//
protected $table = 'tbl_pets_category';
public function diskusi(){
return $this->hasMany(Diskusi::class,'id_pets_category');
}
}
and another
class Category extends Model
{
//
protected $table = 'tbl_category';
public function diskusi(){
return $this->hasMany(Diskusi::class,'id_category');
}
}
and this is my migration for Diskusi
class CreateTblDiskusi extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('tbl_diskusi', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('title');
$table->string('content');
$table->string('topic');
$table->unsignedBigInteger('id_user');
$table->unsignedBigInteger('id_category');
$table->unsignedBigInteger('id_pets_category');
$table->timestamps();
$table->foreign('id_user')->references('id')
->on('users')->onDelete('cascade');
$table->foreign('id_category')->references('id')
->on('tbl_category')->onDelete('cascade');
$table->foreign('id_pets_category')->references('id')
->on('tbl_pets_category')->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('tbl_diskusi');
}
}
the condition is
i want my Diskusi have one category, and one pets_category
but when i create Diskusi like this
public function create(Request $request)
{
$diskusi = new Diskusi;
$diskusi->title = $request->title;
$diskusi->content = $request->content;
$diskusi->topic = $request->topic;
$diskusi->id_user = Auth::user()->id;
$diskusi->id_category = $request->id_category;
$diskusi->id_pets_category = $request->id_pets_category;
if ($request->photo != ''){
foreach ($request->photo as $itemPhoto) {
# code...
$photo = new Photo;
$rand = $this->generateRandomString() . 'jpg';
//taroh foto di server
// file_put_contents('storage/photo/diskusi/' . $rand , base64_decode($request->photo));
$photo->path_photo = $rand;
$photo->save();
}
}
$diskusi->save();
$diskusi->user;
$diskusi->category;
$diskusi->pets_category;
return response()->json([
'success' => true,
'message' => 'posted',
'post' => $diskusi
]);
}
the response like this
please help me, i tried with many tutorial relational laravel but it's not working for me. i dont know where my false, please tell me my false.
*note: sorry for bad english
You've to define your foreign key in relationship.
public function category(){
return $this->belongsTo(Category::class,'id_category','id');
}
public function pets_category(){
return $this->belongsTo(PetsCategory::class,'id_pets_category','id');
}
If you don't pass the foreign key then by default it judges 'category_id' or 'pets_category_id' but you've given id_category and id_pets_category

can't attach topic_id in the comment table

I am making a forum where users can create topics and leave a reply just like this forum.
I made a relationship just like below.However, when I save an article the topic_id does not get attached.I think the saveReply method is wrong.
Also,in this case how do you pass comments on the particular post to the view in the show method??
I am a noob,so if my question is vague I am sorry,but any help will be appreciated!!
Route
Route::group(['middleware' => 'web'], function () {
Route::get('forums','ForumsController#index');
Route::get('forums/create','ForumsController#create');
Route::post('forums', 'ForumsController#store');
Route::get('forums/{category_id}/{title}','ForumsController#show');
Route::post('forums/{category_id}/{title}', 'ForumsController#saveReply');
});
forumcontroller
class ForumsController extends Controller
{
public function index()
{
$categories = Category::all();
$topics = Topic::latest()->get();
return view('forums.index',compact('categories','topics'));
}
public function create()
{
$categories = Category::lists('title', 'id');
return view('forums.create', compact('categories'));
}
public function store(Request $request)
{
Auth::user()->topics()->save(new Topic($request->all()));
flash()->success('投稿しました','success');
return redirect('forums');
}
public function show($category_id, $title)
{
Topic::where(compact('category_id','title'))->first();
return view('forums.post', compact('topic'));
}
public function saveReply (Request $request)
{
Auth::user()->comments()->save(new Comment($category_id,$request->all()));
flash()->success('投稿しました','success');
return redirect()->back();
}
}
topic model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class topic extends Model
{
protected $fillable = [
'title',
'body',
'category_id'
];
public function category()
{
return $this->belongsTo('App\category');
}
public function user()
{
return $this->belongsTo('App\User');
}
public function comments()
{
return $this->hasMany('App\Comment');
}
}
user model
<?php
namespace App;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name', 'email', 'password',
];
/**
* The attributes excluded from the model's JSON form.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
public function articles()
{
return $this->hasMany('App\Article');
}
public function topics()
{
return $this->hasMany('App\Topic');
}
public function comments()
{
return $this->hasMany('App\Comment');
}
}
comment model
class Comment extends Model
{
protected $fillable = [
'reply',
'user_id',
'topic_id'
];
public function topic()
{
return $this->belongsTo('App\Topic');
}
public function user()
{
return $this->belongsTo('App\User');
}
}
comment table
class CreateCommentsTable extends Migration
{
public function up()
{
Schema::create('comments', function (Blueprint $table) {
$table->increments('id');
$table->text('reply');
$table->integer('user_id')->unsigned();
$table->integer('topic_id')->unsigned();
$table->timestamps();
});
}
public function down()
{
Schema::drop('comments');
}
}
The Request::all returns an array of all inputs so when you are doing:
new Comment($category_id,$request->all())
You'll get something like this:
1['some' => 'thing', 'other'=> 'values']
Which could be the problem so try this instead:
new Comment(array_merge(['category_id' => $category_id ], $request->all())
When on development/local environment, set the debug true so you'll get meaningful error messages so you can findout the exect problem easily.

Resources