Get top rows from relation - laravel

I have 2 models: Video, Country and want to retrieve the top 5 videos for each country ordered by number of views, and want to do that using the video model not the country model so i shall start with
$videos = Video::with('user')->orderBy('views', 'desc')->get();
So the code shall return videos from the top 5 for reach country
and will be appreciate if the solution that starts with County::with('videos') is
given

This should help, it return videos without iterating any loops.
$countries = Country::with(['videos' => function($query) {
$query->orderBy('views', 'desc')->take(5);
}])->get();
You must need this loop to get videos
$topVideos = [];
foreach ($countries as $country) {
$topVideos[] = $country->videos;
}

$countries = Country::with(['videos' => function($query){
$query->orderBy('views', 'desc');
}])
->get();
foreach ($countries as $country) {
$top_5_videos = $country->videos->take(5);
}

Related

Laravel whereHas Returns all records

I have have 3 tables in my projects they are:
products(can have Multiple Variants)
variants (belongsto product)
product_attributes (this have product_id,attribute_id,value_id)
I want to filter variants from a product by value ids thats comes from form request as example (1,2,6)
I have tried like this:
$poruduct_id = $request->product_id;
$value_ids = $request->value_ids;
$searched_variants = Variant::whereHas('product.attributeValues', function ($query) use ($value_ids, $product_id) {
$query->whereIn('value_id', [$value_ids]);
})->where('product_id', $product_id)->get();
dd($searched_variants);
But the problem is the query returns all records from the product. What is the solution to filter exactly the values that the product Variants have?
Thank you.
-UPDATED-
I have tried like this but nothing changed
$searched_variants = Variant::select('product_id')->whereHas('product.attributeValues', function ($query) use ($value_ids, $product_id) {
$query->whereIn('value_id', [$value_ids]);
})->groupBy('product_id')
->havingRaw('COUNT(*) = ?', [count((array) $value_ids)])
->get();
-**FİNALLY SOLUTİON**-
I made it like this : I get the value code that is in for example Large the code is L
get all the codes to controller and executed this query ı hope this helps someone
1.$value_codes=$request->value_codes;
2.$value_codes_array=explode(',',$value_codes);
3.$product_id=$request->product_id;
4.$searchValues = preg_split('/,/', $value_codes_array, -1, PREG_SPLIT_NO_EMPTY);
$searchValues = preg_split('/,/', $value_idss, -1, PREG_SPLIT_NO_EMPTY);
$variants= Variant::where(function ($q) use ($searchValues) {
foreach ($searchValues as $value) {
$q->orWhere('sku', 'like', "%-{$value}")
->orWhere('sku', 'like', "%-{$value}-%")
->orWhere('sku', 'like', "{$value}-%");
}
})->where('product_id',$product_id)->get();
dd($variants);
If you have n variants to one product, you query should be like:
Product Model
public function variants: HasMany relationships
//usage
$produtc->variants->and here the query function
You need to use it in this way, it should work:
$productId = $request->product_id;
$valueIds = $request->value_ids;
$searchedVariants = Variant::whereHas('product.attributeValues', function ($query) use ($valueIds) {
$query->distinct()->whereIn('value_id', $valueIds);
}, '=', count($valueIds))->where('product_id', $productId)->get();

Filtering eloquent relationship many to many query

I have
Users, Departments, Positions, table.
User belongs to many department
Department has many positions
User belongs to many positions
I want a query that would give a single department with list of users and their position in that department.
$department = DepartmentView::with(['users.positions' => function($query) {
// do something?
}])->where('tag', $tag)
->get();
I also get the department through tag, and not ID. Position table does not have tag column, only department ID.
--
I could make it work by defining static Id, however the problem is I get the department via tag.
public function getDepartmentByTag($tag) {
$departmentId = 1; FOR TESTING PURPOSE, IT WORKS
$department = DepartmentView::with(['users.positions' => function($query) use($departmentId) {
$query->whereHas('department', function($query) use($departmentId) {
$query->where('departmentId', $departmentId);
});
}])->where('tag', $tag)
->get();
}
I want to only get the user position that is in the department
DepartmentView::with(['users.positions' => function($q) use($tag) {
$q->whereHas('department', function($q) use($tag) {
$q->where('tag', $tag);
});
}])
->get();

Filter model with HasMany relationship

