Laravel Eloquent Subquery with condition - laravel

How to add a subquery to the main query with a condition? The result of the request should be similar to this:
select
`tasks`.*,
`users`.`name` as `user_name`,
(select count(*) from tasks_favorites on tasks_favorites.task_id = tasks.id and tasks_favorites.user_id = 38) as `is_favorite`
from `tasks`
left join `users` on `users`.`id` = `tasks`.`user_id`
where
`tasks`.`id` = 149
I try this query but I get an error:
$task = DB::table('tasks')
->select(
'tasks.*',
'users.name as user_name',
)
->when(Auth::check(), function($query) {
return $query->addSelect(
DB::table('tasks_favorites')->where('tasks_favorites.task_id', 'tasks.id')->where('tasks_favorites.user_id', auth()->user()->id)->count()
) ;
})
->leftJoin('users', 'users.id', 'tasks.user_id')
->where('tasks.id', $task_id)
->get()
->first() ;

did you try the selectRaw or raw method?
something like this
$task = DB::table('tasks')
->select(
'tasks.*',
'users.name as user_name',
)
->when(Auth::check(), function($query) {
return $query->addSelect(
DB::raw('select count(id) from tasks_favorites where tasks_favorites.task_id=tasks.id AND tasks_favorites.user_id='.auth()->user()->id.' as mycount')
);
})
->leftJoin('users', 'users.id', 'tasks.user_id')
->where('tasks.id', $task_id)
->get()
->first() ;

Related

Laravel multiple count on multiple join

i have four table socket , office , container, project i need to join these table with primey table soket and add count in each table group by socket.id .
$data['socjetsreport'] = DB::table('socket')
->limit(5)
->join('attached', 'attached.socket_id', '=', 'socket.id', 'left outer')
->join('office', 'office.socket_id', '=', 'socket.id', 'left outer')
->join('container', 'container.socket_id', '=', 'socket.id', 'left outer')
->join('project', 'project.socket_id', '=', 'socket.id', 'left outer')
->join('users', 'users.id', '=', 'socket.employee_id', 'left outer')
->select('socket.id as id', 'socket.name as name', 'users.name as uname',
DB::raw("count(attached.socket_id) as attccount"))
->groupBy('socket.id')
->get();
I try to apply it in MySQL query and it run well but in laravel it just give me wrong count.
This is my MySQL query:
SELECT
users.name,
COUNT(*) AS attached,
(
SELECT COUNT(*) FROM socket
LEFT OUTER JOIN container ON socket.id = container.socket_id
WHERE socket.id = #id
) AS container,
(
SELECT COUNT(*) FROM socket
LEFT OUTER JOIN project ON socket.id = project.socket_id
WHERE socket.id = #id
) AS project,
(
SELECT COUNT(*) FROM socket
LEFT OUTER JOIN office ON socket.id = office.socket_id
WHERE socket.id = #id
) AS office
FROM socket
LEFT OUTER JOIN attached ON socket.id = attached.socket_id
LEFT OUTER JOIN users ON socket.employee_id = users.id WHERE socket.id = #id
GROUP BY users.name
Since LEFT OUTER JOIN is the same as LEFT JOIN you could just use the query builder's leftJoin() method.
For all the subqueries in your SELECT statement, you should use the query builder's selectSub() method.
/**
* Add a subselect expression to the query.
*
* #param \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder|string $query
* #param string $as
* #return $this
*
* #throws \InvalidArgumentException
*/
public function selectSub($query, $as)
{
[$query, $bindings] = $this->createSub($query);
return $this->selectRaw(
'('.$query.') as '.$this->grammar->wrap($as), $bindings
);
}
The resulting query looks like this:
use Illuminate\Database\Query\Builder;
$data['socjetsreport'] = DB::table('socket')
->select('users.name')
->selectRaw('count(*) as attached')
->selectSub(
function (Builder $query) use ($socketId) {
return $query->selectRaw('count(*)')->from('socket')
->leftJoin('container', 'socket.id', 'container.socket_id')
->where('socket.id', $socketId);
},
'container'
)
->selectSub(
function (Builder $query) use ($socketId) {
return $query->selectRaw('count(*)')->from('socket')
->leftJoin('project', 'socket.id', 'project.socket_id')
->where('socket.id', $socketId);
},
'project'
)
->selectSub(
function (Builder $query) use ($socketId) {
return $query->selectRaw('count(*)')->from('socket')
->leftJoin('office', 'socket.id', 'office.socket_id')
->where('socket.id', $socketId);
},
'office'
)
->leftJoin('attached', 'socket.id', 'attached.socket_id')
->leftJoin('users', 'socket.employee_id', 'users.id')
->where('socket.id', $socketId)
->groupBy('users.name')
->get();
Or like this if you are hung up on using LEFT OUTER JOIN.
use Illuminate\Database\Query\Builder;
$data['socjetsreport'] = DB::table('socket')
->select('users.name')
->selectRaw('count(*) as attached')
->selectSub(
function (Builder $query) use ($socketId) {
return $query->selectRaw('count(*)')->from('socket')
->join('container', 'socket.id', '=', 'container.socket_id', 'left outer')
->where('socket.id', $socketId);
},
'container'
)
->selectSub(
function (Builder $query) use ($socketId) {
return $query->selectRaw('count(*)')->from('socket')
->join('project', 'socket.id', '=', 'project.socket_id', 'left outer')
->where('socket.id', $socketId);
},
'project'
)
->selectSub(
function (Builder $query) use ($socketId) {
return $query->selectRaw('count(*)')->from('socket')
->join('office', 'socket.id', '=', 'office.socket_id', 'left outer')
->where('socket.id', $socketId);
},
'office'
)
->join('attached', 'socket.id', '=', 'attached.socket_id', 'left outer')
->join('users', 'socket.employee_id', '=', 'users.id', 'left outer')
->where('socket.id', $socketId)
->groupBy('users.name')
->get();
PHP 7.4's shorthand closures can make those selectSub statements look a bit smaller
->selectSub(
fn(Builder $query) => $query->selectRaw('count(*)')->from('socket')
->leftJoin('container', 'socket.id', 'container.socket_id')
->where('socket.id', $socketId),
'container'
)
You can try checking the SQL for yourself by replacing ->get() with toSql() and dumping the result.
I solve it by using this query
$socjetsreport = DB::table('socket')
->select("socket.id", "socket.name",
DB::raw("(SELECT COUNT(id) AS attchedcount FROM attached WHERE attached.socket_id=socket.id) as attchedcount"),
DB::raw("(SELECT COUNT(id) AS officecount FROM office WHERE office.socket_id=socket.id) as officecount"),
DB::raw("(SELECT COUNT(id) AS containercount FROM container WHERE container.socket_id=socket.id) as containercount"),
DB::raw("(SELECT COUNT(id) AS projectcount FROM project WHERE project.socket_id=socket.id) as projectcount"),
DB::raw("(SELECT users.name AS employname FROM users WHERE users.id=socket.employee_id) as employname")
)
->orderBy('socket.id', 'asc')
->get();

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

