Count giving wrong amount in laravel - laravel

I'm trying to get the amount of items I have based on what I have in my rank column. So if I have 3 items that has a rank of high then I want it to show 3 and if I have a rank of low and it has 2 items I need it to show 2.
But the issue I'm having is that it's counting all my items so I'm getting a count of 5.
Here is my code
$items = Item::all();
foreach($items as $item)
{
if($item->rank === 'high')
{
$count = $item->count();
dd($count);
}
}

You can use filter collection:
$items = Item::all();
$highRank = $items->filter(function ($value, $key) {
return $value->rank == 'high';
});
$highRank->count();
More detail with this collection check this doc: https://laravel.com/docs/8.x/collections#method-filter

You can use group by rank and get the count
$Items = Item::select('rank', DB::raw('count(*) as total'))
->groupBy('rank')
->get();

simple used
$items = Item::where('rank','high')->get()->count();

Related

How to add a momentary value to a sql query in Laravel

I have this query in Laravel 8:
$articles = Article::whereIn('category_id', $categories)->where(function ($query) {
$query->where(function ($query2) {
$query2->where('draft', 0)->whereNull('publication_date');
})->orWhere(function ($query2) {
$query2->where('draft', 0)->where('publication_date', '<=', DateHelper::currentDateTime());
});
})->orderBy('publicated_at', 'DESC')->get();
I need to add a momentary id to this query named desc_id, so that the values $articles that i query have a desc_id based on the publication_date.
The result that i need to achieve is like this:
id
name
category
publication_date
desc_id
3
bananas
news
2022-01-16 17:30:00
1
27
kiwis
news
2021-12-05 21:30:00
3
50
apples
news
2022-01-07 09:14:00
2
I've tried adding it by passing all the elements of $articles into an empty array adding a value:
$count =0;
$allArticles = [];
foreach ($articles as $article) {
$count++;
$allArticles[] = $article->with('desc_id', $count);
}
I know that my method is incorrect but i hope that it helps understanding my question.
It looks like you want to have the line number of each row which can be achieve like this :
Article::whereIn(...)->select()
->addSelect(DB::raw('RANK() OVER(ORDER BY publicated_at DESC) as desc_id'))
->orderBy('publicated_at', 'DESC')
->get();

check count in one to many relation in laravel

I have products table that each of the product has many orders,product.php
public function orders()
{
return $this->hasMany(Order::class,'product_id');
}
I can get products that order by order_count with this code:
$products = Product::withCount('orders')->orderBy('orders_count', 'desc')->get();
Now I want to get products in controller that their orders count is bigger than 5,
How I can do it?
One way to do this would to use whereHas():
$products = Product::withCount('orders')
->whereHas('orders', null, '>', 5)
->orderBy('orders_count', 'desc')
->get();
I don't think it's mentioned in the docs but you don't have to pass a closure as the 2nd param, and the 3rd and 4th can be used to pass the operator and the count.
Alternatively, you could use having() instead:
$products = Product::withCount('orders')
->having('orders_count', '>', 5)
->orderBy('orders_count', 'desc')
->get();
You can use mapping on your collection to filter out records e.g. :`
$collection = collect($products)->map(function ($product) {
return $product->orders->count() > 5;
})
->reject(function ($name) {
return $product->orders->count() < 5;
});`
This is just an example, you can put more conditions if required.
Hope this helps.
The below code should give you just the orders with a count greater than 5
$products = Product::withCount('orders')->where('orders_count', '>', 5)->orderBy('orders_count', 'desc')->get();
Adding ->where() should do it.
Hope that helps.
Update
Try
->whereRaw('count("orders.id") > 5')
This should work to get products with more than 5 orders.

Laravel merge logic

