Laravel how to query with Auth ID from another table - laravel

I have a forms_table where I query all my results for authenticated user with where Auth::id() in user_id column.
Each of these rows have a boolean column alternation. If this is 1, then it means there is a row in another table called form_alternation where I have the Auth::id(). This means the Auth::id() does not exist in the initial forms_table.
How can I retrieve the rows from the forms_table with where clause where('user_id', Auth::id()) and show the rows where the Auth::id() exists in the form_alternations?
Currently I have this and it shows me 0 rows because the last Auth::id() clause.
$baseQuery->with(['alternated' => function($q){
$q->where('user_id', Auth::id());
}]);
$baseQuery->where('user_id', Auth::id());

As i see it you want your query to do something similar to this (in half pseudo code).
where (forms.user.id = Auth::id() OR (users.alternation = 1 AND alternated.user_id = Auth::id()))
This could be achieved with Eloquent, as i understand your domain, try something like this.
use Illuminate\Database\Eloquent\Builder;
// wrap it in where, to avoid other queries precedence ruining the or clause
$query->where(function (Builder $query) {
// First part, where we assume user id is in forms
$query->where('user_id', Auth::id());
// Second part, check alternation or your relationship
$query->orWhere(function (Builder $query) {
$query->where('alternation', true);
// Use whereHas to check if your forms has the user as a relationship
$query->whereHas('alternated', function (Builder $query) {
$query->where('user_id', Auth::id());
});
});
});
This should provide similar logic to the pseudo example, simply utilizing orWhere() logic and you can check relationships with whereHas().

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: Use order by in combination with whereHas

I want to retrieve a collection of data, which is ordered by the start_date of the relation
Basically I want to achieve this, with Laravel Models (the code below works perfectly)
$posts = DB::table('posts')
->leftJoin(
'threads',
'posts.id',
'=',
'threads.postable_id'
)
->where('threads.postable_type', '=', 'App\Post')
->orderBy('threads.start_date')
->paginate($request->input('limit', 2));
So in this case, I'm fetching ALL Posts and those are ordered by the start_date of the thread relation.
Those are not my actual tables but this works perfectly!
Because I'm using https://laravel.com/docs/8.x/eloquent-resources this is not the ideal solution to retrieve sorted data.
So instead I want to use the orderBy clause somewhere here
$posts = Post::whereHas('thread', function ($query) {
$query->where('end_date', '>=', Carbon::now());
});
But I just cannot make this work. I've tried this
$posts = Post::whereHas('thread', function ($query) {
$query->where('end_date', '>=', Carbon::now())
->orderBy('start_date');
});
and I also appended this to the actual relation:
public function thread(): MorphOne
{
return $this->morphOne('App\Thread', 'postable')->orderBy('start_date');
}
If you look at your code:
$posts = Post::whereHas('thread', function ($query) {
$query->where('end_date', '>=', Carbon::now())
->orderBy('start_date');
});
the whereHas will only return Post associate with a thread which the function return true.
Try this:
$posts = Post::with('thread')->has('thread')->orderBy('thread.start_date')->get();
This will fetch all Post with Thread only if they have at least one Thread and then orderBy the start_date of the Thread.
You don't have to do the whereHas function because when you call ->with('thread') it'll use you this :
public function thread(): MorphOne
{
return $this->morphOne('App\Thread', 'postable')->orderBy('start_date');
}
whereHas doesnt retrieve the relationship.
If you need even more power, you may use the whereHas and orWhereHas methods to define additional query constraints on your has queries, such as inspecting the content of a comment: Laravel whereHas
Don't do :
$posts = Post::with('thread')->orderBy('thread.start_date');
If there is no thread on some post, post without thread will be fetch with value null for their key thread and you will have an unexpected result when you try to orderBy.
First of all I want to thank Elie Morin for his help but I found out that I definitely need to use joins for that task.
In my example, I wanted to order the main query (posts) by the relation's start_date
Doing what you suggested
$posts = Post::with('thread')->has('thread')->orderBy('thread.start_date')->get();
Would only order the thread by start_date and not the ENTIRE query.
Which is why I came up with something like this:
$posts = Post::has('thread')
->select('posts.id')
->leftJoin(
'thread',
'posts.id',
'=',
'thread.postable_id'
)
->where('thread.postable_type', '=', 'App\Post')
->where('thread.end_date', '>=', Carbon::now())
->orderBy('thread.start_date')
->with('thread');
return PostResource::collection($posts->paginate(2));

query joins only last row from relationship

User can apply many times to single order. The last apply is the one I want. My code:
$users = User::whereHas('applies', function ($query) use ($order_id){
$query->where('order_id', $order_id);
})->with(['applies' => function($query) use ($order_id){
$query->orderBy('id', 'desc')->where('order_id', $order_id)->first();
}])->paginate($request->perPage);
This limit the number of applies only in the last user, the other applies records are empty. Removing first() from with() shows all applies in all users. How is that? Please help.
I've tried limit(1), take(1)->get(), take(1), but it also fails.
There is no simple way to limit eager loading.
You can use a HasOne relationship:
public function latestApply() {
return $this->hasOne(Apply::class)->orderBy('id', 'desc');
}
$users = User::whereHas('applies', function ($query) use ($order_id){
$query->where('order_id', $order_id);
})->with(['latestApply' => function($query) use ($order_id){
$query->where('order_id', $order_id);
}])->paginate($request->perPage);
This will still fetch all applies in the background, but only show the latest one for each user.
See also https://stackoverflow.com/a/50571322/4848587 for other possible solutions.

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);

Eloquent filter on pivot table created_at

I want to filter the contents of two tables which have an Eloquent belongsToMany() to each other based on the created_at column in the pivot table that joins them. Based on this SO question I came up with the following:
$data = ModelA::with(['ModelB' => function ($q) {
$q->wherePivot('test', '=', 1);
}])->get();
Here I'm using a simple test column to check if it's working, this should be 'created_at'.
What happens though is that I get all the instances of ModelA with the ModelB information if it fits the criteria in the wherePivot(). This makes sense because it's exactly what I'm telling it to do.
My question is how do I limit the results returned based on only the single column in the pivot table? Specifically, I want to get all instances of ModelA and ModelB that were linked after a specific date.
OK, here it goes, since the other answer is still wrong.
First off, wherePivot won't work in whereHas closure. It's BelongsToManys method and works only on the relation object (so it works when eager loading).
$data = ModelA::with(['relation' => function ($q) use ($someDate) {
$q->wherePivot('created_at', '>', $someDate);
// or
// $q->where('pivot_table.created_at', '>', $someDate);
// or if the relation defines withPivot('created_at')
// $q->where('pivot_created_at', '>', $someDate);
}])->whereHas('ModelB', function ($q) use ($someDate) {
// wherePivot won't work here, so:
$q->where('pivot_table.created_at', '>', $someDate);
})->get();
You are using Eager Loading Constraints, which constrain only, like you said, the results of the related table.
What you want to use is whereHas:
$data = ModelA::whereHas('ModelB' => function ($q) {
$q->wherePivot('test', '=', 1);
})->get();
Be aware that ModelB here refers to the name of the relationship.

Resources