I'm having issues with an array returned from DB::select(). I'm heavily using skip and take on Collections of eloquent models in my API. Unfortunately, DB::select returns an array, which obviously doesn't work with skip and take's. How would one convert arrays to a collection that can utilise these methods?
I've tried
\Illuminate\Support\Collection::make(DB::select(...));
Which doesn't quite work as I expected, as it wraps the entire array in a Collection, not the individual results.
Is it possible to convert the return from a DB::select to a 'proper' Collection that can use skip and take methods?
Update
I've also tried:
$query = \Illuminate\Support\Collection::make(DB::table('survey_responses')->join('people', 'people.id',
'=', 'survey_responses.recipient_id')->select('survey_responses.id', 'survey_responses.response',
'survey_responses.score', 'people.name', 'people.email')->get());
Which still tells me:
FatalErrorException in QueryHelper.php line 36:
Call to a member function skip() on array
Cheers
I would try:
$queryResult = DB::table('...')->get();
$collection = collect($queryResult);
If the query result is an array, the collection is filled up with your results. See the official documentation for the collection. Laravel5 Collections
For anyone else that's having this sort of problem in Laravel, I figured out a work around with the following solution:
$query = DB::table('survey_responses')->join('people', 'people.id', '=', 'survey_responses.recipient_id')
->select('survey_responses.id', 'survey_responses.response', 'survey_responses.score', 'people.name', 'people.email');
if(isset($tags)){
foreach($tags as $tag){
$query->orWhere('survey_responses.response', 'like', '%'.$tag.'%');
}
};
// We apply the pagination headers on the complete result set - before any limiting
$headers = \HeaderHelper::generatePaginationHeader($page, $query, 'response', $limit, $tags);
// Now limit and create 'pages' based on passed params
$query->offset(
(isset($page) ? $page - 1 * (isset($limit) ? $limit : env('RESULTS_PER_PAGE', 30)) : 1)
)
->take(
(isset($limit) ? $limit : env('RESULTS_PER_PAGE', 30))
);
Basically, I wasn't aware that you could run the queries almost incrementally, which enabled me to generate pagination chunks before limiting the data returned.
Related
I have code like this
$tag = Tag::where('slug' = $slug)->first();
$posts = $tag->posts;
It works correctly but I want to use limit, orderBy, offset and other operation on posts. So it works
$posts = $tag->posts->where('accept', 1);
But it doesn't works
$posts-> $tag->posts->orderBy('created_at', 'desc');
//or
$posts-> $tag->posts
->offset($offset)
->limit($limit);
I must use offset and limit into query from var.
How I can do that?
When you set up your initial query Tag::where('slug' = $slug)->first(); you're using Query Builder and it's methods. But when Laravel returns the results, they're returned as a collction object -- those have very similar but slightly different methods available. https://laravel.com/docs/5.8/collections#available-methods
On a collection or its children, instead of orderBy() you would use sortBy() or sortByDesc(). Those will return an instance of the collection, sorted by your specified key. $results = $posts->sortBy($sorting);
The same idea with limit, in this case you can use the splice method. (Collections are basically php arrays on steroids) Splice accepts two parameters, a starting index and a limit. So, to get only the first 10 items, you could do this: $results = $posts->splice(0, 10);
And of course, you can also chain those togeather as $results = $tag->posts->sortBy('id')->splice(0, 10);
When you use child, Eloquent create another subquery, then result is added to parent, thats way its not sorting properly.
A solution could be join tables:
$tags = Tag::where('tags.slug' = $slug)
->join('tags', 'tag.post_id', '=', 'posts.id')
->orderBy('posts.created_at', 'desc')
->select('tags.*')
->get();
I have many individual models and I want to combine them all to take advantage of eagerloading.
$match_query = "select * from feeds " .
"WHERE (user_id IN ($users_size)) " .
"OR (target_id IN ($slugs_size) AND feedable_type = 'review') " .
"ORDER BY created_at DESC LIMIT 5;";
//Query works fine and return 2 results
$results = DB::select($match_query, $consolidatedArray);
//to convert returned php standard obj to array
$results = json_decode(json_encode($results), True);
$all = [];
foreach($results as $result){
$x = new \App\Feed($result);
//I am able to get relation like this for single item
// $x->user;
$all[] = $x;
}
//but this thing is not working (i am trying to eager load)
$data = $all->with('user');
I get the following error
ErrorException in FeedsRepository.php line 67:
Trying to get property of non-object
What is the solution?
$all is an array. Array is not an object. So when you call $all->with(), you get an error "Trying to get property of non-object". Pretty straightforward :).
Your code seems to be needlessly convoluted. You query is not too hard to do in Eloquent and Query Builder. Read a bit of documentation and start using its awesome features instead of hacking your way around that :).
You code could be replaced with this much more readable snippet (or something like this):
$results = Feed::whereIn('user_id', $users_size)
->orWhere(function($q) use($slugs_size) {
$q->whereIn('target_id', $slugs_size)->orWhere('feedable_type', 'review');
})
->with('user')
->orderBy('created_at', 'desc')->take(5)->get();
The important thing to remember here is that eloquent also packs all the power of query builder. In most cases it will be easier to read and maintain than full SQL queries.
Reading material:
https://laravel.com/docs/master/eloquent
https://laravel.com/docs/master/queries
I am very new to Laravel and php and i am facing an issue with a collection. The collection is generated in this way:
$users = $media->campaign->users;
Which return this data:
[{id: 1, name: "name", suspended: 0},{id: 2, name: "name2", suspended: 1}]
How can i filter this object in laravel 4.1 to get only the elements that have 0 as suspended?
Use array_filter(array $array[, callable $callback[, int $flag]]):
array_filter($users, function($value) {
return($value->suspended === 0);
});
Check more in Laravel 4.2 documentation, Taylor wrote there that filtering collections use array_filter function. Also you should can use $users = $users->filter(function($user) {}); method.
Also, thanks to #xAoc, you can use filtering on SQL query:
$users = $media->campaign
->users()
->where("suspended", "=", 0)
->get();
Since you're doing a direct "equals" comparison, Laravel's Collection has a where() method you can use. For example:
$users = $media->campaign->users;
$users->where('suspended', 0);
This is a good option if you already have the Collection. If, however, you have control over generating the Collection, it would be more beneficial to only get the actual data you're looking for. In this case, you can add the where clause to the SQL statement, so you will only retrieve the final records you want. For example:
$users = $media->campaign->users()->where('suspended', '=', 0);
NB: the where() method on the Collection and the where() method on the query builder have different signatures. The query builder lets you pass in an operator to use ('=', '>', '<', etc). The Collection only does a direct equals comparison. This trips up a lot of people.
Following up on this Question: How to chunk results from a custom query in Laravel
I try
DB::connection('mgnt')->select($query)->chunk(200, function($orders) {
foreach ($orders as $order) {
//a bunch of code...
}
});
But I get the following error:
FatalErrorException in MigrationController.php line 98:
Call to a member function chunk() on array
Is chunking possible without having an appropriate Eloquent ORM Model?
I try to chunk because I get a blank page (can't find any errrors in any log) if the query returns too many results.
I think right now it max 50.000 results that I can query at once. Is that maybe due to some restriction or limitation in Laravel?
Well since the query will just return an array of objects you can simply use PHP's array_chunk():
$result = DB::connection('mgnt')->select($query);
foreach(array_chunk($result, 200) as $orders){
foreach($orders as $order){
// a bunch of code...
}
}
Here's what chunk() on an eloquent model does:
$results = $this->forPage($page = 1, $count)->get();
while (count($results) > 0)
{
// On each chunk result set, we will pass them to the callback and then let the
// developer take care of everything within the callback, which allows us to
// keep the memory low for spinning through large result sets for working.
call_user_func($callback, $results);
$page++;
$results = $this->forPage($page, $count)->get();
}
You could try to do something similar (although I think it should be possible to run your query all at once, but I can't help you with that...)
Add a limit to your SQL query LIMIT 200
Increase the offset with every query you run. First 0, second 1 * 200, third 2 * 200
Do that until the result is returned empty (e.g. with a while loop like above)
I am using Eloquent ORM outside of Laravel-4 and I am building a custom Paginator.
First, I build a query using Fluent Query Builder. I want to get the number of result the query could return using count() and then I do a custom pagination using take(x) and skip(y). I need to do the count() before the take()->skip()->get() so I dont fall outside of the page range. The problem is that when I use the count() method on the query, it seems to remove any select I added previously.
I isolated the problem to this simple example:
$query = DB::table('companies')
->join('countries','companies.country_id','=','countries.id')
->select(
'companies.name as company_name',
'countries.name as country_name'
);
$nbPages = $query->count();
$results = $query->get();
//$results contains all fields of both tables 'companies' and 'countries'
If i invert the order of the count and get, it works fine:
$results = $query->get();
$nbPages = $query->count();
//$results contains only 'company_name' and 'country_name'
Question: is there a more elegant way the using something like this:
$tmp = clone $query;
$nbPages = $tmp->count();
$results = $query->get();
There is not, unfortunately. Open issue on github about the problem: https://github.com/laravel/framework/pull/3416