Создать Модель, Контроллер и миграцию Post. Связать с моделями User и Category Larave
postsphp artisan make:migration create_posts_table --create=postsdatabase/migrations<?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->id();id$table->foreignId('user_id')->constrained()->onDelete('cascade');foreignId('user_id')user_idBIGINT UNSIGNEDconstrained()idusersonDelete('cascade')$table->foreignId('category_id')->nullable()->constrained()->onDelete('set null');foreignId('category_id')category_idBIGINT UNSIGNEDnullable()constrained()idcategoriesonDelete('set null')category_idNULL$table->string('title');$table->text('body');string$table->string('slug')->unique();/posts/my-first-post$table->timestamp('published_at')->nullable();NULL$table->timestamps();created_atupdated_atphp artisan migratePostphp artisan make:model Postapp/Models/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);
* }
*/
}Postprotected $fillable = [...]Post::create()protected $casts = [...]published_atpublic function user(): BelongsToUserpublic function category(): BelongsToCategoryapp/Models/User.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// app/Models/Category.php
// ... другие use директивы ...
use Illuminate\Database\Eloquent\Relations\HasMany;
class Category extends Model
{
// ...
/**
* Получить все посты, относящиеся к этой категории.
*/
public function posts(): HasMany
{
return $this->hasMany(Post::class);
}
// ...
}php artisan make:controller PostController --resourceapp/Http/Controllers/PostController.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', 'Пост успешно удален!');
}
}routes/web.phpPostController<?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(); // Включит маршруты для логина, регистрации и т.д.create_posts_tablepostsuserscategoriesPost$fillable$castsbelongsToUserCategoryUserCategoryhasManyPostPostControllerweb.phpPostControllerresources/views/posts/index.blade.phpcreate.blade.phpshow.blade.phpedit.blade.phpСоздание модели, контроллера и миграции Post с связями User и Category в Laravel 12
В рамках разработки динамического веб-приложения на Laravel 12, одной из ключевых задач является реализация функциональности для управления публикациями (Post). Это включает в себя создание соответствующей модели данных, механизма для взаимодействия с базой данных (миграция) и логики обработки запросов (контроллер). Особое внимание уделяется установлению связей с существующими моделями
User
(для автора публикации) и
Category
(для категоризации публикаций), что обеспечивает целостность данных и гибкость в их обработке.
Шаг 1: Создание миграции для модели Post
Сначала создадим миграцию, которая определит структуру таблицы
postsphp artisan make:migration create_posts_table --create=postsdatabase/migrations<?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->id();id$table->foreignId('user_id')->constrained()->onDelete('cascade');foreignId('user_id')user_idBIGINT UNSIGNEDconstrained()idusersonDelete('cascade')$table->foreignId('category_id')->nullable()->constrained()->onDelete('set null');foreignId('category_id')category_idBIGINT UNSIGNEDnullable()constrained()idcategoriesonDelete('set null')category_idNULL$table->string('title');$table->text('body');string$table->string('slug')->unique();/posts/my-first-post$table->timestamp('published_at')->nullable();NULL$table->timestamps();created_atupdated_atphp artisan migratePostphp artisan make:model Postapp/Models/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);
* }
*/
}Postprotected $fillable = [...]Post::create()protected $casts = [...]published_atpublic function user(): BelongsToUserpublic function category(): BelongsToCategoryapp/Models/User.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// app/Models/Category.php
// ... другие use директивы ...
use Illuminate\Database\Eloquent\Relations\HasMany;
class Category extends Model
{
// ...
/**
* Получить все посты, относящиеся к этой категории.
*/
public function posts(): HasMany
{
return $this->hasMany(Post::class);
}
// ...
}php artisan make:controller PostController --resourceapp/Http/Controllers/PostController.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', 'Пост успешно удален!');
}
}routes/web.phpPostController<?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(); // Включит маршруты для логина, регистрации и т.д.create_posts_tablepostsuserscategoriesPost$fillable$castsbelongsToUserCategoryUserCategoryhasManyPostPostControllerweb.phpPostControllerresources/views/posts/index.blade.phpcreate.blade.phpshow.blade.phpedit.blade.php