How to query field created by addSelect in Laravel? - php

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.

Related

Laravel withSum where clause column not found

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

Selecting only certain columns during nested relation

If I have a query: MyModal::with('relation1.relation2')->get(), how can I limit the fields selected from relation1?
MyModal::with('relation1:column1,column2')->with('relation1.relation2')->get() selects all fields on relation1.
MyModal::with('relation1:column1,column2.relation2')->get() gives an SQL error because it tries to find a column named column2.relation2.
I'm not sure what other approach there could be, so is this possible, or will fetching nested relations always fetch all fields on the first relation?
You should do column select after relation select:
MyModal::with(['relation1.relation2', 'relation1:column1,column2'])->get()
Or you can define it in model itself
public function relation1()
{
return $this->hasOne(Relation1::class)->select(['column1', 'column2']);
}
Model::query()
->with(array('relation1' => function($query) {
$query->select('column1', 'column2');
},'relation2' => function($query) {
$query->select('column1', 'column2');
}))
->get();

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

Laravel 5.1 select field in with function

Can I select value from relationships with function "with" ?
So make something like this:
$test = User::where('id',1)->with(['user_detail' => function($query){
$query->select("detail_1");
}])->get();
Yes I know that I can put select in relation "user_detail" but can I select in with function?
You can select within with as you made the example given below:
$test = User::where('id',1)->with(['user_detail' => function($query){
$query->select("detail_1");
}])->get();
But it won't not work (as you commented in other answer) because you've only selected a single property but the foreign key is not available in your select statement. So, make sure that, you also select the related foreign key as well and then it'll work.
In your case, I believe that, you've to also select the user_id in your select for example:
$test = User::where('id',1)->with(['user_detail' => function($query){
$query->select(
'user_id', // This is required if this key is the foreign key
'detail_1'
);
}])->get();
So, without the foreign key that makes the relation, Eloquent won't be able to load the related models and that's why you get null in your result as you mentioned in other comment.
Yes, you can use select() inside with(). Just pass an array of columns:
$query->select(['detail_1', 'detail_2']);
Alternatively, you can create another relation and add select() to it:
public function userDatails()
{
return $this->hasMany('App\UserDetail')->select(['detail_1', 'detail_2']);
}
$result = Staff::where('live_status',2)
->with('position')->with('department')->with('gender')
->with(['partner' => function($query){
$query->where('alive',0);
}]);

How to alias a table in Laravel Eloquent queries (or using Query Builder)?

Lets say we are using Laravel's query builder:
$users = DB::table('really_long_table_name')
->select('really_long_table_name.id')
->get();
I'm looking for an equivalent to this SQL:
really_long_table_name AS short_name
This would be especially helpful when I have to type a lot of selects and wheres (or typically I include the alias in the column alias of the select as well, and it gets used in the result array). Without any table aliases there is a lot more typing for me and everything becomes a lot less readable. Can't find the answer in the laravel docs, any ideas?
Laravel supports aliases on tables and columns with AS. Try
$users = DB::table('really_long_table_name AS t')
->select('t.id AS uid')
->get();
Let's see it in action with an awesome tinker tool
$ php artisan tinker
[1] > Schema::create('really_long_table_name', function($table) {$table->increments('id');});
// NULL
[2] > DB::table('really_long_table_name')->insert(['id' => null]);
// true
[3] > DB::table('really_long_table_name AS t')->select('t.id AS uid')->get();
// array(
// 0 => object(stdClass)(
// 'uid' => '1'
// )
// )
To use aliases on eloquent models modify your code like this:
Item
::from( 'items as items_alias' )
->join( 'attachments as att', DB::raw( 'att.item_id' ), '=', DB::raw( 'items_alias.id' ) )
->select( DB::raw( 'items_alias.*' ) )
->get();
This will automatically add table prefix to table names and returns an instance of Items model. not a bare query result.
Adding DB::raw prevents laravel from adding table prefixes to aliases.
Here is how one can do it. I will give an example with joining so that it becomes super clear to someone.
$products = DB::table('products AS pr')
->leftJoin('product_families AS pf', 'pf.id', '=', 'pr.product_family_id')
->select('pr.id as id', 'pf.name as product_family_name', 'pf.id as product_family_id')
->orderBy('pr.id', 'desc')
->get();
Hope this helps.
To use in Eloquent.
Add on top of your model
protected $table = 'table_name as alias'
//table_name should be exact as in your database
..then use in your query like
ModelName::query()->select(alias.id, alias.name)
You can use less code, writing this:
$users = DB::table('really_long_table_name')
->get(array('really_long_table_name.field_very_long_name as short_name'));
And of course if you want to select more fields, just write a "," and add more:
$users = DB::table('really_long_table_name')
->get(array('really_long_table_name.field_very_long_name as short_name', 'really_long_table_name.another_field as other', 'and_another'));
This is very practical when you use a joins complex query
I have tried all these options and none works for me. Then I had found something in the Laravel documentation that really works.
You could try this:
DB::table('table_one as t1')
->select(
't1.field_id as id','t2.field_on_t2 as field'
)->join('table_two as t2', function ($join) {
$join->on('t1.field_id ', '=', 't2.field_id');
})->get()
Also note that you can pass an alias as the second parameter of the table method when using the DB facade:
$users = DB::table('really_long_table_name', 'short_name')
->select('short_name.id')
->get();
Not sure if this feature came with a specific version of Laravel or if it has always been baked in.
Same as AMIB answer, for soft delete error "Unknown column 'table_alias.deleted_at'",
just add ->withTrashed() then handle it yourself like ->whereRaw('items_alias.deleted_at IS NULL')
In the latest version of Laravel 9, you can use alias name for column as:
$events = Booking::whereBetween('sessionDateTime', [$today, $nextMonth])->get(['bookings.sessionDateTime as start']); // start is an alias here

Categories