Eloquent - Group By Month, Year and Paginate - php

I am trying to list entries in a table by Month, Year:
May, 2015
Item 1
Item 2
Item 3
June, 2015
Item 1
etc..
I have achieved this with the following code but I would also like to paginate the results. I have tried many different things but none of them seem to work, I am using Laravel 5.
$events = App\Events->orderBy('start', 'asc')->get()->groupBy(function($date) {
return $date->start->format('F, Y');
});
Here is the output for the above query:
{
"April, 2015": [
{
"id": "10",
"event_type_id": "1",
"user_id": "1",
"title": "Testing",
"slug": "testing",
"start": "2015-04-23 17:00:00",
"end": "2015-04-23 17:40:00",
"description": "<h1>MEETING!</h1><p>Let's try this in HTML!<br></p>",
"created_at": "2015-04-19 14:18:33",
"updated_at": "2015-04-21 22:07:41",
"type": {
"id": "1",
"name": "General",
"slug": "general",
"created_at": "2015-04-18 11:24:00",
"updated_at": "2015-04-18 11:24:04"
}
}
],
"May, 2015": [
{
"id": "12",
"event_type_id": "1",
"user_id": "1",
"title": "Test Event",
"slug": "test-event",
"start": "2015-05-15 18:00:00",
"end": null,
"description": "<p>This is a test event with just a start time</p>",
"created_at": "2015-04-21 14:59:56",
"updated_at": "2015-05-02 18:37:53",
"type": {
"id": "1",
"name": "General",
"slug": "general",
"created_at": "2015-04-18 11:24:00",
"updated_at": "2015-04-18 11:24:04"
}
},
{
"id": "9",
"event_type_id": "1",
"user_id": "1",
"title": "Monthly Meeting",
"slug": "monthly-meeting",
"start": "2015-05-23 14:00:00",
"end": "2015-04-16 20:00:00",
"description": "<p>It's a long monthly meeting!</p>",
"created_at": "2015-04-19 13:13:45",
"updated_at": "2015-05-03 08:45:56",
"type": {
"id": "1",
"name": "General",
"slug": "general",
"created_at": "2015-04-18 11:24:00",
"updated_at": "2015-04-18 11:24:04"
}
}
],
"June, 2015": [
{
"id": "11",
"event_type_id": "1",
"user_id": "1",
"title": "Another Meeting Saved",
"slug": "another-meeting-saved",
"start": "2015-06-19 18:00:00",
"end": null,
"description": "<p>It's another meeting afterall</p>",
"created_at": "2015-04-20 15:03:30",
"updated_at": "2015-05-03 08:46:19",
"type": {
"id": "1",
"name": "General",
"slug": "general",
"created_at": "2015-04-18 11:24:00",
"updated_at": "2015-04-18 11:24:04"
}
}
]
}
With LengthAwarePaginator -
$paginator = new LengthAwarePaginator($events, count($events), 1);
return $paginator;
This returns the paginator but the data is the same - meaning the same result set as without the paginator, when I'd expect only one record to be returned per page:
[{
"total": 3,
"per_page": 1,
"current_page": 1,
"last_page": 3,
"next_page_url": "/?page=2",
"prev_page_url": null,
"from": 1,
"to": 3,
"data": {
"data" : ".. same as above"
}
}]

With aggregates you need to implement your own custom paginator, as stated by docs:
Note: Currently, pagination operations that use a groupBy statement
cannot be executed efficiently by Laravel. If you need to use a
groupBy with a paginated result set, it is recommended that you query
the database and create a paginator manually.
See this posts to manually implement pagination:
Laravel 5 - Manual pagination
Manually Creating a Paginator (Laravel 5)

