Laravel withSum where clause column not found - php

I'm using Laravel 9 to perform a withSum on my relationship credit_transactions. I need to return all PersonalAccessToken models where the credit_balance is less than a certain value, for example, 50 - this way my application knows whether to top up their balance.
This is my query:
/**
* Get all keys that can be auto topped-up
*/
public function getEnabledTopUpKeys()
{
return PersonalAccessToken::whereNotNull('region_code')
->where('auto_topup_enabled', true)
->whereNotNull('auto_topup_slug')
->withSum('credit_transactions AS credit_balance', 'delta')
->where('credit_balance', '<', 100)
->get();
}
This doesn't work, it throws an error:
Unknown column 'credit_balance' in 'where clause'
What am I missing to perform this?

withSum generates a sub query, so it can't be used in a where query. But that sub query creates an on-the-fly column that you can add to your query. For instance,
Order::withSum('orderItems', 'quantity')
->take(5)
->get();
creates an aliased column named order_items_sum_quantity, that I can then use as a having query:
Order::withSum('orderItems', 'quantity')
->having('order_items_sum_quantity', '>', 100)
->take(5)->get();
In your case, you may be getting a column named credit_balance_sum_delta that you can use in your query:
PersonalAccessToken::whereNotNull('region_code')
->where('auto_topup_enabled', true)
->whereNotNull('auto_topup_slug')
->withSum('credit_transactions AS credit_balance', 'delta')
->having('credit_balance_sum_delta', '<', 100)
->get();
If that isn't quite the correct column name, try getting one record without the having line, then check the results to find out the column name

Related

How to query field created by addSelect in Laravel?

I have the following query where I am adding three new columns by using the addSelect function
DB::connection('mysql_slave')
->table('applications')
->whereNull('applications.deleted_at')
->when($column != 'contract_return_date' && $column != 'contract_delivery_date',function ($query) use ($column,$date_from,$date_to){
return $query->whereBetween('applications.'.$column, [$date_from, $date_to]);
})
->join('customers','applications.customer_id','=','customers.id')
->join('departments','applications.department_id','=','departments.id')
->select([
'applications.id',
'applications.customer_id',
DB::raw('CONCAT(IFNULL(customers.last_name,"")," ",customers.first_name ) as customers_name'),
DB::raw('CONCAT(IFNULL(applications.last_name,"")," ",applications.first_name ) as contract_name'),
'applications.offer_type as offer_type',
'applications.status_id',
'applications.contract_no',
'applications.current_provider',
'applications.extra_offer',
'applications.offer_warranty',
'applications.department_id',
'customers.mobile_phone as customer_mobile',
'applications.program as program',
'applications.saled_by_text as saler',
'departments.name as department',
'applications.created_at as created_at',
'applications.created_at as saled_at',
DB::raw('IF(applications.sale=1,"NAI","OXI") as sale'),
])
->addSelect(['submission_date'=> StatusLog::select('created_at')
->whereColumn('application_id','applications.id')
->where('status','=',1)
->latest()
->take(1)
])
->addSelect(['resubmission_date'=> StatusLog::select('created_at')
->whereColumn('application_id','applications.id')
->where('status','=',2)
->latest()
->take(1)
])
->addSelect(['error_date' => StatusLog::select('created_at')
->whereColumn('application_id','applications.id')
->whereIn('status', [5, 6])
->latest()
->take(1)
]) ->when($column == 'contract_delivery_date',function ($query) use ($date_from,$date_to){
return $query->whereBetween('submission_date', [$date_from, $date_to]);
});
The above query is used to print data on a datatable.
The query includes columns that were added using the addSelect function, and these columns are being displayed correctly on the table.
However, when I try to query the submission_date field, I am encountering an error:
1054 Unknown Column submission_date.
Is there a way to query columns that were created using the addSelect function in Laravel?
Thank you for your help and I apologize for any errors in my English.
I think you cant do this. The custom select field (submission_date) is computed after the main query results. This is a limitation of the Database.
But you can use the HAVING operator instead.
https://stackoverflow.com/a/67580272/6901915
You do not create a field using addSelect. You only add a field to your query, so you cannot query it. Let's consider an example:
create table abc(id int primary key);
is a table I created as a proof-of-concept. Now, let's fill in some data:
insert into abc(id)
values(1),(2);
Now, let's query it, adding a field to it, called foo:
select id, 2 * id as foo
from abc;
And, now let's filter by foo:
select id, 2 * id as foo
from abc
where foo = 2;
We get an error, see below:
So, if you want to add a field and query by it, then you will need to either hardcode the field's equivalent into your conditional OR create a view of the table where the field exists OR add the field to the table.

Laravel Eloquent Multiple Joins Type Conversion

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

How to make order by in eager load laravel eloquent

I wanna question about how to use order by inside eager load laravel eloquent, I already have a query like this :
$getData = StockIn::select(
StockIn::raw('group_concat(stock_ins.id_stock_in) as id_stock_ins'),
'stock_in_id_type'
)
->with(['type_of_items' => function ($query) {
$query->orderBy('type_of_item');
}])
->orderBy('type_of_items.type_of_item')
->groupBy('stock_ins.stock_in_id_type')
->get();
But when I compile the query and look to the result, the result of my query didn't make result with order by query, Am I making a mistake in my query so that the result is matching with my expectation? Thanks before
Here for my model :
Stock In :
public function type_of_items() {
return $this->belongsTo('App\TypeOfitem', 'stock_in_id_type');
}
Type Of Item :
public function stock_ins() {
return $this->hasMany('App\StockIn');
}
when I try to look on the console, the result of my query like this :
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'type_of_items.type_of_item' in 'order clause' (SQL: select group_concat(stock_ins.id_stock_in) as id_stock_ins, `stock_in_id_type` from `stock_ins` group by `stock_ins`.`stock_in_id_type` order by `type_of_items`.`type_of_item` asc)
You're currently using order by on eager loaded data.
Instead, you need to call it on the model itself.
$getData = StockIn::select(
StockIn::raw('group_concat(stock_ins.id_stock_in) as id_stock_ins'),
'stock_in_id_type'
)
->with(['type_of_items' => function ($query) {
$query->orderBy('type_of_item');
}])
->orderBy('type_of_items.type_of_item')
->groupBy('stock_ins.stock_in_id_type')
->get();
You can also try it out without groupBy first, to be sure you're getting the correct results

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

Searching API with query string

I am currently building an API, I want to be able to search the API via the query string. So for example http://api.dev/?q=12
I can get the value entered but how do I then use this to search the database?
if ($q = Input::get('q'))
{
return( ModelName::where('id', '=', $q));
}
This doesn't seem to change the search and the same data is just returned.
ID's are unique so it doesn't surprise me you're getting the same result.
For Laravel I find the query builder to be better at handling search queries instead of Eloquent.
E.g.
$users = DB::table('users')
->where('votes', '>', 100)
->orWhere('name', 'John')
->get();
Hope this helps.
To search from the db with Eloquent you can use both this ways:
ModelName::where('id', '=', $q)->get();
or
ModelName::find($q);
In the second example Laravel assumes that your Primary Key is id, so in case you have another field as your primary you have to overwrite it, by setting the property in the model.
class ModelName
{
...your props
private $primaryKey = 'yourPrimaryKey'
...
}

Categories