How to merge 2 queries to work with pagination? - laravel

I currently have a query that sums the number of non-active users in the last year and groups them by country and city and then paginates the result:
UserData::query()
->select(
country,
city,
DB::raw('SUM(CASE WHEN end_date IS NULL THEN 0 ELSE 1 END) AS not_active'),
)
->whereBetween('created_at', [Carbon::now()->subYear(), Carbon::now()])
->groupBy('country', 'city')
->paginate('500');
But I also need to add to each group a column that shows how many active users are of the same group, of all time, not just last year:
UserData::query()
->select(
country,
city,
DB::raw('SUM(CASE WHEN end_date IS NULL THEN 1 ELSE 0 END) AS active'),
)
->groupBy('country', 'city')
->get();
Then in the frontend I want to display the data in a table so I want the data to be "merged" so that I can output the matching active and not_active columns together, so the end result should look like that:
country | city | active(all time) | not active(past year)
------------|-----------|------------------|-----------------------
Sweden | Stockholm | 25 | 1
Switzerland | Bern | 43 | 13
But how can it be done when using pagination?
I tried to do it with subqueries but that didn't work: (Note that I am using slightly different query checking whereNull for active users and whereNotNull for non-active users:
$result = UserData::select('end_date', 'country', 'city')
->where(function ($query) {
$query->select('end_date')
->whereNull('end_date')
->whereBetween('created_at', [Carbon::now()->subYear(), Carbon::now()]);
}, 'active')
->where(function ($query) {
$query->select('end_date')
->whereNotNull('end_date')
}, 'not_active')
->groupBy('country', 'city')
->paginate('500');

This can be solved by using a union()
$first = UserData::query()
->select(
'country',
'city',
DB::raw('SUM(CASE WHEN end_date IS NULL THEN 0 ELSE 1 END) AS not_active'),
)
->whereBetween('created_at', [now()->subYear(), now()]);
$second = UserData::query()
->select(
'country',
'city',
DB::raw('SUM(CASE WHEN end_date IS NULL THEN 1 ELSE 0 END) AS active'),
);
$first->union($second)
->groupBy('country', 'city')
->paginate('500');
Which will execute a SQL union query like select *cols* from *table* where *clause* union (select *cols* from *table* where *someOtherClause*)

Related

Laravel search across multiple date ranges

id
listing_id
start_date
end_date
1
1
2023-01-20
2023-01-25
2
1
2023-02-26
2023-02-10
3
1
2023-02-11
2023-02-20
4
1
2023-02-21
2023-02-27
$listings->whereHas(
'availabilityCalendar',
function ($query) use ($start_date, $end_date) {
$query->where('start_date', '>=', $start_date)
->where('end_date', '<=', $end_date);
})
}
Fetch data according to start_date = 2023-01-23 / end_date = 2023-02-20 I sent by frontend. The start_date and end_date values I sent are among the values in the records I wrote above.
If I sent start_date = 2023-01-01 / end_date = 2023-02-20 they wouldn't be included. Or if I had submitted the dates start_date = 2023-01-23 / end_date = 2024-01-01 they would still not be included.
But if I sent the dates start_date = 2023-02-01 / end_date = 2023-02-15 it would have been met.
Can you help with this?
You should make sure your query are casted as date.
You can do some thing like
$startDate = Carbon::createFromFormat('Y-m-d', $start_date);
$endDate = Carbon::createFromFormat('Y-m-d', $end_date);
$query->whereDate('start_date', '>=', $startDate )
->whereDate('end_date', '<=', $endDate );
or
$query->where(DB::raw('DATE(start_date)'), '>=', $startDate )
->where(DB::raw('DATE(end_date)'), '<=', $endDate );

Get data & Ignore null value when using group by in laravel [duplicate]

