Laravel : Search by min and max value from the table - laravel

I am confuse about search with min-max value.In my posts table there is a two field min_price and max_price, on my search there is a couple of thing which I need to covered in search query.
If user search with only max_value, it shows all the posts which price is less than or equal to max_value.
If user search with only min_value, it shows all the posts which price is less than or equal to min_value.
If user search with min_value and max_value, it shows all the posts which price is between min_value and max_value.
If both null, return all posts.
How can I do this ?
My code:
$searchablePost = Post::with(['product','postattribute.attribute.category','user.userDetails'])
->whereIn('product_id', $userApprovalProductIDs)
->whereIn('demand_or_supply', $demand_or_supply);
// skip my search query code
$searchedPost = $searchablePost->offset($offset)->limit($limit)->orderBy('id','desc')->get();
How can I do t

Check:
1. if both (min & max values) are available (i.e. not null):
2. if min value is available:
3. if max value is available:
// if none of them is null
if (! (is_null($min_value) && is_null($max_value))) {
// fetch all between min & max values
$searchablePost = $searchablePost->whereBetween('price', [$min_value, $max_value]);
}
// if just min_value is available (is not null)
elseif (! is_null($min_value)) {
// fetch all greater than or equal to min_value
$searchablePost = $searchablePost->where('price', '>=', $min_value);
}
// if just max_value is available (is not null)
elseif (! is_null($max_value)) {
// fetch all lesser than or equal to max_value
$searchablePost = $searchablePost->where('price', '<=', $max_value);
}
If you have separate fields for min_price & max_price, as mentioned in comment, just change the code as following:
if (! (is_null($min_value) && is_null($max_value))) {
$searchablePost = $searchablePost
->where('min_price', '>=', $min_value)
->where('max_price', '<=', $max_value);
}
elseif (! is_null($min_value)) {
$searchablePost = $searchablePost->where('min_price', '>=', $min_value);
}
elseif (! is_null($max_value)) {
$searchablePost = $searchablePost->where('max_price', '<=', $max_value);
}

You can set $min = 0; and $max = infinite_choosen_number; and append whereBetween method to your query, like the below code:
$searchablePost = Post::with(['product','postattribute.attribute.category','user.userDetails'])
->whereIn('product_id', $userApprovalProductIDs)
->whereIn('demand_or_supply', $demand_or_supply)
->whereBetween('price', ["$min", "$max"])->get();
Reference: https://laravel.com/docs/5.6/queries

You can't do that with a whereIn, you can do that with a where statement.
Something like this
`
$searchablePost = Post::with(['product','postattribute.attribute.category','user.userDetails'])
->whereIn('product_id', $userApprovalProductIDs)
->whereIn('demand_or_supply', $demand_or_supply)
->where('price', '>=', $minPrice)
`
Didn't try it so int might fail but here is the way to do it.

Related

how to make orderBy ignore value 0 in eloquent laravel

My problem is that I have my collection that has a position field and I want it to be sorted in ascending order, but the fields that have the value null or 0 by default are detected as smaller than those that have an index.
My question is how can I make orderBy ignore the value 0 or null.
$listing = Product::get();
$listing = $listing->orderBy('order','ASC');
You could use CASE in your orderBy as a hack to "ignore" 0 (place it last).
$listing = Product::query()
->orderByRaw('CASE WHEN "order" = 0 THEN 0 ELSE 1 END DESC, "order" ASC')
->get();
You can also split it if you prefer.
$listing = Product::query()
->orderByRaw('CASE WHEN "order" = 0 THEN 0 ELSE 1 END DESC')
->orderBy('order')
->get();
$listing = Product::query()
->orderByDesc(DB::raw('CASE WHEN "order" = 0 THEN 0 ELSE 1 END'))
->orderBy('order')
->get();
$listing = Product::orderByRaw('-order DESC')->get();
There is a minus sign before the column.
Instead of asc, we are now sorting as desc, this is because we have inverted the values in #2 and so the sorting must also be inverted now to get the right results.
this working fine for me.

How to get last 7 days records with 0 counts