Many people have pointed me to a widely mentioned paragraph in the Laravel documentation,
Note: Currently, pagination operations that use a groupBy statement
cannot be executed efficiently by Laravel. If you need to use a
groupBy with a paginated result set, it is recommended that you query
the database and create a paginator manually.
Not terribly helpful, since I cannot find any example in the documentation as to exactly how to create a manual paginator using the results of an Eloquent query. So, here is what I was able to come up with. Note that you must use ->take() and ->offset() in the query, otherwise you will end up with the same results on every page (this is where I was getting stuck).
<?php
// routes.php
use Illuminate\Pagination\LengthAwarePaginator as Paginator;
get('test', function(Request $request) {
$page = $request->has('page') ? $request->input('page') : 1; // Use ?page=x if given, otherwise start at 1
$numPerPage = 2; // Number of results per page
$eventType = EventType::find(1); // Not relevant to pagination
$count = $eventType->memberEvents()->count(); // Get the total number of entries you'll be paging through
// Get the actual items
$events = $eventType->memberEvents()->orderBy('start', 'asc')
->take($numPerPage)->offset(($page-1)*$numPerPage)->get()->groupBy(function($date) {
return $date->start->format('F, Y');
});
// Create the paginator with Illuminate\Pagination\LengthAwarePaginator as Paginator
// Pass in the variables supplied above, including the path for pagination links
$paginator = new Paginator($events, $count, $numPerPage, $page, ['path' => $request->url(), 'query' => $request->query()]);
return $paginator;
});

If you want to add groupBy to your data the you should use LengthAwarePaginator object as updated in laravel 5
Try this,
use Illuminate\Pagination\LengthAwarePaginator as Paginator;
$page = ($request->input('page') != null) ? $request->input('page') : 1;
$perPage = 1;
$sliced = array_slice($data, 0, 5); //you can these values as per your requirement
$paginator = new Paginator($sliced, count($data), $perPage, $page,['path' => url()->current(),'query' => $request->query()]);
return $paginator;
$data is your data object and fifth parameters are for next and prev urls
Refer this for more information about paginator,
https://laravel.com/api/5.5/Illuminate/Database/Eloquent/Builder.html#method_paginate

As stated in the Laravel docs...Note: Currently, pagination operations that use a groupBy statement cannot be executed efficiently by Laravel. If you need to use a groupBy with a paginated result set, it is recommended that you query the database and create a paginator manually. Docs

Related

How to use the cursorPaginate/CursorPaginator on a custom built array

$filtered_restros= new \Illuminate\Pagination\CursorPaginator($restaurants, 1);
$data['message'] = "Restaurants fetched successfully";
$data['status'] = "success";
$data['data'] = $filtered_restros;
I did this code and i'm getting fallowing results
{
"message": "Restaurants fetched successfully",
"status": "success",
"data": {
"data": [
{
"id": 5,
"name": "HB Town Restro",
"email": "2321efsd#gmail.com",
"phone_number": "12",
"mobile_number": "454554354",
"address": "hjgjghioiu",
"pincode": "4400255241653",
"latitude": "21.150964",
"longitude": "79.150516",
"restro_type": 1,
"cuisine_type": "American",
"book_now_btn": 1,
"queue_btn": 0,
"is_saved": 0,
"restro_cuisines": [
{
"cuisine_id": 5,
"cuisine_name": "North indian"
}
],
"seating_options": [
"Cafeteria",
"fghfghf",
"Cafeterias",
"indoor"
],
"description": "gj",
"menu_category": "5",
"parking": "Onsite",
"image": "http://localhost/projectx/public/restaurants/hb.jpg",
"status": 1
}
],
"path": "/",
"per_page": 1,
"next_page_url": "/?cursor=eyJfcG9pbnRzVG9OZXh0SXRlbXMiOnRydWV9",
"prev_page_url": null
}
}
after hitting the "next_page_url"
http://localhost/projectx/api/user/explore_all_restaurants?cursor=eyJfcG9pbnRzVG9OZXh0SXRlbXMiOnRydWV9
this printing same results as above means cursor pagination is not working.
When i did same thing using eloquent
$users = DB::table('restros')->orderBy('id')->cursorPaginate(15);
its working properly and pagination is also working and full next page url are automatically generating. but its using eloquent and i want to do the cursor pagination on my processed array and its not working properly.
In short i'm asking how to do cursor pagination on custom array.

Getting Duplicate Data on Response On Laravel Eloquent

