Eloquent Where In With Join Subquery - laravel

I am trying to do this query in Laravel 5.4 with Eloquent and I do not get the subquery to function as it should. This is the original SQL query:
select * from projects p
inner join projects_categories pc on p.id = pc.project_id
where pc.name in (select pc.name from projects p
inner join projects_categories pc on p.id = pc.project_id
where p.id = $project->id) and p.id <> $project->id;
This is what I'm trying to do:
Project::join('projects_categories', 'projects.id', '=', 'projects_categories.project_id')
->whereIn([
['projects_categories.name', function ($query) {
$query->select('projects_categories.name')
->from('projects')
->join('projects_categories', 'projects.id', '=', 'projects_categories.project_id')
->where('project.id', '=', $project->id);
}],
['projects.id', '<>', $project->id]
])
->get();
But all the time I get the error back:
Missing argument 2 for Illuminate\Database\Query\Builder::whereIn().
Could anyone help me with this? Thank you very much for your time and help.

Try this:
Project::join('projects_categories', 'projects.id', '=', 'projects_categories.project_id')
->whereIn('projects_categories.name', function($query) use($project) {
$query->select('projects_categories.name')->from('projects')
->join('projects_categories', 'projects.id', '=', 'projects_categories.project_id')
->where('projects.id', '=', $project->id);
})->where('projects.id', '<>', $project->id)->get();

If in Project.php you have a hasMany relationship for categories i.e
public function categories()
{
return $this->hasMany(Category::class);
}
Then you should be able to return the relationship eloquently like so:
Project::find($id)->with('categories');
Or if you already have a project object loaded
$project->load('categories');
I would checkout the Laravel documentation as the join you posted looks overly complicated!

Related

Eloquent with query on relations with nested WHERE

I'm struggling with Eloquent with query on relation.
For example, I'm looking for only the client John who doesn't have transaction.
How can I do this with Eloquent?
Client model relation
public function transactions()
{
return $this->hasMany(Transaction::class);
}
$results = Client::whereDoesntHave('transactions', function ($query) use ($inputFirst, $period) {
$query->where('transactions.period_id', '=', $period->id)
->where('firstname', '=', $inputFirst);
})
->orderBy('id', 'desc')
->get();
A little help would be great.
Thanks
The issue with your code is that you are nesting the statements. The way you are doing Laravel is generating a SQL like this:
select * from `clients` where not exists
(select * from `transactions`
where `clients`.`id` = `transactions`.`client_id`
and `name` = John)
But the actual SQL code you're looking for is:
select * from `clients` where not exists
(select * from `transactions`
where `clients`.`id` = `transactions`.`client_id`)
and `name` = John)
For that your code should be:
$results = Client::whereDoesntHave('transactions')
->where('firstname', '=', $inputFirst)
->orderBy('id', 'desc')
->get();
*I didn't include the transactions.period_id, coz I wasn't sure if where you're looking to have it. But if it's meant to be inside the second select, leave in the nested statement, if not leave outside.

orwhereNotIn() in eloquent

I am using Laravel Framework 6.16.0 and I am having the following eloquent query:
$symbols = Company::select('*')
->leftJoin('prices', 'companies.id', '=', 'prices.companies_id')
->whereNotIn('companies.id', Price::select('companies_id')->get()->toArray())
->whereNotIn('companies.symbol', APIFound::select('generic_identifier')->get()->toArray())
->limit(1000)
->get();
This should represent the following sql query:
SELECT
*
FROM
companies c
LEFT JOIN prices p ON
c.id = p.companies_id
WHERE
c.id NOT IN(
SELECT
p.companies_id
FROM
prices p
) OR c.symbol NOT IN(
SELECT
f.generic_identifier
FROM
a_p_i_founds f
)
However, I get an and between my whereNotIn() function. How to get an or()?
I appreciate your replies!
you can use (where) with function:
$symbols = Company::select('*')
->leftJoin('prices', 'companies.id', '=', 'prices.companies_id')
->where(function ($query)
{
$query->whereNotIn('companies.id', Price::select('companies_id')->get()->toArray());
})
->orWhere(function ($query){
$query->whereNotIn('companies.symbol', APIFound::select('generic_identifier')->get()->toArray());
})
->limit(1000)
->get();

Trouble converting an SQL query to Eloquent

