Laravel 5 SQL Relations - php

I have some problem with a query in Laravel.
I want to create a filter query by column in server table but I don't know how to do this.
Need help to modify the following line:
$data = $video->files()->with('server')->get();
Model: server
public function files()
{
return $this->hasMany('App\Models\File', 'id', 'server_id');
}
Model: file
public function server()
{
return $this->belongsTo('App\Models\Server', 'server_id', 'id')->ordered();
}
My current query return this:
(but it returned all, I need return only FILE flitered by type = 6 which is server table) I don;t know how to do this. On screen below data about type server is on #relations array

Use WhereHas. Takes the relation name and a closure as a parameter, where you can define sub query like functionality with the relation.
$data = $video->files()->with('server')->whereHas('server', function ($query)
{
$query->where('type', '6');
})->get();

Related

What is the best way to select comments with user data and additional with Laravel?

Im trying to make list with comments, including data about user and his car, that are stored in another tables.
Controller contained such query:
Charging::available()
->with('some.ports', 'shares')
->find($id);
And I rewrote it to such:
Charging::available()
->with('some.ports', 'shares')
->with('chargingComments.user')
->with('chargingComments.car')
->find($id);
Thats how ChargingComments model looks like:
public function comments() {
return $this->belongsTo(\App\Models\Charging::class);
}
public function user() {
return $this->belongsTo(\App\Models\User::class);
}
public function car() {
// here 'id' is the row in the table with users cars
// 'car_id' is in the table with comments
return $this->hasOne(\App\Models\UserCar::class, 'id', 'car_id');
}
It returns me data about each comments` user and his car, but btw I have to somehow limit result to 10 rows. I tried to add
'user' => function($query) {
return $query->take(10);
}])
But it didnt work.
Im sure that should be the better way to write this code, but dont know how
try this
Charging::with('some.ports', 'shares')->with('chargingComments.user')->with('chargingComments.car')->find($id)->latest()->take(10)->get();

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: return JSON model with relation

I'm trying to query a model with a relation.
My method:
public function getLabel($layerId)
{
$labelGroups = Forum_label_group::
join('forum_layer_to_labels', function ($join) use ($layerId) {
$join->on('forum_layer_to_labels.layerId', '=', 'forum_label_groups.id');
})->with('labels')->get()->toJson();
return $labelGroups;
}
The output:
[{"id":4,"name":"Demogruppe","description":"bla","required":1,"created_at":"2016-10-22 12:29:27","updated_at":"2016-10-22 12:29:27","labelGroupId":2,"layerId":2,"labels":[]},{"id":5,"name":"Demogruppe 2","description":"bla 2","required":0,"created_at":"2016-10-22 12:29:27","updated_at":"2016-10-22 12:29:27","labelGroupId":2,"layerId":3,"labels":[]}]
As you can see, the label relation is empty.
Now I'm trying to query a single model instead off all:
public function getLabel($layerId)
{
return Forum_label_group::with('labels')->first()->toJson();
}
the new output:
"{"id":2,"name":"Demogruppe","description":"bla","required":1,"created_at":"2016-10-22 12:29:27","updated_at":"2016-10-22 12:29:27","labels":[{"id":5,"title":"Demo rot","name":"demo-rot","typeId":3,"groupId":2,"created_at":"2016-10-22 12:29:47","updated_at":"2016-10-22 12:29:47"},{"id":6,"title":"Demoblau","name":"demoblau","typeId":1,"groupId":2,"created_at":"2016-10-22 12:30:03","updated_at":"2016-10-22 12:30:03"}]}"
And as you can see now, everything is fine. The whole relation exists. Is there a problem with the initial query? The relation seems to be ok.
And of course it was an small issue ;)
I forgot to add a select() on my query. The original id has been overwritten by the join(). So the method tried to query an labelGroup that doesn't exist.
The correct query:
public function getLabel($layerId)
{
$labelGroups = Forum_label_group::
join('forum_layer_to_labels', function ($join) use ($layerId) {
$join->on('forum_layer_to_labels.layerId', '=', 'forum_label_groups.id');
})->select('forum_label_groups.*')->with('labels')
->get();
return $labelGroups;
}

Laravel Multiple Models Eloquent Relationships Setup?

I have 3 models
User
Pick
Schedule
I'm trying to do something like the following
$picksWhereGameStarted = User::find($user->id)
->picks()
->where('week', $currentWeek)
->first()
->schedule()
->where('gameTime', '<', Carbon::now())
->get();
This code only returns one array inside a collection. I want it to return more than 1 array if there is more than 1 result.
Can I substitute ->first() with something else that will allow me to to return more than 1 results.
If not how can I set up my models relationship to allow this to work.
My models are currently set up as follow.
User model
public function picks()
{
return $this->hasMany('App\Pick');
}
Schedule model
public function picks()
{
return $this->hasMany('App\Pick');
}
Pick model
public function user()
{
return $this->belongsTo('App\User');
}
public function schedule()
{
return $this->belongsTo('App\Schedule');
}
Since you already have a User model (you used it inside you find method as $user->id), you can just load its Pick relationship and load those Picks' Schedule as follows:
EDIT:
Assuming you have a schedules table and your picks table has a schedule_id column. Try this.
$user->load(['picks' => function ($q) use ($currentWeek) {
$q->join('schedules', 'picks.schedule_id', '=', 'schedules.id')
->where('schedules.gameTime', '<', Carbon::now()) // or Carbon::now()->format('Y-m-d'). See what works.
->where('picks.week', $currentWeek);
}])->load('picks.schedule');
EDIT: The code above should return the user's picks which have a schedules.gameTime < Carbon::now()
Try it and do a dump of the $user object to see the loaded relationships. That's the Eloquent way you want.
Tip: you may want to do $user->toArray() before you dump $user to see the data better.
EDIT:
The loaded picks will be in a form of Collections so you'll have to access it using a loop. Try the following:
foreach ($user->picks as $pick) {
echo $pick->schedule->gameTime;
}
If you only want the first pick from the user you can do: $user->picks->first()->schedule->gameTime
I think a foreach loop may be what you're looking for:
$picks = User::find($user->id)->picks()->where('week', $currentWeek);
foreach ($picks as $pick){
$pickWhereGameStarted = $pick->schedule()->where('gameTime', '<', Carbon::now())->get();
}
Try this and see if it's working for you

Creating a query in Laravel by using `with` and `where`

I'm wondering it would be possible to add a where condition to a with.
Such as:
Comment::with('Users')->where('allowed', 'Y')->get();
I was trying to find a more simple way to make queries avoiding the whereHas method which looks quite verbose:
$users = Comment::whereHas('users', function($q)
{
$q->where('allowed', 'Y');
})->get();
The raw query I want internally to generate should be like so:
select * from comments, users
where users.id = comments.user_id and
users.allowed = 'Y'
I'm used to work with CakePHP in which this queries look very simple:
$this->Comments->find('all', array('Users.allowed' => 'Y'));
The relationships I have defined are:
//Comments.php
public function Users()
{
return $this->belongsTo('Users');
}
//Users.php
public function Comments(){
return $this->hasMany('Comments');
}
You may try this
$users = User::with(array('comments' => function($q)
{
$q->where('attachment', 1);
}))->get();
Update : Alternatively you may use a where clause in your relationship in your User model
// Relation for comments with attachment value 1
// and if hasMany relation is used
public function commentsWithAttachment()
{
return $this->hasMany('Comment')->where('attachment', 1);
}
// Relation for all comments
// and if hasMany relation is used
public function comments()
{
return $this->hasMany('Comment');
}
So, you can just use
// Comments with attachment value 1
User::with('commentsWithAttachment')->get();
// All comments
User::with('comments')->get();
Update : I think you want all comments with users where attachment is 1, if this what you want then it should be Comment not User
Comment::with('user')->where('attachment', 1)->get();
In this case your relation should be
public function user()
{
return $this->belongsTo('User'); // if model name is User
}
Because one comment belongs to only one user.

Categories