Sorting collections in Laravel - laravel

I've got a little problem with my Laravel controller and sorting it:
public function index()
{
$achievements = Achievement::all();
$news = News::all();
$livingspaces = Livingspace::all();
$therapies = Therapy::all();
$events = Event::all();
$collection = collect();
foreach ($achievements as $achievement) {
$collection->push($achievement);
}
foreach ($news as $new) {
$collection->push($new);
}
foreach ($livingspaces as $livingspace) {
$collection->push($livingspace);
}
foreach ($events as $event) {
$collection->push($event);
}
foreach ($therapies as $therapy) {
$collection->push($therapy);
}
$sortedData = $collection->sortBy('category')->sortByDesc('created_at');
return response()->json([
'sortedData' => $sortedData
], 200);
}
It's not sorting at all. It should sort if after the data in the created_at timestamp which comes out of the box when creating a new migration for a Laravel controller. But I can't sort the data. I think it has something to do with pushing the data from the DB directly into the collection and it's not looking for "created_at" at all. It's not giving any errors or anything its just not doing anything. The same goes for sortBy.

sortByDesc method has the same signature as the sortBy method, but will sort the collection in the opposite order. By default sortBy to do the ascending operation. If you want to sort by decending:
$sortedData = $collection->sortBy('category', true);

Related

Sorting Data Coming from Resource in laravel

I want to sort data that is coming from Api resource on the basis of their rating that is coming from the resource. I have been working on the SortByDesc method but it's not giving proper results.
public function reviewlist()
{
$post = Post::all();
$list = RatingResource::collection($post);
return $list->sortByDesc('Rating');
}
Got an answer that you have to use this method for applying sort on resource data.
public function reviewlist()
{
$post = Post::all();
$list = RatingResource::collection($post);
$statisticCollection = collect($list);
$sorted = $statisticCollection->sortByDesc('Rating');
return $sorted->values()->all();
}

Laravel Collection paginate does not exist

I'm trying to implement basic pagination when retrieving notifications, but I get the following error.
Method
Illuminate\Notifications\DatabaseNotificationCollection::paginate does
not exist.
public function index()
{
$messages = collect();
$notifications = auth()->user()->unreadNotifications->paginate(5);
foreach ($notifications as $notification) {
$message = NotificationToMessageFactory::make($notification->type)
->toMessage($notification->data);
$messages->push($message);
}
}
You've to call paginate() on query builder instance not on a collection.
Correct syntax will be :
$notifications = auth()->user()->unreadNotifications()->paginate(5);
You should paginate before looping through the collection, otherwise you will be retrieving all records matching the query, when you only need 5. Like this:
$messages = collect();
$notifications = auth()->user()->unreadNotifications()->paginate(5);
foreach($notifications as $notification) {
$message = NotificationToMessageFactory::make($notification->type)->toMessage($notification->data);
$messages->push($message);
}

laravel more than one result from single query