I am new in laravel in php. So it might be very silly mistake. I have song table and song categories table. I am trying to fetch all category with their respective songs. I have implemented larvel eloquent one to many relationship between song category and song.
Here is my code of fetching data:
public function getSongCategoriesWithSongs(){
$json_array = array();
$song_categories = SongCategory::all();
foreach ($song_categories as $item) {
# code...
$json = [];
$json['category'] = $item;
$json['songs'] = $item->songs;
array_push($json_array,$json);
}
return $json_array;
}
Here is response:
[{
"category": {
"id": 1,
"title": "Rock",
"created_at": "2020-12-20T02:58:32.000000Z",
"updated_at": "2020-12-20T02:58:32.000000Z",
"songs": [{
"id": 1,
"title": "Mere Mehboob",
"thumbnail": "https:\/\/static.toiimg.com\/photo\/msid-71407401\/71407401.jpg?108311",
"song_category_id": 1,
"stream_link": "https:\/\/2u039f-a.akamaihd.net\/downloads\/ringtones\/files\/mp3\/mere-mehboob-qayamat-hogi-52150.mp3",
"created_at": "2020-12-20T13:26:30.000000Z",
"updated_at": "2020-12-20T13:26:30.000000Z"
}, {
"id": 2,
"title": " Taaron Ke Shehar",
"thumbnail": "https:\/\/static.toiimg.com\/photo\/msid-71407401\/71407401.jpg?108311",
"song_category_id": 1,
"stream_link": "https:\/\/newmp3ringtones.net\/assets\/sass\/Ringtones\/TaaronKeSheharRingtoneByNehaKakkarJubinNautiyal2145436126.mp3",
"created_at": null,
"updated_at": null
}, {
"id": 3,
"title": "Bewafa Tera Masoom Chehra",
"thumbnail": "https:\/\/static.toiimg.com\/photo\/msid-71407401\/71407401.jpg?108311",
"song_category_id": 1,
"stream_link": "https:\/\/newmp3ringtones.net\/assets\/sass\/Ringtones\/BewafaTeraMasoomChehraRingtoneByJubinNautiyal352778308.mp3",
"created_at": null,
"updated_at": null
}]
}
}, {
"songs": [{
"id": 1,
"title": "Mere Mehboob",
"thumbnail": "https:\/\/static.toiimg.com\/photo\/msid-71407401\/71407401.jpg?108311",
"song_category_id": 1,
"stream_link": "https:\/\/2u039f-a.akamaihd.net\/downloads\/ringtones\/files\/mp3\/mere-mehboob-qayamat-hogi-52150.mp3",
"created_at": "2020-12-20T13:26:30.000000Z",
"updated_at": "2020-12-20T13:26:30.000000Z"
}, {
"id": 2,
"title": " Taaron Ke Shehar",
"thumbnail": "https:\/\/static.toiimg.com\/photo\/msid-71407401\/71407401.jpg?108311",
"song_category_id": 1,
"stream_link": "https:\/\/newmp3ringtones.net\/assets\/sass\/Ringtones\/TaaronKeSheharRingtoneByNehaKakkarJubinNautiyal2145436126.mp3",
"created_at": null,
"updated_at": null
}, {
"id": 3,
"title": "Bewafa Tera Masoom Chehra",
"thumbnail": "https:\/\/static.toiimg.com\/photo\/msid-71407401\/71407401.jpg?108311",
"song_category_id": 1,
"stream_link": "https:\/\/newmp3ringtones.net\/assets\/sass\/Ringtones\/BewafaTeraMasoomChehraRingtoneByJubinNautiyal352778308.mp3",
"created_at": null,
"updated_at": null
}]
}, {
"category": {
"id": 2,
"title": "Soft",
"created_at": null,
"updated_at": null,
"songs": []
}
}, {
"songs": []
}]
As you can see songs get repeated.
UPDATE
Solved using eager loading
public function getSongCategoriesWithSongs(){
return SongCategory::with('songs')->get();
}
But don't know why the foreach method not working.
Try this code
public function getSongCategoriesWithSongs(){
$json_array = array();
$song_categories = SongCategory::all();
foreach ($song_categories as $item) {
$json_array[] = ['category' => $item, 'songs' => $item->songs] ;
}
return $json_array;
}
The problem is that you assign the same relation twice.
Each SongCategory already has a collection of songs inside.
So in your foreach block, you assign a category with $json['category'] => $item which will load all related songs and pass them to the final JSON object. And you duplicate this by passing the next item $json['songs'] = $item->songs to the same array. Default Laravel behavior will be to fetch all related objects and transform them into JSON.
I would suggest you to use Laravel resources to return JSON objects with exact shapes: API Resources.
You can fix your code block without eager loading by removing $json['songs'] = $item->songs assignment.
Eager loading works because you passed all your objects only once.

How to remove a key from an object not array in laravel

