Laravel Eloquent Multiple Where with count - php

Following is the SQL query that I need to perform on laravel eloquent. SQL returns expected output.
SELECT
orders.id,
orders.`status`,
order_type,
COUNT(order_type) as count
FROM
orders
WHERE
orders.`status` = 0 && order_type = 1
ORDER BY
orders.id DESC
what I have tried on laravel is below
$receved = Order::select('status', 'order_type')->where('status',0);
$relase = $receved->where('order_type', 1)->get();
$bulk = $receved->where('order_type', 2)->get();
$distiribute = $receved->where('order_type', 3)->get();
return response()->json([
'success' => true,
'message' => 'Statement Updated',
'orderStatment' => [
'relaseCount' => count($relase),
'bulkCount' => count($bulk),
'distiributeCount' => count($distiribute)
],
], 200);
I seeking recommendation/suggestion to operate this in a correct way
The output I getting on laravel is
'orderStatment' => [
'relaseCount' => 14,
'bulkCount' => 0,
'distiributeCount' => 0
],
the output of expectation and SQL produce is
'orderStatment' => [
'relaseCount' => 14,
'bulkCount' => 5,
'distiributeCount' => 4
],
There are 8 Different type of status and 3 different types of order_type available on Table I want to get each order_type count of every status

You might have better luck doing it all in one query, then getting the data back out.
$receved = Order::select('status', 'order_type', DB::raw('COUNT(id) as order_count'))->where('status',0)
->groupBy('order_type')
->get();
This will give you a collection of all of the order types and their counts in one query. After that, you can get the data back out.
$bulk = $relase = $distiribute = 0;
foreach($receved as $rec) {
if($rec->order_type == 1) $relase = $rec->order_count;
elseif($rec->order_type == 2) $bulk = $rec->order_count;
elseif($rec->order_type == 3) $distiribute = $rec->order_count;
}

The problem you're facing is due to the fact that all of the following statements are manipulating the same query builder object:
$receved = Order::select('status', 'order_type')->where('status',0);
$relase = $receved->where('order_type', 1)->get();
$bulk = $receved->where('order_type', 2)->get();
$distiribute = $receved->where('order_type', 3)->get();
So the actual queries created will be something like this:
All start with: select status, order_type from orders where status = 0 and
order_type = 1;
order_type = 1 and order_type = 2;
order_type = 1 and order_type = 2 and order_type = 3;
This is why the last two queries return 0. It's expected once you see the resulting query.
You can verify this by logging the query (see this answer for details, or the docs here).
$receved is actually getting the where clauses attached to it each time. So you're not just starting with the original statement, but building onto it each time you call where.

Related

Add Select Get Value

I want to get order status name based on order_status_id
I have order statuses in the array:
$orderStatuses = [
1 => 'Waiting',
2 => 'Delivered',
3 => 'Rejected'
];
I need to get the order status name and order_status_id.
$orders = Order::limit(50)->get();
I am now only getting order status name from looping through each order like:
foreach($orders as $order){
$order->order_status_name = $orderStatus[$order->order_status_id];
}
Is there any way to get the order status name without using foreach.
What I tried:
Order::addSelect("$orderStatuses[".'order_status_id'."]". " as order_status_name");
I am unable to get the result. Is there any way to get the result from the eloquent query or do I have to use foreach?
You can create an accessor in the Order model:
private $orderStatuses = [
1 => 'Waiting',
2 => 'Delivered',
3 => 'Rejected'
];
protected $appends = ['status'];
public function getStatusAttribute()
{
return $this->orderStatuses[$this->order_status_id];
}
Then use it in your code as :
$order->status;
More on this here.
-- EDIT
$orders = Order::limit(50)->get();
foreach($orders as $order)
{
$order->status; // as this is computed property.
}
#nakov answer is correct if you want to go with model but if you want to fetch status based on value you can use case in query :
$data = Order::select("*",\DB::Raw("case when order_status_id = 1 then 'Waiting' when order_status_id = 2 then 'Delivered' when order_status_id = 3 then 'Rejected' end as status"))
->limit(50)
->get();
It will provide you status field in your collection
foreach($orders as $order){
$order->order_status_name = $order->status;
}

How to get latest record on a relationship in Laravel

