Refactor Laravel Query - laravel

I have a query that I have built, and I am trying to understand how I can achieve the same thing but in one single query. I am fairly new to Laravel and learning. Anyway someone could help me understand how I can achieve what I am after?
$activePlayerRoster = array();
$pickupGames = DB::table('pickup_games')
->where('pickupDate', '>=', Carbon::now()->subDays(30)->format('m/d/Y'))
->orderBy('pickupDate', 'ASC')
->get();
foreach ($pickupGames as $games) {
foreach(DB::table('pickup_results')
->where('pickupRecordLocatorID', $games->recordLocatorID)
->get() as $activePlayers) {
$activePlayerRoster[] = $activePlayers->playerID;
$unique = array_unique($activePlayerRoster);
}
}
$activePlayerList = array();
foreach($unique as $playerID) {
$playerinfo = DB::table('players')
->select('player_name')
->where('player_id', $playerID)
->first();
$activePlayerList[] = $playerinfo;
}
return $activePlayerList;
pickup_games
checkSumID
pickupDate
startTime
endTime
gameDuration
winningTeam
recordLocatorID
pickupID
1546329808471
01/01/2019
08:03 am
08:53 am
50 Minute
2
f47ac0fc775cb5793-0a8a0-ad4789d4
216
pickup_results
id
checkSumID
playerID
team
gameResult
pickOrder
pickupRecordLocatorID
1
1535074728532
425336395712954388
1
Loss
0
be3532dbb7fee8bde-2213c-5c5ce710

First, you should try to write SQL query, and then convert it to Laravel's database code.
If performance is not critical for you, then it could be done in one query like this:
SELECT DISTINCT players.player_name FROM pickup_results
LEFT JOIN players ON players.player_id = pickup_results.playerID
WHERE EXISTS (
SELECT 1 FROM pickup_games
WHERE pickupDate >= DATE_FORMAT(SUBDATE(NOW(), INTERVAL 30 DAY), '%m/%d/%Y')
AND pickup_results.pickupRecordLocatorID = recordLocatorID
)
Here I'm assuming you know what you're doing with this dates comparison, because it looks weird to me.
Now, let's convert it to Laravel's code:
DB::table('pickup_results')
->select('players.player_name')->distinct()
->leftJoin('players', 'players.player_id', '=', 'pickup_results.playerID')
->whereExists(function ($query) {
$query->select(DB::raw(1))
->from('pickup_games')
->where('pickupDate', '>=', Carbon::now()->subDays(30)->format('m/d/Y'))
->whereRaw('pickup_results.pickupRecordLocatorID = recordLocatorID');
})
->get();

Basically, I would reduce the query to its SQL variant to get directly at its core.
The essence of the query is
select `x` FROM foo WHERE id IN (
select distinct bar.id from bar join baz on bar.id = baz.id);
This can be interpreted in Eloquent as:
$thirtyDaysAgo = Carbon::now()->subDays(30)->format('m/d/Y');
$playerIds = DB::table('pickup_games')
->select('pickup_games.player_id')
->join(
'pickup_results',
'pickup_results.pickupRecordLocatorID',
'pickup_games.recordLocatorID')
->where('pickupDate', '>=', $thirtyDaysAgo)
->orderBy('pickupDate', 'ASC')
->distinct('pickup_games.player_id');
$activePlayers = DB::table('players')
->select('player_name')
->whereIn('player_id', $playerIds);
//>>>$activePlayers->toSql();
//select "player_name" from "players" where "player_id" in (
// select distinct * from "pickup_games"
// inner join "pickup_results"
// on "pickup_results"."pickupRecordLocatorID" = "pickup_games"."recordLocatorID"
// where "pickupDate" >= ? order by "pickupDate" asc
//)
From the resulting query, it may be better to refactor the join as relationship between the Eloquent model for pickup_games and pickup_results. This will help to further simplify $playerIds.

Related

Eloquent - Join a Subquery

