CRUD за полчаса в Laravel: что нужно знать до начала
За полчаса вы соберёте рабочий CRUD для сущности «статья»: миграция, модель, контроллер с ресурсными маршрутами и три Blade-шаблона. Разбираем, что установить и настроить заранее, чтобы не застрять на пятом шаге.

За полчаса вы получите работающий раздел управления записями: список статей, форму создания, редактирование и удаление. Всё на стандартных инструментах Laravel 11.x — без сторонних пакетов, генераторов и админок. В конце в браузере откроется страница со списком статей и кнопками «Создать», «Изменить», «Удалить».
CRUD — это Create, Read, Update, Delete: четыре базовые операции над данными. Laravel умеет разложить их на семь маршрутов одной строкой, и большая часть работы — заполнить контроллер и нарисовать формы.
Что нужно иметь до начала
Проверьте это до первой команды. Если чего-то не хватает, вы упрётесь в ошибку не на шаге установки, а посреди работы, и будете гадать, что пошло не так.
- PHP 8.2 или новее. Laravel 11 не запустится на 8.1. Проверка:
php -v. - Composer 2.x. Проверка:
composer --version. - База данных. Проще всего SQLite — файл, ничего настраивать не нужно. Для MySQL нужен запущенный сервер и созданная база.
- Node.js 18+ и npm — для сборки стилей через Vite. Без них страницы откроются, но без оформления.
- Готовый проект Laravel. Если его ещё нет:
composer create-project laravel/laravel blog, затемcd blog.
Дальше все команды выполняются из корня проекта.
Шаг 1. Настройте базу
Откройте файл .env в корне проекта. Для SQLite достаточно двух вещей: указать драйвер и создать пустой файл базы.
# в .env оставьте только эту строку про подключение
DB_CONNECTION=sqlite
# остальные DB_* (HOST, PORT, DATABASE...) закомментируйте или удалитеСоздайте файл базы:
touch database/database.sqliteНа Windows без утилиты touch создайте пустой файл database/database.sqlite вручную. Проверка: файл появился в папке database.
Шаг 2. Создайте модель с миграцией и контроллером
Одна команда генерирует сразу три файла — модель, миграцию и ресурсный контроллер:
php artisan make:model Article -mcrФлаги значат: -m — миграция, -c — контроллер, -r — контроллер ресурсного типа (с готовыми методами index, create, store и так далее). Проверка: в консоли три строки вида INFO Model [...] created successfully.
Шаг 3. Опишите таблицу в миграции
Откройте свежую миграцию в database/migrations/ — её имя оканчивается на _create_articles_table.php. Внутри метода up() добавьте два поля:
public function up(): void
{
Schema::create('articles', function (Blueprint $table) {
$table->id();
$table->string('title'); // заголовок
$table->text('body'); // текст статьи
$table->timestamps();
});
}Примените миграцию — Laravel создаст таблицу в базе:
php artisan migrateПри первом запуске с SQLite он спросит, создать ли базу — отвечайте yes. Проверка: строка DONE напротив имени миграции.
Шаг 4. Разрешите массовое заполнение полей
Откройте app/Models/Article.php и добавьте свойство $fillable. Без него Laravel не даст сохранить данные из формы и выбросит ошибку MassAssignmentException — это защита от подстановки лишних полей.
class Article extends Model
{
protected $fillable = ['title', 'body'];
}Шаг 5. Зарегистрируйте маршруты
Откройте routes/web.php и добавьте одну строку. Она поднимает сразу семь маршрутов для всех CRUD-операций:
use App\Http\Controllers\ArticleController;
Route::resource('articles', ArticleController::class);Проверка: команда php artisan route:list покажет маршруты articles.index, articles.create, articles.store и другие.
Шаг 6. Заполните контроллер
Откройте app/Http/Controllers/ArticleController.php. Методы уже есть, но пустые. Вот минимально рабочая версия:
use App\Models\Article;
use Illuminate\Http\Request;
class ArticleController extends Controller
{
public function index()
{
$articles = Article::latest()->get();
return view('articles.index', compact('articles'));
}
public function create()
{
return view('articles.create');
}
public function store(Request $request)
{
$data = $request->validate([
'title' => 'required|max:255',
'body' => 'required',
]);
Article::create($data);
return redirect()->route('articles.index');
}
public function edit(Article $article)
{
return view('articles.edit', compact('article'));
}
public function update(Request $request, Article $article)
{
$data = $request->validate([
'title' => 'required|max:255',
'body' => 'required',
]);
$article->update($data);
return redirect()->route('articles.index');
}
public function destroy(Article $article)
{
$article->delete();
return redirect()->route('articles.index');
}
}Обратите внимание на edit(Article $article) — Laravel сам найдёт запись по id из URL и подставит объект. Это route model binding, он работает, потому что имя параметра совпадает с именем ресурса в маршруте.
Валидация в контроллере — не украшение. Без строки $request->validate(...) пустой заголовок попадёт в базу молча, и вы обнаружите битые записи только в проде. Для реального проекта эту логику выносят в Form Request, но для старта хватит и так.Шаг 7. Создайте три шаблона
В папке resources/views/ создайте подпапку articles и три файла. Список — index.blade.php:
<h1>Статьи</h1>
<a href="{{ route('articles.create') }}">Создать</a>
<ul>
@foreach ($articles as $article)
<li>
{{ $article->title }}
<a href="{{ route('articles.edit', $article) }}">Изменить</a>
<form action="{{ route('articles.destroy', $article) }}" method="POST" style="display:inline">
@csrf
@method('DELETE')
<button type="submit">Удалить</button>
</form>
</li>
@endforeach
</ul>Форма создания — create.blade.php:
<h1>Новая статья</h1>
<form action="{{ route('articles.store') }}" method="POST">
@csrf
<input type="text" name="title" placeholder="Заголовок">
<textarea name="body" placeholder="Текст"></textarea>
<button type="submit">Сохранить</button>
</form>Форма редактирования — edit.blade.php. Отличается директивой @method('PUT') и подставленными значениями:
<h1>Редактировать</h1>
<form action="{{ route('articles.update', $article) }}" method="POST">
@csrf
@method('PUT')
<input type="text" name="title" value="{{ $article->title }}">
<textarea name="body">{{ $article->body }}</textarea>
<button type="submit">Обновить</button>
</form>Директивы @csrf и @method обязательны: HTML-формы умеют только GET и POST, а для PUT и DELETE Laravel читает скрытое поле _method. Без @csrf вы получите ошибку 419.
Шаг 8. Запустите и проверьте
php artisan serveОткройте http://127.0.0.1:8000/articles. Проверка: появилась страница «Статьи» и ссылка «Создать». Создайте запись, отредактируйте, удалите — все три операции должны работать без ошибок.
Если не получилось
- SQLSTATE[HY000] [14] unable to open database file — файл
database/database.sqliteне создан или путь в.envуказывает не туда. Создайте файл и повторитеphp artisan migrate. - Add [title] to fillable property (MassAssignmentException) — вы пропустили шаг 4. Добавьте
$fillableв модель. - 419 Page Expired при отправке формы — забыли
@csrfвнутри формы. - The GET method is not supported for route articles — в форме удаления или редактирования нет
@method('DELETE')/@method('PUT'), либо метод формы не POST. - View [articles.index] not found — шаблон лежит не в
resources/views/articles/или назван иначе. Имя должно точно совпадать с тем, что передаётся вview().
Что делать дальше
Рабочий каркас готов, но в проде так не оставляют. Следующие шаги по порядку:
- вынести валидацию из контроллера в Form Request —
php artisan make:request StoreArticleRequest; - показывать ошибки валидации в шаблонах через
@errorи старые значения черезold(), иначе пользователь теряет введённый текст; - добавить пагинацию: заменить
get()наpaginate(15)в методе index; - подключить общий layout, чтобы не дублировать разметку в каждом шаблоне;
- настроить авторизацию через policy, если создавать и удалять статьи должны не все.
Связь статьи с автором, загрузка изображений и мягкое удаление — отдельные темы, здесь мы собирали только базовый CRUD на одной таблице.
Разбор научной статьи: Сложный текст — понятным языком, с терминами
Спросить за 12 ₽Источники: Laravel 11 — Controllers (Resource Controllers), Laravel 11 — Eloquent: Getting Started
Частые вопросы
У меня Laravel 10, а не 11 — сработает?
Обязательно ли использовать SQLite?
Почему форма удаления отправляется как POST, а не DELETE?
Что делать, если страница открывается, но без стилей?
Насколько это безопасно выкладывать в прод?
Route::resource создаёт семь маршрутов, а методов в контроллере шесть — почему?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.