find_in_set within left join in Laravel - php

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

Related

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 to do a full join in Laravel Eloquent?

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

Laravel 4 unable to join on multiple conditions using ->on( ) more than once

In a nutshell I am trying to do a join with more than one condition. We are using legacy Laravel 4, and the actual class I've tracked it to is Illuminate\Database\Query\Builder. Here is what I am adding:
->leftJoin('node_fields AS visible_for_categories', function($join){
$join->on('nv2.id', '=', 'visible_for_categories.node_version_id');
$join->on('visible_for_categories.name', '=', 'visible_for_categories');
})
It works fine with the first $join->on( ) call, but the page fails if the second on is called. why is this and what is the proper way to do this in Laravel 4?
The way that worked for me on the above query is as follows:
->leftJoin('node_fields AS visible_for_categories', function($join){
$join->on('nv2.id', '=', 'visible_for_categories.node_version_id');
$join->on('visible_for_categories.name', '=', DB::raw("'visible_for_categories'"));
})
The query builder will assume all three values in the on() function are fields, not strings, and will parse out the . period as well.
The general assumption is that JOINS will have the relational field joins to create structure, and the WHERE conditionals will provide the desired filter. However anyone who's worked esp. with LEFT or RIGHT joins knows this is not always possible.
Be careful for SQL injection using DB::raw but in this case, an EAV table, we're dealing with a fixed string, not a variable.
Try
->leftJoin('node_fields AS visible_for_categories', function($join){
$join->on('nv2.id', '=', 'visible_for_categories.node_version_id')
->on('visible_for_categories.name', '=', 'visible_for_categories');
})

Laravel 5. Using the USING operator

I tried to find it for a long time, and I can't believe that Laravel doesn't have this functionality.
So, I can write:
select * from a join b where a.id = b.id
or more beautiful:
select * from a join b using(id)
First case is simple for Laravel:
$query->leftJoin('b', 'a.id', '=', 'b.id')
But how to write second case? I expect that it should be simple and short, like:
$query->joinUsing('b', 'id')
But thereis no such method and I can't find it.
PS: it's possible that the answer is very simple, it's just hard to find by word "using", because it's everywhere.
UPDATE
I'm going deeper to source, trying to make scope or pass a function to join, but even inside of this function I can't to anything with this $query. Example:
public function scopeJoinUsing($query, $table, $field) {
sql($query->join(\DB::raw("USING(`{$field}`)")));
// return
// inner join `b` on USING(`id`) ``
// yes, with "on" and with empty quotes
sql($query->addSelect(\DB::raw("USING(`{$field}`)")));
// return
// inner join `b` USING(`id`)
// but in fields place, before "FROM", which is very logic :)
}
So even if forget about scope , I can't do this in DB::raw() , so it's impossible... First time I see that something impossible in Laravel.
So, the answer is - it's impossible.
Laravel doesn't support this functionality, which is really sad.
I fix it in Laravel source code, my PULL REQUEST here - https://github.com/laravel/framework/pull/12773
Nothing is impossible in Laravel.
Until support for join using is added to Laravel core, you can add support for it by installing this Laravel package: https://github.com/nihilsen/laravel-join-using.
It works like this:
use Illuminate\Support\Facades\DB;
use Nihilsen\LaravelJoinUsing\JoinUsingClause;
DB::table('users')->leftJoin(
'clients',
fn (JoinUsingClause $join) => $join->using('email', 'name')
);
// select * from `users` left join `clients` using (`email`, `name`)

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