Laravel - SELECT only certain columns within a `::with` controller function - select

The following code I have is working perfectly fine, however, it returns more data than what is necessary from each table:
public function getIndex()
{
$alerts = Criteria::with('bedrooms', 'properties')
->where('user_id', '=', Auth::id())
->get();
$this->layout->content = View::make('users.alert.index',
array('alerts' => $alerts));
}
What I'd like to do is, for example, select only the bedroom column out of the bedroom table. As it stands now, it returns all columns.
I have tried:
public function getIndex()
{
$alerts = Criteria::with('bedrooms' => function($q){
$q->select('bedroom');
}, 'properties')
->where('user_id', '=', Auth::id())
->get();
$this->layout->content = View::make('users.alert.index',
array('alerts' => $alerts));
}
But I am presented with the following error:
syntax error, unexpected '=>' (T_DOUBLE_ARROW)
Any help as to how I can achieve this will be hugely appreciated.
Update
public function getIndex()
{
$alerts = Criteria::with(
['coordinate' => function($w){
$w->select('name', 'id');
}],
['bedrooms' => function($q){
$q->select('bedroom', 'criteria_id');
}]
, 'properties')
->where('user_id', Auth::id())
->get();
$this->layout->content = View::make('users.alert.index',
array('alerts' => $alerts));
}
The correct select query works for which ever is queried first. If I swap the queries around, the bedroom function works correctly, but the rest aren't eager loaded nor does the select query work.

Just pass an array there:
// note [ and ]
$alerts = Criteria::with(['bedrooms' => function($q){
$q->select('bedroom', 'PK / FK');
}, 'properties'])
Also mind that you need to select the keys of that relation (primary key/foreign key of the relation).

The answer to your update question is that you need to eager load other values in the same array some thing like this.
public function getIndex()
{
$alerts = Criteria::with(
['coordinate' => function($w)
{
$w->select('name', 'id');
},
'bedrooms' => function($q)
{
$q->select('bedroom', 'criteria_id');
}
, 'properties'])
->where('user_id', Auth::id())
->get();
$this->layout->content = View::make('users.alert.index',
array('alerts' => $alerts));
}

Related

Modify Solr Query by adding additional filters in TYPO3