I get data from database using this query
$cats = Category::with(['cat_trans' => function($q) use($lang){
$q->where('lang_code', $lang);
}])->with(['cat_prod' => function($query) use ($lang,$currency){
$query->with(['pro_trans' => function ($q) use ($lang){
$q->where('lang_code', $lang);
}]);
////////////
$query->with(['pro_price' => function ($q) use ($currency){
$q->with('currency_code')->where('cur_code', $currency);
}]);
///////////
}])->whereHas('account_type', function($qr) use ($account_type){
$qr->where('account_type_id', $account_type);
})->get();
I'm trying to remove the empty objects from the result, when I tried this I got the following response
{
"1": {
"id": 1,
"parent_id": null,
"order": 1,
"name": "Moblie",
"slug": "mobile-1",
"created_at": "2018-07-08 09:41:08",
"updated_at": "2018-07-08 10:30:17",
"cat_trans": [
{
"id": 1,
"category_id": 1,
"field": "title",
"value": "Mobile",
"lang_code": "en",
"created_at": "2018-07-08 09:51:59",
"updated_at": "2018-07-08 09:51:59"
},
{
"id": 2,
"category_id": 1,
"field": "desc",
"value": "smart",
"lang_code": "en",
"created_at": "2018-07-08 09:52:41",
"updated_at": "2018-07-08 09:52:41"
},
{
"id": 12,
"category_id": 1,
"field": "slug",
"value": "mobile-1",
"lang_code": "en",
"created_at": null,
"updated_at": null
}
],
}
}
I want to remove the key "1" from all the responses.
I used unset to get this value using this code
foreach ($cats as $k) {
if (count($k->cat_prod) == 0) {
unset($cats[$va]);
}
$va++;
}
Then I tried using array_values but it displays that I can just use array_values on an array not an object.
You can remove key "1" by simply doing
array_values((array)$cats)
You were receiving error for this function like "Can Only be used on array" was due to Laravel Query returns object , so you just need to do type conversion on it.
$cats->toArray() if u need either to convert object to array or convert with some specific conditions
Your output looks like JSON so you can do this to eliminate the 1:
json_encode( $cats->{1} );
You can use laravel simple method mapWithKeys()
$cats = $cats->mapWithKeys(function ($item) {
return $item > 2;
});
$keyed->all();

Laravel many to many with 3 models

