paginate() рахує сторінки через OFFSET, а cursorPaginate() продовжує від останнього показаного запису.
Post::query()->orderBy('id')->paginate(20);
// SELECT ... ORDER BY id LIMIT 20 OFFSET 19980 (сторінка 1000)
// + SELECT COUNT(*) ...
Post::query()->orderBy('id')->cursorPaginate(20);
// SELECT ... WHERE id > 19980 ORDER BY id LIMIT 21
Чому курсор швидший на далеких сторінках: OFFSET 19980 змушує базу прочитати й відкинути майже 20 тисяч рядків. Умова id > ... за індексом одразу стає на потрібне місце - тисячна сторінка відкривається так само швидко, як перша. До того ж немає запиту COUNT(*), який на великих таблицях теж дорогий.
Ще одна перевага: якщо поки користувач гортає, додаються нові записи, OFFSET зсуває сторінки - записи повторюються чи пропадають. Курсор такого не має.
Обмеження курсора:
- немає номерів сторінок і загальної кількості - лише «далі» й «назад»;
- сортування має бути за унікальною колонкою чи їх комбінацією (
created_at+id); - колонки сортування мають бути в індексі.
Коли що: нескінченна стрічка, API, експорт - курсор. Таблиця в адмінці з номерами сторінок - звичайна пагінація, simplePaginate() якщо загальна кількість не потрібна.