Select SUM from subquery while using whereHas in Laravel - mysql

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

Related

Laravel. How to get a record, that has all user ids in related table?

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.

laravel eloquent query with relations

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

how to to make eloquent scope with whereHas like sql query below?

how to to make eloquent scope with whereHas like sql query below
table Property(id, title, slug, category_id, location_id,image)
table Category(id, name, slug)
table City ( id, name, slug)
The simple sql query that i need
Select * from property
join category on property.category_id=category.id
join city on property.location_id = city.id
where category.name = $query and city.name=$query
I want to make the eloquent scope in the Property Model
This is easy with relationships.
Based on your query, let's say this is your Property model:
class Property extends Model
{
public function category()
{
return $this->belongsTo(Category::class);
}
public function city()
{
return $this->belongsTo(City::class, 'location_id');
}
Now I can write my Eloquent query like this:
$query = 'Example';
$properties = Property::where('name', $query)
->whereHas('category', function (Builder $builder) use ($query) {
$builder->where('name', $query);
})->get();
Please note that Builder is imported from Illuminate\Database\Eloquent\Builder. You can also add with('category', 'city') to the query above in order to eager load those relationships.

Laravel: get models if count of relation has some condition

I have User and UserComplains Models.
I like to retrieve users that have UserComplains more than 2 times in the last 24 hours.
users:
id
user_complains:
complained_id ->ref-> users.id
created_at
this is what I tried and it is working:
$users = User::select('users.*')->join('user_complains' , 'users.id' , '=' , 'user_complains.complained_id')
->whereRaw("(
select count(*) from `user_complains`
where `user_complains`.`complained_id` = `users`.`id`
and `user_complains`.`created_at` > ?) >= ?" , [now()->subHours(24), 2])
->groupBy("users.id")
->get();
the above code is fine and is working, but I wonder is there a better way to do that?!
For something like this you can use whereHas(). :
$users = User::whereHas('*relationship*', function ($query) {
$query->where('created_at', '>=', now()->subDay(1));
}, '>', 2)->get();
As mentioned in the documentation, you can pass additional checks as the 3rd and 4th param so in this case you want to say where the user has more that 2 user_complains.
NB You will need to replace *relationship* with the actual name of the relationship.
You can do the following:
User::whereHas('complaints', function($query) {
$query->where('created_at', '>=', '2020-04-26');
}, '>', 2)->get();
In order for this to work though you need to have set up a relationship between your User and UserComplaint models.
class User extends Model
{
public function complaints()
{
return $this->hasMany(UserComplaint:class);
}
}

Where not Exists en Laravel

Could anybody tell me what error I might have in my laravel query, basically what I want is to list the records of a table whose id is not related in another table. I did it in Mysql with this query: SELECT * FROM item WHERE NOT EXISTS (SELECT null FROM qualifications WHERE grades.item_id = item.id AND qualifications.user_id = 2);
but now I need to do this same query in laravel, I tried it this way:
codigo
and what I get is this syntax error that I do not know how to solve anymore:
error
I am very grateful to anyone who can tell me what I am doing wrong, or in what form I make that query in Laravel.
You can also rewrite your query as left join like
SELECT i.*
FROM item i
LEFT JOIN qualifications q ON q.item_id = i.id AND q.user_id = 2
WHERE q.item_id IS NULL
In query builder you can write it as
DB::table('item as i')
->select('i.*')
->leftJoin('qualifications as q', function ($join) use($user_id) {
$join->on('q.item_id', '=', 'i.id')
->on('q.user_id', '=', $user_id);
})
->whereNull('q.item_id')
->get();
Another approach which i suggest you to go with, is setup your relations and models and do it with eloquent way
class Item extends Model
{
public function qualifications()
{
return $this->hasMany(\App\Models\Qualification::class, 'item_id');
}
}
class Qualification extends Model
{
public function qualifications()
{
return $this->belongsTo(Item::class, 'item_id');
}
}
And then you can use Querying Relationship Absence
Item::whereDoesntHave('qualifications', function ($query) use($user_id) {
$query->where('user_id', '=', $user_id);
})->get();