In my case i need to use collections together. But also i need that model too. I decided to merge eloquents. Like this:
$products = Product::orderBy('created_at', 'desc')->get();
$sliders = Slider::orderBy('order', 'asc')->get();
$news = News::orderBy('order', 'asc')->get();
$collection = new ModelCollection();
$result = $collection->merge($products)->merge($news)->merge($sliders)->sortByDesc('created_at');
dd($result);
$products has 4 data, $slider also has 4 data, $news has 3 data.
But i am allways get 4 data by sorting created_at. Why i can't get 11 data from the collection?
https://laravel.com/docs/5.6/collections#method-merge
in this case there is no limit or rules.
in the interest of brevity i am trying to make an array with my selected models. If its a collection it keeps model. If its just an array also i need to add the model name for each data collections.
array(
Products > product1,product2,product3,product4
News > news1, news2, news3
Slider > slider1, slider2, slider3
)
If you want all collections in one array you can do by this,
$products = Product::orderBy('created_at', 'desc')->get();
$sliders = Slider::orderBy('order', 'asc')->get();
$news = News::orderBy('order', 'asc')->get();
$result = array();
$result['products'] = $products;
$result['sliders'] = $sliders;
$result['news'] = $news;
This will create collection $result like you want,
array(
products > product1,product2,product3,product4
news > news1, news2, news3
sliders > slider1, slider2, slider3
)
Hope this will help you. Comment if any doubts.

Get top rows from relation

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

Laravel how do I get the row number of an object using Eloquent?

I'd like to know the position of a user based on its creation date. How do I do that using Eloquent?
I'd like to be able to do something like this:
User::getRowNumber($user_obj);
I suppose you want MySQL solution, so you can do this:
DB::statement(DB::raw('set #row:=0'));
User::selectRaw('*, #row:=#row+1 as row')->get();
// returns all users with ordinal 'row'
So you could implement something like this:
public function scopeWithRowNumber($query, $column = 'created_at', $order = 'asc')
{
DB::statement(DB::raw('set #row=0'));
$sub = static::selectRaw('*, #row:=#row+1 as row')
->orderBy($column, $order)->toSql();
$query->remember(1)->from(DB::raw("({$sub}) as sub"));
}
public function getRowNumber($column = 'created_at', $order = 'asc')
{
$order = ($order == 'asc') ? 'asc' : 'desc';
$key = "userRow.{$this->id}.{$column}.{$order}";
if (Cache::get($key)) return Cache::get($key);
$row = $this->withRowNumber($column, $order)
->where($column, '<=',$this->$column)
->whereId($this->id)->pluck('row');
Cache::put($key, $row);
return $row;
}
This needs to select all the rows from the table till the one you are looking for is found, then selects only that particular row number.
It will let you do this:
$user = User::find(15);
$user->getRowNumber(); // as default ordered by created_at ascending
$user->getRowNumber('username'); // check order for another column
$user->getRowNumber('updated_at', 'desc'); // different combination of column and order
// and utilizing the scope:
User::withRowNumber()->take(20)->get(); // returns collection with additional property 'row' for each user
As this scope requires raw statement setting #row to 0 everytime, we use caching for 1 minute to avoid unnecessary queries.
$query = \DB::table(\DB::raw('Products, (SELECT #row := 0) r'));
$query = $query->select(
\DB::raw('#row := #row + 1 AS SrNo'),
'ProductID',
'ProductName',
'Description',
\DB::raw('IFNULL(ProductImage,"") AS ProductImage')
);
// where clauses
if(...){
$query = $query->where('ProductID', ...));
}
// orderby clauses
// ...
// $query = $query->orderBy('..','DESC');
// count clause
$TotalRecordCount = $query->count();
$results = $query
->take(...)
->skip(...)
->get();
I believe you could use Raw Expresssions to achieve this:
$users = DB::table('users')
->select(DB::raw('ROW_NUMBER() OVER(ORDER BY ID DESC) AS Row, status'))
->where('status', '<>', 1)
->groupBy('status')
->get();
However, looking trough the source code looks like you could achieve the same when using SQLServer and offset. The sources indicates that if you something like the following:
$users = DB::table('users')->skip(10)->take(5)->get();
The generated SQL query will include the row_number over statement.
[For Postgres]
In your model
public function scopeWithRowNumber($query, $column = 'id', $order = 'asc'){
$sub = static::selectRaw('*, row_number() OVER () as row_number')
->orderBy($column, $order)
->toSql();
$query->from(DB::raw("({$sub}) as sub"));
}
In your controller
$user = User::withRowNumber()->get();

Resources