3 models in laravel 4 - select with relation whereHas Query - php

I have 3 models:
User
Planet
Sunsystem
Relations:
In the User Model:
public function planets() {
return $this->hasMany('Planet');
}
In the Planet Model:
public function sunsystem() {
return $this->belongsTo('Sunsystem');
}
public function user() {
return $this->belongsTo('User');
}
And in the sunsystem Model:
public function planets() {
return $this->hasMany('Planet');
}
Now I want select all the sunsystems with all the related planets
but only the sunsystems where the related planets belongs to the actual user (in this example ID 12).
But how did I get the correct result?
This is what I try, but it gives me only ONE sunsystem, but I expect 2 sunsystem. I think my query is wrong...:
$s = Sunsystem::whereHas('Planets', function($q) {
$q->whereHas('User', function ($u) {
$u->whereUserId(12);
});
})->get();
This also doesn't work:
$s = Sunsystem::select('Sunsystems.*')
->join('planets','planets.sunsystem_id','=','sunsystems.id')
->join('users','users.id','=','planets.user_id')
->where('users.id','=',12)
->get();
If there are 2 planets with different sunsystem_id but the same user_id I get only one sunsystem, but I expect two.

Please Try it
$s = Sunsystem::select('Sunsystems.*')
->join('planets','planets.sunsystem_id','=','Sunsystems.id')
->join('users','users.id','=','planets.user_id')
->where('users.id','=',12)
->get();

Related

laravel query relationship to check if data exists

I have a set up like this,
QUIZ MODEL
public function scores()
{
return $this->hasMany('App\Score');
}
SCORE MODEL
public function quiz()
{
return $this->belongsTo('App\Quiz');
}
public function user()
{
return $this->belongsTo('App\User');
}
USER MODEL
public function scores()
{
return $this->hasMany('App\Score');
}
Some background, a quiz should only be playable by a user if that user does not already have a score for said quiz, what I am wanting to do is that if a user has a relationship with a quiz via having a score I want to stop that quiz being return in the query, here is my attempt,
$quiz = Quiz::with('questions.answers')
->has('scores.user', 2)
->whereDate('date_playable', '=', $date)
->first();
However this returns no quizes regardless of whether the user has a score for it or not. Can anyone enlighten me on how to only return quizes that a user does not currently have a score for?
You are currently searching for a quiz that does not have more than 2 scores for any user.
What you need is whereDoesntHave instead:
$quiz = Quiz::with('questions.answers')
->whereDoesntHave('scores', function ($query) use ($user) {
$query->where('user_id', $user->id);
})
->whereDate('date_playable', '=', $date)
->first();
Where $user is the App\User instance that you are querying for.
There could be multiple approaches to achieve that outcome. I am thinking about creating a many to many relationship between Quizzes and Users, taking scores as the middle table.
User
{
public function quizzes()
{
return $this->belongsToMany(Quiz::class, 'scores');
}
}
Then to get the desired quiz:
$quiz = Quiz::with('questions.answers')
->whereKeyNot($user->quizzes()->pluck('id')->all())
->whereDate('date_playable', '=', $date)
->first();

Laravel 5.4 - Return single row from joined table

I have model Album (table albums) and model Photo (table photos). When I'm fetching all albums to page, I would like to display first photo from each album as album image.
Album model:
public function photos()
{
return $this->hasMany('App\Photo');
}
Photo model:
public function album()
{
return $this->belongsTo('App\Album');
}
Query:
public function getAlbums()
{
return DB::table('albums')
->join('photos', 'albums.id', '=', 'photos.album_id')
->select('photos.title', 'albums.title AS album_title', 'albums.slug AS album_slug')
->get();
}
It returns all photos with album title. How to modify the query to return only first photo from each album? How to use LIMIT 1 in joined table? Or is there another way to do this? Thanx.
Reslut
Why not do things the Laravel way instead of raw DB queries? There's a trick to doing exactly this. First in your Album model, add a method (I use latest() but maybe you want to do an oldest(), or some other limit described in the docs
):
public function firstPhoto() {
return $this->hasOne('App\Photo')->latest();
}
Now you can do:
public function getAlbums() {
return Albums::with('firstPhoto')->get();
}
Replace get() with first() since get returns an object instance, first will return a single instance;
public function getAlbums()
{
return DB::table('albums')
->join('photos', 'albums.id', '=', 'photos.album_id')
->select('photos.title', 'albums.title AS album_title', 'albums.slug AS album_slug')
->first()
}