I have an eloquent query that gets the total count records (created_at) of the last 7 days. But the problem is if one of these days have 0 records, this doesn't appear in the final data.
My query:
$data = Data::whereBetween('created_at', [Carbon::now()->subDays(6)->format('Y-m-d')." 00:00:00", Carbon::now()->format('Y-m-d')." 23:59:59"])
->groupBy('date')
->orderBy('date')
->get([
DB::raw('DATE(created_at) as date'),
DB::raw('count(*) as total')
])
->pluck('total', 'date')->toArray();
What I get:
[
"2020-04-14" => 1
"2020-04-16" => 1
"2020-04-18" => 1
"2020-04-19" => 1
]
What I expected:
[
"2020-04-14" => 1
"2020-04-15" => 0
"2020-04-16" => 1
"2020-04-17" => 0
"2020-04-18" => 1
"2020-04-19" => 1
"2020-04-20" => 0
]
Any suggestions?
SOLUTION:
-Based on Gary Houbre's proposal:
$results = Data::whereBetween('created_at', [Carbon::now()->subDays(6)->format('Y-m-d')." 00:00:00", Carbon::now()->format('Y-m-d')." 23:59:59"])
->groupBy('date')
->orderBy('date')
->get([
DB::raw('DATE_FORMAT(created_at, "%Y-%m-%d") as date'),
DB::raw('count(*) as total')
])
->keyBy('date')
->map(function ($item) {
$item->date = Carbon::parse($item->date);
return $item;
});
$period = new DatePeriod(Carbon::now()->subDays(6), CarbonInterval::day(), Carbon::now()->addDay());
$graph = array_map(function ($datePeriod) use ($results) {
$date = $datePeriod->format('Y-m-d');
return $results->has($date) ? $results->get($date)->total : 0;
}, iterator_to_array($period));
Looking directly Sql : How to include "zero" / "0" results in COUNT aggregate?
Into a same table : How to get the record if Count is zero in Laravel
You need to add an outer join into your request with Eloquent.
My idea is to create a for loop to check the days.
If there is no record on a date then print 0
Loop Iteration:
Catch the first Day (Suppose 14)
Catch the last Day
Then check in every iteration it is greater than one or many
Thus, I hope you will get normally.
We had a similar problem while trying to put back-end data into the chart. Since some of the days were missing it didn't look well. Our solution was;
Create a function like this;
public function generateDates(Date $startDate, Date $endDate, $format = 'Y/m/d'): Collection
{
$dates = collect();
$startDate = $startDate->copy();
for ($date = $startDate; $date->lte($endDate); $date->addDay()) {
$dates->put($date->format($format), 0);
}
return $dates;
}
In your case it's going to be (today and today - six days) and you will union returning collection with your query collection. What it does is; it create a date range from the keys and fill them with zero. When your query collection has some value other than zero - it is going to overwrite it.

Refactor Laravel Query

I have a query that I have built, and I am trying to understand how I can achieve the same thing but in one single query. I am fairly new to Laravel and learning. Anyway someone could help me understand how I can achieve what I am after?
$activePlayerRoster = array();
$pickupGames = DB::table('pickup_games')
->where('pickupDate', '>=', Carbon::now()->subDays(30)->format('m/d/Y'))
->orderBy('pickupDate', 'ASC')
->get();
foreach ($pickupGames as $games) {
foreach(DB::table('pickup_results')
->where('pickupRecordLocatorID', $games->recordLocatorID)
->get() as $activePlayers) {
$activePlayerRoster[] = $activePlayers->playerID;
$unique = array_unique($activePlayerRoster);
}
}
$activePlayerList = array();
foreach($unique as $playerID) {
$playerinfo = DB::table('players')
->select('player_name')
->where('player_id', $playerID)
->first();
$activePlayerList[] = $playerinfo;
}
return $activePlayerList;
pickup_games
checkSumID
pickupDate
startTime
endTime
gameDuration
winningTeam
recordLocatorID
pickupID
1546329808471
01/01/2019
08:03 am
08:53 am
50 Minute
2
f47ac0fc775cb5793-0a8a0-ad4789d4
216
pickup_results
id
checkSumID
playerID
team
gameResult
pickOrder
pickupRecordLocatorID
1
1535074728532
425336395712954388
1
Loss
0
be3532dbb7fee8bde-2213c-5c5ce710
First, you should try to write SQL query, and then convert it to Laravel's database code.
If performance is not critical for you, then it could be done in one query like this:
SELECT DISTINCT players.player_name FROM pickup_results
LEFT JOIN players ON players.player_id = pickup_results.playerID
WHERE EXISTS (
SELECT 1 FROM pickup_games
WHERE pickupDate >= DATE_FORMAT(SUBDATE(NOW(), INTERVAL 30 DAY), '%m/%d/%Y')
AND pickup_results.pickupRecordLocatorID = recordLocatorID
)
Here I'm assuming you know what you're doing with this dates comparison, because it looks weird to me.
Now, let's convert it to Laravel's code:
DB::table('pickup_results')
->select('players.player_name')->distinct()
->leftJoin('players', 'players.player_id', '=', 'pickup_results.playerID')
->whereExists(function ($query) {
$query->select(DB::raw(1))
->from('pickup_games')
->where('pickupDate', '>=', Carbon::now()->subDays(30)->format('m/d/Y'))
->whereRaw('pickup_results.pickupRecordLocatorID = recordLocatorID');
})
->get();
Basically, I would reduce the query to its SQL variant to get directly at its core.
The essence of the query is
select `x` FROM foo WHERE id IN (
select distinct bar.id from bar join baz on bar.id = baz.id);
This can be interpreted in Eloquent as:
$thirtyDaysAgo = Carbon::now()->subDays(30)->format('m/d/Y');
$playerIds = DB::table('pickup_games')
->select('pickup_games.player_id')
->join(
'pickup_results',
'pickup_results.pickupRecordLocatorID',
'pickup_games.recordLocatorID')
->where('pickupDate', '>=', $thirtyDaysAgo)
->orderBy('pickupDate', 'ASC')
->distinct('pickup_games.player_id');
$activePlayers = DB::table('players')
->select('player_name')
->whereIn('player_id', $playerIds);
//>>>$activePlayers->toSql();
//select "player_name" from "players" where "player_id" in (
// select distinct * from "pickup_games"
// inner join "pickup_results"
// on "pickup_results"."pickupRecordLocatorID" = "pickup_games"."recordLocatorID"
// where "pickupDate" >= ? order by "pickupDate" asc
//)
From the resulting query, it may be better to refactor the join as relationship between the Eloquent model for pickup_games and pickup_results. This will help to further simplify $playerIds.

