Пагинация новостей в Laravel 11: пошаговая инструкция
За 20 минут соберём вывод новостей постранично: миграция, модель, контроллер, маршрут и Blade со ссылками «вперёд/назад». В конце — что делать, если ссылки не появились или считаются не так.

К концу этой инструкции у вас будет страница /news, которая показывает по 10 новостей и рисует ссылки «1 2 3 … Далее» внизу. Всё на встроенном пагинаторе Laravel, без сторонних пакетов. На чистом проекте займёт минут двадцать. Инструкция написана для Laravel 11.x и PHP 8.2+; для 10.x команды те же, отличия отмечу отдельно.
Что нужно иметь до начала
Проверьте, что всё это уже есть, иначе застрянете на середине:
- установленный Laravel 11.x — проверьте командой
php artisan --version; - настроенное подключение к базе в файле
.env(переменныеDB_CONNECTION,DB_DATABASEи остальные) и работающая база — SQLite подойдёт для проб; - запущенный локальный сервер:
php artisan serveотдаёт сайт наhttp://127.0.0.1:8000; - немного новостей в базе — их мы добавим через сидер на шаге 4, руками ничего вписывать не придётся.
Пагинатор Laravel по умолчанию отдаёт разметку под Tailwind CSS. Если Tailwind в проекте нет, ссылки будут работать, но выглядеть голо — на последнем шаге покажу, как переключить на Bootstrap.
Шаг 1. Создаём модель, миграцию и контроллер разом
Одна команда создаёт три файла сразу:
php artisan make:model News -mc
Флаг -m добавляет миграцию, -c — контроллер. В консоли появятся три строки вида INFO Model [app/Models/News.php] created successfully и то же для миграции и контроллера. Если вместо этого видите ошибку про уже существующий файл — значит, модель News у вас уже есть, тогда уберите лишние флаги.
Шаг 2. Описываем таблицу в миграции
Откройте свежий файл в database/migrations — имя оканчивается на _create_news_table.php. Приведите метод up() к такому виду:
public function up(): void
{
Schema::create('news', function (Blueprint $table) {
$table->id();
$table->string('title'); // заголовок новости
$table->text('body'); // текст
$table->timestamp('published_at')->nullable();
$table->timestamps();
});
}
Примените миграцию:
php artisan migrate
В консоли должна появиться строка DONE напротив вашей миграции. Если Laravel спросит про создание базы SQLite — ответьте yes, файл создастся сам.
Шаг 3. Разрешаем массовое заполнение
Чтобы сидер и формы могли записывать поля, откройте app/Models/News.php и добавьте свойство $fillable внутрь класса:
class News extends Model
{
protected $fillable = ['title', 'body', 'published_at'];
}
Без этого свойства при попытке создать запись через News::create() Laravel выбросит MassAssignmentException. Это защита от того, чтобы через форму случайно не записали лишние колонки.
Шаг 4. Наполняем базу тестовыми новостями
Чтобы пагинация было видно, нужно больше десяти записей. Быстрее всего — через фабрику и сидер. Создайте фабрику:
php artisan make:factory NewsFactory
В файле database/factories/NewsFactory.php опишите генерацию:
public function definition(): array
{
return [
'title' => fake()->sentence(4),
'body' => fake()->paragraph(),
'published_at' => now(),
];
}
Теперь создайте 30 новостей прямо из консоли Tinker:
php artisan tinker
\App\Models\News::factory()->count(30)->create();
После второй строки Tinker вернёт коллекцию из 30 объектов. Выйдите командой exit. Проверить, что записи легли в базу, можно тут же: \App\Models\News::count() должно вернуть 30.
Шаг 5. Запрашиваем данные с пагинацией в контроллере
Откройте app/Http/Controllers/NewsController.php. Метод paginate() сам читает номер страницы из параметра ?page= в адресе — вам не нужно его ловить руками.
<?php
namespace App\Http\Controllers;
use App\Models\News;
class NewsController extends Controller
{
public function index()
{
// 10 новостей на страницу, сначала свежие
$news = News::orderByDesc('published_at')->paginate(10);
return view('news.index', ['news' => $news]);
}
}
Метод возвращает не обычную коллекцию, а объект LengthAwarePaginator — он знает общее число записей и умеет сам построить ссылки. Именно поэтому в шаблоне сработает $news->links().
Ключевое отличиеpaginate()отsimplePaginate(): первый делает лишний запросCOUNT(*), чтобы знать общее число страниц и нарисовать номера. Второй считать не умеет и рисует только «Назад/Далее», зато на больших таблицах работает быстрее.
Шаг 6. Прописываем маршрут
В файле routes/web.php добавьте:
use App\Http\Controllers\NewsController;
Route::get('/news', [NewsController::class, 'index']);
Проверьте, что маршрут зарегистрирован: php artisan route:list должен показать строку с GET|HEAD news.
Шаг 7. Выводим новости и ссылки в Blade
Создайте файл resources/views/news/index.blade.php:
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<title>Новости</title>
</head>
<body>
<h1>Новости</h1>
@foreach ($news as $item)
<article>
<h2>{{ $item->title }}</h2>
<p>{{ $item->body }}</p>
</article>
@endforeach
{{-- ссылки пагинации --}}
{{ $news->links() }}
</body>
</html>
Откройте http://127.0.0.1:8000/news. Вы увидите 10 новостей и внизу блок ссылок. Кликните «Далее» — в адресе появится ?page=2, а содержимое сменится. Это и есть работающая пагинация.
Если не получилось
Три ошибки, которые встречаются чаще всего.
Call to undefined method ... links()— значит, в контроллере вместоpaginate()вызвалиget()илиall(). Обычная коллекция ссылки строить не умеет. Вернитеpaginate(10).- Ссылки есть, но выглядят как голый список без стилей — в проекте не подключён Tailwind. Либо подключите его, либо переключите пагинатор на Bootstrap (см. следующий раздел).
View [news.index] not found— файл лежит не там. Он должен называться именноresources/views/news/index.blade.php: точка вnews.indexозначает подпапку.- При переходе на страницу 2 всё пусто — новостей меньше, чем страниц. Проверьте
News::count()в Tinker: для двух страниц по 10 нужно минимум 11 записей.
Что настроить дальше
Базовый вывод готов. Что стоит сделать следующим шагом:
- Переключить стиль ссылок на Bootstrap. В
app/Providers/AppServiceProvider.phpв методеboot()вызовитеPaginator::useBootstrapFive();(не забудьтеuse Illuminate\Pagination\Paginator;сверху). Точный список доступных методов сверьте в разделе Pagination документации Laravel — набор зависит от версии. - Сохранить параметры фильтра при переходе по страницам. Если добавите поиск или фильтр, вызывайте
$news->withQueryString()передlinks(), иначе?search=...потеряется на второй странице. - Отдать пагинацию во фронтенд как JSON. Просто верните
$newsиз контроллера API — Laravel сам добавит в ответ поляcurrent_page,last_page,totalи ссылки.
Курсорная пагинация (cursorPaginate()) для больших лент, где COUNT(*) становится дорогим, — отдельная тема, здесь мы её не разбирали.
Репетитор 1—11 класс и ВУЗ: Математика и физика методом наведения на мысль, помощь с ДЗ.
Спросить за 8 ₽Источники: Laravel — Database: Pagination (официальная документация), Laravel — Eloquent: Getting Started
Частые вопросы
У меня Laravel 10, а не 11. Инструкция подойдёт?
Как поменять число новостей на странице?
Ссылки пагинации не появляются, хотя записей много. Почему?
Пагинатор делает лишний запрос COUNT — это опасно в проде?
Можно ли отдавать пагинацию в React или Vue без Blade?
Как сохранить параметры поиска при переходе на следующую страницу?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.