How do I order records not matched in leftJoin? - mysql

I'm new to Laravel and having a problem returning my results from this query in the correct order. I have a $products variable that contains both "A" and "B" type of products.
I'm doing a leftJoin with a match_products table to order the records by match_products.sku, but only "B" records are contained in that table.
$products = $products->with(['matches' => function ($quer) use ($business) {
$quer->where('user_id', $business->user_id)->with('skuDiscount');
}])->withCount(['variations' => function ($q) use ($business) {
$q->whereHas('seller_variations', function ($qu) use ($business) {
$qu->where('user_id', $business->user_id);
});
}])->leftJoin('match_products', function ($join) use ($business) {
$join->on('products.id', '=', 'match_products.product_id')
->where('match_products.user_id', '=', $business->user_id);
})
->orderBy('match_products.sku', $order)
->select('products.*')
->limit($perPage)
->offset($perPage * ($page - 1))
->get();
return response()->json(['products' => $products, 'productsCount' => $productsCount]);
My results are coming out:
A records
B records (in order by Sku)
but I need them to be the opposite:
B records (in order by Sku)
A records
I assume this is because the leftJoin is returning the matched records after the non-matched records but I need to return them before the non-matched records. I'd appreciate any help someone could please give me.

Related

How to optimize a laravel query, to find the average from another joined table

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

How to build query for multiple condition for same column in laravel query builder?

I want to get count from 'user' table with 'plan_validity' is null or more than today's date. both condition need to check for same column with user_id and status = 1.
$currentUser = User::where('user_id', $card_details->user_id)
->where('status', 1)
->where(function (Builder $query) {
return $query
->whereDate('plan_validity', '>=', Carbon::now())
->orWhere('plan_validity', null);
})->count();
giving error
[2021-11-23 10:40:31] production.ERROR: Argument 1 passed to App\Http\Controllers\ProfileController::App\Http\Controllers\{closure}() must be an instance of App\Http\Controllers\Builder, instance of Illuminate\Database\Eloquent\Builder given, called in /home/hellovcard/public_html/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php on line 237 {"userId":17,"exception":"[object] (TypeError(code: 0): Argument 1 passed to App\\Http\\Controllers\\ProfileController::App\\Http\\Controllers\\{closure}() must be an instance of App\\Http\\Controllers\\Builder, instance of Illuminate\\Database\\Eloquent\\Builder given, called in /home/hellovcard/public_html/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php on line 237 at /home/hellovcard/public_html/app/Http/Controllers/ProfileController.php:34)
how to modify above mentioned query?
this is output I need to get
enter image description here
You are not combining your "where" clauses properly. With the "orWhere" at the end, it essentially ignores all the conditions (ie. which user it relates to) of the preceding where clauses, so adds to the results any row where plan_validity is null. So after you query by user ID and status, you then need to combine the remainder of the query into a logical group like so :
$user = Users::where('user_id', $card_details->user_id)
->where('status', 1)
->where(function (Builder $query) {
return $query
->whereDate('plan_validity', '>=', Carbon::now())
->orWhere('plan_validity', null);
})
->get();

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

Select SUM from subquery while using whereHas in Laravel

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