Laravel Eloquent Multiple Joins Type Conversion - php

When I do a query like this with multiple joins and retrieve a single column on the nested join, in that example h.mv ,it looses it's type information in laravel and gets treated as a string whereas the column in the mysql database is a DECIMAL.
Account::select('accounts.*', 'accounts.id', 'h.mv', 'h.mv_local')
->with('currency')
->join('sem', 'sem.entity_id', '=', 'accounts.id')
->join('s', 's.id', '=', 'sem.security_id')
->join('h', 'h.security_id', '=', 's.id')
->join('st', 'st.id', '=', 's.type1_id')
->join('hA', 'hA.id', '=', 'h.entity_id')
->where('st.name', '!=', 'CA') //
->where('h.date', $this->T)
->whereIn('accounts.type', ['pa', 'f'])
->where('hA.type', '!=', 'subMandate')
->whereRaw('`hA`.`asset_id` = `accounts`.`asset_id`')
->when($this->mandate_ids, function ($query) {
return $query->whereIn('accounts.id', $this->custodial_ids);
})
->get();
Is that expected behaviour when dealing with multiple joins? I can find old articles stating that the pdo bridge will treat every column as a string and laravel is doing the type conversion so I'm wondering what the constraints are on type conversion or even if that is actually what's happening?
Any experts on eloquent or the pdo layer who could give me some advice would be greatly appreciated

Related

How to retrieve records by as a range (As1-As10) in laravel?

is there any possibility to retrieve records as a range.For an example there is a table with column as ref_numbers.Sample values for the ref_numbers are A1,A2,A3,A4....AB1,AB2,AB3,AB4...
I want to retrieve the records of specific range starting value and end value.Such as in this case (A1-A2,AB1-AB4)
How can i achieve this ,Using eloquent or query builder or Raw queries in laravel?
You can use this where between operator.
$data = DB::table('table_name')
->whereBetween('ref_numbers', ['A1', 'A10'])
->get();
or
$data = DB::table('table_name')
->where('ref_numbers', '>', 'A1')
->where('ref_numbers', '<', 'A10')
->get();
You can read here more https://laravel.com/docs/9.x/queries#additional-where-clauses

how to chunk a join query in laravel

Somewhere in my Laravel application, the following query is likely to return a very large results set:
$data = $query->join('accommodation_rooms', 'accommodations.id', '=', 'accommodation_rooms.accommodation_id')
->join('discounts', 'accommodation_rooms.id', '=', 'discounts.accommodation_room_id')
->select('accommodation_rooms.id')
->orderBy('discounts.amount', 'desc')
->select('discounts.amount', 'accommodations.*')
->groupBy('discounts.amount', 'accommodation_rooms.id');
return $data;
I wondered how I could load a part of the data faster but only load the rest later maybe using some pagination mechanism or something.
Given the data is sent from an API, I want to know how I could chunk this data.
Thank you.
You can use paginate method for this purpose
$data = $query->join('accommodation_rooms', 'accommodations.id', '=', 'accommodation_rooms.accommodation_id')
->join('discounts', 'accommodation_rooms.id', '=', 'discounts.accommodation_room_id')
->select('accommodation_rooms.id')
->orderBy('discounts.amount', 'desc')
->select('discounts.amount', 'accommodations.*')
->groupBy('discounts.amount', 'accommodation_rooms.id')->paginate(15);
You can change the numbers of record to fetch in given parameter to paginate function e.g 15.

Eloquent - where has pivot users orWhere has none

I'm trying to write a function that will get all "buckets" that are assigned to the auth'd user and/or buckets that have NO USERS assigned.
Relations and such, work as they should. Unless I'm missing something?
How can I get all buckets user is assigned too - and also include buckets where no users (including the auth user) are assigned.
Buckets user is assigned to
Buckets where NO users have been assigned. i.e. pivot table contains no rows for bucket, etc.
My issue very likely stems from the orWhere query...
$buckets = Team::currentTeam()->buckets()->with('user')->whereHas('user', function($query) {
$query->where('user_id', Auth::user()->id)
->orWhere('user_id', function() {
$query->count();
}, '<', 0);
})->get();
Didn't tested this but I think this should work. You're looking to remove that orWhere query and add orHas('user', '=', 0).
$buckets = Team::currentTeam()->buckets()->with('user')->whereHas('user', function($query) {
$query->where('user_id', Auth::user()->id);
})->orHas('user', '=', 0)->get();
Another possible solution you might consider is using a left join.
Heads-up: this might not be accurate as I don't know your db schema.
Team::currentTeam()
->buckets()
->leftJoin('users', 'users.bucket_id', '=', 'buckets.id')
->where(function($query) {
$query->where('users.id', $user_id)
->orWhereNull('user.id');
});
Please also check this blog post
AND-OR-AND + brackets with Eloquent
https://laraveldaily.com/and-or-and-brackets-with-eloquent/

