I have two tables, users and projects and another table called users_project used to perform many to many relation.
Users columns: id, firstname, lastname, ... (others not important for this post)
Projects columns: id, commercial, name, ... (others not important for this post)
Users_project: user_id, project_id
In my User model class I have two methods:
public function commercial_projects() {
return $this->hasMany('App\Project', 'commercial', 'id');
}
and
public function assigned_projects() {
return $this->belongsToMany(
'App\Project',
'users_project',
'user_id',
'project_id'
);
}
This methods works fine.
The first return how many projects have the user involved as commercial user
The second return how many projects are assigned to user
I want to have another method that return all project where user are involved in (commercial or assigned)
I wrote this method as follow:
public function projects() {
return $this -> assigned_projects() -> union( $this->commercial_projects() );
}
But I have the error caused by pivot column added by belongsToMany relationship:
"SQLSTATE[21000]: Cardinality violation: 1222 The used SELECT statements have a different number of columns (SQL: (select `projects`.*, `users_project`.`user_id` as `pivot_user_id`, `users_project`.`project_id` as `pivot_project_id` from `projects` inner join `users_project` on `projects`.`id` = `users_project`.`project_id` where `users_project`.`user_id` = 13) union (select * from `projects` where `projects`.`commercial` = 13 and `projects`.`commercial` is not null))"
How can I remove pivot column from database request or find a way to fix this issue?
Tnx a lot
I think you need to select only projects columns of assigned_projects
->select("projects.*")
To be like this
return $this->assigned_projects()->select("projects.*")->union($this->commercial_projects());
Related
I'm attaching user permissions to several different models using a polymorphic many-to-many relationship - the pivot table is called permissables:
Permissables
- id
- user_id
- permissable_type
- permissable_id
- role
- created_at
- updated_at
This is working well - I can return a list of permitted users for my Project model using:
public function users()
{
return $this->morphToMany(User::class, 'permissable')->withPivot('role')->withTimestamps();
}
If I want to filter this to just owners - ie where the pivot field role is equal to owner - I can do the following in Tinker:
Project::first()->users->where('pivot.role', '=', 'owner')
But if I try this in my model:
public function owners()
{
return $this->users()->where('pivot.role', '=', 'owner');
}
when I call Project->owners in Tinker, I get an error:
>>> Project::first()->owners
[!] Aliasing 'Project' to 'App\Project' for this Tinker session.
Illuminate/Database/QueryException with message 'SQLSTATE[HY000]: General error: 1 no such column: pivot.role (SQL: select "users".*, "permissables"."permissable_id" as "pivot_permissable_id", "permissables"."user_id" as "pivot_user_id", "permissables"."permissable_type" as "pivot_permissable_type", "permissables"."role" as "pivot_role", "permissables"."created_at" as "pivot_created_at", "permissables"."updated_at" as "pivot_updated_at" from "users" inner join "permissables" on "users"."id" = "permissables"."user_id" where "permissables"."permissable_id" = 1 and "permissables"."permissable_type" = App/Project and "pivot"."role" = owner)'
For the first question:
return $this->users->where('pivot.role', '=', 'owner');
This line works because $this->users return the users' collection with pivot(The key name is pivot). And you are using where-method to filter users collection.
For the second question:
$this->users() change the laravel code to raw sql like:
select users.*, permissables.permissable_id as pivot_permissable_id, permissables.user_id as pivot_user_id, permissables.permissable_type as pivot_permissable_type, permissables.role as pivot_role, permissables.created_at as pivot_created_at, permissables.updated_at as pivot_updated_at
from users
inner join permissables on users.id = permissables.user_id
where permissables.permissable_id = 1 and permissables.permissable_type = "App/Project"
So the Intermediate Table name is permissables, not pivot. Mysql cannot found the intermediate's column when applying where('pivot.role', 'owner') to users().
Laravel provide wherePivot method, which will add intermediate table name for you.
public function owners()
{
return $this->users()->wherePivot('role', 'owner'); // change to where pemissables.role = 'owner'
}
I am learning relationships in Laravel php framework and I am trying to build this query
SELECT * FROM users u INNER JOIN link_to_stores lts ON u.id=lts.user_id INNER JOIN stores s ON lts.store_id=s.store_id WHERE lts.privilege = 'Owner'
I built this in Model
Link_to_store.php
public function store()
{
return $this->belongsTo('App\Store');
}
public function user()
{
return $this->belongsTo('App\User');
}
User.php
public function store_links()
{
return $this->hasMany('App\Link_to_store');
}
Store.php
public function user_links()
{
return $this->hasMany('App\Link_to_store');
}
I tried this query but this only joins user and link_to_store table
$personal_stores = Auth::user()->store_links->where('privilege','=','Owner');
Now I am confused how to join store table too. Can anyone help with this?
Schema is like this
Stores Table
store_id store_name
Users Table
id name
Link_to_stores Table
id store_id user_id privilege
I suppose store_links is actually a pivot table. In this case, you can use belongsToMany(), this will automatically take care of the pivot table.
To do this, in your User model you change the store function to this:
function stores() {
return $this->belongsToMany('App\Store', 'store_links', 'user_id', 'store_id')->withPivot('privilege');
}
Because the primary key of stores is not id, you will have to define this in you Store model with the following line:
protected $primaryKey = 'store_id';
Now to get the stores for a user, you simply call
$stores = Auth::user->stores()->wherePivot('privilege', 'Owner')->get();
I am learning relationships in Laravel php framework and I am trying to build this query
SELECT * FROM users u INNER JOIN link_to_stores lts ON u.id=lts.user_id INNER JOIN stores s ON lts.store_id=s.store_id WHERE lts.privilege = 'Owner'
You are trying to do a join here. You can do a join like this:
$stores = User::join('link_to_stores as lts', 'users.id', '=', 'lts.user_id')->join('stores as s', 'lts.store_id', '=', 's.id')->where('lts.privilege', 'Owner')->get();
But like Jerodev pointed out, it seems like Many to Many relationship might make more sense in your case. The difference is that relationship will actually execute 2 queries (1 for original model, 1 for relationship). It will then attach the related models to the original model (which is extremely handy).
I have the following models in my application
User
Group
Task
which have the following relationships
User and Group have a many-to-many relationship
Task and Group have a many-to-many relationship
So basically a user can belong to more than one group and each group can have more than one task.
Following is the table structure.
users
id
name
groups
id
name
tasks
id
name
group_user
id
group_id (foreign key with groups table)
user_id (foreign key with users table)
group_tasks
id
group_id (foreign key with groups table)
task_id (foreign key with tasks table)
Now I want to retrieve all the tasks for the user.
I tried the following approaches and both didn't work.
Approach 1
$user->groups() gives the list of groups for a user
$group->tasks() gives the list of tasks for a group
So I tried
$user->groups()->tasks() but it didn't work.
Approach 2
I tried Has Many Through by adding this to my User model
public function tasks()
{
return $this->hasManyThrough(Task::class, Group::class);
}
but even that didn't work. The following is the error that I am getting
QueryException in Connection.php line 713:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'groups.user_id' in 'field list' (SQL: select `tasks`.*, `groups`.`user_id` from `tasks` inner join `groups` on `groups`.`id` = `tasks`.`group_id` where `groups`.`user_id` = 1)
My guess is that this is happening because it is expecting one-to-many relationship, but I have a many-to-many relationship.
So is there a way to retrieve it without getting all groups and then looping through them?
User Model
public function groups()
{
return $this->belongsToMany('App\Group');
}
Group Model
public function tasks()
{
return $this->belongsToMany('App\Task');
}
Task Model
public function groups()
{
return $this->belongsToMany('App\Group');
}
Retrieving all tasks for a user.
$user = User::find(1);
$user->load('groups.tasks');
$tasks = $user->groups->pluck('tasks')->collapse();
You can also take a look at the extension of the HasManyThrough here: https://github.com/staudenmeir/eloquent-has-many-deep
It helps you to retrieve many sub-levels of your relationships.
In your case, it would be
User -> belongsToMany(Groups) -> blongsToMany (Tasks)
just add your method to the user model like:
public function tasks()
{
return $this->hasManyDeep(
'App\Task',['App\Group']
);
}
I have a Pivot table thats used to join two other tables that have many relations per hotel_id. Is there a way I can eagerload the relationship that pulls the results for both tables in one relationship? The raw SQL query, works correctly but when using belongsToMany the order is off.
Amenities Pivot Table
id
hotel_id
distance_id
type_id
Distance Table
id
name
Type Table
id
name
RAW Query (This works fine)
SELECT * FROM amenities a
LEFT JOIN distance d ON a.distance_id = d.id
LEFT JOIN type t ON a.type_id = t.id WHERE a.hotel_id = ?
My "Hotels" Model is using belongsToMany like so
public function distance() {
return $this->belongsToMany('Distance', 'amenities', 'hotel_id', 'distance_id');
}
public function type() {
return $this->belongsToMany('Type', 'amenities', 'hotel_id', 'type_id');
}
This outputs the collection, but they are not grouped correctly. I need to loop these into select fields side by side as entered in the pivot table, so a user can select a "type" and the "distance", but the order is off when using the collection. The raw query above outputs correctly.
Hotels::where('id','=','200')->with('distance', 'type')->take(5)->get();
Ok Solved it. So apparently you can use orderBy on your pivot table. Incase anyone else has this issue this is what I did on both relationships.
public function distance() {
return $this->belongsToMany('Distance', 'amenities', 'hotel_id', 'distance_id')->withPivot('id')->orderBy('pivot_id','desc');
}
public function type() {
return $this->belongsToMany('Type', 'amenities', 'hotel_id', 'type_id')->withPivot('id')->orderBy('pivot_id','desc');
}
It's not really a great practice to include other query building steps in the relationship methods on your models. The relationship method should just define the relationship, nothing else. A cleaner method is to apply eager load constraints. (scroll down a bit) Consider the following.
Hotels::where('id', 200)->with(array(
'distance' => function ($query)
{
$query->withPivot('id')->orderBy('pivot_id','desc');
},
'type' => function ($query)
{
$query->withPivot('id')->orderBy('pivot_id','desc');
},
))->take(5)->get();
If you find that you are eagerly loading this relationship in this way often, consider using scopes to keep things DRY. The end result will allow you to do something like this.
Hotels::where('id', 200)->withOrderedDistance()->withOrderedType()->take(5)->get();
P.S. Your models should be singular. Hotel, not Hotels. The model represents a single record.
Solved by using ->withPivot('id')->orderBy('pivot_id','desc');
Posted answer in the question.
I have 3 tables
User: user_serno(primary key), username, email
Role: role_serno(primary key), name
User_role_pivot: user_role_privot_serno(primary key), user_serno(foreign key), role_serno(foreign key)
And I have a belongsToMany in my UserModel
public function roles() {
return $this->belongsToMany('App\RoleModel',
'USER_ROLE_PIVOT',
'ROLE_SERNO',
'ROLE_SERNO');
I am trying to fetch a user with roles and I keep getting empty result, checked query it's not correct.
$user = UserModel::find(121)->roles;
I can fetch the user, but roles isn't working, below are the queries executed:
First:
select t2.* from ( select rownum AS "rn", t1.* from (select * from USER where USER.USER_SERNO = '121') t1 ) t2 where t2."rn" between 1 and 1
Second:
select ROLE.*, USER_ROLE_PIVOT.ROLE_SERNO as pivot_ROLE_SERNO from ROLE inner join USER_ROLE_PIVOT on ROLE.ROLE_SERNO = USER_ROLE_PIVOT.ROLE_SERNO where USER_ROLE_PIVOT.ROLE_SERNO is null
Why does it have ROLE_SERNO is null ! I think it should be USER_SERNO = 121
The belongToMany function in your UserModel should be defined like this :
public function roles() {
return $this->belongsToMany('App\RoleModel',
'User_role_pivot',
'user_serno',
'role_serno');
For mentioning the associated keys, first mention the current model's(UserModel) key and then the other model's(RoleModel) key. You are using same key in both.