Wherein query to related tables - laravel

I have been trying to a query where in I want to do some validation on the related tables. I would like to do something like this using Laravel 5
select
*
from
table1
INNER JOIN table3 on(table1.id = table3.table1_id)
INNER JOIN table2 on(table1.id = table2.table1_id)
where
table2.column2 in ('arr1', 'arr2');
Also table1 is related to 5 - 7 tables and I want to eager load all of this. Here's what I had so far
$reports = Table1::with(
[
'table3',
'table4',
'table5.table6',
'table7',
'table8',
])
->where('created_at', '>=', date('Y-m-d', strtotime($request->get('from'))))
->where('created_at', '<', date('Y-m-d', strtotime($request->get('to'))))
->with('table2',function($query) use($table1_column){
return $query->whereIn('table1_column',$table1_column);
});
But this displays everything. Even the items that does not exist in table2. What I would like to achieve is to create a result where all items is the only items that exists in table2. Meaning all transaction made using items in table2.
Assuming the item in table2 has an ids of 123, 456, and 789 then I would like to display all record related to this id's
How can I make this kind of result

You can use the whereHas method.
$reports = Table1::with(
[
'table3',
'table4',
'table5.table6',
'table7',
'table8',
])
->whereHas('table2', function($query) use($table1_column){
return $query->whereIn('table1_column',$table1_column);
})
->where('created_at', '>=', date('Y-m-d', strtotime($request->get('from'))))
->where('created_at', '<', date('Y-m-d', strtotime($request->get('to'))));
Note that this does not include the table 2 data in the result set. If you need that include the table2 relation name in the with method call.

Related

Convert DB::Select to Query Builder

i has raw query in laravel like this
public function getPopularBook(){
$book = DB::select("
with totalReview as(
SELECT r.book_id , count(r.id)
FROM review r
GROUP BY r.book_id
)
SELECT *
from totalReview x
left JOIN (
SELECT b.*,
case when ((now() >= d.discount_start_date and now() <= d.discount_end_date) or (now() >= d.discount_start_date and d.discount_end_date is null)) then (b.book_price-d.discount_price)
ELSE b.book_price
end as final_price
FROM discount d
right JOIN book b
on d.book_id = b.id
) as y
on x.book_id = y.id
ORDER BY x.count DESC, y.final_price ASC
LIMIT 8"
);
return $book;
}
so when i want to return a paginate, it doesn't work so can i convert this to query build to use paginate
This is a very un-optimized raw query in itself. You are performing too many Join in Subquery just to sort by price
i'm assuming the database table:
books[ id, name, price ]
reviews[ id, book_id ]
discounts[ id, book_id, start_date, end_date, discount_price]
Look how easy it is if you just use Eloquent:
Book::withCount('reviews')->orderBy('reviews_count')->get();
this will give you all the Books order by number of reviews
now with the final price, this can be a bit tricky, let's take a look at a query when we don't consider discount time
Book::withCount('reviews')
->withSum('discounts', 'discount_price') //i'm assuming a book can have many discount at the same time, so i just sum them all
->addSelect(
DB::raw('final_price AS (books.price - discounts_sum_discount_price)')
)
->orderBy('reviews_count', 'asc') // =you can specify ascending or descending
->orderBy('final_price', 'desc') //in laravel chaining multiple orderBy to order multiple column
->get();
I dont even need to use Subquery!! But how do we actually only add the "active" discount?, just need to modify the withSum a bit:
Book::withCount('reviews')
->withSum(
[
'discounts' => function($query) {
$query->where('start_date', '<=', Carbon::now())
->where('end_date', '>=', Carbon::now())
}
],
'discount_price'
)
->addSelect(
DB::raw('final_price AS (books.price - discounts_sum_discount_price)')
)
->orderBy('reviews_count', 'asc') // =you can specify ascending or descending
->orderBy('final_price', 'desc') //in laravel chaining multiple orderBy to order multiple column
->get();
and it is done
What about pagination? just replace the get() method with paginate():
Book::withCount('reviews')
->withSum(['discounts' => function($query) {
$query->where('start_date', '<=', Carbon::now())->where('end_date', '>=', Carbon::now())
}],'discount_price')
->addSelect(DB::raw('final_price AS (books.price - discounts_sum_discount_price)')) //just format to be a bit cleaner, nothing had changed
->orderBy('reviews_count', 'asc')
->orderBy('final_price', 'desc')
->paginate(10); //10 books per page
WARNING: this is written with ELoquent ORM, not QueryBuilder, so you must define your relationship first

