laravel eloquent relationship using "with", "whereHas" and "whereIn" clauses - laravel

I have a model "Project" that belongsToMany sites and each site belongsTo a single Locality and each locality belongsTo a single State.
On the other hand I have a user that belongsToMany States
I want to list the projects that are in the same state as the user, so ...
$user = Auth::getUser();
$userStates = array();
foreach($user->profile->states as $singleState){
$userStates[] = $singleState->id;
}
Project::with(['sites','locality','state'])
->whereHas('state', function($q) use($userStates) {
// Query the name field in status table
$q->whereIn('id', $userStates); // '=' is optional
})
I am using OctoberCMS which is built on laravel, However I keep getting an error that says:
Call to undefined method October\Rain\Database\QueryBuilder::state()

The following should get you the projects limited to the authenticated user's states:
$projects = Project::with(['sites.locality.state' => function ($query) {
$query->whereIn('id', auth()->user()->profile->states->pluck('id')->toArray());
}])->get();
update
This should not return projects without a site:
$projects = Project::whereHas('sites.locality.state', function ($query) {
$query->whereIn('id', auth()->user()->profile->states->pluck('id')->toArray());
}])->get();

Related

Laravel: How to limit subquery many-to-many when use pivot

I have model IgAccount, which hasMany model IgPost
Then I have model IgHashtag which is connected via pivot table to IgPost
My pivot table is ig_hashtag_ig_post
Now, i want to get all IgHashtag whereHas IgPost which belongsTo user's IgAccount
and I want to also load this with last 4 IgPost, for each IgHashtag
I have query like:
$igHashtags = IgHashtag::with(['igposts' => function ($query) use ($igAccount) {
$query->where('ig_posts.ig_account_id', $igAccount->id) // filter posts for each hashtag, where posts belongsTo hashtag AND only user's ig_account
->select('media_url')
->orderByDesc('from_ig_timestamp');
//->take(4); //Not working as expected, this take 4 posts for whole collection, I need 4 posts for each hashtag
}])->whereHas('igposts', function ($query) use ($igAccount) {
$query->where('ig_account_id', $igAccount->id); //filter only hashtags, which belongsTo posts which belongsTo users ig account
})
->withCount(['igposts' => function ($query) use ($igAccount) {
$query->where('ig_posts.ig_account_id', $igAccount->id);
}])
->orderByDesc('igposts_count')->paginate(10);
This ->take(4) loads 4 posts but for whole collection. In result, one hashtag has 2 loaded posts and another two 2 loaded post, others zero. And I need 4 posts for each hashtag:
0:Object
hashtag:"fotimGalaxy"
id:3578
igposts:Array[1]
igposts_count:44
1:Object
hashtag:"dnesfotim"
id:3576
igposts:Array[2]
igposts_count:30
2:Object
hashtag:"BLINNKER"
id:3762
igposts:Array[0]
igposts_count:13
3:Object
hashtag:"dnesJem"
id:3575
igposts:Array[1]
igposts_count:12
when ->take(4) is removed from code, works as expected, but it load ALL available Posts for each IgHashtag, right filtered:
data:Array[10]
0:Object
hashtag:"fotimGalaxy"
id:3578
igposts:Array[44]
igposts_count:44
1:Object
hashtag:"dnesfotim"
id:3576
igposts:Array[30]
igposts_count:30
2:Object
hashtag:"BLINNKER"
id:3762
igposts:Array[13]
igposts_count:13
3:Object
hashtag:"dnesJem"
id:3575
igposts:Array[12]
igposts_count:12
How to query this with last 4 posts for each hashtags?
Please try this after fixing the names. BTW, it's really a bad idea to give everything the same prefix.
Hastag::with('posts', function($query) {
$query->where('account_id', Auth::id())->latest()->limit(4)
})->get()->filter(function($hastag, $key) {
return $hastag->posts->count() > 0
});
I found solution.
It is not elegant, but it works as expected.
$igHashtags = IgHashtag::whereHas('igposts', function ($query) use ($igAccount) {
$query->where('ig_account_id', $igAccount->id); //filter only hashtags, which belongsTo posts which belongsTo users ig account
})
->withCount(['igposts' => function ($query) use ($igAccount) {
$query->where('ig_posts.ig_account_id', $igAccount->id);
}])
->orderByDesc('igposts_count')->paginate(10);
$igHashtags->getCollection()->transform(function ($value) use ($igAccount) {
$value->igposts = IgHashtag::where('id', $value->id)
->first()
->IgPosts()
->select('caption', 'media_url')
->where('ig_account_id', $igAccount->id)
->orderByDesc('from_ig_timestamp')
->take(4)
->get();
return $value;
});

Laravel where condition not working on query WITH relation

