Laravel 5 - multi-dimensional groupBy, orderBy & p

2019-06-02 05:02发布

问题:

I have a Post model and I am trying to ->paginate(), ->groupBy() and ->orderBy().

public function index()
{
    $posts = Post::where('verified', '1')
            ->orderBy('created_at','desc')
            ->groupBy('topic', 'publisher_id')
            ->paginate(5);

    // dd($posts);
}

Meanwhile, data in Database looks like this:

|  id  |  verified  |  topic  | publisher_id |  body  | created_at |
|  25  |      1     |  Forest |       3      |   EE   |  10.12.50  |
|  24  |      1     |  Forest |       3      |   DD   |  10.11.40  |
|  23  |      1     |  Forest |       3      |   CC   |  10.10.30  |

|  22  |      1     |  Dance  |       2      |   BB   |   9.50.50  |
|  21  |      1     |  Dance  |       2      |   AA   |   9.40.40  |

|  20  |      1     |  Music  |       1      |   ZZ   |   9.30.30  |
|  19  |      1     |  Music  |       1      |   XX   |   9.20.20  |
|  18  |      1     |   Art   |       1      |   YY   |   9.10.10  |
|  17  |      1     |   Art   |       1      |   WW   |   9.00.00  |

|  16  |      1     |   Ski   |       2      |   KK   |   7.00.00  |

When I uncomment the the dd() and run the code, I get this log:

 LengthAwarePaginator {
                       ...
                      items : {
                           items : {
                                   0 => Post{..}
                                   1 => Post{..}
                                   2 => Post{..}
                                   3 => Post{..}
                                     ...
                                   }
                              }
                        ...
                       }

0 => Post{#249} : "published_by: "3", "body": "CC", "created_at": "10.10.30"

1 => Post{#250} : "published_by: "1", "body": "XX", "created_at": "9.20.20"

2 => Post{#251} : "published_by: "1", "body": "WW", "created_at": "9.00.00"

3 => Post{#252} : "published_by: "2", "body": "KK", "created_at": "7.00.00"

It looks so weird for a reason. It did groupBy for user-3, but not for others. Also, it's pulling the earliest created at rather than latest. Changing desc to asc like ->orderBy('created_at', 'asc')would put everything completely out of track.

In other words, returning 'CC' for user-3, Forest instead of 'EE' for user-3, Forest

Then I thought maybe it's the ->paginate(5) messing things up.

public function post()
{
    $posts = Post::where...
                        ...
            ->paginate(5);

    $postsTry = Post::where('verified', '1')
            ->orderBy('created_at','desc')
            ->groupBy('topic', 'publisher_id')
            ->get();

     // dd($postsTry);
 }

I get a Collection with only the items like above object in it. (0 => Post{..}, 1 => Post{..}, 2 => Post{..}).

It's grouping data as being earliest first rather than latest first. What I am missing? What is the thing I am doing wrong?


To wrap up, note that what I want to get is:

'EE' for user-3, Forest

'BB' for user-2, Dance

'ZZ' for user-1, Music

'YY' for user-1, Art

回答1:

Removed my previous answer but will still include the note from Laravel Pagination about chaining groupBy() with paginate().

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.

UPDATE

Due to your unusual date format for the created_at field, you'll need integer casted values for it to used by the order by statement properly (as an integer, not a string). Here's a working example:

Post::selectRaw("*, CAST(REPLACE(created_at, '.', '') AS UNSIGNED INTEGER) ca")
    ->where('verified', '1')
    ->orderBy('ca', 'desc')
    ->groupBy('topic', 'publisher_id')
    ->paginate();


回答2:

You'll need to use a subquery (or another join method) to get the top item in your group. See Getting The Rows Holding the Group-wise Maximum of a Certain Column

Removed my example.