Select only rows which relation Laravel - php

I have a problem with selecting only rows with relations. I've tried many different solutions. I have 2 tables Movies and Movie_links. I want to get only movies which have links.
Models:
Movie.php
public function links()
{
return $this->hasMany('App\Movie_link', 'movie', 'id');
}
Movie_link.php
public function movie()
{
return $this->belongsTo('App\Movie', 'movie','id')->first();
}
Controller:
I use this code to get all movies which have links and this code works but i want something more efficient:
Movie::latest()
->select('id', 'title', 'poster', 'rating')
->whereIn('id', Movie_link::select('movie')->distinct()->pluck('movie'))
->wherenotnull('poster')
->limit(14)
->get()
But I want more performance so I want to use this code:
Movie::has('links')
->select('id', 'title', 'poster', 'rating')
->withCount('links')
->orderByDesc('id')
->limit(14)
->get()
But with this code request takes 15 seconds! If I change DESC to ASC it is good.

How about using a join:
Movie::query()->from('Movies as M')
->join('Movie_links as L', 'M.id', '=', 'L.movie')
->select('M.id', 'M.title', 'M.rating', 'M.poster', DB::raw('COUNT(L.id) as C'))
->groupBy('M.id')
->having('C', '>', 0)
->orderByDesc('M.id')
->limit(14)
->get();

Related

Laravel - Nested relationship, order parent model by most recent nested

Having a hard time understanding how to order my Laravel model by a nested relationship.
Here are the Models.
User.php
// Has many small_groups through a pivot table
public function small_groups()
{
return $this->belongsToMany('App\Models\SmallGroup')->withPivot('type')->withTimestamps();
}
SmallGroup.php
// Has many SmallGroupLessons
public function small_group_lessons()
{
return $this->hasMany('App\Models\SmallGroupLesson');
}
SmallGroupLessons.php
// Has many SmallGroupLessonComments
public function small_group_lesson_comments()
{
return $this->hasMany('App\Models\SmallGroupLessonComment');
}
SmallGroupLessonsComment.php
// Belongs to SmallGroupLesson
public function small_group_lesson()
{
return $this->belongsTo('App\Models\SmallGroupLesson');
}
What's I'm trying to do, is pull all of the user's small groups, ordered by the most recent SmallGroupLessonComment if one exists. I've been doing some research, and it sounds like using Laravels ORM in this use case will not work. However, I'm not entirely sure on how to create the join on the nested relationship.
I tried the following, but this only pulls in the most latest SmallGroupLessonComment, however, it does not order the entire result set.
$small_groups = $user->small_groups()->with([
'small_group_lessons' => function($q) {
$q->with([
'latest_comment' => function($q) {
$q->orderBy('created_at', 'asc');
}
]);
}
])->paginate($limit);
Update
Was able to solve it via the following...
$small_groups = $user->small_groups()->with([
'small_group_lessons' => function($q) {
$q->with([
'latest_comment' => function($q) {
$q->orderBy('created_at', 'asc');
}
]);
}
])
->leftJoin('small_group_lessons', 'small_group_lessons.small_group_id', '=', 'small_groups.id')
->leftJoin('small_group_lesson_comments', 'small_group_lesson_comments.small_group_lesson_id', '=', 'small_group_lessons.id')
->orderBy('small_group_lesson_comments.created_at', 'desc')
->paginate($limit);
Update #2
The above doesn't work. I get multiple small groups back that are the same item.
Update #3
This query is pretty close, but it's just ordered by the most recent SmallGroupLesson. Ideally, we order by the SmallGroupLessonComment 🤔
$small_groups = $user->small_groups()->with(
[
'small_group_lessons' => function($q) {
$q->with('latest_comment');
$q->orderBy('created_at', 'desc');
}
],
)
->orderBy(
SmallGroupLesson::select('created_at')
->whereColumn('small_group_id', 'small_groups.id')
->orderBy(SmallGroupLessonComment::select('created_at')
->whereColumn('small_group_lesson_id', 'small_group_lessons.id')
->orderBy('created_at', 'desc')
->limit(1), 'desc')
->limit(1), 'desc'
)
->paginate();
$data=User::select('*')->leftJoin('small_group_lessons','small_group_lessons.user_id','user.id')
->ordeBy('small_group_lessons.created_at','DESC')->get();
try like this
I was able to solve it via the following. Ordering based off the latest comment now works correctly.
$small_groups = $user->small_groups()->with([
'small_group_lessons' => function($q) {
$q->with('latest_comment');
$q->orderBy('created_at', 'desc');
}],
)
->orderBy(
SmallGroupLesson::select('small_group_lesson_comments.created_at')
->join('small_group_lesson_comments', 'small_group_lessons.id', '=', 'small_group_lesson_comments.small_group_lesson_id')
->whereColumn('small_group_id', 'small_groups.id')
->latest()
->limit(1), 'desc'
)
->paginate();

How can I pick single record from one to many relation?

