How to do a full join in Laravel Eloquent? - php

How do I perform a full join given two tables t1 and t2 in laravel eloquent?

If you were using MySQL.
MySQL has no inbuilt support for full outer join.
But you can use the code like this below to achieve that.
$table2 = DB::table('t2')
->rightJoin('t1', 't1.id', '=', 't2.t1_id')
$table1 = DB::table('t1')
->leftJoin('t2', 't1.id', '=', 't2.t1_id')
->unionAll($table1)
->get();

Most of query builder join functions have an optional argument called $type ='inner'
so if you database supports full join (e.g: postgres) just pass "full" as the $type parameter

Related

How to convert a sql which has a join within a left join to query builder?

I need to write a query builder from sql which has a join within a left join.
This is the SQL which has a join within another left join.
select v_dts.* from v_dts
left join(select in_d2.ref_book from in_h
inner join in_d2 on(in_h.doc_code=in_d2.doc_code and in_h.book=in_d2.book)
where in_h.doc_code='IN' group by in_d2.ref_book) as i
on(v_dts.book=i.ref_book)
(Problem)This is the Query Builder that I try to convert from the SQL above.
$order_progress = DB::table('v_dts')
->select('v_dts.*')
->leftJoin('in_h',function($join_in_h){
$join_in_h->select('in_d2.ref_book');
$join_in_h->join('in_d2',function($join_in_d2){
$join_in_d2->on('in_h.doc_code','=','in_d2.doc_code');
$join_in_d2->on('in_h.book','=','in_d2.book');
$join_in_d2->where('in_h.doc_code','=','IN');
$join_in_d2->groupBy('in_d2.ref_book');
});
})
->get();
However, my query builder is wrong.
It show an error message
SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax
and generated SQL
SQL: select v_dts.* from v_dts left join (in_h inner join in_d2 on in_h.doc_code = in_d2.doc_code and in_h.book = in_d2.book and in_h.doc_code = IN)
Anyone can help me to figure out my problem?
Thanks.
You need a subquery left join. ->leftJoin('in_h',function($join_in_h){...} does not do a subquery join; that's a regular left join with a fancy join clause.
Instead use ->leftJoinSub.
You may use the joinSub, leftJoinSub, and rightJoinSub methods to join a query to a sub-query. Each of these methods receive three arguments: the sub-query, its table alias, and a Closure that defines the related columns.
Write the subquery.
$ref_books = DB::table('in_h')
->select('in_d2.ref_book')
->join('in_d2', function($join){
$join->on('in_h.doc_code','=','in_d2.doc_code')
->where('in_h.book','=','in_d2.book');
})
->where('in_h.doc_code','=','IN')
->groupBy('in_d2.ref_book');
And use it in another query.
$order_progress = DB::table('v_dts')
->select('v_dts.*')
->leftJoinSub($ref_books, 'i',function($join){
$join->on('v_dts.book', '=', 'i.ref_book');
})
->get();
You can use ->dd() and ->dump() to dump the generated SQL for debugging.
See "Sub-Query Joins" in the Laravel docs.
Note: I don't have Laravel so I can't check this is 100% correct, but it should get you on the right path.

Laravel query builder two tables in left join

I have the following query which I'm trying to convert into Laravel's query builder so I can take advantage of automatic escaping etc.
SELECT subjects.name, report_comments.comment
FROM subjects
LEFT JOIN (report_comments, library_comments) ON subjects.id = library_comments.subject_id
AND report_comments.library_comment_id = library_comments.id
AND report_comments.report_id = 1
Effectively what the query says is 'get the names of all the subjects, and if they have a matching report_comment (via the intermediate library_comments table), return that along with the subject' (a subject has either one or zero report_comments for the given criteria). The query works if I run it directly in MySQL and returns the results I'd expect. The report_comment.report_id = 1 is hard-coded at the moment but will eventually be a placeholder so that any report_id can be passed in.
So far I've managed to get:
DB::table('subjects')->select(['subjects.name', 'report_comments.comment'])->leftJoin('report_comments', function ($join) {
$join->on('subjects.id', '=', 'library_comments.subject_id')
->on('report_comments.library_comment_id', '=', 'library_comments.id')
->on('report_comments.report_id', '=', '1');
})
If I add toSql the result is:
select `subjects`.`name`, `report_comments`.`comment` from `subjects` left join `report_comments` on `subjects`.`id` = `library_comments`.`subject_id` and `report_comments`.`library_comment_id` = `library_comments`.`id` and `report_comments`.`report_id` = `1`
This is almost what I want, except it fails because the library_comments table is not mentioned at all:
Illuminate/Database/QueryException with message 'SQLSTATE[42S22]: Column not found: 1054 Unknown column 'library_comments.subject_id' in 'on clause' (SQL: select `subjects`.`name`, `report_comments`.`comment` from `subjects` left join `report_comments` on `subjects`.`id` = `library_comments`.`subject_id` and `report_comments`.`library_comment_id` = `library_comments`.`id` and `report_comments`.`report_id` = `1`)'
What I need to do is tell the leftJoin function about report_comments and library_comments, but there doesn't seem to be any way to do this. I tried:
leftJoin(['report_comments', 'library_comments'], function($join)
on a guess that Laravel might convert an array of table names into (report_comments, library_comments), but that didn't work and gave me the following warning:
PHP Notice: Array to string conversion in /home/paul/sites/report-assistant/vendor/laravel/framework/src/Illuminate/Database/Grammar.php on line 39
Is there a way to pass multiple tables into leftJoin, or do I need to completely rewrite the query in order to work with Laravel's query builder?
I'm using laravel/framework version 5.8.21 and all my dependencies are up to date (composer update && npm update).
Use BD::raw
write query like this and It will work
DB::table('subjects')->select(['subjects.name, report_comments.comment'])->leftJoin(DB::raw('(report_comments, library_comments)'), function ($join) {
$join->on('subjects.id', '=', 'library_comments.subject_id')
->on('report_comments.library_comment_id', '=', 'library_comments.id')
->on('report_comments.report_id', '=', '1');
})
Not sure if this will work but i assume it will be somthing along these lines, hopefully you get something out of it.
Basically added a check to see if the relationship exists if it does then join it.
Subject::select('subjects.name, report_comments.comment')
->leftJoin('library_comments', 'subjects.id, '=', library_comments.subject_id')
->leftJoin('report_comments', function($join){
if(report->library->relationship){
$join->on('report_comments.library_comment_id', '=', 'library_comments.id')
->where('report_comments.report_id', '=', '1');
}
})
After a bit of tinkering, I managed to find the answer in two parts:
First, I had to tweak this part of the join:
on('report_comments.report_id', '=', '1')
and replace it with:
where('report_comments.report_id', '=', '1')
If I didn't do this, Laravel would quote 1 with backticks, causing MySQL to interpret it as a column name.
The other change was to use DB::raw, which I was trying to avoid but I don't think it's too bad in this situation because I'm passing a hardcoded string rather than user input (or anything influenced by user input). The leftJoin now looks like:
leftJoin(DB::raw('(report_comments, library_comments)')

How Can I Use Join In The Leftjoin With Laravel?

I have a question on leftjoin & join in laravel
I want to rewrite following sample to laravel, how can I do this ?
LEFT JOIN(advertsolution_f SF
JOIN function_d FD ON SF.fd_id = FD.fd_id
JOIN function_m FM ON FD.fm_id = FM.fm_id)
ON SM.ads_id = SF.ads_id
i try to rewrite it like this,but i think it is not a good idea.
->leftJoin($SF,$SM.'.ads_id',$SF.'.ads_id')
->leftjoin($FD,$SF.'.fd_id',$FD.'.fd_id')
->leftjoin($FM,$FD.'.fm_id',$FM.'.fm_id')
because i have no idea how to join table to $SF privately.
Map like this,
->leftJoin($SF,$SM.'.ads_id','=',$SF.'.ads_id')
->leftjoin($FD,$SF.'.fd_id','=',$FD.'.fd_id')
->leftjoin($FM,$FD.'.fm_id','=',$FM.'.fm_id')
You are missing = sign in query.
EDIT
EXAMPLE YOU GIVEN, I am writing query on behalf of it, you just need to map with your original fields and tables,
DB::table('a')
->leftJoin("b","b.a_id","=","a.id")
->leftJoin("c","c.b_id","=","b.id")
->leftJoin("d","d.bd_id","=","b.bdid")
->get();
Map this query, as per your requirement,
it should work.
For making such complex query you can use RAW EXPRESSIONS like:
$users = DB::table('users')
->select(DB::raw('count(*) as user_count, status'))
->where('status', '<>', 1)
->get();
Raw Expressions
Sometimes you may need to use a raw expression in a query. These expressions will be injected into the query as strings, so be careful not to create any SQL injection points! To create a raw expression.
Reference

find_in_set within left join in Laravel

How can I use the find_in_set() with laravel query builder. Here is my raw query:
SELECT *
FROM table1 as t1
LEFT JOIN table2 as t2 ON find_in_set(t2.country, t1.fk_country_id)
you can use DB:raw as in
DB::table('table1')->leftJoin('table2', function($join){
$join->on(DB::raw("find_in_set(table2.country, table1.fk_country_id)"));
});
===================
Edit : Uchiha answer is the accurate one, since laravel "on" requires 3 arguments: a field, operator,field . i.e on('table1.id','=','table2.id')
Using $join->on( was generating an incorrect query in my case (Laravel 5.4).
The incorrect SQL generated was like t1 left join t2 on find_in_set(t2.tag, t1.tags) = where t1.foo..
Here, whereRaw worked for me
DB::table('table1')->leftJoin('table2', function($query) {
$query->whereRaw("find_in_set(...)");
})
Reference: From issue referring Laravel 5.5 (https://github.com/laravel/framework/issues/23488)
You can use DB::raw like as
DB::table('table1')->leftJoin('table2', function($join){
$join->on(DB::raw("find_in_set(table2.country, table1.fk_country_id)",DB::raw(''),DB::raw('')));
});
The above does not work for recent versions of laravel.
Use DB::raw("find_in_set(...)") ,">", DB::raw("'0'")
DB::table('table1')->leftJoin('table2', function($join){
$join->on(DB::raw("find_in_set(table2.country, table1.fk_country_id)", ">" , DB::raw("'0'")));
});
It doesn't work the way the above answers are mentioned.
Use where clause instead of on. It worked in my case.
DB::table('table1')->leftJoin('table2', function($join){
$join->whereRaw("find_in_set(table2.country, table1.fk_country_id)");
});

Issue in Laravel 5.1 Inner Join Query

Below is my Query in Laravel 5.1
\App\Models\Project\Bids\ProjectBid_Model
::selectRaw('B.*')
->join('tblproject P','B.projectid','=','P.projectid')
->where('P.WhoCreatedTheProject',14)
->first()
and below is the equivalant query
select B.* from `tblprojectbid`
inner join `tblproject P` on `B`.`projectid` = `P`.`projectid`
where `P`.`WhoCreatedTheProject` = 14 limit 1
What's the problem ?
Please check the line 1 in Query: select B.* from tblprojectbid.
What's the question ?
How can I change
select B.* from tblprojectbid
to
select B.* from tblprojectbid B
If you want to use Eloquent I'm afraid there is no easy way to do it.
I use in this case full table name for model for instance
\App\Models\Project\Bids\ProjectBid_Model
::selectRaw('bid_table.*')
->join('tblproject AS P','bid_table.projectid','=','P.projectid')
->where('P.WhoCreatedTheProject',14)
->first()
However it's also possible that you set alias in ProjectBid_Model:
protected $table = 'bid_table AS B';
The con is you will have this table always aliased with B, so in case you have 2 models with same alias (in this case B), you won't be able to change it later just for one table, so I think the better is 1st approach (without using alias)
Here is the final solution.
\App\Models\Project\Bids\ProjectBid_Model
::selectRaw('B.*')
->from('tblprojectbid as B')
->join('tblproject as P','B.projectid','=','P.projectid')
->where('P.WhoCreatedTheProject',14)
->first()
try this.
\DB::table('tblprojectbid as B')
->select()
->join('tblproject as P','B.projectid','=','P.projectid')
->where('P.WhoCreatedTheProject',14)
->first()

Categories