Different values when use DB::raw() in second param on where()

I have next query in Laravel Eloquent:
$buildings = Building::select('buildings.*')->join(
DB::raw('('.
(
IngameBuilding::select('buildings.building_id', 'buildings.level')
->join('buildings', 'buildings.id', '=', 'ingame_buildings.building_id')
->toSql()
).
') as `added_buildings`'), 'added_buildings.building_id', '=', 'buildings.building_id')
->where('buildings.level', '>', 'added_buildings.level')
->get();
This query returns all allowed rows from base, but one row more. When I added DB::raw() in where() return values is valid.
Good-working code:
$buildings = Building::select('buildings.*')->join(
DB::raw('('.
(
IngameBuilding::select('buildings.building_id', 'buildings.level')
->join('buildings', 'buildings.id', '=', 'ingame_buildings.building_id')
->toSql()
).
') as `added_buildings`'), 'added_buildings.building_id', '=', 'buildings.building_id')
->where('buildings.level', '>', DB::raw('`added_buildings`.`level`'))
->get();
Why first code workig, hmm.. Wrong?
I'm not a big fan of Laravel at all.
I've got only small experience with this framework but i'm almost sure that where function accepts only a 'constant' values to be checked against.
If you'll get an output of this query using toSQL method on the query object you will see that eloquent will convert it as something like:
(...) where buildings.level > 'added_buildings.level'
so the condition checks if the buildings.level (whatever the type is)
is greater than the given string and not the column value.
Using the DB::raw you're getting the proper sql as the eloquent won't parse/convert it.
You would need to use whereRaw method I suppose.
http://laravel.com/docs/4.2/queries#introduction

Laravel Eloquent maximize efficiency but keep elegant structure

Im working in a sensitive section of my app and i need to make sure to minimize the number of querys. I can easily do this with a multiple joins. The question is: is there a way to do this with beauty?
Elequent relationships are a good place to start but most of the time it requires multiple query.
The eager loading method used in this article looks alot better but still requires at least 2 querys and uses a whereIn statement instead of a join.
Article Example Of Eager Loading:
$users = User::with('posts')->get();
foreach($users as $user)
{
echo $user->posts->title;
}
Using Eager Loading, Laravel would actually be running the following
select * from users
select * from posts where user_id in (1, 2, 3, 4, 5, ...)
My current solution is to use laravel scopes in a way not intented.
public static function scopeUser($query) // join users table and user_ranks
{
return $query->join('users', 'users.id', '=', 'posts.user_id')
->join('user_ranks', 'users.rank_id', '=', 'user_ranks.id');
}
public static function scopeGroup($query,$group_id) // join feeds,group_feeds (pivot) and groups tables
{
return $query->join('feeds', 'feeds.id', '=', 'posts.feed_id')
->join('group_feed', 'feeds.id', '=', 'group_feed.feed_id')
->join('groups', 'groups.id', '=', 'group_feed.group_id')
->where("groups.id","=",$group_id);
}
The resulting query looks like this:
$posts = Post::take($limit)
->skip($offset)
->user() // scropeUser
->group($widget->group_id) // scropeGroup
->whereRaw('user_ranks.view_limit > users.widget_load_total')
->groupBy('users.id')
->orderBy('posts.widget_loads', 'ASC')
->select(
'posts.id AS p_id',
'posts.title AS p_title',
'posts.slug AS p_slug',
'posts.link AS p_link',
'posts.created_on AS p_create_on',
'posts.description AS p_description',
'posts.content AS p_content',
'users.id AS u_id',
'users.last_name AS u_last_name',
'users.first_name AS u_first_name',
'users.image AS u_image',
'users.slug AS u_slug',
'users.rank_id AS u_rank',
'user_ranks.name AS u_rank_name',
'user_ranks.view_limit AS u_view_limit'
)
->get();
Because of column name collisions i then need a huge select statement. This works and produces a single query, but its far from sexy!
Is there a better way to deal with big joined querys?
You could try to actually add the selects with aliases in the scope.
Note: This is totally untested
public static function scopeUser($query) // join users table and user_ranks
{
foreach(Schema::getColumnListing('users') as $column){
$query->addSelect('users.'.$related_column.' AS u_'.$column);
}
$query->addSelect('user_ranks.name AS u_rank_name')
->addSelect('user_ranks.view_limit AS u_view_limit');
$query->join('users', 'users.id', '=', 'posts.user_id')
->join('user_ranks', 'users.rank_id', '=', 'user_ranks.id');
return $query;
}
There is also no need to alias the post columns with a p_ prefix... But if you really want to, add another scope that does that and use addSelect()

Categories