Laravel sortBy paginate

У меня есть таблица постов и таблица комментариев, комментарий принадлежит посту, и у меня есть настройка отношений в модели Пост и комментарий. Я сортировал посты по количеству комментариев каждого поста так:

      $posts = Post::with('comments')->get()->sortBy(function($post) {
           return $post->comments->count();
      });

Что мне интересно, так это как я могу разбить эти отсортированные посты на страницы?

      $posts = Post::with('comments')->get()->sortBy(function($post) {
           return $post->comments->count();
      })->paginate(20);

не работает и выдает ошибку, в которой говорится, что paginate - неопределенный метод.

3 ответа

Решение

Я не знаю, можете ли вы сделать это с помощью Eloquent, но вы можете использовать для этого соединение:

$posts = Post::leftJoin('comments','posts.id','=','comments.post_id')->
               selectRaw('posts.*, count(comments.post_id) AS `count`')->
               groupBy('posts.id')->
               orderBy('count','DESC')->
               paginate(20);

Однако, похоже, что в этом случае все записи берутся из базы данных и отображаются только записи из paginator, поэтому, если у вас много записей, это пустая трата ресурсов. Кажется, вы должны сделать ручную нумерацию страниц для этого:

$posts = Post::leftJoin('comments','posts.id','=','comments.post_id')->
               selectRaw('posts.*, count(comments.post_id) AS `count`')->
               groupBy('posts.id')->
               orderBy('count','DESC')->
               skip(0)->take(20)->get();

с помощью skip а также take но я не эксперт по Eloquent и, возможно, есть лучшее решение для достижения вашей цели, так что вы можете подождать, и, возможно, кто-то даст лучший ответ.

Это звучит очевидно, но Eloquent не будет возвращать набор результатов здесь, а вернет коллекцию.

Если копаться в источнике (Builder::get звонки Builder::getFresh, который вызывает Builder::runSelect, который вызывает Connection::select), вы обнаружите, что он намерен просто вернуть результаты, которые затем помещаются в коллекцию (которая имеет метод sortBy).

/**
 * Run a select statement against the database.
 *
 * @param  string  $query
 * @param  array  $bindings
 * @param  bool  $useReadPdo
 * @return array
 */
public function select($query, $bindings = array(), $useReadPdo = true)
{
  return $this->run($query, $bindings, function($me, $query, $bindings) use ($useReadPdo)
  {    
    if ($me->pretending()) return array();

    // For select statements, we'll simply execute the query and return an array
    // of the database result set. Each element in the array will be a single
    // row from the database table, and will either be an array or objects.
    $statement = $this->getPdoForSelect($useReadPdo)->prepare($query);

    $statement->execute($me->prepareBindings($bindings));

    //** this is a very basic form of fetching, it is limited to the PDO consts.
    return $statement->fetchAll($me->getFetchMode());
  });  
}

Если вы хотите выполнить нумерацию страниц без загрузки каждого элемента, вам нужно использовать решение @Marcin (продублировано ниже):

$posts = Post::leftJoin('comments','posts.id','=','comments.post_id')->
           selectRaw('posts.*, count(comments.post_id) AS `count`')->
           groupBy('posts.id')->
           orderBy('count','DESC')->
           skip(0)->take(20)->get();

Просто удалите get() в цепочечных вызовах и посмотреть, что вы получаете, paginate должен заменить вызов get().

Другие вопросы по тегам