Join 2 Temp tables laravel

I have following temp tables I wanna join them In query builder but I am failing to do so. I wanna join these 2 tables so I can do i1.imp/i2.imp
$subQuery1 = MyModel::query()
->from("table as i1")
->select(
\DB::raw('sum(col) as col1'),
\DB::raw('co1')
)->where('stamp', '>=', '2022-03-01 14:25:00')
->where('stamp', '<', '2022-03-07 14:30:00')
->groupBy(
'co1'
);
$subQuery2 = MyModel::query()
->from("table as i2")
->select(
\DB::raw('sum(col) as col1'),
\DB::raw('co1')
)->where('stamp', '>=', '2022-03-01 14:20:00')
->where('stamp', '<', '2022-03-07 14:25:00')
->groupBy(
'co1'
);
You can use from, fromSub or table and pass in a subquery instead of a table.
You can do the same with joinSub for joins.
You need to provide an alias though.
For example, to use $subquery1 as the main table and join it with $subquery2, the resulting query could look like this:
$results = DB::query()
->select(.....)
->fromSub($subquery1, 'i1')
->joinSub($subquery2, 'i2', function ($join) {
$join->on('i1.col', '=', 'i2.col');
// ->orOn(....)
});
->where(....)
->get();
Laravel 9.x API - fromSub
Queries - Subquery Joins

Convert SQL to Laravel Eloquent Statement