Trying to get this query to work in eloquent
A user can be in multiple teams however I want to generate a list of users NOT in a specific team. The following SQL query works if executed directly but would like to make it cleaner by converting it to eloquent
SELECT * FROM users LEFT JOIN team_members ON team_members.member_id = users.id WHERE NOT EXISTS ( SELECT * FROM team_members WHERE team_members.member_id = users.id AND team_members.team_id = $team_id )
This should provide a list of all the users that are not members of team $team_id
This is a guess ad you do not give much info on your Eloqent models but here is a hint of where to go:
User::doesnthave('teamMembers', function($builder) use($team_id){
return $builder->where('team_members.team_id');
});
That is assuming you have a "User" model with a "teamMembers" relationship setup on it
You may have a closer look in the Laravel docs for doesntHave
Laravel 5.8
Let's assume you have model name "User.php"
& there is method name "teamMembers" in it.
Basic
$users = User::doesntHave('teamMembers')->get();
Advance
use Illuminate\Database\Eloquent\Builder;
$users = User::whereDoesntHave('teamMembers', function (Builder $query) {
$query->where('id', '=', {your_value});
})->get();
You can find details description in this link >>
https://laravel.com/docs/5.8/eloquent-relationships#querying-relationship-absence
Laravel 5.2
Example:
DB::table('users')
->whereExists(function ($query) {
$query->select(DB::raw(1))
->from('orders')
->whereRaw('orders.user_id = users.id');
})
->get();
Check this link for advance where clause:
https://laravel.com/docs/5.2/queries#advanced-where-clauses
You can use below example
$list = User::leftJoin('users', 'users.id', '=', 'team_members.member_id')
->whereNotExists(function ($query) use ($team_id) {
$query->from('team_members')
->whereRaw('team_members.member_id = users.id')
->where('team_members.team_id', '=', $team_id);
})
->get();

Eloquent Complex Joins

i have made a scope
public function scopeCollaborative($query){
return $query->leftJoin('collaborative', function($join){
$join->on('imms.phone2', '=', 'collaborative.phone')
->orOn('imms.phone', '=', 'collaborative.phone')
->where('collaborative.user_id', '=', App('CURUSER')->id);
});
}
in Query log this scope adds:
left join `cs_collaborative` on
`cs_imms`.`phone2` = `cs_collaborative`.`phone` or
`cs_imms`.`phone` = `cs_collaborative`.`phone` and
`cs_collaborative`.`user_id` = 3
but i need to have:
left join `cs_collaborative` on
(`cs_imms`.`phone2` = `cs_collaborative`.`phone` or
`cs_imms`.`phone` = `cs_collaborative`.`phone`) and
`cs_collaborative`.`user_id` = 3
i didn't found a good solution, JoinClause have functions: On, orOn, where, orWhere.
but non of all can take function as input and to group query...
someone ideals?
Laravel doesn't let you build such join clause, so you need this to make it work:
public function scopeCollaborative($query){
return $query->leftJoin('collaborative', function($join){
$join->on('imms.phone2', '=', 'collaborative.phone')
->where('collaborative.user_id', '=', App('CURUSER')->id)
->orOn('imms.phone', '=', 'collaborative.phone')
->where('collaborative.user_id', '=', App('CURUSER')->id);
});
}
you can consider to use Doctrine ORM is more powerfull, bur less easy in the beginning...

A JOIN With Additional Conditions Using Query Builder or Eloquent

