Laravel simplePaginate() for Grouped Data - laravel

I have the following query.
$projects = Project::orderBy('created_at', 'desc');
$data['sorted'] = $projects->groupBy(function ($project) {
return Carbon::parse($project->created_at)->format('Y-m-d');
})->simplePaginate(5);
When I try to paginate with the simplePaginate() method I get this error.
stripos() expects parameter 1 to be string, object given
How can I paginate grouped data in this case?

The created_at attribute is already casted as a Carbon Object (by default in laravel models). that's why you are getting that error. Try this:
$projects = Project::orderBy('created_at', 'desc')->get();
$data['sorted'] = $projects->groupBy(function ($project) {
return $project->created_at->format('Y-m-d');
})->simplePaginate(5);
this answer is just for the error you're getting. now if you want help with the QueryBuilder, can you provide an example of the results you're expecting to have and an example of the database structure ?

The pagination methods should be called on queries instead of collection.
You could try:
$projects = Project::orderBy('created_at', 'desc');
$data['sorted'] = $projects->groupBy('created_at');

The problem was solved. I was create custom paginator via this example:
https://stackoverflow.com/a/30014621/6405083
$page = $request->has('page') ? $request->input('page') : 1; // Use ?page=x if given, otherwise start at 1
$numPerPage = 15; // Number of results per page
$count = Project::count(); // Get the total number of entries you'll be paging through
// Get the actual items
$projects = Project::orderBy('created_at', 'desc')
->take($numPerPage)->offset(($page-1)*$numPerPage)->get()->groupBy(function($project) {
return $project->created_at->format('Y-m-d');
});
$data['sorted'] = new Paginator($projects, $count, $numPerPage, $page, ['path' => $request->url(), 'query' => $request->query()]);

simplePaginate Method is exist in the path below:
Illuminate\Database\Eloquent\Builder.php::simplePaginate()

Related

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.

Eloquent accessing array inside object

I have a get formule that returns some nested relationships in an array. I was wondering how to access them in a where statement.
The initial get
$taken = UserWork::with('work.place')
->with('user')
->with('work.timeslot')
->get();
I tried something like this
$taak = $taken->where('work.timeslot[0].start_hour',"17:00:00")->first();
json result from $taken
Using with will endup with two queries. if you want to bring the user with timeslot null then there no need to add whereHas
$callback = function($query) {
$query->where('start_hour',"17:00:00");
};
$taken = UserWork::whereHas('work.timeslot', $callback)
->with(
['work.place', 'user', 'work.timeslot' => $callback]
)->get();

Laravel alternative to paginate on collection?

On my website, I have Submissions, and submissions can have comments.
Comments can have upvotes and downvotes, leading to a total "score" for the comment.
In this example, before passing the comments to the view, I sort them by score.
$comments = Comment::where('submission_id', $submission->id)->where('parent_id', NULL)->get();
$comments = $comments->sortByDesc(function($comment){
return count($comment['upvotes']) - count($comment['downvotes']);
});
This works fine. The higher the score of a comment, the higher it is sorted.
However, I want to paginate these results.
If I do ->paginate(10) instead get(), the following sortByDesc will only sort those 10 results.
So logically I would want to add the paginator after the sortByDesc like so:
$comments = $comments->sortByDesc(function($comment){
return count($comment['upvotes']) - count($comment['downvotes']);
})->paginate(10);
However this will return the error:
Method Illuminate\Database\Eloquent\Collection::paginate does not
exist.
as expected.
My question is, what is the alternative to using paginate in this situation?
EDIT:
When trying the response of #party-ring (and switching the double quotes and single quotes) I get the following error:
SQLSTATE[42000]: Syntax error or access violation: 1064 You have an
error in your SQL syntax; check the manual that corresponds to your
MariaDB server version for the right syntax to use near '["upvotes"])
- count($comment["downvotes"]) desc limit 10 offset 0' at line 1 (SQL: select * from comments where submission_id = 1 and parent_id is
null order by count($comment["upvotes"]) -
count($comment["downvotes"]) desc limit 10 offset 0)
You are trying to paginate after the get, the solution i try on my website is this and it works
$users = User::where('votes', '>', 100)->get();
$page = Input::get('page', 1); // Get the ?page=1 from the url
$perPage = 15; // Number of items per page
$offset = ($page * $perPage) - $perPage;
return new LengthAwarePaginator(
array_slice($users->toArray(), $offset, $perPage, true), // Only grab the items we need
count($users), // Total items
$perPage, // Items per page
$page, // Current page
['path' => $request->url(), 'query' => $request->query()] // We need this so we can keep all old query parameters from the url
);
You could add a macro:
if (!Collection::hasMacro('paginate')) {
Collection::macro('paginate', function ($perPage = 25, $page = null, $options = []) {
$options['path'] = $options['path'] ?? request()->path();
$page = $page ?: (Paginator::resolveCurrentPage() ?: 1);
return new LengthAwarePaginator(
$this->forPage($page, $perPage)->values(),
$this->count(),
$perPage,
$page,
$options
);
});
}
Then you can use a collection to paginate your items:
collect([1,2,3,4,5,6,7,8,9,10])->paginate(5);
See Extending Collections under Introduction
Give this a try:
$comments = Comment::where('submission_id', $submission->id)
->where('parent_id', NULL)
->orderBy(DB::raw("count($comment['upvotes']) - count($comment['downvotes'])"), 'desc')
->paginate(10);`
SortBy returns a Collection, whereas you can only call paginate on an instance of QueryBuilder. OrderBy should return an instance of QueryBuilder, and you should be able to do the subtraction using a DB::raw statement.
** edit
I have just read about orderByRaw, which might be useful in this scenario:
$comments = Comment::where('submission_id', $submission->id)
->where('parent_id', NULL)
->orderByRaw('(upvotes - downvotes) desc')
->paginate(10);`
You might have to play around a bit with your subtraction above as I don't know the structure of your comments table.
A couple of links which might be useful:
laravel orderByRaw() on the query builder
https://laraveldaily.com/know-orderbyraw-eloquent/

