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.
Related
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.
I am trying to make a filter in laravel. This following filter works
$posts= Post::where('category',$request->category)->orderBy('id','desc')->paginate(10);
But when I try to do something like this
public function index(Request $request)
{
$posts= Post::where('category',$request->category)->get();
$posts->latest()->paginate(10);
dd($posts);
It doesn't work. Can someone explain why is this and provide me the code that works. My project have multiple filter.
Error
Because $posts = Post::all(); already execute a query.
Post::where('category',$request->category)->latest()->paginate(10)->get();
would be what you want.
A note:latest requires the created_at column
You should go
$posts = Post::where('category',$request->category)->latest()->paginate(10);
the get request is unnecessary as the paginate will execute the query.
The first one makes the query by pagination i.e fetch 10 records per constructed page
For the second one, based on observation, you most likely have encountered at least 2 errors:
The first, on the line that used the get method because that method requires at least one parameter.
Type error: Too few arguments to function Illuminate\Support\Collection::get()
The other since its a collection, and since there is nothing like paginate or latest method on collection therefore throws other errors. You should check Collection's Available methods to have a glimpse of the methods allowed on collection.
One of the best solutions is to simply order the result when making the query:
Blog::where('category',$request->category)
->orderBy('created_at', 'desc') //you may use also 'updated_at' also depends on your need
->paginate(10);
This way you have the latest coming first int the pagination and also having not worrying about paginating a collection
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
I'm trying to count some items from the table and join it with the another table so I use the following code
Article::join("article_comments", "article_comments.article_id", "=","articles.id")->
select(["articles.title", "articles.content", "articles.created_at", DB::raw('count(article_comments.id) as commentsCount')])->paginate(10) ;
But I always get only first item
Shouldn't there be a group by if you use aggregates?
Something like:
Article::join("article_comments", "article_comments.article_id", "=","articles.id")->
select(["articles.title", "articles.content", "articles.created_at", DB::raw('count(article_comments.id) as commentsCount')])->
groupBy('articles.id')->
paginate(10);
Also since you're no using your joined table for filtering you might wanna consider eager loading. However in your case you only get articles with comments. When using eager loading you'd get all articles (including the ones without comments).
Something like that should work:
Article::with([
'comments' => function ($query) {
$query
->select('article_id', DB::raw('COUNT(`id`) AS `commentCount`'))
->groupBy('article_id')
}
])
->paginate(10);
Now you should be able to access the count like this:
echo $article->comments->first()->commentCount;
Did not test the solution. You might wanna check if $article->comments->first() exists.
Or if you wanna expand your model a little here are some even better thoughts
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']) );