This question already has answers here:
GROUP BY - do not group NULL
(6 answers)
Closed 1 year ago.
I want to group by the data based on content_related_group. But there's a condition where content_related_group is null, and I don't want it to group the data with null value.
I've tried like this:
$products = DB::table('product')
->select('*', DB::raw('count(*) as total'))
->orderBy('created_at', 'desc')
->groupBy('content_related_group')
->paginate(9);
It's working, but it grouping the null data into one. I don't want it, what I want is to get all data from database and only group the the data with same content_related_groupinto one.
Example data:
id | content_related_group
1 | content_1
2 | content_1
3 | null
4 | null
Result (the null data keep separated from each other & the same content_related_group is grouped:
id | content_related_group
1 | content_1
2 | null
3 | null
It is possible? Thanks
try this if you want to remove the null rows:
$products = DB::table('product')
->select('*', DB::raw('count(*) as total'))
->whereNotNull('content_related_group')
->orderBy('created_at', 'desc')
->groupBy('content_related_group')
->paginate(9);
try this if you want to keep null value grouped to one row
$x_products = DB::table('product')->whereNull('content_related_group');
$products = DB::table('product')
->whereNotNull('content_related_group')
->union($x_products)
->select('*', DB::raw('count(*) as total'))
->orderBy('created_at', 'desc')
->groupBy('content_related_group')
->paginate(9);

Treating different sql values into the same group

I have a gender table. For genders other than male and female, the default value would be 'other' but due to some errors, the database has some users with gender value = null. So instead of changing the data directly. Is there a query to get the other and null values as the same type.
id | username | gender
1 | mark | male
2 | samantha | female
3 | rupert | other
4 | collins | null
$genders = User::whereNull('deleted_at')
->select([
'gender',
DB::raw('count(*) as total')
])
->groupBy('gender')
->get();
the query above gives 4 labels ['male','female','other','null']. Is there a way to get 3 labels instead ['male','female','other'] and treat the null values the same as the other value ?
You can handle it with a DB::raw query, I think. Please try this one:
$genders = User::whereNull('deleted_at')
->select([
DB::raw('case when gender is null then \'other\' else gender end as gender'),
DB::raw('count(*) as total')
])
->groupBy(DB::raw('case when gender is null then \'other\' else gender end'))
->get();

I have no data with joined ad_categories table

In laravel 8 I make request with condition on joined ad_categories table :
$adsCategories = [1,2,3,4,6,7,8];
$data = Ad
::whereDate('expire_date', '>=', $date_start)
->whereDate('expire_date', '<', $date_end)
->orderBy($order_by, $order_direction)
->with('adCategories')
->with('creator')
->withCount('adLocations')
->leftJoin('ad_categories', 'ad_categories.ad_id', '=', 'ads.id')
->whereIn('ad_categories.category_id', $adsCategories)
->get();
and tracing sql :
SELECT `ads`.*, ( SELECT count(*)
FROM `ad_locations`
WHERE `ads`.`id` = `ad_locations`.`ad_id`) AS `ad_locations_count`
FROM `ads`
LEFT JOIN `ad_categories` on `ad_categories`.`ad_id` = `ads`.`id`
WHERE date(`expire_date`) >= '2021-04-01' AND date(`expire_date`) < '2021-05-01' AND `ad_categories`.`category_id` in ('1', '2', '3', '4', '6', '7', '8')
ORDER BY `expire_date` asc, `price` desc
I got no rows returned, but in database I see ralated rows :
Manually removing rows with ad_categories.category_id` :
SELECT `ads`.*, ( SELECT count(*)
FROM `ad_locations`
WHERE `ads`.`id` = `ad_locations`.`ad_id`) AS `ad_locations_count`
FROM `ads`
LEFT JOIN `ad_categories` on `ad_categories`.`ad_id` = `ads`.`id`
WHERE date(`expire_date`) >= '2021-04-01' AND date(`expire_date`) < '2021-05-01'
ORDER BY `expire_date` asc, `price` desc
I got all data I need.
What is wrong ?
Thanks!
use select...
$data = Ad
::whereDate('expire_date', '>=', $date_start)
->whereDate('expire_date', '<', $date_end)
->orderBy($order_by, $order_direction)
->with('adCategories')
->with('creator')
->withCount('adLocations')
->leftJoin('ad_categories', 'ad_categories.ad_id', '=', 'ads.id')
->whereIn('ad_categories.category_id', $adsCategories)
->select('ads.*', 'ad_categories.*') // <- add this line of code
->get();

Raw Laravel query as a collection with conditions

I've got a fairly complex query that I'd rather not write in Eloquent so I've written it raw. The only problem is that I need to be able to search/filter through the data using the front-end this query is connected to.
This what I've tried but I'm getting an "Call to a member function get() on null" error.
Here's my code:
$report = collect(DB::connection('mysql2')->select("SELECT
t2.holder,
t2.merchantTransactionId,
t2.bin,
t2.last4Digits,
t3.expDate,
(CASE WHEN t3.expDate < CURDATE() THEN 'Expired'
WHEN t3.expDate > CURDATE() THEN 'Due to expire' END) AS expInfo,
t2.uuid
FROM transactions AS t2
INNER JOIN (
SELECT t1.uuid, t1.holder, t1.bin, t1.last4Digits, LAST_DAY(CONCAT(t1.expiryYear, t1.expiryMonth, '01')) AS expDate
FROM transactions t1
JOIN (SELECT t1.merchant_access
FROM total_control.users,
JSON_TABLE(merchant_access, '$[*]' COLUMNS (
merchant_access VARCHAR(32) PATH '$')
) t1
WHERE users.id = :userId
) AS t2
ON t1.merchantUuid = t2.merchant_access
WHERE t1.paymentType = 'RG'
AND t1.status = 1
) t3
ON t2.uuid = t3.uuid
WHERE t3.expDate BETWEEN DATE_SUB(CURDATE(), INTERVAL 30 DAY) AND DATE_ADD(CURDATE(), INTERVAL 30 DAY)
GROUP BY t2.holder, t2.bin, t2.last4Digits
ORDER BY t2.holder ASC", ['userId' => $request->userId]))
->when($request->search['holder'], function($q) use ($request) {
$q->where('t2.holder', 'LIKE', '%'.$request->search['holder'].'%');
})->get();
return $report;

Resources