I've been working on a few tables where through a rather complex relationship (that I'm trying to clean up, but I still need reports made from the data through my Laravel).
At the moment, I can pull the data using the following SQL query to my MySQL database:
SELECT
customers.id,
customers.customer_name,
SUM(shipments.balance) AS shipmentBalance
FROM customers
LEFT JOIN shipments
ON customers.id = shipments.bill_to
AND balance > (SELECT IFNULL(SUM(payments_distributions.amount),0)
FROM payments_distributions
WHERE payments_distributions.shipment_id = pro_number)
GROUP BY customers.id, customers.customer_name
ORDER BY shipmentBalance DESC
LIMIT 5;
I'm just not sure how to rewrite it properly into the whereRaw or DB::raw statements that Laravel Eloquent requires, as my previous attempts have failed.
Update
Here is the closest solution I have tried:
DB::table('customers')
->select('customers', DB::raw('SUM(shipments.balance) AS shipmentBalance'))
->leftJoin(
DB::raw('
(select shipments
ON customers.id = shipments.bill_to
AND balance > (SELECT IFNULL(SUM(payments_distributions.amount),0)
FROM payments_distributions
WHERE payments_distributions.shipment_id = pro_number)'))
->groupBy('customers.id')
->orderByRaw('shipmentBalance DESC')
->limit(5)
->get();
Update 2
Edit for Dom:
Using everything as it stands with your answer, I get the following response:
SQLSTATE[42S22]: Column not found: 1054 Unknown column '' in 'on clause' (SQL: select customers.id, customers.customer_name,SUM(s.balance) AS shipmentBalance from `customers` left join `shipments` as `s` on `customers`.`id` = `s`.`bill_to` and s.balance > (SELECT IFNULL(SUM(payments_distributions.amount),0) FROM payments_distributions WHERE payments_distributions.shipment_id = s.pro_number) = `` group by `customers`.`id`, `customers`.`customer_name` order by SUM(s.balance) DESC limit 5)
But if I remove this section, it brings up the page and the customers (though in the wrong order as I have removed one of the necessary components:
$join->on(DB::raw('s.balance >
(SELECT IFNULL(SUM(payments_distributions.amount),0)
FROM payments_distributions
WHERE payments_distributions.shipment_id = s.pro_number)
'));
Is there anything I can provide you with to get this specific statement to work with your entire answer?
Use this:
DB::table('customers')
->select('customers.id', 'customers.customer_name', DB::raw('SUM(shipments.balance) AS shipmentBalance'))
->leftJoin('shipments', function($join) {
$join->on('customers.id', 'shipments.bill_to')
->where('balance', '>', function($query) {
$query->selectRaw('IFNULL(SUM(payments_distributions.amount),0)')
->from('payments_distributions')
->where('payments_distributions.shipment_id', DB::raw('pro_number'));
});
})
->groupBy('customers.id', 'customers.customer_name')
->orderByDesc('shipmentBalance')
->limit(5)
->get();
Without the Models containing relationships or being able to test on this specific project, this is the most eloquent way I can think of performing your task.
The benefit of starting with the Customer model is you will have a laravel collection and can paginate as needed. Also review the eloquent docs, they help you understand all the different options. Hope his helps.
P.S. Start by using your model in your controller or wherever you are placing this query with:
use App\Customer
The query
$theQuery = Customer::select(DB::raw('customers.id, customers.customer_name,SUM(s.balance) AS shipmentBalance'))
->leftJoin('shipments as s', function($join)
{
$join->on('customers.id', '=', 's.bill_to');
$join->on(DB::raw('s.balance >
(SELECT IFNULL(SUM(payments_distributions.amount),0)
FROM payments_distributions
WHERE payments_distributions.shipment_id = s.pro_number)
'));
})
->groupBy('customers.id', 'customers.customer_name')
->orderByRaw('SUM(s.balance) DESC')
->limit(5)
->get();

Laravel Left Join Query

I am using laravel 5.3 and I have some left join query with error in laravel query method.
This is my normal query
SELECT bran.branchName,sch.schoolName From m_schoolbranch bran
LEFT JOIN m_students stu ON stu.schoolNo=bran.schoolNo AND stu.branchNo=bran.branchNo
LEFT JOIN m_school sch ON sch.schoolNo=stu.schoolNo where stu.userNo='0000000001';
And this is my new laravel Query
DB::table('m_schoolbranch')
->join('m_students', 'm_schoolbranch.schoolNo', '=', 'm_students.schoolNo')
->join('m_students', 'm_schoolbranch.branchNo', '=', 'm_students.branchNo')
->join('m_school', 'm_schoolbranch.schoolNo', '=', 'm_school.schoolNo')
->select('m_school.schoolName', 'm_schoolbranch.branchName')
->where('m_students.userNo',$userNo)
->get();
In these query I need to match two column in table m_students so I put like this
->join('m_students', 'm_schoolbranch.branchNo', '=', 'm_students.branchNo')
But i show error...
Tables in the query need to have unique names, otherwise the DB has no way of knowing which m_schoolbranch should be used when evaluating m_schoolbranch.schoolNo.
You could use unique table aliases in your join statements but I recommend using multiple conditions on the join. Just like you use in your original SQL query. See here: https://stackoverflow.com/a/20732468/4437888
DB::table('m_schoolbranch')
->join('m_students', function($join)
{
$join->on('m_schoolbranch.schoolNo', '=', 'm_students.schoolNo');
$join->on('m_schoolbranch.branchNo', '=', 'm_students.branchNo');
})
->join('m_school', 'm_schoolbranch.schoolNo', '=', 'm_school.schoolNo')
->select('m_school.schoolName', 'm_schoolbranch.branchName')
->where('m_students.userNo',$userNo)
->get();

Laravel ordering results of a left join

I am trying to replicate the below SQL in Laravels Eloquent query builder.
select a.name, b.note from projects a
left join (select note, project_id from projectnotes order by created_at desc) as b on (b.project_id = a.id)
where projectphase_id = 10 group by a.id;
So far I have:
$projects = Project::leftjoin('projectnotes', function($join)
{
$join->on('projectnotes.project_id', '=', 'projects.id');
})
->where('projectphase_id', '=', '10')
->select(array('projects.*', 'projectnotes.note as note'))
->groupBy('projects.id')
->get()
which works for everything except getting the most recent projectnotes, it just returns the first one entered into the projectnotes table for each project.
I need to get the order by 'created_at' desc into the left join but I don't know how to achieve this.
Any help would be much appreciated.
Your subquery is unnecessary and is just making the entire thing inefficient. To be sure though, make sure this query returns the same results...
SELECT
projects.name,
notes.note
FROM
projects
LEFT JOIN
projectnotes notes on projects.id = notes.project_id
WHERE
projects.projectphase_id = 10
ORDER BY
notes.created_at desc
If it does, that query translated to the query builder looks like this...
$projects = DB::table('projects')
->select('projects.name', 'notes.note')
->join('projectnotes as notes', 'projects.id', '=', 'notes.project_id', 'left')
->where('projects.projectphase_id', '=', '10')
->orderBy('notes.created_at', 'desc')
->get();

Resources