Query builder with max() - laravel-4

I am trying to rewrite this sql to laravel 4
SELECT
*
FROM
SolutionFile
WHERE
SolutionFile.group_id = $group_id and
version = (
SELECT
max(s1.version)
FROM
SolutionFile s1
WHERE
s1.group_id = $group_id
)
I wrote this request
SolutionFile::where('group_id', '=', $group_id)
->whereRaw('version = (select max(`version`) from files where group_id = ' . $group_id . ')')->get();
which works perfectly, but I want to rewrite it in "laravel way" without whereRaw.
I tried this request
SolutionFile::where('group_id', '=', $group_id)
->where(function($query) use($group_id) {
return $query->where('version', '=', function() use($group_id) {
return SolutionFile::where('group_id', '=', $group_id)->max('version');
});
});
but it returns an empty set.
Is there any way to rewrite it? Where did I make a mistake in last request?

I found simple solution for this
SolutionFile::where('group_id', '=', $group_id)
->where(function($query) use($group_id) {
return $query->where('version', '=', SolutionFile::where('group_id', '=', $group_id)->max('version'));
})->get()
There was no need to use function() as third param, only result from this function.

Related

Laravel query not giving desired result

Can someone help fix this query. Query is returning null result for "j.code" and "accounts.title". And I am 100% sure that it should return result. I think my left join are missing something. Is this the proper way of using left join within a left join.
$query = DB::table('autostk')
->where('autostk.branchid', $branch_id)
->where('autostk.itemcode',$request->itemcode)
->whereDate('autostk.date','<=',$request->tdate)
->leftjoin('journal AS j', function($join) use ($branch_id) {
$join->on('autostk.refno', '=', 'j.vno')
->where('j.code', '>=', 100)
->where('j.branchid', $branch_id)
->where('j.vtype', '=', 'autostk.vtype')
->leftjoin('accounts', 'j.code', '=', 'accounts.code')
->where('accounts.branchid', $branch_id);
})
->select('j.code','accounts.title','autostk.*')
->orderBY('date')->get()
->map(function ($item, $key) {
return (array) $item;
})
->all();
The raw query being generated is :
select `j`.`code`, `accounts`.`title`, `autostk`.* from `autostk`
left join (`journal` as `j` left join `accounts` on `j`.`code`=`accounts`.`code`)
on `autostk`.`refno` = `j`.`vno` and `j`.`code` >= ? and `j`.`branchid` = ?
and `j`.`vtype` = ? and `accounts`.`branchid` = ? where `autostk`.`branchid` = ?
and `autostk`.`itemcode` = ? and date(`autostk`.`date`) <= ? order by
`autostk`.`date` asc
UPDATE :
While checking the QueryLog i noticed that the binding for 'j'.'vtype' is "autostk.vtype"
Applying the query in workbench with 'autostk.vtype' returned null results.
But when I changed it to 'autostk'.'vtype' the results showed up correctly.
How to make this change in Laravel Eloquent ?
Please try like below:
$query = DB::table('autostk')
->where('autostk.branchid', $branch_id)
->where('autostk.itemcode',$request->itemcode)
->whereDate('autostk.date','<=',$request->tdate)
->leftjoin('journal as j', function($join) use ($branch_id) {
$join->on('autostk.refno', '=', 'j.vno')
->where('j.code', '>=', 100)
->where('j.branchid', $branch_id)
->where('j.vtype', '=', 'autostk.vtype')
->leftjoin('accounts', 'j.code', '=', 'accounts.code')
->where('accounts.branchid', $branch_id);
})
->select('j.code','accounts.title','autostk.*')
->orderBy('autostk.date')->get()
->map(function ($item, $key) {
return (array) $item;
})
->all();
There is one problem:
->orderBy('autostk.date')
And you can use toArray() instead of map() like this
->orderBy('autostk.date')->get()->toArray();
Found the solution. Correct query is :
$query = DB::table('autostk')
->where('autostk.branchid', $branch_id)
->where('autostk.itemcode',$request->itemcode)
->whereDate('autostk.date','<=',$request->tdate)
->leftjoin('journal AS j', function($join) use ($branch_id) {
$join->on('autostk.refno', '=', 'j.vno')
->on('autostk.vtype', '=', 'j.vtype')
->where('j.code', '>=', 100)
->where('j.branchid', $branch_id)
->leftjoin('accounts', 'j.code', '=', 'accounts.code')
->where('accounts.branchid', $branch_id);
})
->select('j.code','accounts.title','autostk.*')
->orderBY('autostk.date')->get()
->map(function ($item, $key) {
return (array) $item;
})
->all();
->leftjoin('journal as j', function($join) use ($branch_id) {
as instead of AS

Parameters Group on eloquent query

I have little problem with my search query at the moment. When i run a search query with only the parameter "comite_id" it's working but when i want to run the search query with "comite_id" and the whereBetween with "date_min" and "date_max" the result is not right , it's seems the result don't care about the whereBetween clause because i don't get the right date range.
Someone knows where i'm doing wrong? thanks a lot in advance
Here my controller :
public function getRetrocession(Request $request){
$comites = Structure::where('type_structure_id' , '=' ,'3')->pluck('nom_structure' , 'id');
$search = $request->input('comite_id');
$dt_min = $request->input('dt_min');
$dt_max = $request->input('dt_max');
if ($search) {
$query = Retrocession::where('comite_id', '=', $search)
->orWhere(function ($q) use($search , $dt_min , $dt_max) {
$q->where('comite_id', '=', $search)
->whereBetween('dt_retrocession', [ $dt_min , $dt_max]);
});
}else {
$query = Retrocession::select();
}
$retrocessions = $query->orderBy('dt_retrocession', 'DESC')->paginate(10)
->appends(['recherche' => $search]);
return view('retrocession/index' , compact('retrocessions' , 'comites'));
}
Because you are using orWhere() it always call both where() and orWhere(); thus including the result of where() and orWhere(). You can try this way
$query = Retrocession::where('comite_id', $search)
->when($dt_min && $dt_max, function($q) use ($dt_min, $dt_max) {
$q->whereBetween('dt_retrocession', [$dt_min, $dt_max]);
});
So you always run the first where, and only include whereBetween() WHEN $dt_min and $dt_max are both true.
Bonne chance Mathieu.

Why query returns empty collection?

I have the following query was built by Laravel:
$res = Announcement::whereExists(function ($query) {
$query->select(DB::raw(1))
->from('announcement_category')->join('user_category', 'user_category.category_id', '=', 'announcement_category.category_id')
->where('user_category.user_id', '=', 1)
->where('announcement_category.announcement_id', '=', 'announcements.id');
});
dd($res->get());
The code above gives me empty collection: dd($res->get());.
The plain SQL code of this query is:
select * from `announcements` where exists (select 1 from
`announcement_category` inner join `user_category` on
`user_category`.`category_id` = `announcement_category`.`category_id` where `user_category`.`user_id` = 1
and `announcement_category`.`announcement_id` = announcements.id)
and `announcements`.`deleted_at` is null
If execute this directly in MySQL, I get two result rows.
But why dd($res->get()); retuns me empty?
I don't think there is a whereExists in eloquent model... try this:
$res = DB::table('announcement')->whereExists(function ($query) {
$query->select(DB::raw(1))
->from('announcement_category')->join('user_category', 'user_category.category_id', '=', 'announcement_category.category_id')
->where('user_category.user_id', '=', 1)
->where('announcement_category.announcement_id', '=', 'announcements.id');
})->get();

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

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