get('q'); $posts = BlogPost::with('anime') ->when($q, fn($query) => $query->where('title', 'like', "%{$q}%")) ->orderByDesc('created_at') ->paginate(20); $stats = [ 'total' => BlogPost::count(), 'published' => BlogPost::where('status', 'published')->count(), 'draft' => BlogPost::where('status', 'draft')->count(), 'ai' => BlogPost::where('ai_generated', true)->count(), ]; return view('admin.blog.index', compact('posts', 'stats', 'q')); } public function create() { $animes = Anime::where('is_published', true)->orderBy('title')->get(['id', 'title']); $post = new BlogPost(); return view('admin.blog.edit', compact('post', 'animes')); } public function store(Request $request) { $data = $this->validated($request); $data['slug'] = BlogPost::generateSlug($data['title']); $data['published_at'] = $data['status'] === 'published' ? now() : null; BlogPost::create($data); return redirect()->route('admin.blog.index')->with('success', 'Blog yazısı oluşturuldu.'); } public function edit(BlogPost $blog) { $animes = Anime::where('is_published', true)->orderBy('title')->get(['id', 'title']); return view('admin.blog.edit', compact('blog', 'animes')); } public function update(Request $request, BlogPost $blog) { $data = $this->validated($request); if ($data['status'] === 'published' && !$blog->published_at) { $data['published_at'] = now(); } $blog->update($data); return redirect()->route('admin.blog.index')->with('success', 'Blog yazısı güncellendi.'); } public function destroy(BlogPost $blog) { $blog->delete(); return redirect()->route('admin.blog.index')->with('success', 'Blog yazısı silindi.'); } public function generateAi(Request $request, DeepSeekService $deepseek) { $request->validate(['anime_id' => 'required|exists:animes,id']); if (!$deepseek->isConfigured()) { return response()->json(['error' => 'DeepSeek API anahtarı ayarlanmamış. Admin > Ayarlar > deepseek_api_key'], 422); } set_time_limit(120); $anime = Anime::with('genres')->findOrFail($request->anime_id); $genreIds = $anime->genres->pluck('id'); $related = Anime::where('is_published', true) ->where('id', '!=', $anime->id) ->whereHas('genres', fn($q) => $q->whereIn('genres.id', $genreIds)) ->orderByDesc('rating') ->limit(5) ->get(['id', 'title', 'slug']) ->map(fn($a) => ['slug' => $a->slug, 'title' => $a->title]) ->toArray(); $data = $deepseek->generateBlogPost($anime, $related); if (!$data || empty($data['content'])) { return response()->json(['error' => 'AI içerik üretemedi: ' . $deepseek->lastError], 422); } $content = preg_replace_callback( '/\[LINK:([^\]]+)\]([^\[]*)\[\/LINK\]/', function ($m) { $slug = trim($m[1]); $label = trim($m[2]); try { return '' . $label . ''; } catch (\Exception $e) { return $label; } }, $data['content'] ); $linkedIds = []; if (!empty($data['linked_slugs'])) { $linkedIds = Anime::whereIn('slug', $data['linked_slugs'])->pluck('id')->toArray(); } return response()->json([ 'title' => $data['title'] ?? '', 'excerpt' => $data['excerpt'] ?? '', 'content' => $content, 'focus_keyword' => $data['focus_keyword'] ?? $anime->title, 'meta_description' => $data['meta_description'] ?? '', 'faq' => $data['faq'] ?? [], 'linked_anime_ids' => $linkedIds, ]); } public function bulkGenerate(Request $request) { $count = min(5, (int) $request->get('count', 3)); set_time_limit(300); try { \Artisan::call('animexe:generate-blogs', ['--count' => $count, '--force' => false]); $output = \Artisan::output(); return redirect()->route('admin.blog.index')->with('success', 'AI blog üretimi tamamlandı: ' . trim($output)); } catch (\Throwable $e) { return redirect()->route('admin.blog.index')->with('error', 'Hata: ' . $e->getMessage()); } } private function validated(Request $request): array { return $request->validate([ 'title' => 'required|string|max:255', 'excerpt' => 'nullable|string', 'content' => 'nullable|string', 'cover_image' => 'nullable|string|max:500', 'focus_keyword' => 'nullable|string|max:255', 'meta_title' => 'nullable|string|max:255', 'meta_description' => 'nullable|string', 'meta_keywords' => 'nullable|string', 'status' => 'required|in:draft,published', 'anime_id' => 'nullable|exists:animes,id', 'reading_time' => 'nullable|integer|min:1|max:60', ]); } }