I am trying to get all rows and distinct column from single query. but paginate method is only giving result but not pagination option like total prev next etc..
$offers = Offer::whereHas('users', function ($q) use ($authUser) {
$q->where('user_id', $authUser->parent_id);
$q->where('publisher_id', '=', $authUser->id);
});
and distinct column
$websites = $offers->distinct()->get(['website']);
with pivot columns (just wanted to show my full query)
$offers->orderBy($sortBy, $orderBy)->paginate($perPage)->map(function ($offer) {
if (!empty($offer->users)) {
$manager = $publisher = '';
foreach ($offer->users as $user) {
$manager = $user->pivot->user_id;
$publisher = $user->pivot->publisher_id;
}
$offer->manager = $manager;
$offer->publisher = $publisher;
}
return $offer;
});
Return
return response()->json([
'offers' => $offers,
'websites' => $websites
], 200);
hope my question will make sense.
Thanks.
You should run getCollection() before mapping to get the paginator's underlying collection.
(https://laravel.com/api/7.x/Illuminate/Pagination/LengthAwarePaginator.html#method_getCollection)
$offers->orderBy($sortBy, $orderBy)->paginate($perPage)
->getCollection()
->map(function ($offer) {
// ...
return $offer;
});
I'm answering based on it being $offers:
Your usage of map() is copying the modified results of your paginate() call to a new collection and that collection does not include the pagination information. That's why you no longer have pagination information.
Since there result of paginate() is already a usable collection, you can use each() instead of map() which will alter the objects in-place.

laravel 5.7 : how to filter search?

I'm trying to make an advanced search by multiple inputs :
$orders = new Order;
if ($request->has('client_id')) {
$orders->where('client_id' ,$request->client_id);
}
if ($request->has('city_id')) {
$orders->where('city_id', $request->city_id);
}
dd($orders->get());
but this code returns all database records.
And if i use get method with the query in one line it works just fine!
$orders->where('city_id', $request->city_id)->get();
dd($orders);
Any ideas?
a simple way, just do this
$query = Order::query();
if ($request->has('client_id')) {
$query->where('client_id' ,$request->client_id);
}
if ($request->has('city_id')) {
$query->where('city_id', $request->city_id);
}
dd($query->get());
You should set the variable $order each time you add the clause e.g:
$orders = new Order;
if ($request->has('client_id')) {
$orders = $orders->where('client_id' ,$request->client_id);
}
if ($request->has('city_id')) {
$orders = $orders->where('city_id', $request->city_id);
}
// then run $orders->get(); or other stuff
The new values of $orders would have been updated accordingly.
Its quite interesting that way you declared the model caused the problem. By creating a new instance of Order using new keyword, the model has not recognize the default query builder (for some reason), meaning that dump()ing the model there's no wheres array to push the new where into.
Other ways you could do it is to have an initial query e.g
Order::whereNotNull('id');
//Otherwise as suggested use `->query()` or `newQuery()`
Mind you query() is like an alias of newQuery() except that query() creates a fresh instance of the model before calling newQuery().
You can instead use getQuery() to retrieve the $query property of the model instead of the two as you don't need to re-instantiate the builder.
Therefore, you can have:
$orders = Order::getQuery();
if ($request->has('client_id')) {
$orders->where('client_id' ,$request->client_id);
}
if ($request->has('city_id')) {
$orders->where('city_id', $request->city_id);
}
// then run $orders->get(); or other stuff
PS: I tested this on L5.4
try to initialize the query
$query = Order::select("*");
if ($request->has('client_id')) {
$query->where('client_id' ,$request->client_id);
}
if ($request->has('city_id')) {
$query->where('city_id', $request->city_id);
}
dd($query->get());

Nested loop caused slow in performance

foreach ($id_prs as $ids) {
list($id_pr, $id_cart) = explode('-', $ids);
foreach ($id_cart as $id) {
$r = Cart::find($id);
/// column to update value
$r->save();
}
}
This is how my loop looks like.
E.g. $id_prs consist of 10 data, while each id_prs may consist of 20 data etc.
From there, i found will take longer time to loop when a lots of data from each $id_cart.
How can i solve the performance issue, is there any solution?
This is known as n+1 problem, every time you iterate inside foreach additional query is created.
So this line is the evil, because it's querying data every iteration.
$r = Cart::find($id);
You can make it better like this:
$cart = Cart::all();
foreach ($id_prs as $ids) {
list($id_pr, $id_cart) = explode('-', $ids);
foreach ($id_cart as $id) {
// Get it from collection rather than query it from database
$cart->where('id', $id)->first()->save();
}
}
Where you acctualy query all carts inside variable as a collection, and you just manipulating with that collection localy without touching database (only when you hit save method).
Your code and variables are not clear enough. But a better solution is:
foreach ($id_prs as $ids) {
list($id_pr, $id_cart) = explode('-', $ids);
$items = Cart::whereIn('id', $id_cart) -> get();
foreach($items as $item) {
Cart::where('id', $item -> id) -> limit(1) -> update([
// Columns to update
]);
}
}
If you want to update same value for all the records
$allIds = [];
foreach ($id_prs as $ids) {
list($id_pr, $id_cart) = explode('-', $ids);
foreach ($id_cart as $id) {
$allIds[] = $id;
}
}
$cart = Cart::whereIn('id',$allIds)->update(['columns'=>'value']);

Resources