query('q', '')); $posts = Post::query() ->when($q !== '', function ($qb) use ($q) { $qb->where('title', 'like', "%{$q}%") ->orWhere('slug', 'like', "%{$q}%"); }) ->latest('created_at') ->paginate(20) ->withQueryString(); return view('admin.posts.index', compact('posts', 'q')); } public function create(): View { return view('admin.posts.create'); } public function store(Request $request): RedirectResponse { $data = $this->validateData($request); $slug = trim((string) ($data['slug'] ?? '')); if ($slug === '') { $slug = $this->uniqueSlug($data['title']); } $coverImageValue = $data['cover_image'] ?? null; if ($request->hasFile('cover_image_file')) { $coverImageValue = $request->file('cover_image_file')->store('posts/covers', 'public'); } Post::create([ 'title' => $data['title'], 'slug' => $slug, 'excerpt' => $data['excerpt'] ?? null, 'body' => $data['body'], 'cover_image' => $coverImageValue, 'author_id' => Auth::id(), 'published_at' => $request->boolean('published') ? now() : null, ]); return redirect()->route('admin.posts.index')->with('status', 'Post created'); } public function edit(Post $post): View { return view('admin.posts.edit', compact('post')); } public function update(Request $request, Post $post): RedirectResponse { $data = $this->validateData($request, $post->id); $slug = trim((string) ($data['slug'] ?? '')); if ($slug === '') { $slug = $this->uniqueSlug($data['title'], $post->id); } $coverImageValue = $data['cover_image'] ?? $post->cover_image; if ($request->hasFile('cover_image_file')) { $coverImageValue = $request->file('cover_image_file')->store('posts/covers', 'public'); } $post->update([ 'title' => $data['title'], 'slug' => $slug, 'excerpt' => $data['excerpt'] ?? null, 'body' => $data['body'], 'cover_image' => $coverImageValue, 'published_at' => $request->boolean('published') ? ($post->published_at ?? now()) : null, ]); return redirect()->route('admin.posts.index')->with('status', 'Post updated'); } public function destroy(Post $post): RedirectResponse { $post->delete(); return redirect()->route('admin.posts.index')->with('status', 'Post deleted'); } protected function validateData(Request $request, ?int $ignoreId = null): array { return $request->validate([ 'title' => ['required', 'string', 'max:255'], 'slug' => ['nullable', 'string', 'max:255', 'unique:posts,slug' . ($ignoreId ? ',' . $ignoreId : '')], 'excerpt' => ['nullable', 'string', 'max:500'], 'body' => ['required', 'string'], 'cover_image' => ['nullable', 'string', 'max:2048'], 'cover_image_file' => ['nullable', 'image', 'mimes:jpeg,jpg,png,webp,avif,gif', 'max:8192'], 'published' => ['nullable', 'boolean'], ]); } protected function uniqueSlug(string $title, ?int $ignoreId = null): string { $base = Str::slug($title); if ($base === '') { $base = 'post'; } $slug = $base; $i = 1; while (Post::where('slug', $slug) ->when($ignoreId, fn($q) => $q->where('id', '!=', $ignoreId)) ->exists()) { $slug = $base . '-' . (++$i); } return $slug; } }