Laravel 5.1 Where Condition In Many To Many relationships - php

Hello I'm very new in Laravel and, I'm having the following models :
organizations
-----------------
id
name
public function users()
{
return $this->belongsToMany( 'App\User' );
}
users
-----------------
id
name
organization_user
------------------
user_id
organization_id
The there is a many to many relationship between organization and user. Now I want to find the organization where the user belongs to. How can I add a where condition for the user in the relationship.
For example I want to find all the organization of the logged in user. I'm trying the following :
$organisations = Organisation::where('user_id' , '=' , $user->id)->paginate(10);
But its not working because user_id is in my Pivot Table. Please help and Thanks in advance.

I think you need to use wherePivot() function in your code like
$organisations = Organisation::with(array('user'=> function($query) use ($user){
$query->wherePivot('user_id' , '=' , $user->id);
}) )->paginate(10);
May be this can help

I think below code may help you
$organisations = Illuminate\Support\Facades\DB::table('organizations')
->join('organization_user', 'organizations.id', '=', 'organization_user.organization_id')
->join('users', 'users.id', '=', 'organization_user.user_id')
->where('users.id', '=', $user->id)
->paginate(10);

I think you need to add this to your User Model
public function organizations()
{
return $this->belongsToMany('App\Organization');
}
and then will be something simple like this
$organizations = Auth()->user()->organizations;

First of all add your relations in your models:
User.php
public function organizations()
{
return $this->hasMany(organization_user::class);
}
Or
public function organizations()
{
return $this->hasManyThrough(organizations::class,organization_user::class);
}
I recommend the second one. For more information follow this:
Laravel hasManyThrough
Please note that use same idea for organization model to create the relation

Related

Laravel Spatie Query Builder - Wrong query when make join

I have a problem with packages spatie/Laravel-query-builder. I used this package for easy way to filter and sort my query but it's not like that :D
I trying to filter result who have two relation - shop and employee. In short, it wants to filter a list of store reports
Look, this is my code. When I use join method then in response receives data with incorrect ID. When comment join methods all it's okey but I need sorting by relation.
return ReportControlResource::collection(
QueryBuilder::for(Report::class)
->with(['employee', 'shop'])
->allowedFilters('shop.name', 'employee.name')
->join('employees', 'employees.id', '=', 'reports.employee_id')
->join('shops', 'shops.id', '=', 'reports.shop_id')
->allowedSorts(['employees.name'])
->get()
);
My relation in Report model:
public function shop(): BelongsTo
{
return $this->belongsTo(Shop::class);
}
public function employee(): BelongsTo
{
return $this->belongsTo(Employee::class);
}
Relation in Shop model:
public function reports()
{
return $this->hasMany(Report::class);
}
And in Employee model
public function reports()
{
return $this->hasMany(Report::class);
}
Do you have any ideas?
I noticed that the ID is being overwritten, but not shop_id and employee_id, but the ID why??
I think the problem is with the library itself. The creators did not take into account the reverse situation of joining tables as in my case.
Look at example from Doc:
$addRelationConstraint = false;
QueryBuilder::for(User::class)
->join('posts', 'posts.user_id', 'users.id')
->allowedFilters(AllowedFilter::exact('posts.title', null, $addRelationConstraint));
And my join
->join('employees', 'employees.id', '=', 'reports.employee_id')
But this join work like this
->join('employees', 'reports.id', '=', 'reports.employee_id')
But why? I checked the order in many ways, even disconnecting filtering and it did not change anything
I found solution for the problem. I had to select a column and now all works fine :-)
return EvidenceControlResource::collection(
QueryBuilder::for(EvidenceControl::class)
->allowedIncludes('employee', 'shop')
->select('evidence_controls.*', DB::raw('employees.id as employee_id'))
->join('employees', 'evidence_controls.employee_id', '=', 'employees.id')
->select('evidence_controls.*', DB::raw('shops.id as shop_id'))
->join('shops', 'evidence_controls.shop_id', '=', 'shops.id')
->allowedFilters('shop.name', 'employee.name')
->allowedSorts(['employees.name', 'shops.name'])
->get()
);
This is not clean code, but when create own class with implements Sort the code might look a lot better.
Thanks Guy's for help! Good luck! :-)

Laravel 5.6.8 Eloquent and many to many + many to one joins