How OrderBy afterwards HasMany and BelongsTo Relationships

I created a relationship between the "Review, Games and Info" tables, unfortunately, though, the main table is Games, and he orders me all for Games, While I would like to order the ID of "review" table.
Models: Review
public function games()
{
return $this->hasMany('App\Models\Giochi', 'id_gioco', 'id');
}
Models: Giochi
public function review()
{
return $this->belongsTo('App\Models\Review', 'id', 'id_gioco');
}
public function infogiochi()
{
return $this->belongsTo('App\Models\InfoGiochi', 'id', 'id_gioco');
}
Models: InfoGiochi
public function games()
{
return $this->hasMany('App\Models\Giochi', 'id', 'id_gioco');
}
Controller:
$review = Giochi::with('Review','InfoGiochi')->orderBy('id','DESC')->get();
Here is a screenshot of my json:
How do I order content for review table IDs?
You have 2 options. You use a join and order in the sql statement or you order it after retrieving the results in the collection.
Using Join
Giochi::select('giocos.*')
->with('Review','InfoGiochi')
->leftJoin('reviews', 'reviews.id', '=', 'giocos.id_gioco')
->orderBy('reviews.id','DESC')
->get();
Sorting Collection
Giochi::with('Review','InfoGiochi')
->get()
->sortByDesc(function($giochi) {
return $giochi->review->id;
});
This would be the shortest version to sort on the collection:
Giochi::with('review')
->get()
->sortByDesc('review.id');
You can modify your relationship query when you fire it:
Giochi::with([
'Review' => function ($query) { return $query->orderBy('id','DESC'); },
'InfoGiochi'
])->orderBy('id','DESC');
You can try with a raw query or you can use ->orderBy() directly on review function.

Laravel querying multiple related models

For example: I have these models in my application.
User, Profile, Interest.
I linked the users table with the profiles table by adding the user_id column in the profiles table. And I linked profiles and interests by using a pivot table (interest_profile), Which is (as obvious) will have two columns (profile_id, interest_id).
However, I want to query the users who are associated with a profile, too see who is associated with a particular interest, In other words: "select all users who are having (in their profiles) that particular interest".
I know that I can do this with raw SQL by joining the four tables and then use (where clause).. But I want to do it the Laravel way.
Thanks in advance.
First make sure you have your relationships setup correctly on your models like:
class User extends Model
{
public function profile()
{
return $this->hasOne(Profile::class);
}
}
class Profile extends Model
{
public function user()
{
return $this->belongsTo(User::class);
}
public function interests()
{
return $this->belongsToMany(Interest::class, 'interest_profile');
}
}
class Interest extends Model
{
public function profiles()
{
return $this->belongsToMany(Profile::class, 'interest_profile');
}
}
Then you can use whereHas() to constrain a query by a related model and dot notation for nested relations. So your query would be:
User::whereHas('profile.interests', function($query) use ($interestName) {
return $query->where('name', $interestName);
})->get();
That would just return a collection of users. If you wanted to return their profiles and interests as well you would use with():
User::whereHas('profile.interests', function($query) use ($interestName) {
return $query->where('name', $interestName);
})
->with('profile.interests')
->get();
Assuming the User model has a relationship profile and the Profile model has a relationship interests, you can do this.
$interest_id = 1;
$users = User::whereHas('profile', function ($query) use ($interest_id) {
$query->whereHas('interests', function ($query) use ($interest_id) {
$query->where('id', $interest_id);
});
})->get();

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