I have a complicated query that I would like to translate either in Eloquent ORM or with the Query Builder for a Laravel site, but I can't do it, can someone help me?
Here is my SQL query
SELECT opp_id, risk_study.rst_id, risk_study.rst_status
FROM opportunity
LEFT JOIN risk_study ON risk_study.rst_id =
(SELECT risk_study_quote_vehicle.rst_id
FROM risk_study_quote_vehicle
INNER JOIN quote_vehicle ON quote_vehicle.quv_id = risk_study_quote_vehicle.quv_id
INNER JOIN quote ON quote.quo_id = quote_vehicle.quo_id
WHERE quote.opp_id = opportunity.opp_id
ORDER BY risk_study_quote_vehicle.rst_id DESC
LIMIT 0,1)
WHERE 1 = 1
AND opportunity.per_id_process = '5'
AND opportunity.opp_locked = '1'
Here is my solution.
But this query does not retrieve values from the risk_study table.
$opportunities = Opportunity::select('opp_id', 'risk_study.rst_id', 'risk_study.rst_status')
->leftJoin('risk_study', function ($join) {
$join->on('risk_study.rst_id', '=', DB::raw('"SELECT risk_study_quote_vehicle.rst_id FROM risk_study_quote_vehicle INNER JOIN quote_vehicle ON quote_vehicle.quv_id = risk_study_quote_vehicle.quv_id INNER JOIN quote ON quote.quo_id = quote_vehicle.quo_id WHERE quote.opp_id = opportunity.opp_id ORDER BY risk_study_quote_vehicle.rst_id DESC LIMIT 0,1"'));
})
->where([
['per_id_process', 5],
['opp_locked', 1],
])
->get();
Finally, I wrote the query and it works ! Thanks !
$opportunities = Opportunity::query()
->select(
'opp_id',
'risk_study.rst_id',
'risk_study.rst_status'
)
->from('opportunity')
->leftJoin('risk_study', function ($join) {
$join->where('risk_study.rst_id', function ($sub) {
$sub->select('risk_study_quote_vehicle.rst_id')
->from('risk_study_quote_vehicle')
->join('quote_vehicle', 'quote_vehicle.quv_id', 'risk_study_quote_vehicle.quv_id')
->join('quote', 'quote.quo_id', 'quote_vehicle.quo_id')
->whereColumn('quote.opp_id', 'opportunity.opp_id')
->orderByDesc('risk_study_quote_vehicle.rst_id')
->limit(1);
});
})
->where('opportunity.per_id_process', '5')
->where('opportunity.opp_locked', '1')
->get();
You should be able to use a Closure as the second parameter of where() inside your join Closure.
I don't think Laravel's query builder supports the LIMIT 0,1 statement.
I don't know why you need WHERE 1 = 1, but you should be able to use whereRaw for that.
When you're done, the result looks very alike a formatted SQL query.
$opportunities = Opportunity::query()
->select(
'opp_id',
'risk_study.rst_id',
'risk_study.rst_status'
)
->from('opportunity')
->leftJoin('risk_study', function ($join) {
$join->where('risk_study_quote_vehicle.rst_id', function ($sub) {
$sub->select('risk_study_quote_vehicle.rst_id')
->from('risk_study_quote_vehicle')
->join('quote_vehicle', 'quote_vehicle.quv.id', 'risk_study_quote_vehicle.quv_id')
->join('quote', 'quote.quo_id', 'quote_vehicle.quo_id')
->where('quote_opp_id', 'opportunity.opp_id')
->orderByDesc('risk_study_quote_vehicle.rst_id')
->limit(1);
});
})
->whereRaw('1 = 1')
->where('opportunity.per_id_process', '5')
->where('opportunity.opp_locked', '1')
->get();

Laravel 8 - How I do where clause in table added with join