I have many to many connect with between user - cityarea.
I have also area which connect cityarea (One cityarea can connect only one area).
I have this database structure:
users
id
username
password
cityareas
id
name
area_id
cityarea_user
id
cityarea_id
user_id
areas
id
name
Next I have Models
User
public function cityareas()
{
return $this->belongsToMany('App\Cityarea');
}
Cityarea
public function area()
{
return $this->belongsTo('App\Area');
}
public function users()
{
return $this->belongsToMany('\App\User');
}
Area
public function cityareas()
{
return $this->hasMany('App\Cityarea');
}
QUESTION:
How I can get all users where areas.name = "South" with Eloquent ?
Thanks!!
By using whereHas, you can do:
$users = User::whereHas('cityareas.area', function ($query) {
$query->where('name', 'South');
})->get();
Jeune Guerrier solution is perfect, but you can use with() method of eloquent If you also need cityarea collection along with users collection.
$users = User::with('cityareas')->whereHas('cityareas.area', function ($query) {
$query->where('name', 'South');
})->get();
This is exactly what the belongs to many relationships is built for.
You simply have to do, Cityarea::where('name', 'South')->first()->users;
If you want to do something further with the query, e.g. sort by users created at, you can do
Cityarea::where('name', 'South')->first()->users()->orderBy('creaated_at', desc')->get();
Note that if there is no such Cityarea with name 'South', the ->first() query above will return null and therefore will fail to fetch the users.
A more performant way to do it programmatically is to use the whereHas approach as discussed in the comments below.

Laravel 5.5 three table join

Edit: I am using API routes, so i have no views and such
I have a database like this
User:
id
name
card:
id
card_number
user_id
swipe:
id
time
card_number
the relationships go as followed user has one card one card has many swipe
the joins would be as such
user.id = card.user_id
card.card_number = swipe.card_number
In Larvel i have 3 models.
user
card
swipe
user Model
class user extends Model
{
public function card()
{
return $this->hasOne('App\card','user_id','id');
}
}
in a controller if i do
$model = user::with('card')->get();
i get the card data joined with the user data as expected.
class card extends Model
{
public function person()
{
return $this->belongsTo('App\user');
}
public function swipe(){
return $this->hasMany('App\swipe','card_number','card_number');
}
}
if i now do
$model = user::with('card.swipe')->get();
which i would expect to return all the users with their cards, and swipes associated to that card.
However what i get is
500 internal server error
What am i doing wrong here?
For Example you can use like this.
$shares = DB::table('shares')
->join('users', 'users.id', '=', 'shares.user_id')
->join('follows', 'follows.user_id', '=', 'users.id')
->where('follows.follower_id', '=', 3)
->get();
You cant use like this,
user::with('card.swipe')->get();
Use Has many Through
https://laravel.com/docs/5.5/eloquent-relationships#has-many-through
user - countries (in docs)
card - users (in docs)
swipe - posts (in docs)
$country->posts;
$user->swipe;
I don't know if this is right solution, but you can try:
public function swipe(){
return $this->hasMany('App\swipe')->where('card_number','card_number');
}
You should be able to extract your data in your controller like this:
return user::with(['card' => function($query) {
$query->with('swipe');
}])->get();
Make sure your swipe model "belongsTo('App\card')".

laravel eloquent sort by relationship

I have 3 models
User
Channel
Reply
model relations
user have belongsToMany('App\Channel');
channel have hasMany('App\Reply', 'channel_id', 'id')->oldest();
let's say i have 2 channels
- channel-1
- channel-2
channel-2 has latest replies than channel-1
now, i want to order the user's channel by its channel's current reply.
just like some chat application.
how can i order the user's channel just like this?
channel-2
channel-1
i already tried some codes. but nothing happen
// User Model
public function channels()
{
return $this->belongsToMany('App\Channel', 'channel_user')
->withPivot('is_approved')
->with(['replies'])
->orderBy('replies.created_at'); // error
}
// also
public function channels()
{
return $this->belongsToMany('App\Channel', 'channel_user')
->withPivot('is_approved')
->with(['replies' => function($qry) {
$qry->latest();
}]);
}
// but i did not get the expected result
EDIT
also, i tried this. yes i did get the expected result but it would not load all channel if there's no reply.
public function channels()
{
return $this->belongsToMany('App\Channel')
->withPivot('is_approved')
->join('replies', 'replies.channel_id', '=', 'channels.id')
->groupBy('replies.channel_id')
->orderBy('replies.created_at', 'ASC');
}
EDIT:
According to my knowledge, eager load with method run 2nd query. That's why you can't achieve what you want with eager loading with method.
I think use join method in combination with relationship method is the solution. The following solution is fully tested and work well.
// In User Model
public function channels()
{
return $this->belongsToMany('App\Channel', 'channel_user')
->withPivot('is_approved');
}
public function sortedChannels($orderBy)
{
return $this->channels()
->join('replies', 'replies.channel_id', '=', 'channel.id')
->orderBy('replies.created_at', $orderBy)
->get();
}
Then you can call $user->sortedChannels('desc') to get the list of channels order by replies created_at attribute.
For condition like channels (which may or may not have replies), just use leftJoin method.
public function sortedChannels($orderBy)
{
return $this->channels()
->leftJoin('replies', 'channel.id', '=', 'replies.channel_id')
->orderBy('replies.created_at', $orderBy)
->get();
}
Edit:
If you want to add groupBy method to the query, you have to pay special attention to your orderBy clause. Because in Sql nature, Group By clause run first before Order By clause. See detail this problem at this stackoverflow question.
So if you add groupBy method, you have to use orderByRaw method and should be implemented like the following.
return $this->channels()
->leftJoin('replies', 'channels.id', '=', 'replies.channel_id')
->groupBy(['channels.id'])
->orderByRaw('max(replies.created_at) desc')
->get();
Inside your channel class you need to create this hasOne relation (you channel hasMany replies, but it hasOne latest reply):
public function latestReply()
{
return $this->hasOne(\App\Reply)->latest();
}
You can now get all channels ordered by latest reply like this:
Channel::with('latestReply')->get()->sortByDesc('latestReply.created_at');
To get all channels from the user ordered by latest reply you would need that method:
public function getChannelsOrderdByLatestReply()
{
return $this->channels()->with('latestReply')->get()->sortByDesc('latestReply.created_at');
}
where channels() is given by:
public function channels()
{
return $this->belongsToMany('App\Channel');
}
Firstly, you don't have to specify the name of the pivot table if you follow Laravel's naming convention so your code looks a bit cleaner:
public function channels()
{
return $this->belongsToMany('App\Channel') ...
Secondly, you'd have to call join explicitly to achieve the result in one query:
public function channels()
{
return $this->belongsToMany(Channel::class) // a bit more clean
->withPivot('is_approved')
->leftJoin('replies', 'replies.channel_id', '=', 'channels.id') // channels.id
->groupBy('replies.channel_id')
->orderBy('replies.created_at', 'desc');
}
If you have a hasOne() relationship, you can sort all the records by doing:
$results = Channel::with('reply')
->join('replies', 'channels.replay_id', '=', 'replies.id')
->orderBy('replies.created_at', 'desc')
->paginate(10);
This sorts all the channels records by the newest replies (assuming you have only one reply per channel.) This is not your case, but someone may be looking for something like this (as I was.)

Laravel get a single row from hasMany Relation

I have the following database Structure:
users
________
id
user_fields
________
id
name
user_field_values
________
id
user_id
field_id
value
I have a basic hasMany Relation on UserFieldValues but this looks awful for me, especially because i don't want to iterate the whole Relation just to get a specific user_field_values.value on a specific user_fields.name.
So what i want to get is a user_fields_values.value from the current user where user_fields.name = ?
My idea is to make a belongsToMany Relation but I dont get it to work.
I really would appreciated your help.
Solution
Thanks to Lê Trần Tiến Trung, "load" was the keyword i was looking for.
public function userFields() {
return $this->belongsToMany('\App\Models\UserFields',"user_field_values", 'user_id', 'field_id')->withPivot('value');
}
public function userField($fieldName) {
$this->load(['userFields' => function($q) use ($fieldName) {
$q->where('slug', '=', $fieldName);
}])->first();
}
The easiest way is using belongsToMany, withPivot to get value, add where clause to select correctly relation. Here is example, not tested.
// User.php
public function fields()
{
return $this->belongsToMany('Field', 'user_field_values')->withPivot('value');
}
// query
$users = User::all();
$users->load(['fields' => function($q) use ($fieldName) {
$q->where('name', '=', $fieldName);
});
foreach ($users as $user) {
echo $user->fields->first()->pivot->value;
}

Categories