Select unique records from child with condition on parent relationship

I have two tables, Shows and Episodes, each episode has show_id linking them one to many.
Now I need to get latest 6 episodes, one per show, where show.active is true
I've tried the following code:
$episodes = Episode::select(DB::raw('t.*'))
->from(DB::raw('(SELECT * FROM episodes ORDER BY id DESC) t'))
->whereHas('show', function($query) {
$query->where('active', '=', true);
})
->groupBy('t.show_id')
->take(6)
->get();
Unfortunately, I get the following:
Column not found: 1054 Unknown column 'episodes.show_id' in 'where clause' (SQL: select t.* from (SELECT * FROM episodes ORDER BY id DESC) t where exists (select * from shows where episodes.show_id = shows.id and active = 1) group by t.show_id limit 6)
I've also tried:
$episodes = Episode::where('active', true)
->orderBy('id', 'DESC')
->whereHas('show', function($query) {
$query->where('active', '=', true);
})
->groupBy('show_id')
->take(6)
->get();
It shows no error, but doesn't return latest of each show, groupBy gets the first record, I need the latest
This should work:
$episodes = Episode::where('active', true)
->whereHas('show', function($query) {
$query->where('active', '=', true);
})
->groupBy('show_id')
->orderBy('id', 'DESC')
->take(6)
->get();
You can try this
$episodes = Episode::selectRaw('max(id) as id, show_id')
->whereHas('show', function($query) {
$query->where('active', '=', true);
})
->orderBy('id', 'DESC')
->groupBy('show_id')
->take(6)
->get();
You can use a WHERE IN subquery:
$ids = Episode::selectRaw('max(id)')
->whereHas('show', function ($query) {
$query->where('active', '=', true);
})->groupBy('show_id');
$episodes = Episode::whereIn('id', $ids)
->take(6)
->get();

Laravel 5.6 DB::RAW count()

How do I count the number of rows in score column where score is less than 50?
$user = DB::table('users')
->leftJoin('rankings', 'rankings.user_id', '=', 'users.id')
->select('users.*', DB::raw("( (count(rankings.score) ) ) as `countBelow50` ")
Check for the advanced join clauses: https://laravel.com/docs/5.7/queries#joins
$user = DB::table('users')
->leftJoin('rankings', function($join) {
$join->on('rankings.user_id', '=', 'users.id')
->where('rankings.score','<','50');
})
->select('users.*', DB::raw("( (count(rankings.score) ) ) as `countBelow50` ")

Laravel query builder not replacing question mark

I'm running the following query:
return $this->hasMany('App\Task', 'company')
->whereNotIn('id', function($query)
{
$query->from('tasks')->join('projects', function($join)
{
$join->on('projects.id', '=', 'tasks.project')
->where('projects.status', '=', Project::STATUS_ARCHIVED);
})
->select('tasks.id');
});
But if I output the whole raw query I get the following:
select * from `tasks` where `tasks`.`company` = 1 and `id` not in (select `tasks`.`id` from `tasks` inner join `projects` on `projects`.`id` = `tasks`.`project` and `projects`.`status` = ?)
As you can see at the end of the raw query there's a question mark that wasn't replaced with the actual value, instead 'tasks'.'company' = 1 was.
You can listen to the illuminate.query event. Before the query add the following event listener:
use Event;
Event::listen('illuminate.query', function($query, $params, $time)
{
dd([
$query, // prepared statement
$params, // query params (? symbols will be replaced with)
$time // execution time
]);
});
I found a solution to this issue by manually setting the bindings using
->setBindings([Project::STATUS_ARCHIVED]);
Here's the whole snippet:
return $this->hasMany('App\Task', 'company')
->whereNotIn('id', function($query)
{
$query->from('tasks')->join('projects', function($join)
{
$join->on('projects.id', '=', 'tasks.project')
->where('projects.status', '=', '?');
})
->select('tasks.id')
->setBindings([Project::STATUS_ARCHIVED]);
})
->where('status', '=', Task::STATUS_INCOMPLETE);
You didn't add ->get(); to the end of the query.
Try:
return $this->hasMany('App\Task', 'company')
->whereNotIn('id', function($query)
{
$query->from('tasks')->join('projects', function($join)
{
$join->on('projects.id', '=', 'tasks.project')
->where('projects.status', '=', Project::STATUS_ARCHIVED);
})
->select('tasks.id');
})->get();

Resources