Is it allowed to have two tables, each with a belongsToMany() relation to each other, using the same pivot table?
Here is my User table:
And here is my Org table:
And here is the pivot table:
Here is the Eloquent User.php model:
<?php
namespace App;
use Illuminate\Auth\Authenticatable;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class User extends Model {
protected $table = 'users';
public $timestamps = true;
use Authenticatable;
use SoftDeletes;
protected $dates = ['deleted_at'];
public function orgs()
{
return $this->belongsToMany('App\Org', 'org_user', 'org_id', 'user_id')->withPivot('role_id');
}
}
And here is the Eloquent Org.php model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Org extends Model {
protected $table = 'orgs';
public $timestamps = true;
use SoftDeletes;
protected $dates = ['deleted_at'];
public function users()
{
return $this->belongsToMany('App\User', 'org_user', 'org_id, user_id')->withPivot('role_id');
}
}
Is it OK to do this? (i.e. use a shared pivot table) Does anyone with experience foresee any issues here? Or maybe someone could comment on standard practice/share some insights?
Absolutely you can! This is the beauty of a many to many relationship, in fact the example at http://laravel.com/docs/4.2/eloquent#many-to-many has the User and Role classes both set with belongsToMany. Thought being in that case (and sounds like in yours as well) that a user can have multiple roles and each role can be associated with multiple users.
Regarding your last two questions, the only part that gave me pause is the use of a role_id within the pivot table. The code you provided doesn't go into detail on what purpose that serves. As long as the User would only ever have one role associated with an Org this is fine, but if it could ever be more than one I would recommend another table to hold that User to Org role relationship.
Related
I currently have three tables: users, roles, and a user_to_role “pivot” table defining a many-to-many relationship between users and roles:
users
protected $fillable = [
'name', 'email', 'password',
];
user_to_role
protected $fillable = [
'id', 'user_id', 'role_id'
];
roles
protected $fillable = [
'id', 'role_name',
];
The role_name values are admin and client.
When a user logs in, I want to show a view for the specific role that the user is assigned. I don't really know how to do that in the controller, however. I have something like the following, but I know it won’t work:
public function index()
{
if (Auth::user()->role_id==1) {
// and something here which I don't know
return view('homeadmin');
}
}
I know I have to take the id from the roles table, make the connection with the user_to_role pivot, and then join that with the users table, but I don't really know how.
You need to define a relationship between User model and Role model.
# User.php
public function roles()
{
return $this->belongsToMany(Role::class, 'user_to_role');
}
Optionally, define the relationship on Role model as well.
# Role.php
public function users()
{
return $this->belongsToMany(User::class, 'user_to_role');
}
Then, you can access the relationship and use collection methods on it.
public function index()
{
// or Auth::user()->roles->contains('role_name', 'admin') if you want to be specific
if (Auth::user()->roles->contains('id', 1)) {
return view('homeadmin');
}
return view('homeuser');
}
Optionally, you could make a method in the User model to check if an user is admin or client.
# User.php
public function isAdmin()
{
return $this->roles->contains('id', 1); // or contains('role_name', 'admin')
}
public function isClient()
{
return $this->roles->contains('id', 2); // or contains('role_name', 'client')
}
public function index()
{
if (Auth::user()->isAdmin()) {
return view('homeadmin');
}
return view('homeclient');
}
Eloquent Relationships - Many to Many
Collections - contains() method
First of all, if you have User and Role Model mapping to your users and roles table, the convention is to name your pivot table role_user. But you can get along with your current table naming as well.
I would agree the answer of IGP and add a few more suggestions.
If you just need to implement role and user and don't have to build it yourself, there are plenty of existing packages that can help you handle role-permission. You don't really needs to build from scratch. For example, depends on the Laravel version you use, you may choose;
spatie/laravel-permission
Bouncer
Zizaco/Entrust
If you would like to implement role management yourself, when you define your relationship, you need to think about if a user would have multiple roles in the future. Based on what you show us right now, there are only client and admin role. Looks like a user would only be either client or admin but not both. And if you are sure those are the only two roles and a user would be either one, you don't need to have roles table at all. You can just add a boolean column such as is_admin in users table to flag the role.
But let's say you will have more roles, and a user can have multiple roles at the same time. Then you DO need to define a many to many relationship. Other answers already provide example on that pretty well. I would also suggest to define a universal role-handling model function to check all roles. In your User.php model,
public function hasRole($role)
{
// check if user have any of the specified roles
if (is_array($role)) {
foreach($role as $r) {
if ($this->roles->contains('role_name', $r)) {
return true;
}
}
return false;
} else {
return $this->roles->contains('role_name', $role);
}
}
That way, in anywhere in your App, you can check your user role by calling
Auth::user()->hasRole('admin');
or check if user contains any role in a list by calling
Auth::user()->hasRole(['admin', 'client']);
Here is my problem. I have a table called user and table called skills, also I have a pivot table that connects these two called EmployeeSkill. I am trying to fetch the skills that belong to the user but when i use tinker it returns Base table or view not found: 1146 Table 'pfe_sirh_db.skill_user' doesn't exist (SQL: select skills.*, skill_user.user_id as pivot_user_id, skill_user.skill_id as pivot_skill_id, skill_user.employee_id as pivot_employee_id from skills inner join skill_user on skills.id = skill_user.skill_id where skill_user.user_id = 1)' and am using swagger by the way it returns this "withTimestamps": false
class User extends Authenticatable{
protected $table = "users";
public function skills()
{
return $this->belongsToMany(Skill::class);
}
}
and
class Skill extends Model
{
public function User()
{
return $this->belongsToMany(User::class);
}
}
and the pivot table
class EmployeeSkill extends Model
{
protected $table = "employee_skills";
protected $fillable = [
'employee_id', 'skill_id', 'note'
];
}
Your pivot table should be named skill_user (singular) following the model name they belong to. And you don't need a EmployeeSkill model for this relation to work, you only need the table.
As said in the documentation:
to determine the table name of the relationship's joining table, Eloquent will join the two related model names in alphabetical order. However, you are free to override this convention. You may do so by passing a second argument to the belongsToMany method
And then you should exclude this line:
protected $table = "employee_skills";
change your table name to skill_user
And in your pivot table you should use user_id as a foreign key
You can retrieve all skills from a user by doing this:
$user = User::find(1);
$user->skills();
This will give you all the skills from the user with the id 1
Be aware that you can use any table name and any foreign key name but if you do that you need to specify the names on the relations. So i recommend you doing what i 've told you, since is the standard way
I have two servers and both of them contain several tables. Many of them contain relations. Now I need to join those tables and fetch data. I have no clue how to write this sort of query. Currently, I'm working in Laravel. Any suggestions will help me.
Thanks in advance.
If you want to use model relationships you can add connection and table field in your model;
class User extends Model {
public $connection = 'firstconnection';
public $table = 'users';
...
public function comments() {
return $this->hasMany(Comment::class);
}
}
class Comment extends Model {
public $connection = 'secondconnection';
public $table = 'comments';
...
}
You can define connections in your config/database.php, default connection is mysql.
If you write raw queries you can use full table path (specify database):
SELECT * FROM db1.users JOIN db2.comments ON db1.users.id = db2.comments.user_id;
Note: you must have enough privileges on both tables to join and select data. If you use exists, has or semething like that where ORM needs to join two table.
Hope this helps you
I Have three tables
#1 Table timeline which is my reference table with an Auto incremented ID which is stored in column id
#2 timeline_videos
#3 timeline_else
What happens is on post if a video is uploaded with the post
it will go into Table #2 ,anything else goes into Table #3.
#2-3 have the Auto Increment Id from the Table timeline stored in a column pid
On query of The Timeline I need to join both tables data using id=pid
so I can use the rest of the Relational Data with the post.
I have done a bit of research and can't seem to find a method for doing so.
So far the code I have
Controller
$groupposts = timeline::where([
['owner','=',$owner],['id','<',$lastid],
])
->join('timeline_videos','timeline.id','=','timeline_videos.pid')
//->join('timeline_else','timeline.id','=','timeline_else.pid')
->orderBy('id','desc')
->limit(5)
->get();
This works with no errors with the second Join commented out but I need to also grab the timeline_else data .
Update --
I have now decided to use Eloquent Relationships to join the tables,
my question now is what type of relationship do I have between the
tables?
I realize it basically needs to be able to switch between two tables to
grab data based on the fact that timeline_videos and timeline_else will not be "JOIN" but separated by type .
The tables need to Join with table #1 timeline based on a column I now have named type for clarifying where to look and matching/joining using the id = pid
You can use relationships.
it sounds like timelines has many videos and has many video failures
https://laravel.com/docs/5.5/eloquent-relationships#one-to-many
you would have a model for each table and set up the relationships
timelines model:
public function videos()
{
return $this-> hasMany('App\Videos');
}
public function videoFailures()
{
return $this-> hasMany('App\videoFailures');
}
videos model:
public function timeline()
{
return $this->belongsTo('App\Timelines');
}
videos failures model:
public function timeline()
{
return $this->belongsTo('App\Timelines');
}
You can then go:
$timeLine = Timmeline::find($id);
to find videos of the time lines you would do:
$videos = $timeLine->videos();
to find else:
$videoElse = $timeLine-> videoFailures();
By using some of what Parker Dell supplied and a bit more trial and error
My Models Looks like
timeline
class timeline extends Model
{
protected $table ='timeline';
public $timestamps = false;
public function videos()
{
return $this->hasMany('App\timeline_videos','pid','id');
}
public function else()
{
return $this->hasMany('App\timeline_ect','pid','id');
}
}
timeline_ect.php ,I changed the name of the table
class timeline_ect extends Model
{
protected $table='timeline_ect';
public $timestamps = false;
public function timeline()
{
return $this->belongsTo('App\Models\timeline','pid','id');
}
}
timeline_videos
class timeline_videos extends Model
{
protected $table='timeline_videos';
public $timestamps = false;
public function timeline()
{
return $this->belongsTo('App\timeline','id','pid');
}
}
Then Lastly my Controller
$timeline = timeline::with('videos','else')
->orderBy('id','desc')
->limit(5)
->get();
So far no Problem query is correct.
My contacts model is not hiding fields. This worked in Laravel4 with no problems, so did something change? am i doing it wrong?
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Contact extends Model
{
protected $table = 'users';
protected $hidden = ['confirmation_code', 'confirmed', 'status'];
protected $dates = ['deleted_at'];
}
I have even tried the reverse by only allowing certain fields
protected $visible = ['lName', 'fName', 'company', 'phone']
Could the problem be that my Contacts model is using the same table as my Users model? If thats the case. Whats the right way to have 2 models use the same table?
I missed the fact that i had 2 different contact models... in different namespaces.. I was referencing the wrong namespace. So everything is working like it should. It was all me not paying attention.