Let's say I have two models: Park and Items. Each Park can have a number of Items, through a HasMany relationship.
In Items table there's a park_id field and a type flag: an Item can be a fountain, or a basketball court, or whatever.
What I need to do is to filter the parks to obtain only those who has ALL of the item types passed in an array. This is what I have:
$parks = Park::whereHas('items', function($q) use ($request) {
$q->where('type', json_decode($request->type_list));
})->get();
But it's not working properly. Any ideas?
Thanks a lot, and happy new year to all!
Edit: I've found a working though really ugly solution:
$types_array = json_decode($request->type_list, true);
$results = [];
// A collection of parks for each item type
foreach($types_array as $type) {
$results[] = Park::whereHas('items', function($q) use ($type) {
$q->where('type', $type);
})->get();
}
// Intersect all collections
$parks = $results[0];
array_shift($results);
foreach ($results as $collection) {
$parks = $collection->intersect($parks);
}
return $parks;
You can use a foreach in whereHas method. It should be something like this:
$array = json_decode($request->type_list, true)
$parks = Park::whereHas('items', function($q) use ($array) {
foreach ($array as $key => $type) {
$q->where('type', $type);
}
})->get();
I think using where clause to match all item's type for the same row will result nothing, it's like doing where id = 1 and id = 2 and this is impossible because id can take only one value, what you should do is to use whereIn to get all items that match at least one type and then use groupBy to group result by park_id, then in the having clause you can check if the group count equal the type_list count:
$types = json_decode($request->type_list, true);
$parks = Park::whereHas('items', function($q) use ($types) {
$q->select('park_id', DB::raw('COUNT(park_id)'))
->whereIn('type', $types)
->groupBy('park_id')
->havingRaw('COUNT(park_id) = ?', [ count($types) ]);
})->get();
You can add multiple whereHas() constraint to one query:
$types_array = json_decode($request->type_list, true);
$query = Park::query();
foreach($types_array as $type) {
$query->whereHas('items', function($q) use($type) {
$q->where('type', $type);
});
}
$parks = $query->get();

How to get all item same category with Eloquent

Hi all,
I'm building an application in Laravel, I want to get all item in an article category without using this Eloquent.
My code is
$info = Article::find($article_id);
$cate_info = $info->article_categories()->first();
if($cate_info){
$same = Article::with(['article_categories' => function ($query) use ($cate_info){
$query->where('articles_category.ID' ,$cate_info->ID);
}])->where('ID' ,'!=' ,$article_id)->get();
}
And I get all articles. How to solve.
Article and Article_Category is in many to many relationship.
Thanks.
If you want to have all the articles of a particular article_categories you can use whereHas method, now suppose you have name as a field column in categories table, so you can have something like this:
public function getData(Request $request)
{
$category = $request->categoryName;
// $category = 'ABC Category';
$articles = Article::whereHas('article_categories', function($query) use($category) {
$query->where('name', 'like', '%'.$category.'%');
})->get();
return response()->json(['articles' => $articles], 200);
}
Hope this helps.

Best way to return the sum aggregation of a relations property

I have a simple structure where a post has many votes. A vote has an "value" property which is either 1 or -1
When reading all posts I'd love to select this sum for each post into a custom property on post level. Currently i do this
$posts = Post::where('published_at', '<=', $date)
->orderBy('published_at', 'desc')
->simplePaginate(20);
$posts->each(function($post) {
$post->overallRating = $post->getRating();
});
This is fully working, however I think it's not that good to make like 20 queries to the database to read the ratings. Is there a way to simplify this in the actual fetch of the posts?
public function getRating()
{
return $this->votes->sum('value');
}
If you want to keep the votes included in the in the pagination results then I would suggest adding with('votes') so they're at least eager loaded i.e.
$posts = Post::with('votes')
->where('published_at', '<=', $date)
->orderBy('published_at', 'desc')
->simplePaginate(20);
However, if you don't want/aren't bothered about having the votes and you just want the ratings for each post you could add the following scope to your Post model:
public function scopeWithRating(Builder $query)
{
if (is_null($query->getQuery()->columns)) {
$query->select([$query->getQuery()->from . '.*']);
}
$query->selectSub(
$this->votes()->getRelationExistenceQuery(
$this->votes()->getRelated()->newQuery(), $query, new Expression('sum(value)')
)->toBase(),
'rating'
);
}
Then:
$posts = Post::withRating()
->where('published_at', '<=', $date)
->orderBy('published_at', 'desc')
->simplePaginate(20);
Hope this helps!
Try this:
$posts = Post::where('published_at', '<=', $date)
->orderBy('published_at', 'desc')
->with(['votes' => function($query) {
$query->sum('value');
}])->simplePaginate(20);

Resources