How to customize Laravel WhereBetween if the value is not found

Im using Laravel WhereBetween like this
$from = '2015-01-02';
$to = '2015-01-30';
$users = User::whereBetween('created_at', [$from,$to])->get();
If the date value not found in User model, then the date will not show right?
I still want to show the date, even the date not found then I set value to 0.
Then the output will be like this
[
{'2015-01-02': 201},
{'2015-01-03': 0},
{'2015-01-04': 0},
{'2015-01-05': 7},
...
{'2015-01-30': 0}
]
Thanks.
You need to execute following query at first to get all user created at the given range,
$data = User::select([
DB::raw('DATE(created_at) AS date'),
DB::raw('COUNT(id) AS count'),
])
->whereBetween('created_at', [$from,$to])
->groupBy('date')
->orderBy('date', 'ASC')
->get();
With above query, you will get count of user for each day and this will not give 0 value if no users created at that day.
Now, what you have to do is to change above collection to array and insert 0 values if no users created.
$userCount = $data->toArray();
$dataByDay = array();
foreach($userCount as $count){
$dataByDay[$count->date] = $count->count;
}
Now, insert zero values as:
$dateFrom = Carbon::parse($from)->format('Y-m-d');
$dateTo = Carbon::parse($to)->format('Y-m-d');
$days = $dateTo->diffInDays($dateFrom);
for($i=0; $i<$days; $i++){
$dateString = $dateFrom->format('Y-m-d');
if(!isset($chartDataByDay[ $dateString ] {
$dataByDay[ $dateString ] = 0;
}
}
Now, $dataByDay array gives what you want.
Hope you understand.

Laravel 5 - how to get a random row from first 30 records in eloquent?

I am trying to get a random row from the top 30 records in a table. I sort all the records by score first, and take 30 records in a scope of the eloquent model:
public function scopePopular($query, $d)
{
return $query->where('d', $d)->orderBy('score', 'desc')->take(30);
}
Then in a class:
$cnt = Record::popular($d)->count();
if ($cnt == 0)
return;
$randIndex = rand(0, $cnt-1);
$record = Record::popular($d)->skip($randIndex)->take(1)->first();
return $record;
But when I check in php artisan tinker, I found that Record::popular($d)->count(); will return all the records number instead of 30. How can I correct this problem? Thanks.
Use get() before count() to run the query before count:
$cnt = Record::popular($d)->get()->count();
You are running the query 2 times. That is not necessary.
$cnt = Record::popular($d)->count(); // First query
if ($cnt == 0)
return;
$randIndex = rand(0, $cnt-1);
$record = Record::popular($d)->skip($randIndex)->take(1)->first(); // Second query
return $record;
Instead you can do it like this:
return Record::popular($d)->get()->random(); // One query only

Resources