Laravel HasManyThrough not working - php

I want get all merchant users for a specific merchant. Here is my setup;
merchant
- id
merchant_user_permission
- merchant_id (equal to merchant.id)
- user_id (equal to merchant_user.id)
merchant_user
- id
In my code I have tried:
class Merchant extends Model
{
public function merchant_users()
{
return $this->hasManyThrough(MerchantUser::class, MerchantUserPermission::class, 'user_id');
}
}
But the resulting sql is invalid.
string(374) "SQLSTATE[42S22]: Column not found: 1054 Unknown column 'merchant_user.merchant_user_permission_id' in 'on clause' (SQL: select count(*) as aggregate from merchant_user inner join merchant_user_permission on merchant_user_permission.id = merchant_user.merchant_user_permission_id where merchant_user_permission.user_id = 123-3456-7789-qwerty)"
Here is a working sql example:
SELECT mu.`id`, mu.`email`
FROM `merchant` m
LEFT JOIN `merchant_user_permission` mup ON m.id = mup.merchant_id
LEFT JOIN `merchant_user` mu ON mu.id = mup.user_id
WHERE mup.`merchant_id` = '123-3456-7789-qwerty'
How can replicate that using Eloquent 5.4?

try this
Make many to many relationship
class Merchant extends Model
{
public function merchant_users()
{
return $this->belongsToMany('App\ MerchantUser', 'merchant_user_permission', 'merchant_id', 'merchant_user_id');
}
}
then
$permissions = App\Merchant::find($merchant_id)->merchant_users()->get();

Related

Laravel get all data through pivot Table

Hello I've got problem with getting the name of Group which Recipent is member.
I use 3 tables for that subject : recipent, group and group_recipent.
In group_recipent table I've got only columns like recipent_id and group_id. All is correct to Laravel naming convention.
It's my Recipent model:
public function groups()
{
return $this->belongsToMany(Group::class)->withPivot('group_recipent', 'recipent_id', 'group_id');
}
And my Group model:
public function recipents()
{
return $this->belongsToMany(Recipent::class)->withPivot('group_recipent', 'recipent_id', 'group_id');
}
Accessing data:
$user = Recipent::find(4);
$data="";
foreach ($user->groups as $group)
{
$data .= $group->name ." ";
}
dd($data);
After trying to access the name of a group through pivot table I get an error:
Illuminate\Database\QueryException
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'group_recipent.group_recipent' in 'field list' (SQL: select groups.*, group_recipent.recipent_id as pivot_recipent_id, group_recipent.group_id as pivot_group_id, group_recipent.group_recipent as pivot_group_recipent from groups inner join group_recipent on groups.id = group_recipent.group_id where group_recipent.recipent_id = 4)
I wanted to acces the name all names of group which recipent is member of but I get error like above. Have you any idea how to solve that? :/
Try to delete from your models 'group_recipent'. I assume you're wonder that the 1st field is pivot table name. It should look like this:
return $this->belongsToMany(Recipent::class)->withPivot('recipent_id', 'group_id');
If someone got other idea, just add comment below.

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

Laravel 5.8 Eloquent query without using DB::raw() -- get rows based on latest value of many to many relationship