Hi I want to know how can i do this query in Laravel 8 , I tried adding the join clause but not work as expected, i need join clause? Or maybe there is another form to do it. I search others examples but i donĀ“t see anythat help me. The query is the next:
DB::table('escandallo_p as esc')
->select("esc.material", "esc.referencia", "esc.ancho", "esc.proveedor", "esc.punto",
"esc.precio", "esc.consumo", "esc.veces", "esc.001", "esc.002", "esc.003", "esc.004",
"esc.005", "esc.006", "esc.007", "esc.008", "esc.009", "esc.010", "esc.011", "esc.um", "esc.merma", "esc.importe", "esc.tipo", "esc.xtalla", "esc.fase",
DB::raw("(select anulado from prototipos_p as p where p.prototipo = '".$scandal[0]->prototipo."' and p.tipo = 'c' and p.referencia = esc.referencia )"),
// ignore
//original query "(select anulado from prototipos_p as p where p.prototipo = ",$request->prototipo," and p.tipo = 'c' and p.referencia = esc.referencia ) as 'anulado'",
// "(select clase from prototipos_p as p where p.prototipo = ",$request->prototipo," and p.tipo = 'c' and p.referencia = esc.referencia ) as 'clase'")
//Converted query ->select('pro.anulado')->where('pro.prototipo', $request->prototipo)
// ->where("p.prototipo", "=", $request->prototipo)
->where("esc.id_escandallo", "=", $request->id_escandallo)
->where("esc.id_version", "=", $request->version)
->orderBy("id","asc")
->get();
!!!! I need to pass the esc.referencia to the sub select query
The second select is the conversion of the select inside "" ( i know this is wrong is only for explain it).
Thank you in advance for any suggestion.
Best regards
EDIT: I can solve my problem with DB::raw, but if anyone know others methos are welcome!
You need to pass callback to the join query to add the extra query to the laravel's join method,
Example from Laravel Doc:
DB::table('users')
->join('contacts', function ($join) {
$join->on('users.id', '=', 'contacts.user_id')
->where('contacts.user_id', '>', 5);
})
->get();
It is explained in Laravel's doc, Advanced Join Clauses
There is Subquery support too Subquery Joins,
Eg:
$latestPosts = DB::table('posts')
->select('user_id', DB::raw('MAX(created_at) as last_post_created_at'))
->where('is_published', true)
->groupBy('user_id');
$users = DB::table('users')
->joinSub($latestPosts, 'latest_posts', function ($join) {
$join->on('users.id', '=', 'latest_posts.user_id');
})
->get();
These two might help you to achieve what you are trying
After test joins, joinSub, whereIn and other forms of doing this, I solved my problem using the DB::raw():
DB::table('escandallo_p as esc')
->select('parameters',....,
DB::raw("(SELECT column //(ONLY ONE)
FROM table
WHERE column = '".$parameter."' ...) AS nombre"),
)
->where('column', "=", $parameter)
->orderBy("id","asc")
->get();

how to union and groupby in laravel

i want to get contact id who was send chat or was i send chat to him
with native query i can get result but when i implement in laravel its going difficult
this my native query
select * from `users` where `users`.`id` in (
select `to` from messages where `from` = 2 group by `to`
union
select `from` from messages where `to` = 2 group by `from`
)
what i find difficult is how union after group by or group by after union with make same column number, i using merge but the result is wrong
this what i have try in laravel
$to = Message::select('to')->where('from',auth()->id())->groupBy('to')->get();
$from = Message::select('from')->where('to',auth()->id())->groupBy('from')->get();
$tofrom = $to->merge($from);
dd($tofrom);
please if any body can help
Unions are described in the documentation. To achieve your specific requirements you can do:
$final = User::whereIn('id', function ($query) {
$from = Message::select('from')->where('from',auth()->id())->groupBy('from');
$query->from('messages')->where('to',auth()->id())->groupBy('to')->union($from);
})->get();
Disclaimer: I have not actually tested this but I think it should work.
merge() is the method of collection. Not the Eloquent Builder or Query Builder.
However, It think you want to find user.id in the array.
You can convert the collection to array:
$to = Message::where('from',auth()->id())->groupBy('to')->pluck('to');
$from = Message::where('to',auth()->id())->groupBy('from')->pluck('from');
$tofrom = $to->merge($from)->toarray();
User::whereIn('id', $tofrom)->get();
oh finally i got the answer this is code
$contacts = User::select('users.id','users.name','users.email','users.profile_image')
->join('messages',function($join){
$join->on('users.id','messages.from');
$join->orOn('users.id','messages.to');
})
->where(function($query){
$query->where('messages.from',auth()->id())->orWhere('messages.to',auth()->id());
})
->groupBy('users.id','users.name','users.email','users.profile_image')
->get();

Convert raw SQL to use Eloquent Query Builder

How can I convert the following complex SQL query to use the Eloquent query builder? I want to use methods such as join() and where(), get() etc.
The below query returns a list of locations along with counts for vouchers that have been redeemed.
select
a.location_name,
'' as dates,
a.places,
sum(netvalue155),
sum(netvalue135) from
(
select
l.id,
l.location_name,
b.places,
case when v.net_value = 155 then 1 else 0 end as netvalue155,
case when v.net_value = 135 then 1 else 0 end as netvalue135
from locations l
left join bookings b on l.id = b.location_id
left join vouchers v on b.voucher_code = v.voucher_code
) a
right join locations l on l.id = a.id
group by a.location_name
EDIT
I am trying the below code, which throws the error SQLSTATE[42S22]: Column not found: 1054 Unknown column 'sub.id' in on clause
$subQuery = DB::table('locations')
->select(
'locations.id',
'locations.location_name',
DB::raw('"'.$request->get('dates').'" as dates'),
DB::raw('sum(bookings.id) as number'),
DB::raw('round(sum(bookings.final_price/1.2), 2) as paidbycard'),
DB::raw('case when bookings.voucher_value = 155 then round(sum(bookings.voucher_value/1.2), 2) else 0.00 end as voucher155'),
DB::raw('case when bookings.voucher_value = 135 then round(sum(bookings.voucher_value/1.2), 2) else 0.00 end as voucher135'),
DB::raw('case when bookings.transfer_fee = 10 then round(sum(bookings.transfer_fee/1.2), 2) else 0.00 end as transfer_fee'))
->leftJoin('bookings', 'locations.id', '=', 'bookings.location_id');
$meatBookQuery = DB::table('orders')->select(DB::raw('sum(orders_items.price) as total'))
->join('orders_items', 'orders.id', '=', 'orders_items.order_id')
->where('orders_items.item_name', 'The Meat Book');
$booking = DB::table(DB::raw("({$subQuery->toSql()}) as sub, ({$meatBookQuery->toSql()}) as meatBook"))
->mergeBindings($subQuery)
->mergeBindings($meatBookQuery)
->select('sub.location_name', 'sub.dates', 'sub.number', 'sub.paidbycard', 'sub.voucher155', 'sub.voucher135', 'sub.transfer_fee', DB::raw('round(sum(sub.voucher155 + sub.voucher135 + sub.transfer_fee + sub.paidbycard), 2) as total'), 'meatBook.total')
->leftJoin('locations', 'locations.id', '=', 'sub.id')
->leftJoin('bookings', 'bookings.location_id', '=', 'sub.id')
->groupBy('sub.location_name');
First of all
I often see people asking for how to rebuild a complex SQL query in Laravels Query Builder. But not every operation which is possible in SQL or MySQL is implemented as a function in Laravels Query Builder. This means you can't rebuild every SQL query in Query Builder without using raw SQL.
What does this mean for your SQL query?
Some things like sub queries (the from (select ...) part) and the case when ... part is not implemented in Query Builder. At least therefore you will have to use the raw expression with the DB::raw() function. I'm not sure about the sum() if this is already possible but you will surely find that in the docs.
Other things like joins are implemented as a function:
$users = DB::table('users')
->join('contacts', 'users.id', '=', 'contacts.user_id')
->join('orders', 'users.id', '=', 'orders.user_id')
->select('users.id', 'contacts.phone', 'orders.price')
->get();
see Laravel Documentation: Queries - Joins
And you can even mix up Query Builder functions with raw expressions:
$users = DB::table('users')
->select(DB::raw('count(*) as user_count, status'))
->where('status', '<>', 1)
->groupBy('status')
->get();
see Laravel Documentation: Queries - Raw Expression
Example for a sub query:
$subQuery = DB::table('locations')
->leftJoin('bookings', 'locations.id', '=', 'bookings.location_id')
->leftJoin('vouchers', 'bookings.voucher_code', '=', 'vouchers.voucher_code')
->select('locations.id', 'locations.location_name', 'bookings.places');
$query = DB::table(DB::raw("({$subQuery->toSql()} as sub"))
->mergeBindings($subQuery)
->select(...)
->rightJoin(...)
->groupBy('sub.location_name')
->get();
So you can rebuild some parts of the query in Query Builder and use raw expressions wherever you need to.
To debug a query while you build it Laravels query logging function can be very helpful.

How to Join same table in laravel

I wan to wirte a join query to connect same table, and without ON, but when i write it in laravel without on it is showing error
$key = DB::table('api_keys as ak')
->join('api_keys as bk','')
->where('ak.api_key', $api_key)->where('ak.user_id',0)
->pluck('api_key');
want to build the below query,
SELECT * FROM `api_keys` as ak
JOIN `api_keys` as bk
WHERE ak.`api_key`=$akey
and ak.`user_id`=$auser
and bk.`user_id`=$bsuer
and bk.`api_key`=$bkey
You must provide an ON clause for the join. More about where ON clauses are required can be found in this answer.
You can view the generated query using toSql() on a QueryBuilder object:
echo $key = DB::table('api_keys as ak')
->join('api_keys as bk','')
->where('ak.api_key', $api_key)->where('ak.user_id',0)
->toSql();
Which in your case returns:
select * from `api_keys` as `ak` inner join `api_keys` as `bk`
on `` `` where `ak`.`api_key` = ? and `ak`.`user_id` = ?
In your case it isn't totally clear what you are trying to achieve, but you might consider joining on api_key or the primary key of the api_keys table, if that is different:
$key = DB::table('api_keys as ak')
->join('api_keys as bk','ak.api_key', '=', bk.api_key)
->where('ak.api_key', $api_key)->where('ak.user_id',0)
->pluck('api_key');
DB::table('registerusers as a')
->join('registerusers as b', 'a.id', 'b.refer_id')
->where('a.username', 'b.username')
->where('b.id', 'a.refer_id')
->value('b.id');
without using on clause in laravel query builder you can use following
$key = DB::table(DB::raw('api_keys as ak, api_keys as bk'))
->where('ak.api_key', '=', $api_key)
->where('ak.user_id','=',0)
->where('ak.PK','=','bk.PK')
->pluck('ak.api_key')
where PK references to your table's primary key.
result will in your case.
select * from api_keys as ak, api_keys as bk where ak.api_key= 'api_key_value' and ak.user_id = 0 and ak.PK = bk.PK
I solved this by creating my own class and starting out with a base query which I modify to apply the join (using Laravel's joinSub function) as follows:
public function __construct()
{
$this->query = DB::table('question_responses as BASE');
}
public function applyFilter($questionId, $questionValue) {
$filterTableStr = 'filter_table_'.$questionId;
$filterIdStr = 'filter_id_'.$questionId;
$filterQuery = DB::table('question_responses AS '.$filterTableStr)
->select('survey_response_id AS '.$filterIdStr)
->where($filterTableStr.'.question_short_name', $questionId)
->where($filterTableStr.'.value', $questionValue);
$resultTableStr = 'result_table_'.$questionId;
$this->query = $this->query
->joinSub($filterQuery, $resultTableStr, function($join) use ($resultTableStr, $filterIdStr) {
$join->on('BASE.survey_response_id', '=', $resultTableStr.'.'.$filterIdStr);
});
}
After applying my required filters I can just call $this->query->get() as normal to obtain the result.
The important part was to make sure that each resulting table and join fields has unique names.
With this method I can apply unlimited filters to my base query.

Resources