Laravel pluck an array from nested relationship

I need to get only the roomnumber arrays returned from the following query:
$roomnumbers = Room::with(['floorroomcount' => function($query){
$query->with('roomnumber')->get();
}])->where('roomtype_id', $roomtype_id)->get();
Tried:
The follow pluck is returning floorroomcount
$roomnumbers->pluck('floorroomcount');
but i need roomnumber array, how can i get?
This gives you all roomnumber results in one collection:
$roomnumbers->pluck('floorroomcount')->collapse()->pluck('roomnumber')->collapse();
You may shorten #Jonas Staudenmeir's answer like so:
$roomnumbers->pluck('floorroomcount.*.roomnumber.*')->collapse();
pluck('*') is essentially the same as collapse() in this particular context.
This is working, but with many loop and echoing directly, if anything can be simplified please let me know :
$roomnumbers = Room::with(['floorroomcount.roomnumber'])->where('roomtype_id', $roomtype_id)->get();
$floorroomcounts = $roomnumbers->pluck('floorroomcount');
$records = $floorroomcounts->map(function($floorroomcount, $value){
return $floorroomcount->pluck('roomnumber')->flatten();
})->values()->all();
foreach($records as $record){
foreach($record as $row){
echo '<option value='.$row->id.'>'.$row->roomnumber.'</option>';
}
}
//return response()->json($roomnumbers);
Try,
$roomnumbers = Room::with(['floorroomcount' => function($query){
$query->with('roomnumber')->get();
}])
->where('roomtype_id', $roomtype_id)
->get();
$records = $roomnumbers->map(function($element, $value){
return $element->map(function($e, $v){
return $e->roomnumber;
});
})->values()->all();
map() is a Laravel collection method so you need to import the collection facade on the top of the controller like: use Illuminate\Support\Collection;
In Laravel 5.1 and + you can use flatten() on collection.
method flattens a multi-dimensional collection into a single dimension:
$roomnumbers->flatten()->pluck('floorroomcount');

Laravel Add Additional Rows to an Eloquent Object

I want to add an "On This Day" feature which should display records from The Previous Years. I have some Entries, all of them have a 'date' attribute. This is what I've been trying so far:
public function filterByDay($id){
$entries = Entry::where('id', $id)->get();
$currentDay = $entries[0]->date;
$oldestYear = Entry::orderBy('date','asc')->first()->date;
$previousYear = $currentDay;
while($previousYear >= $oldestYear ){
$previousYear = $currentDay->subYear();
$entries->push(Entry::where('date', $previousYear)->get());
}
return view('home')->with(compact('entries'));
}
I must send a Collection of "Entry" type from this controller method so that I can use $entry->title etc in the view. But whenever I'm using $entries->push(...) , I'm getting a Collection instance, not Entry instance. How can I convert the Collection back into Entry instance? Or what is the alternative? I'm using Laravel 5.5. Some help will be much appreciated.
You can combine whereDay, whereYear and whereMonth methods to achieve it in one liner:
$entries = Entry::where('id', $id)->get();
$today = Carbon\Carbon::now();
$oldestYear = Entry::orderBy('date','asc')->first()->date;
$allEntries = Entry::whereDay('date', $today->day)
->whereYear('date', '>=', $oldestYear)
->whereMonth('date', $today->month)
->get();
return view('home')->with(compact('allEntries'));

Resources