I'm trying to add custom filters to the query (TYPO3 v10, EXT:solr 11.2). However, this doesn't want to work.
After I simplified the use-case significantly and debugged it, I'm still not further, but rather more confused.
Filter works, if added via TypoScript:
plugin.tx_solr {
search {
filter {
jobTitle = title:Dev*
}
}
}
The same filter added via modifySearchQuery-hook does not work:
public function modifyQuery(Query $query)
{
$filterQuery = new FilterQuery([
'key' => 'jobTitle2',
'value' => 'title:Dev*',
]);
return $query->addFilterQuery($filterQuery);
}
When debugging the query, both filters look the same.
Thanks to Guido, who hit me on the right point: sometimes, keys are keys.
In the hook, the array-keys for FilterQuery have to be key and query (not value, as I've used)
public function modifyQuery(Query $query)
{
$filterQuery = new FilterQuery([
'key' => 'jobTitle2',
'query' => 'title:Dev*', // <-- correct key
]);
return $query->addFilterQuery($filterQuery);
}

Selectet data and data from with(), Laravel

I want to select some columns and some data from with(), the problem is that I get only data from select().
$today = date('Y-m-d', strtotime('-7 days'));
$contracts = Contract::select('
'contracts.id',
'contracts.contract_value_exc_VAT_total',
'customers.account_name',
'users.name',
)
->whereHas('dates', function($q) use($today){
return $q->whereDate('date', '>=', $today)
->where(function($q) {
$q->where('lkp_contract_date_tag_id', 4)
->orwhere('lkp_contract_date_tag_id', 7);
});
})
->with(['dates' => function($q){
$q->select('id', 'date');
}])
->join('customers','contracts.customer_id', 'customers.id')
->leftJoin('users','contracts.account_manager_select', 'users.id')
->get();
return response()->json($contracts);
From response, dates are null
//date....
dates: []
//date...
You can do it without using the select()
You can have the relations in the ContractModel. You can always process the data after getting from the database and manipulate it in a format you want to return.
There are two options to do that
Do it here in the controller itself.
Create an API resource for the Contract. (https://laravel.com/docs/7.x/eloquent-resources#introduction)
I would suggest the latter as it's more convenient.
For both of them you need to do this first. Make some changes in the contract model.
ContractModel.php
// I'm assuming that you have dates relation in the contract(because you've added it in the `with()` for eager loading.)
public function dates(){
...
}
// Instead of joining while doing the query, add the following
// relations in the contract as well.
public function customer(){
return $belongsTo('App\Customer', 'customer_id', 'id');
}
public function accountManagerSelect(){
return $belongsTo('App\User', 'account_manager_select', 'id');
}
This is how you go with the API resource approach.
Create the Contract Api Resource. And this is how the toArray() method should be.
toArray() {
// Get the dates in the format you want. I'have added the below
// format by considering the 'select' statement you added for
// dates.
$dates = [];
// will use the relation dates, to get the associated dates.
foreach($this->dates as $date){
array_push($dates, [
'id' => $date->id,
'date' => $date->date,
]);
}
return [
'id' => $this->id, // id of the contract.
'contract_value_exc_VAT_total' => $this
->contract_value_exc_VAT_total,
'account_name' => $this->account_name,
// This will use the accountManagerSelect relation to get the
// User instance and then you can access the name from that.
'name' => $this->accountManagerSelect->name,
'dates' => $dates, // The dates variable that we created earlier.
];
}
All you need to do is return using API resource in your controller.
instead of doing this
return response()->json($contracts);
Use Api resource
return ContractResource::collection($contracts);

Yii2: How to do a simple join query?

I am learning how to do simple queries using the Yii2 framework. I use PostgreSQL.
I am trying to join two tables and get the data from both tables with a where condition.
The tables are called Admins and Persons.
The join use field called idadm.
The condition is idadm = 33. This works great but the result has data only from the Admins table and I need data from the other table.
Here is my example:
$query = \app\models\Admins::find()
->select('*')
->leftJoin('persons', 'persons.idadm = admins.idadm')
->where(['admins.idadm' => 33])
->with('persons')
->all();
I am following the Yii2 official guide: http://www.yiiframework.com/doc-2.0/guide-db-active-record.html
Update: Here I show the updated code that doesn't solve de problem:
You need to write all column name in select().
$query = \app\models\Admins::find()
->select('admin.*,persons.*') // make sure same column name not there in both table
->leftJoin('persons', 'persons.idadm = admins.idadm')
->where(['admins.idadm' => 33])
->with('persons')
->all();
And also you need to define person table attributes in Admin model.
Second way is get records as array,so you dont need to define attributes in Admin model.
$query = \app\models\Admins::find()
->select('admin.*,persons.*') // make sure same column name not there in both table
->leftJoin('persons', 'persons.idadm = admins.idadm')
->where(['admins.idadm' => 33])
->with('persons')
->asArray()
->all();
Ensure that active record has required relations, e.g. something like follows:
class Admins extends \yii\db\ActiveRecord {
public function table() {
return "admins";
}
public function getPersons()
{
return $this->hasMany(Person::className(), ['idadm' => 'idadm']);
}
}
class Person extends \yii\db\ActiveRecord {
public function table() {
return "persons";
}
}
Then use joinWith to build query:
$query = Admins::find()
->joinWith('persons')
->limit(1);
$result = $query->createCommand()->getSql();
echo $result;
Here is produced query:
SELECT `admins`.* FROM `admins`
LEFT JOIN `person` ON `admins`.`idadm` = `person`.`idadm` LIMIT 1

Laravel Eloquent - orderBy column find in related model

INFORMATIONS
Database
events | id | to_user_id | from_user_id |
event_details | id | event_id | when |
Event.php
class Event extends Eloquent {
protected $table = 'events';
public function event_detail() {
return $this->hasOne('EventDetail');
}}
EventDetail.php
class EventDetail extends Eloquent {
protected $table = 'event_details';
public function event() {
return $this->belongsTo('Event', 'event_id');
}}
QUESTION
I want to get all events (Event model) and order by 'when' column in related EventDetail model. I wrote a query and it works:
$event = Event::join('event_details as p', 'p.event_id', '=', 'event.id')
->orderBy('p.when', 'asc')
->select('events.*')
->with('event_detail')
->get();
but when i would like to add ->where('to_user_id', '=', 0) clause i get error. How can I fix it?
Moreover I want to know if this query is correct with good practise?
Can you write it better?
Ok, had to recreate your project/tables
All tests are running correctly but I have noticed a few things you may consider changing.
$events = Event::with(['event_detail'=> function($query) {
$query->orderBy('when', 'asc');
}])
->where('to_user_id', 0)
->get();
Also your code may be better if you have an Event
public function event_detail(){
//considering you will have multiples? or just the one event detail?
return $this->hasMany('EventDetail');
}
As you can see, the one event I stored has 2 event details & they are all ordered by ASC.
You can use the other method that doesnt utilize 'with()' but I prefer it. Very nice for json results where you can daisychain a lot of related models.
Hope this helps
I solved the problem with this query. I should add ->where('events.to_user_id', '=', 0) so
$event = Event::join('event_details as p', 'p.event_id', '=', 'event.id')
->orderBy('p.when', 'asc')
->select('events.*')
->where('events.to_user_id', '=', 0)
->with('event_detail')
->get();
However, I would like to know if this query is correct with good practise? Can you write it better?

How to get specific fields or update theme and get the relation fildes from api calls in yii2 REST API?

I'm trying default yii2 api rest calls GET to get some model fields or PUT to update some model fields but I can't find a way to do this. I can only get all the fields or update theme all. Any help to do this? And how can I get the related relational field to this model?
I'm trying like this like
GET localhost/my-website-name/api/web/v1/vendors/
PUT localhost/my-website-name/api/web/v1/vendors/1
one way that I know for customizing fields is overriding fields function in your model like this
public function fields() {
return [
'id',
'iso3' => function() {
return base64_encode($this->iso3);
}
];
}
How to get specific fields and get the relation fields from api calls?
By default, yii\db\ActiveRecord::fields() returns all model attributes which have been populated from DB as fields, while yii\db\ActiveRecord::extraFields() should return the names of model's relations.
Take this model for example:
class Image extends ActiveRecord
{
public function attributeLabels()
{
return [
'id' => 'ID',
'owner_id' => 'Owner ID',
'name' => 'Name',
'url' => 'Url',
'created_at' => 'Created At',
'updated_at' => 'Updated At',
];
}
public function getOwner()
{
return $this->hasOne(Owner::className(), ['id' => 'owner_id']);
}
public function extraFields()
{
return ['owner'];
}
}
Here I did override the extraFields() method to define the owner relationship. Now if I want to retreive all images but selecting id and name fields only and each resource should also hold its related owner data I would simply request this url:
GET example.com/images?fields=id,name&expand=owner
note: you can also use comma separation to expand more than one relation
In case you want to permanently remove some fields like created_at and updated_at you can also override the fields() method:
public function fields()
{
$fields = parent::fields();
unset($fields['created_at'], $fields['updated_at'], $fields['owner_id']);
return $fields;
/*
// or could also be:
return ['id', 'name','url'];
*/
}
this way the following request should only return image's id, name and url fields along with their related owner :
GET example.com/images?expand=owner
If owner's fields should be filtered too then override its fields() method too in its related class or a child class of it that you tie to the image model by using it when defining the relation.
See official documentation for further details.
PUT to update some model fields
Yii only updates dirty attributes. so when doing:
PUT example.com/images/1 {"name": "abc"}
the generated SQL query should only update the name column of id=1 inside database.
Hello i manage to find a solution for this situations
first get some model fields only and with related entities fildes:
public function fields() {
return [
'name',
'phone_number',
'minimum_order_amount',
'time_order_open',
'time_order_close',
'delivery_fee',
'halal',
'featured',
'disable_ordering',
'delivery_duration',
'working_hours',
'longitude',
'latitude',
'image',
'owner' => function() {
$owner = Owners::findOne($this->owner_id);
return array($owner);
}
];
}
if you want to remove some fildes
public function fields()
{
$fields=parent::fields();
unset($fields['id']);
return $fields;
}
update only specific fields
public function beforeSave($insert)
{
$lockedValues = ['name', 'halal', 'featured', 'latitude', 'longitude', 'image', 'status', 'created_at', 'updated_at'];
foreach ($lockedValues as $lockedValue) {
if ($this->attributes[$lockedValue] != $this->oldAttributes[$lockedValue])
throw new ForbiddenHttpException($lockedValue . " can't be changed.");
}
return parent::beforeSave($insert); // TODO: Change the autogenerated stub
}