has('tag')) { $tag = $normalize($request->tag); $query->whereHas('tags', function ($q) use ($tag) { $q->whereRaw("REPLACE(name, '‌', ' ') LIKE ?", ["%{$tag}%"]); }); } if ($request->has('category')) { $category = $normalize($request->category); $query->whereHas('category', function ($q) use ($category) { $q->whereRaw("REPLACE(name, '‌', ' ') LIKE ?", ["%{$category}%"]); }); } return response()->json( $query->get()->map(fn ($q) => $this->formatQuestion($q))->values() ); } /** * Shape a question into the legacy (old Flutter) response: category_id and * category are always present and non-null, tags is always an array, and * timestamps are always ISO strings — so the old client never hits a null. */ private function formatQuestion(Question $question): array { $question->loadMissing(['tags', 'category']); $createdAt = optional($question->created_at)->toIso8601String(); $updatedAt = optional($question->updated_at)->toIso8601String(); $categoryId = $question->category_id ?? 0; $category = $question->category; $categoryPayload = $category ? [ 'id' => $category->id, 'name' => $category->name ?? '', 'created_at' => optional($category->created_at)->toIso8601String() ?? $createdAt, 'updated_at' => optional($category->updated_at)->toIso8601String() ?? $updatedAt, ] : [ 'id' => $categoryId, 'name' => '', 'created_at' => $createdAt, 'updated_at' => $updatedAt, ]; return [ 'id' => $question->id, 'title' => $question->title ?? '', 'category_id' => $categoryId, 'created_at' => $createdAt, 'updated_at' => $updatedAt, 'tags' => $question->tags->values(), 'category' => $categoryPayload, ]; } /** * Store a newly created resource in storage. */ public function store(Request $request) { $data = $request->validate([ 'title' => 'required|string', 'category' => 'nullable|string', 'tags' => 'nullable|array', 'tags.*' => 'string' ]); $category = null; if (!empty($data['category'])) { $category = Category::firstOrCreate(['name' => $data['category']]); } $question = Question::create([ 'title' => $data['title'], 'category_id' => $category?->id ]); if (!empty($data['tags'])) { $tagIds = []; foreach ($data['tags'] as $tagName) { $tag = Tag::firstOrCreate(['name' => $tagName]); $tagIds[] = $tag->id; } $question->tags()->sync($tagIds); } return response()->json($this->formatQuestion($question), 201); } /** * Display the specified resource. */ public function show(Question $question) { return response()->json($this->formatQuestion($question)); } /** * Update the specified resource in storage. */ public function update(Request $request, Question $question) { $data = $request->validate([ 'title' => 'sometimes|string', 'category' => 'nullable|string', 'tags' => 'nullable|array', 'tags.*' => 'string' ]); if (isset($data['title'])) { $question->update(['title' => $data['title']]); } if (array_key_exists('category', $data)) { if ($data['category']) { $category = Category::firstOrCreate(['name' => $data['category']]); $question->update(['category_id' => $category->id]); } else { $question->update(['category_id' => null]); } } if (isset($data['tags'])) { $tagIds = []; foreach ($data['tags'] as $tagName) { $tag = Tag::firstOrCreate(['name' => $tagName]); $tagIds[] = $tag->id; } $question->tags()->sync($tagIds); } return response()->json($this->formatQuestion($question->fresh())); } /** * Remove the specified resource from storage. */ public function destroy(Question $question) { $question->delete(); return response()->json(['message' => 'سوال حذف شد']); } }