Laravel Many to many relationship pivot not wowrking - php

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.

Related

Laravel Eloquent fail union between two relations

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());

Laravel use a pivot field to filter polymorphic many to many relationship

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'
}

toSql() on Eloquent Relationship returning `where id = null`

I'm working on 3-dimensional pivot table
Architecture
I've made the following tables and models.
register_colleges( model: RegisterCollege )
- college_id
- random field
programs ( model: Program )
- id
- program_name
courses ( model: Course )
- id
- course_name
course_program_register_college
- course_id
- program_id
- college_id
Now the problem I'm facing is that my view shows null result when I try to access the program_name (say). This is expected by me because when I convert it toSql(), it shows the following query-
select * from 'programs' inner join 'course_program_register_college' on 'programs'.'id' = 'course_program_register_college'.'program_id' where 'course_program_register_college'.'college_id' is null
I'm unable to figure out why this query is trying to fetch data where 'course_program_register_college'.'college_id' is null.
RegisterCollege.php (model)
// pivot table is looking for register_college_id as a primary key field
protected $primarykey = 'college_id';
public function programs(){
return $this->belongsToMany('App\Program',
'course_program_register_college', 'college_id');
}
EDIT:-
Eloquent Query
{{ $college->programs()->toSql() }} // in some view.blade.php

Handling relationship in model in laravel

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).

Retrieve distant relation through has-many-through for many-to-many relation in Laravel

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']
);
}

Categories