Как реализовать пагинацию с использованием курсора, чтобы при этом отображались соседние страницы?

Здравствуйте! <br/> В Laravel имеется возможность курсорной пагинации как с использованием QueryBuilder, так и с Eloquent. Однако такая пагинация предоставляет только ссылки для перехода вперед и назад. Например, <a href="https://www.kufar.by/l/r~minsk?cursor=eyJ0IjoiYWJzIiwiZiI6dHJ1ZSwicCI6NSwicGl0IjoiMjg5MDMwMzIifQ%3D%3D" rel="nofollow"><b><i>здесь также применяется курсорная пагинация</i></b></a>, но при этом предусмотрены ссылки на курсоры ближайших страниц — если мы на странице 5, то указаны ссылки для страниц 3, 4, 6, 7 и всегда 1. Можете подсказать, как реализовать подобный функционал? <br/>
  • 14 декабря 2024 г. 13:33
Ответы на вопрос 2
Для реализации курсорной пагинации с отображением соседних страниц в Laravel, вам нужно немного модифицировать стандартную логику пагинации.

Курсорная пагинация обычно использует только курсор вперед и назад, однако вы можете создать свои собственные курсоры для соседних страниц, используя offset и limit. Далее приведены шаги, которые помогут вам в этом процессе:

1. **Создание контроллера**: В вашем контроллере, где вы обрабатываете запросы на получение данных, добавьте логику для получения соседних курсоров.

2. **Получение необходимых данных**: Для реализации соседних курсоров, вы должны сохранить данные о последнем полученном элементе. Затем используйте SQL-запрос, чтобы выбрать соседние записи.

3. **Формирование курсоров**: Создайте курсоры для страниц, которые отображаются рядом с текущей. Например, если вы находитесь на странице 5, вычислите курсоры для страниц 3, 4, 6 и 7, а также для первой страницы.

4. **Генерация ссылок**: Генерируйте ссылки на соседние страницы, передавая нужные курсоры в URL.

### Пример кода

Допустим, у вас есть модель `Post`, и вы хотите реализовать курсорную пагинацию с отображением соседних страниц. Вот пример логики в контроллере:

```php
public function index(Request $request)
{
    $currentCursor = $request->input('cursor');
    $limit = 10;

    // Получаем данные с курсорной пагинацией
    $postsQuery = Post::query();

    // Применение курсора к запросу
    if ($currentCursor) {
        $postsQuery->where('id', '>', decodeCursor($currentCursor));
    }
    
    // Получаем текущую страницу
    $posts = $postsQuery->take($limit)->get();
    
    // Получаем курсоры для соседних страниц
    $previousCursor = $this->getCursor($postsQuery, $limit, -1);
    $nextCursor = $this->getCursor($postsQuery, $limit, 1);
    
    // Генерация курсоров для соседних страниц
    $neighborCursors = [];
    if ($previousCursor) {
        $neighborCursors[] = $this->generateCursorLink($previousCursor);
    }
    if ($nextCursor) {
        $neighborCursors[] = $this->generateCursorLink($nextCursor);
    }

    return view('posts.index', [
        'posts' => $posts,
        'previousCursor' => $previousCursor,
        'nextCursor' => $nextCursor,
        'neighborCursors' => $neighborCursors,
    ]);
}

private function getCursor($query, $limit, $direction)
{
    $cursorQuery = $query->orderBy('id');
    if ($direction < 0) {
        $cursorQuery->take(1)->skip(max(0, $query->count() - $limit - 1))->get()->last();
    } else {
        $cursorQuery->skip(count($query->get()) - 1)->take(1)->get();
    }

    return $cursorQuery->first();
}

private function generateCursorLink($cursor)
{
    return url()->current() . '?cursor=' . encodeCursor($cursor->id);
}

private function encodeCursor($id)
{
    return base64_encode($id);
}

private function decodeCursor($cursor)
{
    return base64_decode($cursor);
}
```

### Комментарии к коду:

- `getCursor()`: Эта функция получает предыдущий или следующий элемент в зависимости от направления.
- `encodeCursor()` и `decodeCursor()`: Эти функции для кодирования и декодирования курсора, чтобы использовать его в URL.
- `generateCursorLink()`: Эта функция создает ссылку для курсора.

### Визуализация в представлении

В вашем представлении вы можете отобразить курсоры так, чтобы пользователи могли переходить на соседние страницы:

```blade
@if($neighborCursors)
    <div class="pagination">
        @foreach($neighborCursors as $cursorLink)
            <a href="{{ $cursorLink }}">{{ $cursorLink }}</a>
        @endforeach
    </div>
@endif
```

Этот подход позволит вам отображать не только предыдущую или следующую страницу, но и соседние страницы, используя курсорную пагинацию.
Но, как выбрать записи для страниц до курсора, обратный OFFSET какой-то?


Так же как и после, но с сортировкой по убыванию.
Похожие вопросы