I have one to many relation based two tables users and games and there is also bridge table users_games (linking user_id to games).
I want to fetch a single record from games table based on provided game_id for specific user. I did some research and found whereHas() which is returning all games which are belongs to specific user. But I need to fetch one based on game_id. Can some one kindly let me know how can I fix issue in below script
$GameInfo = User::with('games')->whereHas('games', function ($query) use($request)
{
$query->where('game_id', '=', $request->game_id);
})->find(request()->user()->id);
Is this what you're trying to do?
$GameInfo = $request
->user()
->games()
->where('game_id', $request->game_id)
->first();
try this:
$GameInfo = User::with(['games' => function ($query) use($request)
{
$query->where('game_id', $request->game_id);
}])->whereHas('games', function ($query) use($request)
{
$query->where('game_id', '=', $request->game_id);
})->find(request()->user()->id);
If your relation 'games' is a hasMany() with table 'users_games', You can try this code
$GameInfo = User::with(['games' => function ($query) use($request)
{
$query->where('game_id', $request->game_id);
}])
->where('users.id', <user_id_variable>)
->first();
And the relation 'games' in User Model as
public function games()
{
return $this->hasMany('App\Models\UserGames', 'user_id', 'id');
}

OrderBY On model relationship eloquant on controller?

I am not able to find any perfect solution for it.
Controller:
$servicerequest = ServiceRequest::selectRaw('count(id) as totalservice,max(created_date) as last_service,service_provider_id,id,service_id,account_id,service_request,created_date')->with(['account' => function($first) use ($keyword) {
$first->select('id', 'restaurant_name')->orderBy('restaurant_name', 'DESC');
}])
->with(['serviceProvider' => function($query) use ($keyword) {
$query->select('id', 'company_name');
}])->groupBy('account_id')
->orderBy('company_name', 'DESC')
->paginate(100);
I need an order by on model relation table field and that effect on main table data. because it's and one to one relationship so no need to order by on the inside.
Like I need to the orderby whole on relations data.
Collection:
You must make join your relation table to used orderBy relation table.
You can try this code.
$servicerequest = ServiceRequest::selectRaw('count(id) as totalservice, max(created_date) as last_service,service_provider_id,id,service_id,account_id,service_request,created_date, SERVICEPROVIDERTABLE.company_name')
->join('serviceProvider', 'SERVICEPROVIDERTABLE.id', '=', 'SERVICEREQUESTTABLE.service_provider_id')
->with([
'account' => function ($first) use ($keyword) {
$first->select('id', 'restaurant_name')
->orderBy('restaurant_name', 'DESC');
}
])
//->with([
// 'serviceProvider' => function ($query) use ($keyword) {
// $query->select('id', 'company_name');
// }
//])
->groupBy('account_id')
->orderBy('SERVICEPROVIDERTABLE.company_name', 'DESC')
->paginate(100);
i hope this works.

Selecting and ordering data from a query using Laravel Eloquent

I am trying to use Laravel and eloquent to return results based on the following query.
$blogPosts = BlogPosts::with('blog_user', 'blog_categories', 'blog_comments', 'tags')
->orderBy('created_at', 'desc')
->paginate(5);
Ok, so that is fine, it return exactly what it should, all the blog posts with associated relations to other tables.
What I now want to do is return only the $blogPosts where a tag is clicked by the user. So let's say there is a tag "PHP", so I pass in that value as $tag to the method. I then have something like this.
public function tag_search($tag)
{
$blogPosts = BlogPosts::with('blog_user', 'blog_categories', 'blog_comments', 'tags')
->where('tags', $tag)
->orderBy('created_at', 'desc')
->paginate(5);
$categories = BlogCategories::with('blog_posts')->get();
$data = array('blogPosts' => $blogPosts, 'categories' => $categories,
);
return view('blog.index')->with($data);
}
Now my issue is actually relatively simple I guess, if the where clause was a column in the BlogPosts table it would work, I know this because I tried that.
However the above won't work as is, I can only use;
->where('x', y)
Where x is a field in the BlogPosts table. I want to return a set of values where the submitted $tag is the same as one associated to the tags attached to the blog posts.
Make sense? I think I am over thinking it the point I am just not thinking now :)
Add the column field 'tags' to your table and then these queries:
$blogPosts = BlogPosts::with('blog_user', 'blog_categories', 'blog_comments', 'tags')
->where('tags', '=', $tag)
->orderBy('created_at', 'desc')
->paginate(5);
$categories = BlogCategories::with('blog_posts')->get();
return view('blog.index', compact ('categories', 'blogposts'));
Ok so the answer was to use whereHas like this.
$blogPosts = BlogPosts::whereHas('tags', function ($query) use ($tag) {
$query->where('name', $tag);
})->orderBy('created_at', 'desc')
->paginate(5);
That returns a search based on the associated elements.

Laravel OrderBy Relationship count with pagination

I have many projects and each has many orders, some completed, some not.
I want to order them by the amount of completed orders like this:
$products = $products->orderBy(function($product) {
return $product->orders->where('status', 2)->count();
})->paginate(15);
I know the order call doesn't work like this but this is the best was show the problem. SortBy doesn't work because I want to use pagination.
Finally found a good solution for the problem:
$products = $products->join('orders', function ($join) {
$join->on('orders.product_id', '=', 'products.id')
->where('orders.status', '=', 2);
})
->groupBy('products.id')
->orderBy('count', $order)
->select((['products.*', DB::raw('COUNT(orders.product_id) as count')]))->paginate(50);
Try this if it works:
$categories = Prodcuts::with(array('orders' => function($query) {
$query->select(DB::raw('SELECT * count(status) WHERE status = 2 as count'))
$query->orderBy('MAX(count)')
}))->paginate(15);
return View::make('products.index', compact('categories'));
Note: This is not tested.
From 5.2 on wards you can use the withCount for counting relationship result.
In this case the
$products = $products->withCount(['orders' => function ($query) {
$query->where('status', 2);
}]);
Reference : https://laravel.com/docs/5.2/eloquent-relationships

Categories