I'm trying to add a condition using a JOIN query with Laravel Query Builder.
<?php
$results = DB::select('
SELECT DISTINCT
*
FROM
rooms
LEFT JOIN bookings
ON rooms.id = bookings.room_type_id
AND ( bookings.arrival between ? and ?
OR bookings.departure between ? and ? )
WHERE
bookings.room_type_id IS NULL
LIMIT 20',
array('2012-05-01', '2012-05-10', '2012-05-01', '2012-05-10')
);
I know I can use Raw Expressions but then there will be SQL injection points. I've tried the following with Query Builder but the generated query (and obviously, query results) aren't what I intended:
$results = DB::table('rooms')
->distinct()
->leftJoin('bookings', function ($join) {
$join->on('rooms.id', '=', 'bookings.room_type_id');
})
->whereBetween('arrival', array('2012-05-01', '2012-05-10'))
->whereBetween('departure', array('2012-05-01', '2012-05-10'))
->where('bookings.room_type_id', '=', null)
->get();
This is the generated query by Laravel:
select distinct * from `room_type_info`
left join `bookings`
on `room_type_info`.`id` = `bookings`.`room_type_id`
where `arrival` between ? and ?
and `departure` between ? and ?
and `bookings`.`room_type_id` is null
As you can see, the query output doesn't have the structure (especially under JOIN scope). Is it possible to add additional conditions under the JOIN?
How can I build the same query using Laravel's Query Builder (if possible) Is it better to use Eloquent, or should stay with DB::select?
$results = DB::table('rooms')
->distinct()
->leftJoin('bookings', function($join)
{
$join->on('rooms.id', '=', 'bookings.room_type_id');
$join->on('arrival','>=',DB::raw("'2012-05-01'"));
$join->on('arrival','<=',DB::raw("'2012-05-10'"));
$join->on('departure','>=',DB::raw("'2012-05-01'"));
$join->on('departure','<=',DB::raw("'2012-05-10'"));
})
->where('bookings.room_type_id', '=', NULL)
->get();
Not quite sure if the between clause can be added to the join in laravel.
Notes:
DB::raw() instructs Laravel not to put back quotes.
By passing a closure to join methods you can add more join conditions to it, on() will add AND condition and orOn() will add OR condition.
If you have some params, you can do this.
$results = DB::table('rooms')
->distinct()
->leftJoin('bookings', function($join) use ($param1, $param2)
{
$join->on('rooms.id', '=', 'bookings.room_type_id');
$join->on('arrival','=',DB::raw("'".$param1."'"));
$join->on('arrival','=',DB::raw("'".$param2."'"));
})
->where('bookings.room_type_id', '=', NULL)
->get();
and then return your query
return $results;
You can replicate those brackets in the left join:
LEFT JOIN bookings
ON rooms.id = bookings.room_type_id
AND ( bookings.arrival between ? and ?
OR bookings.departure between ? and ? )
is
->leftJoin('bookings', function($join){
$join->on('rooms.id', '=', 'bookings.room_type_id');
$join->on(DB::raw('( bookings.arrival between ? and ? OR bookings.departure between ? and ? )'), DB::raw(''), DB::raw(''));
})
You'll then have to set the bindings later using "setBindings" as described in this SO post:
How to bind parameters to a raw DB query in Laravel that's used on a model?
It's not pretty but it works.
The sql query sample like this
LEFT JOIN bookings
ON rooms.id = bookings.room_type_id
AND (bookings.arrival = ?
OR bookings.departure = ?)
Laravel join with multiple conditions
->leftJoin('bookings', function($join) use ($param1, $param2) {
$join->on('rooms.id', '=', 'bookings.room_type_id');
$join->on(function($query) use ($param1, $param2) {
$query->on('bookings.arrival', '=', $param1);
$query->orOn('departure', '=',$param2);
});
})
I am using laravel5.2 and we can add joins with different options, you can modify as per your requirement.
Option 1:
DB::table('users')
->join('contacts', function ($join) {
$join->on('users.id', '=', 'contacts.user_id')->orOn(...);//you add more joins here
})// and you add more joins here
->get();
Option 2:
$users = DB::table('users')
->join('contacts', 'users.id', '=', 'contacts.user_id')
->join('orders', 'users.id', '=', 'orders.user_id')// you may add more joins
->select('users.*', 'contacts.phone', 'orders.price')
->get();
option 3:
$users = DB::table('users')
->leftJoin('posts', 'users.id', '=', 'posts.user_id')
->leftJoin('...', '...', '...', '...')// you may add more joins
->get();
For conditional params we can use where,
$results = DB::table('rooms')
->distinct()
->leftJoin('bookings', function($join) use ($param)
{
$join->on('rooms.id', '=', 'bookings.room_type_id')
->where('arrival','=', $param);
})
->where('bookings.room_type_id', '=', NULL)
->get();
There's a difference between the raw queries and standard selects (between the DB::raw and DB::select methods).
You can do what you want using a DB::select and simply dropping in the ? placeholder much like you do with prepared statements (it's actually what it's doing).
A small example:
$results = DB::select('SELECT * FROM user WHERE username=?', ['jason']);
The second parameter is an array of values that will be used to replace the placeholders in the query from left to right.
My five cents for scheme LEFT JOIN ON (.. or ..) and (.. or ..) and ..
->join('checks','checks.id','check_id')
->leftJoin('schema_risks', function (JoinClause $join) use($order_type_id, $check_group_id, $filial_id){
$join->on(function($join){
$join->on('schema_risks.check_method_id','=', 'check_id')
->orWhereNull('schema_risks.check_method_id')
;
})
->on(function($join) use ($order_type_id) {
$join->where('schema_risks.order_type_id', $order_type_id)
->orWhereNull('schema_risks.order_type_id')
;
})
->on(function($join) use ($check_group_id) {
$join->where('schema_risks.check_group_id', $check_group_id)
->orWhereNull('schema_risks.check_group_id')
;
})
->on(function($join) use($filial_id){
$join->whereNull('schema_risks.filial_id');
if ($filial_id){
$join->orWhere('schema_risks.filial_id', $filial_id);
}
})
->on(function($join){
$join->whereNull('schema_risks.check_risk_level_id')
->orWhere('schema_risks.check_risk_level_id', '>' , CheckRiskLevel::CRL_NORMALLLY );
})
;
})

Resources