Создать Модель, Контроллер и миграцию Post. Связать с моделями User и Category Larave - shat - 02-23-2026
Создание модели, контроллера и миграции Post с связями User и Category в Laravel 12
В рамках разработки динамического веб-приложения на Laravel 12, одной из ключевых задач является реализация функциональности для управления публикациями (Post). Это включает в себя создание соответствующей модели данных, механизма для взаимодействия с базой данных (миграция) и логики обработки запросов (контроллер). Особое внимание уделяется установлению связей с существующими моделями
User
(для автора публикации) и
Category
(для категоризации публикаций), что обеспечивает целостность данных и гибкость в их обработке.
Шаг 1: Создание миграции для модели Post
Сначала создадим миграцию, которая определит структуру таблицы
.
bash
Копировать
Код: php artisan make:migration create_posts_table --create=posts
Эта команда создаст файл миграции в папке
. Откройте его (название файла будет содержать дату и время создания).
php
Копировать
Код: <?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('posts', function (Blueprint $table) {
$table->id(); // Первичный ключ (auto-increment)
$table->foreignId('user_id')->constrained()->onDelete('cascade'); // Внешний ключ к таблице users
$table->foreignId('category_id')->nullable()->constrained()->onDelete('set null'); // Внешний ключ к таблице categories, может быть null
$table->string('title'); // Поле для заголовка поста
$table->text('body'); // Поле для основного текста поста
$table->string('slug')->unique(); // Уникальный slug для URL
$table->timestamp('published_at')->nullable(); // Дата и время публикации (null = черновик)
$table->timestamps(); // Поля created_at и updated_at
});
// Дополнительно: Добавление индекса для category_id, если он не nullable
// Если category_id не nullable, то onDelete('set null') не будет работать,
// и нужно будет использовать onDelete('restrict') или удалить этот пост, если категория удалена.
// В данном примере мы сделали category_id nullable.
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('posts');
}
};
Объяснение миграции:- : Создает автоинкрементный первичный ключ (
).
Код: $table->foreignId('user_id')->constrained()->onDelete('cascade');
:
- : Создает поле
типа
.
- : Автоматически создает внешний ключ, ссылающийся на столбец
в таблице
.
- : При удалении пользователя все его посты также будут удалены.
Код: $table->foreignId('category_id')->nullable()->constrained()->onDelete('set null');
:
Код: foreignId('category_id')
: Создает поле
типа
.
- : Позволяет этому полю быть пустым (null). Это полезно, если, например, категория поста удалена, но сам пост мы хотим сохранить.
- : Создает внешний ключ, ссылающийся на столбец
в таблице
.
- : При удалении категории,
у связанных постов будет установлен в
.
Код: $table->string('title');
: Поле для заголовка поста.
Код: $table->text('body');
: Поле для основного текста поста (может хранить больше текста, чем
).
Код: $table->string('slug')->unique();
: Уникальное строковое поле, часто используется для красивых URL (
).
Код: $table->timestamp('published_at')->nullable();
: Поле для даты публикации. Если
, пост считается черновиком.
Код: $table->timestamps();
: Создает поля
и
.
Шаг 2: Применение миграции
Выполните миграции, чтобы создать таблицы в вашей базе данных:
bash
Копировать
Шаг 3: Создание модели Post
Теперь создадим саму модель
.
bash
Копировать
Код: php artisan make:model Post
Откройте файл
и добавьте связи и свойства:
php
Копировать
Код: <?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
class Post extends Model
{
use HasFactory;
/**
* Атрибуты, которые можно массово присваивать.
*
* @var array<int, string>
*/
protected $fillable = [
'title',
'body',
'slug',
'user_id',
'category_id',
'published_at',
];
/**
* Атрибуты, которые должны быть типизированы.
*
* @var array<string, string>
*/
protected $casts = [
'published_at' => 'datetime', // Типизируем published_at как дату/время
];
/**
* Получить пользователя, которому принадлежит пост.
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
/**
* Получить категорию, к которой принадлежит пост.
*/
public function category(): BelongsTo
{
return $this->belongsTo(Category::class);
}
// Если нужно обратное отношение (от User к его постам, от Category к ее постам)
// Вы можете определить это в моделях User и Category соответственно.
/*
* В модели User.php:
* public function posts(): HasMany
* {
* return $this->hasMany(Post::class);
* }
*
* В модели Category.php:
* public function posts(): HasMany
* {
* return $this->hasMany(Post::class);
* }
*/
}
Объяснение модели
:Код: protected $fillable = [...]
: Определяет атрибуты, которые можно массово присваивать (например, при создании поста через
). Это мера безопасности, чтобы предотвратить присваивание нежелательных полей.
Код: protected $casts = [...]
: Позволяет автоматически приводить типы данных столбцов. Здесь
преобразуется в объект Carbon (дату/время).
Код: public function user(): BelongsTo
: Определяет отношение "принадлежит к" (BelongsTo) с моделью
. Каждый пост принадлежит одному пользователю.
Код: public function category(): BelongsTo
: Определяет отношение "принадлежит к" (BelongsTo) с моделью
. Каждый пост принадлежит одной категории (или ни одной, если nullable).
Шаг 4: Обновление моделей User и Category для обратных связей
Откройте
и добавьте отношение к постам:
php
Копировать
Код: // app/Models/User.php
// ... другие use директивы ...
use Illuminate\Database\Eloquent\Relations\HasMany;
class User extends Authenticatable
{
// ...
/**
* Получить все посты, созданные пользователем.
*/
public function posts(): HasMany
{
return $this->hasMany(Post::class);
}
// ...
}
Откройте
Код: app/Models/Category.php
и добавьте отношение к постам:
php
Копировать
Код: // app/Models/Category.php
// ... другие use директивы ...
use Illuminate\Database\Eloquent\Relations\HasMany;
class Category extends Model
{
// ...
/**
* Получить все посты, относящиеся к этой категории.
*/
public function posts(): HasMany
{
return $this->hasMany(Post::class);
}
// ...
}
Шаг 5: Создание контроллера для управления постами
Создадим ресурсный контроллер для обработки CRUD-операций с постами.
bash
Копировать
Код: php artisan make:controller PostController --resource
Откройте
Код: app/Http/Controllers/PostController.php
и добавьте логику, используя созданные модели и связи.
php
Копировать
Код: <?php
namespace App\Http\Controllers;
use App\Models\Post;
use App\Models\Category; // Импортируем Category
use Illuminate\Http\Request;
use Illuminate\Support\Str; // Для генерации slug
class PostController extends Controller
{
/**
* Отображает список всех постов.
*/
public function index()
{
// Получаем все посты с загрузкой связанных пользователей и категорий
$posts = Post::with(['user', 'category'])->latest()->paginate(10); // latest() сортирует по дате создания, paginate() для пагинации
return view('posts.index', compact('posts'));
}
/**
* Показывает форму для создания нового поста.
*/
public function create()
{
$categories = Category::all(); // Получаем все категории для выпадающего списка
return view('posts.create', compact('categories'));
}
/**
* Сохраняет новый пост в базу данных.
*/
public function store(Request $request)
{
// Валидация данных
$validatedData = $request->validate([
'title' => 'required|string|max:255',
'body' => 'required',
'category_id' => 'nullable|exists:categories,id', // Проверяем, что category_id существует в таблице categories
// user_id будет присвоен текущему авторизованному пользователю
]);
// Создаем пост
$post = new Post();
$post->title = $validatedData['title'];
$post->body = $request->body;
$post->user_id = auth()->id(); // Присваиваем ID текущего авторизованного пользователя
$post->category_id = $validatedData['category_id'] ?? null; // Используем null, если category_id не предоставлен
$post->slug = Str::slug($post->title); // Генерируем slug из заголовка
// Если нужно установить published_at, определите это в форме или валидации
// $post->published_at = now(); // Например, для немедленной публикации
$post->save();
return redirect()->route('posts.show', $post->slug)->with('success', 'Пост успешно создан!');
}
/**
* Отображает указанный пост.
*/
public function show(Post $post) // Laravel автоматически найдет пост по ID или slug, если параметр соответствует полю модели.
{
// Загружаем связанные user и category (если не загрузили в index)
$post->load('user', 'category');
return view('posts.show', compact('post'));
}
/**
* Показывает форму для редактирования указанного поста.
*/
public function edit(Post $post)
{
// Проверяем, что текущий пользователь является автором поста
if ($post->user_id !== auth()->id()) {
abort(403, 'Вы не являетесь автором этого поста.');
}
$categories = Category::all(); // Получаем все категории для выпадающего списка
return view('posts.edit', compact('post', 'categories'));
}
/**
* Обновляет указанный пост в базе данных.
*/
public function update(Request $request, Post $post)
{
// Проверяем, что текущий пользователь является автором поста
if ($post->user_id !== auth()->id()) {
abort(403, 'Вы не являетесь автором этого поста.');
}
$validatedData = $request->validate([
'title' => 'required|string|max:255',
'body' => 'required',
'category_id' => 'nullable|exists:categories,id',
// slug обычно не редактируется напрямую, он генерируется из title
]);
$post->title = $validatedData['title'];
$post->body = $request->body;
$post->category_id = $validatedData['category_id'] ?? null;
// Обновляем slug, если заголовок изменился
if ($post->title !== $validatedData['title']) {
$post->slug = Str::slug($validatedData['title']);
}
// Если вы управляете полем published_at через форму, добавьте валидацию и присваивание здесь
$post->save();
return redirect()->route('posts.show', $post->slug)->with('success', 'Пост успешно обновлен!');
}
/**
* Удаляет указанный пост из базы данных.
*/
public function destroy(Post $post)
{
// Проверяем, что текущий пользователь является автором поста
if ($post->user_id !== auth()->id()) {
abort(403, 'Вы не являетесь автором этого поста.');
}
$post->delete();
return redirect()->route('posts.index')->with('success', 'Пост успешно удален!');
}
}
Шаг 6: Определение маршрутов (Routes)
Откройте файл
и добавьте ресурсные маршруты для
.
php
Копировать
Код: <?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\PostController; // Импорт контроллера
use App\Http\Controllers\CategoryController; // Предполагаем, что у вас есть CategoryController
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider and all of them will
| be assigned to the "web" middleware group. Make something great!
|
*/
Route::get('/', function () {
return view('welcome');
});
// Ресурсные маршруты для постов
Route::resource('posts', PostController::class);
// Дополнительно: Если вам нужны отдельные маршруты для категорий
// Route::resource('categories', CategoryController::class);
// Пример загрузки постов с помощью relations
Route::get('/user/{user}/posts', function (\App\Models\User $user) {
// Получаем посты конкретного пользователя с загрузкой категории
$posts = $user->posts()->with('category')->get();
return view('users.posts', compact('posts', 'user')); // Создайте view 'users.posts'
})->name('users.posts');
Route::get('/category/{category}/posts', function (\App\Models\Category $category) {
// Получаем посты конкретной категории с загрузкой пользователя
$posts = $category->posts()->with('user')->get();
return view('categories.posts', compact('posts', 'category')); // Создайте view 'categories.posts'
})->name('categories.posts');
// Для аутентификации (если еще нет)
Auth::routes(); // Включит маршруты для логина, регистрации и т.д.
Сводка:
- Миграция (
): Определила структуру таблицы
, включая внешние ключи к
и
.
- Модель
: Определяет основную сущность поста, массив
,
и отношения
с
и
.
- Модификации
и
: Добавлены отношения
к
.
- Контроллер
: Обрабатывает HTTP-запросы для CRUD-операций с постами, используя модели и их связи.
- Маршруты (
): Определяют URL-адреса для доступа к функциям
.
Теперь у вас есть полностью функциональная система для управления постами, связанными с пользователями и категориями в Laravel 12. Вам также потребуется создать соответствующие Blade-представления (
Код: resources/views/posts/index.blade.php
,
,
,
) для отображения данных и форм.
|