Users have plans stored in the invoices table. These plans are monthly based.
What I need to do
I want to add a new row for user if his plan expire date has reached and they didn't renew their plans (I don't want to update old one)
The issue is
Each user has unlimited rows in invoices table as they renew each month. Now when I try to retrieve their latest row and check the expiring date it gets other rows of those users as well.
Example
My user has 3 rows in invoices
two of them already expired and renewed, the current one is id=3
when I try to expire this id=3 and create id=4 for this user
it gets all 3 rows and send 3 emails to the user.
Code
public function handle()
{
$invoices = Invoice::where('plan_expire', '<=', Carbon::now())->get();
$current = Carbon::now();
$expiredatetime = $current->addDays(30);
$useType = Type::where('name', 'Free')->first();
foreach($invoices as $invoice)
{
Invoice::create([
'user_id' => $invoice->user->id,
'type_id' => $useType->id,
'amount' => $useType->price,
'status' => 'Approved',
'plan_expire' => $expiredatetime->toDateTimeString(),
]);
Mail::to($invoice->user->email)->send(new UserPlansReset($invoice));
}
}
User model
public function invoices()
{
return $this->hasMany(Invoice::class);
}
Invoice model
protected $fillable = [
'user_id', 'type_id', 'amount', 'status', 'plan_expire',
];
protected $casts = [
'plan_expire' => 'datetime',
];
public function user()
{
return $this->belongsTo(User::class);
}
Question
Do you have any idea how I can only get users latest row in invoices table?
Update
based on answers below I changed my code to:
$current = Carbon::now();
$expiredatetime = $current->addDays(30);
$useType = Type::where('name', 'Free')->first();
$users = User::all();
foreach($users as $user){
$latestInvoice = $user->invoices()->latest()->first();
if(!empty($latestInvoice) && $latestInvoice->plan_expire <= Carbon::now()){
Invoice::create([
'user_id' => $user->id,
'type_id' => $useType->id,
'amount' => $useType->price,
'status' => 'Approved',
'plan_expire' => $expiredatetime->toDateTimeString(),
]);
Mail::to($user->email)->send(new UserPlansReset($user));
}
}
Now this function will return
Expected response code 220 but got an empty response
and wont send emails.
Change in Invoice model, add plan_expire in $dates variable instead of $casts :
protected $dates = ["plan_expire"];
You can try like this :
$users = User::all();
foreach($users as $user){
$latestInvoice = $user->invoices()->latest()->first();
if($latestInvoice->plan_expire->isPast()){
//create invoice and mailed it
}
//other steup
}
For Email send return empty response issue , You can check this question click here
Find expired invoices, group by user id and order by plan_expire and select first record in each group.
MySQL server version < 8 don't have window functions that may make it easier to do row numbering in matched rows.
A workaround is to set client variables that can be used to number invoices by the same user starting from 1 and selecting only the first ones.
$now = Carbon::now();
$nowDS = $now->toDateTimeString();
$expired_invoices = "
SET #rownum := 0;
SET #userid := NULL;
SELECT *, uid as user_id, plan_expire
FROM (
SELECT
*,
#rownum := CASE
WHEN #userid = uid
THEN #rownum + 1
ELSE 1
END AS rn,
#userid := user_id AS uid,
num
FROM invoices AS i
WHERE plan_expire <= $nowDS
ORDER BY user_id, plan_expire DESC
) AS num_invoices WHERE rn = 1;
"
$invoices = DB::select($expired_invoices);
Now, $invoices can be iterated over and mail sent to the owner of it.
$expiredatetime = $now->addDays(30);
$useType = Type::where('name', 'Free')->first();
$users = User::all();
foreach ($invoices as $invoice)
{
Invoice::create([
'user_id' => $invoice->user_id,
'type_id' => $useType->id,
'amount' => $useType->price,
'status' => 'Approved',
'plan_expire' => $expiredatetime,
]);
$user = $users->find(['id' => $invoice->user_id]);
Mail::to($user->email)->send(new UserPlansReset($user));
}

Laravel Eloquent Query Not Working

$demos = Demo::whereIn('store_id', [1,2,4], function($query){
$query->where('status', 1);
})->paginate(10);
I know that this thats not working, But how can I work with this logic..?
[Select * from 'demos' where store_id in 1,2,4 and status = 1 ]
If I understand correctly you need something like this.
$demos = Demo::whereIn('store_id', [1, 2, 4])->where('status', 1)->paginate(10);
Chained "where" Eloquent methods == "AND" database query conditions.
Sometimes, it is better not use ORM`ish approach.
They say, old plain SQL is your best friend. So.
$data = \DB::select(
\DB::raw("SELECT * FROM `demos` WHERE `store_id` IN (:id1, :id2, :id3)"), array( ':id1' => 1, ':id2' => 2, ':id3' => 3 )
);
Or, if you have unknown count of store_id entries, you could:
$ids = array(1, 2, 3, 4, ...); // looks like unknown ammount of entries
$data = \DB::select(
\DB::raw("SELECT * FROM `demos` WHERE `store_id` IN (".rtrim(str_repeat('?,', count($ids)),',').")"), $ids
);

fetch record that is equal to 1 or 0 laravel eloqouent

I'm using this one
$notification = notification::where('department', '=', 1)->get();
to fetch all records that has 'department' of 1 in my database (it works)
Now, I want to fetch also all records that neither has 'department' of 1 or 0, any ideas, help?
How about using whereIn or whereNotIn clauses?
$notification = notification::whereIn('department', [1, 2])->get();

Use limit range in yii2?

I want to get data from db using limit 12,20 .
Here is my code:
$Query = new Query;
$Query->select(['um.id as USERid', 'um.first_name', 'um.last_name', 'um.email', 'COUNT(g.id) as guestCount'])
->from('user_master um')
->join('LEFT JOIN', 'guest g', 'g.user_id = um.id')
->limit(12,20)
->groupBy('um.id')
->orderBy(['um.id' => SORT_DESC]);
$command = $Query->createCommand();
$evevtsUserDetail = $command->queryAll();
It is not working. It is giving me all rows. I also tried ->limit([12,20]), not working.
But when I am using limit(12) then I am getting 12 rows.
I want to get rows in limit 12,20 . What should I have to do for that in my this code?
Try this:
$Query = new Query;
$Query->select(['um.id as USERid', 'um.first_name', 'um.last_name','um.email','COUNT(g.id) as guestCount'])
->from('user_master um')
->join('LEFT JOIN', 'guest g', 'g.user_id = um.id')
->limit(20)
->offset(12)
->groupBy('um.id')
->orderBy(['um.id' => SORT_DESC]);
Offset() specifies the starting point and limit() specifies the Number of records. If you want records between 12 and 20 then use limit(8).
For More Info:
http://www.bsourcecode.com/yiiframework2/select-query-model/#offset
http://www.yiiframework.com/doc-2.0/yii-db-querytrait.html#offset%28%29-detail
you can do with Active record
$model = YourModel::find()->where('user_id = :user_id', [':user_id' => \Yii::$app->user->id])->limit(12,20)->all();
OR
$model = YourModel::find()->where('user_id = :user_id', [':user_id' => \Yii::$app->user->id])->with(['job','job.jobRecipient'])->limit(12)->all();

Categories