Yii2 add column from join table without asArray method - php

I was trying to get in my select result column from joined table. My query looks like this:
$query = MediaLibrary::find()->select([
'media_library.*',
'category_name' =>'ctg.name',
'category_id' =>'ctg.id',
'entity_name' =>'ent.name',
'entity_id' =>'ent.id',
'category_alias' =>'ctg.alias',
])->joinWith([
'mediaLibraryCategory' => function ($q) {
$q->from(MediaLibraryCategory::tableName().' ctg');
},
'mediaLibraryEntity' => function ($q) {
$q->from(MediaLibraryEntity::tableName().' ent');
}
])->asArray();
It works, okey. But the problem is that I don't want to get that arrays of data from another tables (asArray() method). But if I delete that method - columns from another tables are gone.
What I am doing wrong? Is there way to add columns without asArray() method?

You're using joinWith() method without second param $eagerLoading. If the $eagerLoading parameter is true, the method will also perform eager loading for the specified relations, which is equivalent to calling with() using the specified relations. To solve your issue, add second param false to your joinWith() function.
More in Yii2 ActiveQuery

Related

Adding custom collection to the eloquent within Laravel Query Builder

I have a query and I want to add a collection using Laravel query builder.
Hotel::addSelect([
'selectableLocations' => AllLocations::orderBy('name')->get()
])
->with('location')
->get();
Well, this returns:
SQLSTATE[HY093]: Invalid parameter number: mixed named and positional parameters (SQL: select [{"id":1,"name":"John Avenue"},{"id":4,"name":"Ontirio Village"},{"id":2,"name":"Rovie"},{"id":3,"name":"Movie Lot"}] from dogs limit 100 offset 0)
I know this may seem like an anti-pattern but the reason I need this is because I have a datatable and want to show a select (dropdown) with AllLocations so that user can change in the datatable.
My idea is that I can do $dog->selectableLocations to see all locations. Because if I don't do it like this, it will query for each row individually.
Is there a way to do that?
If I could achieve it like this, that'd be perfect.
$selectableLocations = AllLocations::get();
$hotels = Hotel::addSelect([
'selectableLocations' => $selectableLocations
])
->with('location')
->get();
EDIT:
Because if I don't do it like this, it will query for each row individually.
Since your primary concern is multiple queries, you could avoid db calls if you implement some sort of caching. Why not include the list as a custom attribute, load collection from cache? Like so:
public function getAllLocationsAttribute()
{
return Cache::remember('all_locations', 30, function(){ //add remove seconds as required
return AllLocatiobs::get();
});
}
How about getting merging the two collections?
$hotels = Hotel::get();
$selectableLocations = AllLocations::get();
$hotels->put('selectableLocations', $selectableLocations);
Now, $hotels->selectableLocations will be a collection of AllLocations.

Why is it not possible to use groupBy() in an eager loading query - Laravel