My goal is to select all issues that have been marked as false-positive. The issues are connected to Status via a ManyToMany relationship with pivot table IssuesStatus. The current status of an issue is determined by the value of the status column on the Status table.
I've come up with a solution that works but seems somehow suspect. I'm looking for a way to rewrite the query using Eloquent query builder without relying on the DB::raw() method.
public function getFalsePositivesAttribute() {
return Issue::where(DB::raw(
'( select `status` '.
'from `issues-status` '.
'left join `status` on `status`.id = `issues-status`.status_id '.
'where `issues-status`.issue_id = `issues`.id '.
'order by `issues-status`.id desc limit 1 )'
), '=', 'false-positive')->get();
}
Example of desired SQL query:
SELECT
`Issues`.id
FROM
`issues` AS `Issues`
LEFT JOIN
`issues-status` `IssueStatus` on `Issues`.id = `IssueStatus`.issue_id
LEFT JOIN
`status` as `StatusTable` on `IssueStatus`.status_id = `StatusTable`.id
WHERE
`Issues`.report_id = 2
AND
(
SELECT
`status`
FROM
`issues-status` `IssueStatus`
LEFT JOIN
`status` `StatusTable` on `StatusTable`.id = `IssueStatus`.status_id
WHERE
`IssueStatus`.issue_id = `Issues`.id
ORDER BY
`IssueStatus`.id desc
LIMIT 1
) = 'false-positive'
GROUP BY
`Issues`.id
Models:
class Issue extends Model {
...
public function status() {
return $this->belongsToMany( Status::class, 'issues-status')
->withTimestamps()
->withPivot('note');
}
...
}
class Status extends Model {
...
public function issues() {
return $this->hasMany(Issue::class);
}
...
}
Tables:
Issues:
id - identity
Status
id - identity
status - string
IssueStatus
id - identity
issue_id - relation to Issues
status_id - relation to Status
created_at - timestamp
note - text
If i understood correctly, you want to fetch the issue where the latest status is equals to something.
Add a latestStatus function to your Issues model:
public function latestStatus()
{
return $this->hasMany(IssueStatus::class)->latest();
}
Now, swap your status function with this one:
public function status() {
return $this->belongsToMany( Status::class, 'issues-status')
->withTimestamps()
->withPivot('note');
}
Then:
//Get all issues which has the desired status
Issue::whereHas('status', function($query){
$query->where('id', $desiredStatusId);
})
->get()
->reject(function($issue){
//filter the collection by the latest issue status,
// and reject those who does not have the desired latest status;
return $issue->latestStatus()->first()->status_id != $desiredStatusId;
});
Hope it helps.

Laravel 5 - Find Fields on "MorphOne" Relationship

I'm quite new to Laravel and I'm now facing this issue while trying to create a query:
I have the following Morphable classes:
\App\User
class User {
public function userable()
{
return $this->morphTo();
}
}
\App\Distributor
class Distributor {
public function user()
{
return $this->morphOne('App\User', 'userable');
}
}
user table has the fields: name, email, status, userable_type and userable_id.
distributor table has the fields: store_code and location_id.
By using Eloquent, i need to start the query from Distributor model and select only the following fields: 'name, email, store_code'.
I'm trying the following, but laravel says user.name doesn't exists :(
$queryBuilder = \App\Distributor::has('user');
$queryBuilder->select(['user.name']);
$queryBuilder->get();
QueryException in Connection.php line 651:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'user.name' in 'field list' (SQL: select user.name from distributor where (select count(*) from user where user.userable_id = distributor.id and user.userable_type = App\Distributor and user.deleted_at is null) >= 1)
I was able to achieve my goal forcing the join relationship, but this seems wrong, I think Eloquent is able to find the relation by itself as the Morph relationship is specified in the Model.
Just for record, this works good:
$queryBuilder = \App\Distributor::has('user');
$queryBuilder->join('user', function($join) {
$join->on('userable_id', '=', 'distributor.id')
->where('userable_type', '=', \App\Distributor::class);
});
$queryBuilder->select(['user.name']);
$queryBuilder->get();
Also, since its a one-to-one like relationship, sometimes I'll need to order the results using one of the users columns
But I need another way to do it without forcing the join, something clean as the first example.
read about with() function in the docs
$queryBuilder->select('id','store_code');
$queryBuilder = \App\Distributor::with(['user'=>function($query){
$query->select('id','name','email')
}]);
$queryBuilder->has('user');
$queryBuilder->get();

Has many through with pivot table

A Venue hasMany Subscription
A Subscription belongsToMany User
How can I retrieve all User from Venue through Subscription?
I imagine something like:
$venue->subscribers // Return all `User`
My database scheme: http://www.laravelsd.com/share/AAjuYS
I have tried
class Venue {
public function subscribers() {
return $this->hasManyThrough('App\User', 'App\Subscription');
}
}
Fails with error:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'users.subscription_id' in 'on clause' (SQL: select `users`.*, `subscriptions`.`venue_id` from `users` inner join `subscriptions` on `subscriptions`.`id` = `users`.`subscription_id` where `users`.`deleted_at` is null and `subscriptions`.`venue_id` = 1)
// In Venue class
public function subscribers()
{
return $this->hasManyThrough('App\User', 'App\Subscription');
}
You can read about it in the documentation, it's pretty straight-forward: http://laravel.com/docs/5.1/eloquent-relationships#Has-Many-Through

Categories