I'm trying to replace a mysql query with laravel eloquent. This is my structure.
Consumers Table
---------------
id, email, name
Transactions Table
-------------------
id, consumer_id, value, bonus_value
Output that I'm trying to achieve
id, email, name, total_value
1, abc#123.com, Al, 11000
2, abc2#123.com, Bl, 200000
This is what I have added in Consumer.php
/**
* Transactions Relationship
*
* #return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function transactions(){
return $this->hasMany(Transaction::class, 'consumer_id');
}
And this is the query I've written so far.
$query = Consumer::query()
->join('consumer_transactions AS ct', function($q) {
$q->on('consumers.id', '=', 'ct.consumer_id')
->where('ct.status', 'processed')
->where('ct.approved', 1)
->select(DB::raw('SUM(ct.value + ct.bonus_value) AS total_points'))
;
})
->whereIn('consumers.id', $consumerIds)
->get()
;
It doesn't return total_points
Join Clause that passed to your join take Illuminate\Database\Query\JoinClause witch doesn't have a method called 'select'.
you select should be out of join clause.
$query = Consumer::query()
->join('consumer_transactions AS ct', function($q) {
$q->on('consumers.id', '=', 'ct.consumer_id')
->where('ct.status', 'processed')
->where('ct.approved', 1);
})
->select( ['consumers.*', DB::raw('SUM(ct.value + ct.bonus_value) AS total_points')])
->whereIn('consumers.id', $consumerIds)
->get();
I'm not fun of joining tables. That's why I can offer you a different approach.
$consumers = Consumer::whereIn('id', $consumerIds)
->with(['transactions' => function($query) {
$query
->where('startus', 'processed')
->where('approved', 1)
}])->get()
->map(function($item, $key) {
return $item->total_points = $item->transactions->value + $item->transactions->bonus_value
});
Related
My query (DB::raw(AVG('pt.progress'))) this part is throwing error at the moment
$query = DB::table('projects as p')
->leftJoin('projects_tasks as pt','pt.project_id', 'p.id')
->select(
'p.id', 'p.project_name', DB::raw(AVG('pt.progress')) //this is where I need the average
);
$query->orderBy($order, $dir);
if ($limit != -1) {
$query->skip($start)->take($limit);
}
$records = $query->get();
Table structure:
projects:
========
id
project_name
...
...
projects_tasks:
===============
id
project_id,
parent, //0 or 1
progress //exmaple value 0.00 to 1.00
How to get the average of progress, where parent_id = 0 and project_id is the same?
The following query does work if I create a function and pass it in a loop, however, I want to optimize it and run it by joining on above query.
$data_row = DB::table('projects_tasks')
->select(DB::raw('(SUM(progress)*100)/count(progress) as project_progress'))
->where(['project_id' => $project_id, 'parent' => 0])
->get();
Seems that you have a syntax error on your query, the problem is in here DB::raw(AVG('pt.progress')).
Since you're using a raw query, the parameter there should be a string, so you must enclose that in quotes / double qoute.
$query = DB::table('projects as p')
->leftJoin('projects_tasks as pt','pt.project_id', 'p.id')
->select(
'p.id', 'p.project_name', DB::raw("AVG('pt.progress')")
);
$query->orderBy($order, $dir);
if ($limit != -1) {
$query->skip($start)->take($limit);
}
$records = $query->get();
There is a chat table and intermediate table.
I need to get from the chat table only those chats for which all user identifiers are found.
Now I am getting records if at least one match is found
My query
$chat = $this->select('chats.*')->distinct()
->rightJoin("chat_user", function ($query) {
$query->on("chats.id", "=", "chat_user.chat_id")
->whereIn("chat_user.user_id", [2, 17]);
})
->where('chats.type', '=', 'single')
->get();
Result
But I need a chat width id 4, because only it matches my request
I also tried to do it like this
$chat = $this->select('chats.*')->distinct()
->rightJoin("chat_user", function ($query) use ($members_ids) {
$query->on("chats.id", "=", "chat_user.chat_id")
->where("chat_user.user_id", 2)
->where("chat_user.user_id", 17);
})
->where('chats.type', '=', 'single')->get();
But result is empty
In plain SQL, this could be achieved with the following query:
SELECT chats.*
FROM chats
WHERE chats.type = 'single'
AND EXISTS (SELECT 1 FROM chat_user WHERE user_id = 2 AND chat_id = chats.id)
AND EXISTS (SELECT 1 FROM chat_user WHERE user_id = 7 AND chat_id = chats.id)
Translated into a Laravel query, we get the following:
$userIds = [2, 7];
$chats = DB::table('chats')
->where('chats.type', 'single')
->where(function (Builder $query) use ($userIds) {
foreach ($userIds as $userId) {
$query->whereExists(fn (Builder $query) => $query
->select(DB::raw(1))
->from('chat_user')
->where('chat_user.user_id', $userId)
->whereColumn('chat_user.chat_id', 'chats.id'));
}
})
->select('chats.*')
->get();
You should do something like this instead:
$chat = $this->select('chats.*')->distinct()
->rightJoin('chat_user', function ($query) {
$query->on('chats.id', '=', 'chat_user.chat_id')
->where('chat_user.user_id', 2)
->where('chat_user.user_id', 17);
})
->where('chats.type', '=', 'single')
->get();
Specifying multiple where works as an AND condition. If you use whereIn() will work as an OR conditions for the supplied values.
I want to display ordered list of stores based on cities from a brand.
This is the code I tried
$brand = Brand::where('slug','=',$slugurl)
->with(['stores' => function($q){
$q->where('status', 1)
->join('cities', function ($join){
$join->on('cities.id', '=', 'stores.city_id')->orderBy('cities.sort_number', 'DESC');
});
}])
->firstOrFail();
The relationship of the tables :
Brand hasMany Stores and Stores belongTo Cities
The listings results output is not ordered based on cities sort_number. Any idea how to achieve this ?
It is useless to order in join's closure.
You need to append the orderBy after join:
$brand = Brand::where('slug','=',$slugurl)
->with(['stores' => function($q){
$q->where('status', 1)
->join('cities', 'cities.id', '=', 'stores.city_id')
->orderBy('cities.sort_number', 'DESC');
}])
->firstOrFail();
This query convert to raw sql is:
select * from brands where slug = ? limit 1;
select * from stores
join cities on cities.id = stores.city_id
where status = 1 and stores.brand_id in (?)
order by cities.sort_number desc;
I have 2 tables, customers and customer_invoices, and I want to get all the customers with a condition on their invoices, and select specific columns (customers.id, customers.last_name, and the sum of the total_price of the invoices for each customer), I have this query :
$result = Customer::whereHas('customerInvoices', function(Builder $q) {
$q->where('customer_invoices.status', 1);
})->select([
'customers.id',
'customers.last_name',
\DB::raw('SUM(customer_invoices.total_price) as sum')
])->get();
customerInvoices is the relation :
public function customerInvoices() {
return $this->hasMany(CustomerInvoice::class);
}
I want to use subqueries instead of joins, so here I can't select this \DB::raw('SUM(customer_invoices.total_price) as sum'), or else I get this error of course :
"SQLSTATE[42S22]: Column not found: 1054 Unknown column 'customer_invoices.total_price' in 'field list' (SQL: select `customers`.`id`, `customers`.`last_name`, SUM(customer_invoices.total_price) as sum from `customers` where exists (select * from `customer_invoices` where `customers`.`id` = `customer_invoices`.`customer_id` and `customer_invoices`.`status` = 1))"
How can I achieve this without using joins ?
You can use withCount() to get sum from related model as
$result = Customer::select([
'customers.id',
'customers.last_name'
])->withCount([
'customerInvoices as invoice_sum' => function($query) {
$query->select(DB::raw('SUM(total_price)'));
}
])->whereHas('customerInvoices', function(Builder $q) {
$q->where('customer_invoices.status', 1);
})->get();
Another approach to get sum, you can define a hasOne() relation in your Customer model like
public function invoice_sum()
{
return $this->hasOne(CustomerInvoice::class)
->select('customer_id',
DB::raw('sum(total_price)')
)->groupBy('customer_id');
}
And in query builder
$result = Customer::select([
'customers.id',
'customers.last_name',
])->with('invoice_sum')
->whereHas('customerInvoices', function(Builder $q) {
$q->where('customer_invoices.status', 1);
})->get();
As per Eloquent : withCount() overrides the $columns on get() issue first put select() mehtod and then use with() function
I've been able to get the query result I need using the following raw sql:
select `person`.`id`, `full_name`, count(actions.user_id) as total
from `persons`
left join `actions`
on `actions`.`person_id` = `persons`.`id`
and `actions`.`user_id` = $user
where `type` = 'mp'
group by `persons`.`id`
But I haven't been able to get it working in eloquent yet.
Based on some similar answers, I'd tried functions within ->where() or leftJoin(), but the count of each person's actions isn't yet being filtered by $user. As it stands:
$query = Person::leftJoin('actions', function($q) use ($user)
{
$q->on('actions.person_id', 'persons.id')
->where('actions.user_id', $user);
})
->groupBy('persons.id')
->where('type', 'foo')
//->where('actions.user_id', '=', $user)
->get(['persons.id', 'full_name', DB::raw('count(actions.id) as total')]);
I'm at least heading in roughly the right direction, right...?
If it's relevant, the Persons.php model has two actions relationships:
public function actions()
{
return $this->hasMany('Action');
}
public function actionsUser($id)
{
return $this->hasMany('Action')->where('user_id', $id);
}
So, for reference, I solved it like so:
$query = Person::leftJoin('actions', function($q) use ($user)
{
$q->on('actions.person_id', '=', 'persons.id')
->where('actions.user_id', '=', "$user");
})
->groupBy('persons.id')
->where('type', 'foo')
->get(['persons.id', 'full_name', DB::raw('count(actions.id) as total')]);
The ->where() clause within leftJoin, oddly, needs the speech marks for the variable to be passed through the sql query correctly (likewise, '2' doesn't seem to work while "2" does).
I found that the where doesn't always work on the leftJoin clause
If in the future you get any trouble with it, I'd suggest you using this:
$query = Person::leftJoin('actions', function($q) use ($user)
{
$q->on('actions.person_id', '=', 'persons.id')
->on('actions.user_id', '=', "$user");
})
->groupBy('persons.id')
->where('type', 'foo')
->get(['persons.id', 'full_name', DB::raw('count(actions.id) as total')]);
Hope it helps someone.
When laravel eloquent just start getting complex like this
For more flexibility and readability I'll just use plain sql statement then hydrate the results.
$sql = "
SELECT `person`.`id`,
`full_name`,
count(actions.user_id) AS total
FROM `persons`
LEFT JOIN `actions`
ON `actions`.`person_id` = `persons`.`id`
AND `actions`.`user_id` = $user
WHERE `type` = 'mp'
GROUP by `persons`.`id`
";
$query = Person::hydrate(
DB::select( $sql )
);