ErrorException:
stripos() expects parameter 1 to be string, object given
For the groupBy() call in the with() method
$user = User::with([
'pricelists' => function($query) {
$query->groupBy(function($var) {
return Carbon::parse($var->pivot->created_at)->format('m');
});
}
])->where('id', $id)->get();
I already saw a few posts talking about how to manage this problem and that it shall not be possible to use groupBy() in eloquent but I do not really understand why...
To be clear:
User and Pricelist model got a many-to-many relationship with the default timestamps() method. I am trying to get the downloaded pricelists grouped by their months they were downloaded from the current user.
After a few attempts I just deleted the above shown => function($query... statement from the with() method and just left the with(['pricelist']) to fetch all datasets and tried this:
$user->pricelists = $user->pricelists->groupBy(function($var) {
return Carbon::parse($var->pivot->created_at)->format('m');
});
return $user->pricelists;
And it works fine and returns an array with multiple arrays for each month... But returning it like this:
return $user;
returns just 1 array with all entries... I do not really get the sense behind it right now...
The two groupBy() method that you are using in the two code you provide are totally different methods.
The first groupBy() where you use it in the callback is actually being called by $query which is a query builder object. The groupBy() here is used to add SQL GROUP BY Statement into the query. And as per the documentation, it only take string variables as parameter.
The groupBy() in your second code is being called by $user->pricelists which is a laravel eloquent collection. The groupBy() method here is actually from the base collection class and is used to group the items inside the collection into multiple collections under the different key defined by the parameter passed to the function. Please read the documentation here.
For your case, the second groupBy() is the one you should be using since you plan to use a callback and will allow you to use more complicated logic.

How to specify two or more conditions within find() method in Laravel?

I'm having problems fetching data using Model::find() with two conditions. I want to return one record that has the given member_id AND its role is guest:
$member = CalendarMembers::find(["member_id" => $r->member_id, "role" => "guest"]);
But this isn't working. I have one user that when created, a new register is added to calendar_members table specifying this user as parent, yet, when attempting to fetch this user automatically created by just giving the member_id, it will be fetched, which SHOULD NOT.
I cannot use other ways such as:
$member = \DB::table("calendar_members")->where([ ["member_id", "=", $r->member_id], ["role", "=", "guest"] ])->first();
Because then $member->delete() or $member->destroy() will throw an error saying either delete or destroy do not exist.
The find() method uses ID to get a record. You need to use the where() and first() methods:
CalendarMembers::where(['member_id' => $r->member_id, 'role' => 'guest'])->first();
you should use where for multiple conditions and get the first row with first() method, find is basically an alias for wehre('id', $id)
To make it clear, find() method will perform database query and returns single model object as result. so find is equal to,
Model::where('id', 1)->first().
find() returns Model object and where() returns QueryBuilder. But you can directly perform on query Builder without fetching model, like,
Model::where('id', 1)->delete().
REF : https://laravel.com/docs/5.6/eloquent

Laravel Eloquent relationship orderby issue

I have a Objective model which has many Action and every Action has one ActionYear. already defined in model.
How to use orderby to sort action in objective through action_year's specific column.
$query = Objective::with('actions.actionYear')
->orderBy('action_year.created_at')
->get();
this through error Undefined table: 7 ERROR: missing FROM-clause entry for table "action_year".
How to solve this. Thank you.
You can order the eager loaded models with a closure:
$query = Objective::with(['actions.actionYear' => function($q){
$q->orderBy('action_year.created_at');
})->get();
If you use function with() then this only makes sure the named relationship is loaded also to avoid extra need for SQL queries later. Othewise it does not introduce any additional changes compared to Objective::all()
One way how to achieve sorted collection is to use sortBy() function after loading the data like this
$query = $query->sortBy(function($objective){
return $objective->actionYear->created_at;
});

Eloquent - Updating all models in a collection

I want to set a certain attribute in all the models of a collection.
in plain SQL:
UPDATE table SET att = 'foo' WHERE id in (1,2,3)
the code i have:
$models = MyModel::findMany([1,2,3]);
$models->update(['att'=>'foo']);
taken from here
but doesn't work. I'm getting
Call to undefined method Illuminate\Database\Eloquent\Collection::update()
the only way i have found it's building a query with the query builder but i'd rather avoid that.
You are returning a collection, not keeping the query open to update. Like your example is doing.
$models = MyModel::whereIn('id',[1,2,3]);
$models->update(['att'=>'foo']);
whereIn will query a column in your case id, the second parameter is an array of the ids you want to return, but will not execute the query. The findMany you were using was executing it thus returning a Collection of models.
If you need to get the model to use for something else you can do $collection = $models->get(); and it will return a collection of the models.
If you do not just simply write it on one line like so;
MyModel::whereIn('id',[1,2,3])->update(['att'=>'foo']);
Another option which i do not recommend is using the following;
$models = MyModel::findMany([1,2,3]);
$models->each(function ($item){
$item->update(['att'=>'foo']);
});
This will loop over all the items in the collection and update them individually. But I recommend the whereIn method.
The best solution in one single query is still:
MyModel::whereIn('id',[1,2,3])->update(['att'=>'foo']);
If you already have a collection of models and you want to do a direct update you can use modelKeys() method. Consider that after making this update your $models collection remains outdated and you may need to refresh it:
MyModel::whereIn('id', $models->modelKeys())->update(['att'=>'foo']);
$models = MyModel::findMany($models->modelKeys());
The next example I will not recommend because for every item of your $models collection a new extra query is performed:
$models->each(function ($item) {
$item->update(['att'=>'foo']);
});
or simpler, from Laravel 5.4 you can do $models->each->update(['att'=>'foo']);
However, the last example (and only the last) is good when you want to trigger some model events like saving, saved, updating, updated. Other presented solutions are touching direct the database but models are not waked up.
Just use the following:
MyModel::query()->update([
"att" => "foo"
]);
Be mindful that batch updating models won't fire callback updating and updated events. If you need those to be fired, you have to execute each update separately, for example like so (assuming $models is a collection of models):
$models->each(fn($model) => $model->update(['att'=>'foo']) );

Categories