i have a simple query that looks like this:
$category = Category::with('translation')
->with(['childCategories' => function ($query) {
$query->active();
}])
->where('id', $id)->first();
Scope and relation:
public function scopeActive($query)
{
return $query->where('active', 1);
}
public function childCategories()
{
return $this->hasManyThrough('App\SupremeShop\Models\Category',
'App\SupremeShop\Models\CategoryToCategory',
'id_parent_category', //category_to_categories
'id', //categories
'id',
'id_category'); // category_to_categories.
}
So i am looking for category, some translations and child categories of given "main" category. And query returns 15 child categories. But in query is scope that should only take active ones and it's not working correctly. When i use dd it shows inactive child categories also. I've tried to remove scope and write simple WHERE but result was the same.
Have somebody any ides why condition is not working properly?
I believe the problem is the column is ambiguous since Category and its relation childCategories are both the same model and share the same table.
The active() scope is getting applied to the parent Category instead of the child Categories.
Try and see if this works
$category = Category::from('categories as c')
->with(['childCategories' => function ($query) {
$query->active();
}])
->where('id', $id)->first();

Laravel Query Builder relationship plus no relationship

I currently have this which works for querying a relationship:
$users = User::query();
$post_id = $request->input('post_id');
$users->whereHas('posts', function ($query) use ($post_id) {
$query->where('id', $post_id);
});
return $users->get();
But in the results of this query I would also like to include Users that do not have any posts connected to them. So the result becomes: users without posts + users with a specific post (code above). Is this possible?
Use doesntHave():
$users->whereHas('posts', function ($query) use ($post_id) {
$query->where('id', $post_id);
})->orDoesntHave('posts');

How to query a table using result of many-to-many relation with Eloquent ORM?

here is link to my database schema:
How can I get all topics from the blogs to which the user is subscribed using his id? I use Eloquent ORM.
I have added the following lines to my User model:
public function blogs()
{
return $this->belongsToMany('Blog', 'blog_subscriptions');
}
And then requested in the controller
User::find(1)->blogs; // User id is hardcoded
With that I got all blogs to which the user is subscribed.
And now I am stuck on how to get the topics of those blogs.
Assuming relations:
// User
belongsToMany('Blog', 'user_subscriptions')
// Blog
belongsToMany('User', 'user_subscriptions')
hasMany('Topic')
// Topic
belongsTo('Blog')
1 My way, that will work with any relation, no matter how deeply nested:
User::with(['blogs.topics' => function ($q) use (&$topics) {
$topics = $q->get()->unique();
}])->find($userId);
2 Eloquent standard methods:
$topics = Topic::whereHas('blog', function ($q) use ($userId) {
$q->whereHas('users', function ($q) use ($userId) {
$q->where('users.id', $userId);
});
})->get();
// or
$user = User::find($userId);
foreach ($user->blogs as $blog)
{
$blog->topics; // collection of topics for every single blog
}
3 Joins:
$topics = Topic::join('blogs', 'topics.blog_id', '=', 'blogs.id')
->join('user_subscriptions as us', function ($j) use ($userId) {
$j->on('us.blog_id', '=', 'blogs.id')
->where('us.user_id', '=', $userId);
})->get(['topics.*']);
Mind that last solution will rely on your pivot data consistency. That said, if you can't be sure, that there are no redundant entries in user_subscriptions (eg. blog or user has been deleted but the pivot entry remains), then you need to further join users as well.
I am new to laravel and I don't have the possibility of test this, but I had a similar need and solved it with something like this:
DB::Query('topics')->join('blogs', 'blogs.id', '=', 'topics.blog_id')
->join('blog_subscriptions', 'blogs.id', '=', 'blog_subscriptions.blog_id')
->select('name.id','table.email')
->where('blog_subscriptions.user_id', '=', $user_id);

Laravel eloquent: get data with model wherePivot equal to custom field

I have an eloquent object Performer that has Albums and Albums have Images
Here is setup:
Model Performer->albums():
public function albums()
{
return $this->belongsToMany('Album','performer_albums','performer_id','album_id');
}
Model Album->images()
public function images()
{
return $this->belongsToMany('Image','album_images','album_id','image_id')->withPivot(['type','size']);
}
I have performer object stored as such:
$performer = Performer::where...->first();
Now I need to get Performer's Albums with images where size is 'large'
So to avoid nesting queries, can I use with()?
I tried
$performer->albums()
->with('images')
->wherePivot('size','large')
->get();
But laravel tells me it's trying to use wherePivot for Performer-Album relationship (M-2-M)
PS. I am also aware that I can do this,
$performer = Performer::with('albums')
->with('albums.images')
->.....-conditions for additional fields in album_images....
->get();
but question remains the same.
You need eager load constraints:
$performer->albums()
->with(['images' => function ($q) {
$q->wherePivot('size','large');
}])
->get();
And btw, no, you can't do this:
Performer::with('albums')
->with('albums.images')
->.....-conditions for additional fields in album_images....
->get();
instead you could do:
Performer::with(['albums.images' => function ($q) {
$q-> .....-conditions for additional fields in album_images....
}])->get();

Resources