Laravel pagination error because of sortBy - php

I am trying to paginate an eloquent object but i can't get it to work. The paginate throws a error because $products is not a query builder object but a collection.
// i get this value from a $POST variable
$customOrderIds = [3,2,1,4,6,5,9,8,10,7,11,12,13,14,15,16,17,20,18,19,21,22]; // I want the products ordered in this sequence
$products = Product::get()->sortBy(function($product) use($customOrderIds)
{
return array_search($product->id, $customOrderIds);
});
$products->paginate(5); // Error is thrown here
I want to keep the order of products that is defined in the $customOrderIds
In other questions they suggest to replace get() function with the paginate function but then my custom order will be only applied to the 5 items in the pagination.
I would rather not use anything with raw sql

paginate is an Eloquent method, so it won't work on your collection. However, collections have a forPage method, which you can use:
The forPage method returns a new collection containing the items that would be present on a given page number. The method accepts the page number as its first argument and the number of items to show per page as its second argument
So what you'll need is
$products->forPage(1, 5);

You have to indeed replace the get with the paginate, but you'll have to do the sorting before you paginate. You can try something along the lines of:
Product::orderByRaw(
'FIELD(id,3,2,1,4,6,5,9,8,10,7,11,12,13,14,15,16,17,20,18,19,21,22)'
)->paginate(5);
Sources:
https://laravel.com/docs/5.8/queries#raw-expressions
https://laracasts.com/discuss/channels/eloquent/custom-orderby-in-laravel-query-builder

Related

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.

Laravel Where clause, paginate and sortByDesc

I am working a project and I would want to use a where clause, paginate and then sort in the collection in specific order. I have tried the result below but keeps throwing the errors below Method:
Illuminate\Database\Eloquent\Collection::links does not exist. (View:
/Applications/XAMPP/xamppfiles/htdocs/vermex/resources/views/equipments.blade.php)
The Product model is where I am getting the data and store in a variable called $equipment. If there is a better way of doing this, please help.
public function equipments()
{
$equipments = Product::where('product_category_id', 3)->paginate(2)-
>sortByDesc('id');
return view('equipments', compact('equipments'));
}
Try putting the orderBy before the paginate
$equipments = Product::where('product_category_id', 3)->orderBy('id', 'desc')->paginate(2);
sortByDesc is a collection method.
paginate will need to be last for links to be available in the blade view.

Method links do not exist on Laravel pagination

I'm trying to achieve pagination and it is working absolutely fine until I add sortByDesc() along with my eloquent query.
web.php (route file)
Route::get('/', function(){
$posts = Post::simplePaginate(5)->sortByDesc("post_id");
//sortByDesc("post_id") this causes the problem
}
When I prepare the view for the pagination with {{ $posts->links() }} in the specified view, I get the following error-
Method links does not exist
If I remove the sorting condition from the query, it works perfectly.
What can be the reason behind this behaviour?
Try putting the sort on the query rather than the pagination:
Post::orderBy('post_id', 'desc')->simplePaginate(5);
To extend to what #RossWilson said.
sortBy is a collection function, not an eloquent function, the correct eloquent function is orderBy.
Also, see simplePaginate() as if you were performing a get(), first(), find().
What would you place first the get or the order? ... maybe the get if you want to order a collection (with sortBy), but since simplePaginate does not return the same collection that a get() would return, sortby does not work. And probably messes up the pagination object/collection.

How to filter laravel collection

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

Use collection->get() instead of collection->pluck()

I am using laravel-permission for managing roles and displaying content. Per the docs you can retrieve a users roles by using $roles = $user->roles()->pluck('name'). My problem is that the data returned is ["admin"] rather than just admin. I was reviewing the collections methods and it looked like get('name') would return what I was looking for. When I try to use the following command Auth::user()->roles()->get('name') I get
1/1
ErrorException in BelongsToMany.php line 360:
Argument 1 passed to Illuminate\Database\Eloquent\Relations\BelongsToMany::getSelectColumns() must be of the type array, string given
It seems to me like the get() method is expecting an array however, I'm trying to reference an item in the array. The raw output of Auth::user()->roles()->get() is [{"id":1,"name":"admin","created_at":"2016-03-10 06:24:47","updated_at":"2016-03-10 06:24:47","pivot":{"user_id":1,"role_id":1}}]
I have found a workaround for pulling the correct content, but it is using regex for removing the unwanted characters that are included in the pluck() method.
preg_replace('/\W/i','', Auth::user()->roles()->pluck('name'))
It seems like I'm missing something or approaching using the get() method incorrectly. Any advice is appreciated.
I think pluck() will return the value of the given column for each model in the collection, which would explain the array. In your case it looks like the user has only one role, so you get an array with only one item. If the user had multiple roles, you would likely get an array with multiple items in it.
On the other hand, the get() method is used to execute a query against the database after a query is built. The results of the query are what is returned. To return a collection of models with only a single value you will need to pass an array with just the one column you want, but that will just select models, which does not appear to be what you ultimately need.
You can try this instead: $roles = $user->roles()->first()->name
The call to first() will grab the first model in the collection returned by roles(), and then you can grab the name of the role from that model.
I typically throw some error checking around this:
$role = $user->roles()->first();
if (is_null($role)) {
//Handle what happens if no role comes back
}
$role_name = $role->name;
That's because an user can have many roles, so roles is a collection.
If you are 100% sure that a user will have only one role, you can easily do
Auth::user()->roles()->first()->name
That will get the first item of that collection (the role) and then its name.

Categories