I've been struggling with a issue the last couple of days. I've just started using Laravel and are getting real fond of the Eloquent-syntax!
But there's a issue when I'm trying to get the correct relation between three models.
I've got this setup:
programs table contains
event_id
user_id
role_id
In my Event-model I've got
public function programs(){
return $this->hasMany(Program::class);
}
In my User-model I've got
public function programs(){
return $this->hasMany(Program::class);
}
In my Role-model I've got
public function programs(){
return $this->hasMany(Program::class);
}
And my Program-model contains
public function event(){
return $this->belongsTo(Event::class);
}
public function user(){
return $this->belongsTo(User::class);
}
public function role(){
return $this->belongsTo(Role::class);
}
And I need to get the following result
Events -> Users -> Role
In my controller I've got
$events = Event::with('programs.role.programs.user')->get();
Which is producing this:
{
"id": 2,
"name": "Concert: Freddy Kalas",
"description": "Freddy Kalas konsert",
"user_id": 2,
"time_from": "12.04.2017 22:00:00",
"time_to": "12.04.2017 23:00:00",
"created_at": "2017-03-20 18:28:44",
"updated_at": "2017-03-20 18:28:44",
"programs": [
{
"id": 2,
"event_id": 2,
"user_id": 2,
"role_id": 1,
"created_at": null,
"updated_at": null,
"role": {
"id": 1,
"name": "Camera operator",
"description": "Operates ordinary cameras or PTZ cameras",
"created_at": "2017-03-20 20:11:06",
"updated_at": "2017-03-20 20:11:06",
"programs": [
{
"id": 1,
"event_id": 3,
"user_id": 2,
"role_id": 1,
"created_at": null,
"updated_at": null,
"user": {
"id": 2,
"name": "Dummy Dum",
"email": "dummy#example.com",
"created_at": "2017-03-20 16:45:09",
"updated_at": "2017-03-20 16:45:09"
}
},
{
"id": 2,
"event_id": 2,
"user_id": 2,
"role_id": 1,
"created_at": null,
"updated_at": null,
"user": {
"id": 2,
"name": "Dummy Dum",
"email": "dummy#example.com",
"created_at": "2017-03-20 16:45:09",
"updated_at": "2017-03-20 16:45:09"
}
}
]
}
}
]
},
{
"id": 3,
"name": "Prøveproduksjon",
"description": "Prøveproduksjon med video og lyd",
"user_id": 1,
"time_from": "11.04.2017 13:00:00",
"time_to": "11.04.2017 17:00:00",
"created_at": "2017-04-03 17:12:37",
"updated_at": "2017-04-03 17:12:37",
"programs": [
{
"id": 1,
"event_id": 3,
"user_id": 2,
"role_id": 1,
"created_at": null,
"updated_at": null,
"role": {
"id": 1,
"name": "Camera operator",
"description": "Operates ordinary cameras or PTZ cameras",
"created_at": "2017-03-20 20:11:06",
"updated_at": "2017-03-20 20:11:06",
"programs": [
{
"id": 1,
"event_id": 3,
"user_id": 2,
"role_id": 1,
"created_at": null,
"updated_at": null,
"user": {
"id": 2,
"name": "Dummy Dum",
"email": "dummy#example.com",
"created_at": "2017-03-20 16:45:09",
"updated_at": "2017-03-20 16:45:09"
}
},
{
"id": 2,
"event_id": 2,
"user_id": 2,
"role_id": 1,
"created_at": null,
"updated_at": null,
"user": {
"id": 2,
"name": "Dummy Dum",
"email": "dummy#example.com",
"created_at": "2017-03-20 16:45:09",
"updated_at": "2017-03-20 16:45:09"
}
}
]
}
}
]
}
I can't get the models to relate to eachother - it seems. The preferred result that I want is that all the Events is tied with many users - and all the users for that event to be tied to one role. How can i accomplish this with Laravel and Eloquent?
Thanks for any responses!
EDIT:
To fetch the data i use the following code to generate the results above
$events = Event::with('programs.role.programs.user')->get();
Content of the programs table
# id, event_id, user_id, role_id, created_at, updated_at
'1', '3', '2', '1', NULL, NULL
'2', '2', '2', '1', NULL, NULL
This would mean that the user with an ID of 2 is only associated with event 3 with an role of 1 and event 2 with a role of 1. As you can see from the results both events has both role 1 and 2. Sorry for bad explanation..
Probably just try to make it one at a time and figure out what is returned.
#Fredrik Angell Moe said:
Got it working now by using:
$events = Event::with('programs.role')->with('programs.user')->get();
user with an ID of 2 is only associated with event 3 with an role of 1
and event 2 with a role of 1. As you can see from the results both
events has both role 1 and 2.
You are using two values. One is id of user and second is id of event.
Laravel can define realtion between only two models. That means in you query you are using the programe_id and event_id.
But user_id is getting neglected. So it will just add the user related to the model instead of filtering with the user_id.
The best way to solve this problem is instead of using the relation go for the raw query where you need to get relation between more than two models.

Laravel 5.2: extracting nested collection

I am working on Laravel 5.2 application. I'm having an issue in extracting the nested collection from the parent collection. I'm trying to get all the posts from the users whom I am following like this:
$posts = \Auth::user()->following()->get()->each(function($following){
return $following->posts;
});
But it is returning me a collection of the users I am following and the posts collection is nested in this collection, such that:
[
{
"id": 2,
"name": "John Doe",
"username": "john",
"email": "johny#example.com",
"created_at": "2016-03-08 11:06:45",
"updated_at": "2016-03-08 11:06:45",
"pivot": {
"follower_id": 1,
"followee_id": 2
},
"posts": [
{
"id": 1,
"user_id": 2,
"title": "Post title 1",
"created_at": "2016-03-08 11:09:22",
"updated_at": "2016-03-08 11:09:22"
},
{
"id": 3,
"user_id": 2,
"title": "Post title 2",
"created_at": "2016-03-09 08:04:18",
"updated_at": "2016-03-09 08:04:18"
}
]
}
]
How can I get all the posts from the all of the users I'm following as a collection?
Any help will be appreciated
$posts = [];
\Auth::user()->following()->get()->each(function($following) use(&$posts){
array_push($posts, $following->posts)
});
Hope this helps.

Categories