| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762 |
- ```php
- <?php
- return [
- 'name' => env('APP_NAME', 'Laravel'),
- 'env' => env('APP_ENV', 'production'),
- 'debug' => (bool) env('APP_DEBUG', false),
- 'url' => env('APP_URL', 'http://localhost'),
- 'timezone' => env('APP_TIMEZONE', 'UTC'),
- 'locale' => env('APP_LOCALE', 'en'),
- 'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'),
- 'faker_locale' => env('APP_FAKER_LOCALE', 'en_US'),
- // ... остальные настройки по умолчанию
- ];
- ```
- ## Шаг 4: Создание миграций
- ### Migration: create_posts_table
- ```bash
- php artisan make:migration create_posts_table
- ```
- ```php
- <?php
- // database/migrations/xxxx_xx_xx_create_posts_table.php
- use Illuminate\Database\Migrations\Migration;
- use Illuminate\Database\Schema\Blueprint;
- use Illuminate\Support\Facades\Schema;
- return new class extends Migration
- {
- public function up(): void
- {
- Schema::create('posts', function (Blueprint $table) {
- $table->id();
- $table->string('title');
- $table->string('slug')->unique();
- $table->text('content');
- $table->enum('status', ['draft', 'published', 'scheduled'])->default('draft');
- $table->timestamp('published_at')->nullable();
- $table->timestamp('scheduled_at')->nullable();
- $table->timestamps();
- $table->softDeletes();
-
- $table->index('status');
- $table->index('scheduled_at');
- });
- }
- public function down(): void
- {
- Schema::dropIfExists('posts');
- }
- };
- ```
- ### Migration: create_comments_table
- ```bash
- php artisan make:migration create_comments_table
- ```
- ```php
- <?php
- // database/migrations/xxxx_xx_xx_create_comments_table.php
- use Illuminate\Database\Migrations\Migration;
- use Illuminate\Database\Schema\Blueprint;
- use Illuminate\Support\Facades\Schema;
- return new class extends Migration
- {
- public function up(): void
- {
- Schema::create('comments', function (Blueprint $table) {
- $table->id();
- $table->foreignId('post_id')->constrained()->onDelete('cascade');
- $table->string('author_name');
- $table->string('author_email');
- $table->text('content');
- $table->enum('status', ['pending', 'approved', 'rejected'])->default('pending');
- $table->timestamps();
-
- $table->index(['post_id', 'status']);
- });
- }
- public function down(): void
- {
- Schema::dropIfExists('comments');
- }
- };
- ```
- Запустите миграции:
- ```bash
- php artisan migrate
- ```
- ## Шаг 5: Создание моделей
- ### Model: Post
- ```bash
- php artisan make:model Post
- ```
- ```php
- <?php
- // app/Models/Post.php
- namespace App\Models;
- use Illuminate\Database\Eloquent\Factories\HasFactory;
- use Illuminate\Database\Eloquent\Model;
- use Illuminate\Database\Eloquent\SoftDeletes;
- use Illuminate\Support\Str;
- use App\Events\PostPublished;
- use App\Events\PostScheduled;
- class Post extends Model
- {
- use HasFactory, SoftDeletes;
- protected $fillable = [
- 'title',
- 'slug',
- 'content',
- 'status',
- 'published_at',
- 'scheduled_at',
- ];
- protected $casts = [
- 'published_at' => 'datetime',
- 'scheduled_at' => 'datetime',
- ];
- protected $dispatchesEvents = [
- 'created' => PostPublished::class,
- ];
- // Автоматическое создание slug
- protected static function boot()
- {
- parent::boot();
-
- static::creating(function ($post) {
- if (empty($post->slug)) {
- $post->slug = Str::slug($post->title);
- }
- });
- }
- // Связь с комментариями
- public function comments()
- {
- return $this->hasMany(Comment::class);
- }
- // Только опубликованные посты
- public function scopePublished($query)
- {
- return $query->where('status', 'published')
- ->whereNotNull('published_at')
- ->where('published_at', '<=', now());
- }
- // Посты, готовые к автопубликации
- public function scopeReadyForPublishing($query)
- {
- return $query->where('status', 'scheduled')
- ->whereNotNull('scheduled_at')
- ->where('scheduled_at', '<=', now());
- }
- // Публикация поста
- public function publish()
- {
- $this->update([
- 'status' => 'published',
- 'published_at' => now(),
- ]);
-
- event(new PostPublished($this));
- }
- // Снятие с публикации
- public function unpublish()
- {
- $this->update([
- 'status' => 'draft',
- ]);
- }
- // Планирование публикации
- public function schedule($dateTime)
- {
- $this->update([
- 'status' => 'scheduled',
- 'scheduled_at' => $dateTime,
- ]);
-
- event(new PostScheduled($this));
- }
- }
- ```
- ### Model: Comment
- ```bash
- php artisan make:model Comment
- ```
- ```php
- <?php
- // app/Models/Comment.php
- namespace App\Models;
- use Illuminate\Database\Eloquent\Factories\HasFactory;
- use Illuminate\Database\Eloquent\Model;
- use App\Events\CommentCreated;
- use App\Events\CommentApproved;
- class Comment extends Model
- {
- use HasFactory;
- protected $fillable = [
- 'post_id',
- 'author_name',
- 'author_email',
- 'content',
- 'status',
- ];
- protected $dispatchesEvents = [
- 'created' => CommentCreated::class,
- ];
- // Связь с постом
- public function post()
- {
- return $this->belongsTo(Post::class);
- }
- // Только одобренные комментарии
- public function scopeApproved($query)
- {
- return $query->where('status', 'approved');
- }
- // Ожидающие модерации
- public function scopePending($query)
- {
- return $query->where('status', 'pending');
- }
- // Одобрить комментарий
- public function approve()
- {
- $this->update(['status' => 'approved']);
- event(new CommentApproved($this));
- }
- // Отклонить комментарий
- public function reject()
- {
- $this->update(['status' => 'rejected']);
- }
- }
- ```
- ## Шаг 6: Создание событий (Events)
- ### Event: PostPublished
- ```bash
- php artisan make:event PostPublished
- ```
- ```php
- <?php
- // app/Events/PostPublished.php
- namespace App\Events;
- use App\Models\Post;
- use Illuminate\Broadcasting\InteractsWithSockets;
- use Illuminate\Foundation\Events\Dispatchable;
- use Illuminate\Queue\SerializesModels;
- class PostPublished
- {
- use Dispatchable, InteractsWithSockets, SerializesModels;
- public function __construct(public Post $post)
- {
- }
- }
- ```
- ### Event: PostScheduled
- ```bash
- php artisan make:event PostScheduled
- ```
- ```php
- <?php
- // app/Events/PostScheduled.php
- namespace App\Events;
- use App\Models\Post;
- use Illuminate\Broadcasting\InteractsWithSockets;
- use Illuminate\Foundation\Events\Dispatchable;
- use Illuminate\Queue\SerializesModels;
- class PostScheduled
- {
- use Dispatchable, InteractsWithSockets, SerializesModels;
- public function __construct(public Post $post)
- {
- }
- }
- ```
- ### Event: CommentCreated
- ```bash
- php artisan make:event CommentCreated
- ```
- ```php
- <?php
- // app/Events/CommentCreated.php
- namespace App\Events;
- use App\Models\Comment;
- use Illuminate\Broadcasting\InteractsWithSockets;
- use Illuminate\Foundation\Events\Dispatchable;
- use Illuminate\Queue\SerializesModels;
- class CommentCreated
- {
- use Dispatchable, InteractsWithSockets, SerializesModels;
- public function __construct(public Comment $comment)
- {
- }
- }
- ```
- ### Event: CommentApproved
- ```bash
- php artisan make:event CommentApproved
- ```
- ```php
- <?php
- // app/Events/CommentApproved.php
- namespace App\Events;
- use App\Models\Comment;
- use Illuminate\Broadcasting\InteractsWithSockets;
- use Illuminate\Foundation\Events\Dispatchable;
- use Illuminate\Queue\SerializesModels;
- class CommentApproved
- {
- use Dispatchable, InteractsWithSockets, SerializesModels;
- public function __construct(public Comment $comment)
- {
- }
- }
- ```
- ## Шаг 7: Создание слушателей (Listeners)
- ### Listener: SendPostPublishedNotification
- ```bash
- php artisan make:listener SendPostPublishedNotification --event=PostPublished
- ```
- ```php
- <?php
- // app/Listeners/SendPostPublishedNotification.php
- namespace App\Listeners;
- use App\Events\PostPublished;
- use Illuminate\Support\Facades\Log;
- class SendPostPublishedNotification
- {
- public function handle(PostPublished $event): void
- {
- Log::info('Post published: ' . $event->post->title);
- // Здесь можно добавить отправку уведомлений, email и т.д.
- }
- }
- ```
- ### Listener: LogPostScheduled
- ```bash
- php artisan make:listener LogPostScheduled --event=PostScheduled
- ```
- ```php
- <?php
- // app/Listeners/LogPostScheduled.php
- namespace App\Listeners;
- use App\Events\PostScheduled;
- use Illuminate\Support\Facades\Log;
- class LogPostScheduled
- {
- public function handle(PostScheduled $event): void
- {
- Log::info('Post scheduled: ' . $event->post->title .
- ' for ' . $event->post->scheduled_at);
- }
- }
- ```
- ### Listener: NotifyAdminAboutComment
- ```bash
- php artisan make:listener NotifyAdminAboutComment --event=CommentCreated
- ```
- ```php
- <?php
- // app/Listeners/NotifyAdminAboutComment.php
- namespace App\Listeners;
- use App\Events\CommentCreated;
- use Illuminate\Support\Facades\Log;
- class NotifyAdminAboutComment
- {
- public function handle(CommentCreated $event): void
- {
- Log::info('New comment awaiting moderation on post: ' .
- $event->comment->post->title);
- // Здесь можно отправить email администратору
- }
- }
- ```
- ### Listener: NotifyCommentAuthor
- ```bash
- php artisan make:listener NotifyCommentAuthor --event=CommentApproved
- ```
- ```php
- <?php
- // app/Listeners/NotifyCommentAuthor.php
- namespace App\Listeners;
- use App\Events\CommentApproved;
- use Illuminate\Support\Facades\Log;
- class NotifyCommentAuthor
- {
- public function handle(CommentApproved $event): void
- {
- Log::info('Comment approved and author notified: ' .
- $event->comment->author_email);
- // Здесь можно отправить email автору комментария
- }
- }
- ```
- ## Шаг 8: Регистрация событий (EventServiceProvider)
- ```php
- <?php
- // app/Providers/EventServiceProvider.php
- namespace App\Providers;
- use App\Events\PostPublished;
- use App\Events\PostScheduled;
- use App\Events\CommentCreated;
- use App\Events\CommentApproved;
- use App\Listeners\SendPostPublishedNotification;
- use App\Listeners\LogPostScheduled;
- use App\Listeners\NotifyAdminAboutComment;
- use App\Listeners\NotifyCommentAuthor;
- use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
- class EventServiceProvider extends ServiceProvider
- {
- protected $listen = [
- PostPublished::class => [
- SendPostPublishedNotification::class,
- ],
- PostScheduled::class => [
- LogPostScheduled::class,
- ],
- CommentCreated::class => [
- NotifyAdminAboutComment::class,
- ],
- CommentApproved::class => [
- NotifyCommentAuthor::class,
- ],
- ];
- public function boot(): void
- {
- //
- }
- }
- ```
- ## Шаг 9: Консольная команда для автопубликации
- ```bash
- php artisan make:command PublishScheduledPosts
- ```
- ```php
- <?php
- // app/Console/Commands/PublishScheduledPosts.php
- namespace App\Console\Commands;
- use App\Models\Post;
- use Illuminate\Console\Command;
- class PublishScheduledPosts extends Command
- {
- protected $signature = 'posts:publish-scheduled';
- protected $description = 'Публикация запланированных постов';
- public function handle()
- {
- $posts = Post::readyForPublishing()->get();
-
- if ($posts->isEmpty()) {
- $this->info('Нет постов для публикации');
- return 0;
- }
- foreach ($posts as $post) {
- $post->publish();
- $this->info("Опубликован пост: {$post->title}");
- }
- $this->info("Всего опубликовано постов: {$posts->count()}");
- return 0;
- }
- }
- ```
- ## Шаг 10: Настройка планировщика (Kernel.php)
- ```php
- <?php
- // app/Console/Kernel.php
- namespace App\Console;
- use Illuminate\Console\Scheduling\Schedule;
- use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
- class Kernel extends ConsoleKernel
- {
- protected function schedule(Schedule $schedule): void
- {
- // Проверка и публикация запланированных постов каждые 5 минут
- $schedule->command('posts:publish-scheduled')
- ->everyFiveMinutes()
- ->withoutOverlapping();
- }
- protected function commands(): void
- {
- $this->load(__DIR__.'/Commands');
- require base_path('routes/console.php');
- }
- }
- ```
- ## Шаг 11: Создание контроллеров
- ### PostController
- ```bash
- php artisan make:controller PostController --resource
- ```
- ```php
- <?php
- // app/Http/Controllers/PostController.php
- namespace App\Http\Controllers;
- use App\Models\Post;
- use Illuminate\Http\Request;
- class PostController extends Controller
- {
- public function index()
- {
- $posts = Post::published()->latest('published_at')->paginate(10);
- return view('posts.index', compact('posts'));
- }
- public function show(Post $post)
- {
- if ($post->status !== 'published') {
- abort(404);
- }
-
- $comments = $post->comments()->approved()->latest()->get();
- return view('posts.show', compact('post', 'comments'));
- }
- public function create()
- {
- return view('posts.create');
- }
- public function store(Request $request)
- {
- $validated = $request->validate([
- 'title' => 'required|max:255',
- 'content' => 'required',
- 'status' => 'required|in:draft,published,scheduled',
- 'scheduled_at' => 'nullable|date|after:now',
- ]);
- $post = Post::create($validated);
- if ($validated['status'] === 'published') {
- $post->publish();
- } elseif ($validated['status'] === 'scheduled' && $validated['scheduled_at']) {
- $post->schedule($validated['scheduled_at']);
- }
- return redirect()->route('posts.show', $post)
- ->with('success', 'Пост успешно создан');
- }
- public function edit(Post $post)
- {
- return view('posts.edit', compact('post'));
- }
- public function update(Request $request, Post $post)
- {
- $validated = $request->validate([
- 'title' => 'required|max:255',
- 'content' => 'required',
- 'status' => 'required|in:draft,published,scheduled',
- 'scheduled_at' => 'nullable|date|after:now',
- ]);
- $post->update($validated);
- if ($validated['status'] === 'published' && $post->status !== 'published') {
- $post->publish();
- } elseif ($validated['status'] === 'scheduled' && $validated['scheduled_at']) {
- $post->schedule($validated['scheduled_at']);
- }
- return redirect()->route('posts.show', $post)
- ->with('success', 'Пост обновлен');
- }
- public function destroy(Post $post)
- {
- $post->delete();
- return redirect()->route('posts.index')
- ->with('success', 'Пост удален');
- }
- }
- ```
- ### CommentController
- ```bash
- php artisan make:controller CommentController
- ```
- ```php
- <?php
- // app/Http/Controllers/CommentController.php
- namespace App\Http\Controllers;
- use App\Models\Post;
- use App\Models\Comment;
- use Illuminate\Http\Request;
- class CommentController extends Controller
- {
- public function store(Request $request, Post $post)
- {
- $validated = $request->validate([
- 'author_name' => 'required|max:255',
- 'author_email' => 'required|email',
- 'content' => 'required|max:1000',
- ]);
- $post->comments()->create($validated);
- return redirect()->route('posts.show', $post)
- ->with('success', 'Комментарий отправлен на модерацию');
- }
- public function moderate()
- {
- $comments = Comment::pending()->with('post')->latest()->paginate(20);
- return view('comments.moderate', compact('comments'));
- }
- public function approve(Comment $comment)
- {
- $comment->approve();
- return back()->with('success', 'Комментарий одобрен');
- }
- public function reject(Comment $comment)
- {
- $comment->reject();
- return back()->with('success', 'Комментарий отклонен');
- }
- }
- ```
- ## Шаг 12: Маршруты (routes/web.php)
- ```php
- <?php
- // routes/web.php
- use App\Http\Controllers\PostController;
- use App\Http\Controllers\CommentController;
- use Illuminate\Support\Facades\Route;
- Route::get('/', [PostController::class, 'index'])->name('home');
- Route::resource('posts', PostController::class);
- Route::post('posts/{post}/comments', [CommentController::class, 'store'])
- ->name('comments.store');
- // Административные маршруты
- Route::prefix('admin')->group(function () {
- Route::get('comments/moderate', [CommentController::class, 'moderate'])
- ->name('comments.moderate');
- Route::post('comments/{comment}/approve', [CommentController::class, 'approve'])
- ->name('comments.approve');
- Route::post('comments/{comment}/reject', [CommentController::class, 'reject'])
- ->name('comments.reject');
- });
|