id()) ->with('components') ->get(); return response()->json($builds); } /** * @OA\Post( * path="/api/builds", * summary="Создать новую сборку", * tags={"PC Builds"}, * @OA\RequestBody( * required=true, * @OA\JsonContent( * required={"name", "component_ids"}, * @OA\Property(property="name", type="string", example="Игровой ПК 2025"), * @OA\Property(property="description", type="string", nullable=true, example="Для игр в 1440p"), * @OA\Property(property="component_ids", type="array", @OA\Items(type="integer", example=1)), * @OA\Property(property="is_ai_generated", type="boolean", default=false), * @OA\Property(property="ai_prompt", type="string", nullable=true, example="Сборка до 1000$ для игр") * ) * ), * @OA\Response(response=201, description="Сборка создана"), * @OA\Response(response=400, description="Ошибка валидации"), * @OA\Response(response=403, description="Запрещено: чужие компоненты") * ) */ public function store(Request $request) { $validated = $request->validate([ 'name' => 'required|string|max:255', 'description' => 'nullable|string', 'component_ids' => 'required|array|min:1', 'component_ids.*' => 'exists:components,id', 'is_ai_generated' => 'nullable|boolean', 'ai_prompt' => 'nullable|string', ]); // Проверяем: все компоненты — либо официальные, либо ваши $invalidComponents = Component::whereIn('id', $validated['component_ids']) ->where(function ($query) { $query->where('is_official', false) ->where('created_by_user_id', '!=', auth()->id()); }) ->pluck('name', 'id') ->toArray(); if (!empty($invalidComponents)) { return response()->json([ 'message' => 'Запрещено использовать неофициальные компоненты, созданные другими пользователями.', 'invalid_components' => $invalidComponents ], 403); } // 👇 ВСТАВЛЯЕМ ПРОВЕРКУ СОВМЕСТИМОСТИ ЗДЕСЬ — ПЕРЕД СОЗДАНИЕМ СБОРКИ $validator = new BuildValidator(); $compatibility = $validator->validateCompatibility($validated['component_ids']); if (!$compatibility['valid']) { return response()->json([ 'message' => 'Сборка содержит несовместимые компоненты.', 'errors' => $compatibility['errors'], 'warnings' => $compatibility['warnings'] ], 422); // 422 Unprocessable Entity } // ✅ Только если совместимость OK — создаём сборку $build = PCBuild::create([ 'user_id' => auth()->id(), 'name' => $validated['name'], 'description' => $validated['description'] ?? null, 'is_ai_generated' => $validated['is_ai_generated'] ?? false, 'ai_prompt' => $validated['ai_prompt'] ?? null, ]); $build->components()->attach($validated['component_ids']); return response()->json([ 'message' => 'Сборка успешно создана.', 'build' => $build->load('components'), 'compatibility' => $compatibility // опционально — для отладки ], 201); } /** * @OA\Get( * path="/api/builds/{id}", * summary="Получить одну сборку по ID", * tags={"PC Builds"}, * @OA\Parameter(name="id", in="path", required=true, @OA\Schema(type="integer")), * @OA\Response(response=200, description="Сборка найдена"), * @OA\Response(response=403, description="Запрещено: не ваша сборка"), * @OA\Response(response=404, description="Не найдено") * ) */ public function show($id) { $build = PCBuild::with('components')->findOrFail($id); if ($build->user_id !== auth()->id()) { return response()->json([ 'message' => 'Вы не можете просматривать эту сборку.' ], 403); } return response()->json($build); } /** * @OA\Put( * path="/api/builds/{id}", * summary="Обновить сборку", * tags={"PC Builds"}, * @OA\Parameter(name="id", in="path", required=true, @OA\Schema(type="integer")), * @OA\RequestBody( * required=true, * @OA\JsonContent( * required={"name", "component_ids"}, * @OA\Property(property="name", type="string"), * @OA\Property(property="description", type="string", nullable=true), * @OA\Property(property="component_ids", type="array", @OA\Items(type="integer")), * @OA\Property(property="is_ai_generated", type="boolean"), * @OA\Property(property="ai_prompt", type="string", nullable=true) * ) * ), * @OA\Response(response=200, description="Сборка обновлена"), * @OA\Response(response=403, description="Запрещено") * ) */ public function update(Request $request, $id) { $build = PCBuild::findOrFail($id); if ($build->user_id !== auth()->id()) { return response()->json([ 'message' => 'Вы не можете редактировать эту сборку.' ], 403); } $validated = $request->validate([ 'name' => 'required|string|max:255', 'description' => 'nullable|string', 'component_ids' => 'required|array|min:1', 'component_ids.*' => 'exists:components,id', 'is_ai_generated' => 'nullable|boolean', 'ai_prompt' => 'nullable|string', ]); // Проверка: все компоненты — либо официальные, либо ваши $invalidComponents = Component::whereIn('id', $validated['component_ids']) ->where(function ($query) { $query->where('is_official', false) ->where('created_by_user_id', '!=', auth()->id()); }) ->pluck('name', 'id') ->toArray(); if (!empty($invalidComponents)) { return response()->json([ 'message' => 'Запрещено использовать неофициальные компоненты, созданные другими пользователями.', 'invalid_components' => $invalidComponents ], 403); } // 👇 ВСТАВЛЯЕМ ПРОВЕРКУ СОВМЕСТИМОСТИ ЗДЕСЬ — ПЕРЕД ОБНОВЛЕНИЕМ $validator = new BuildValidator(); $compatibility = $validator->validateCompatibility($validated['component_ids']); if (!$compatibility['valid']) { return response()->json([ 'message' => 'Сборка содержит несовместимые компоненты.', 'errors' => $compatibility['errors'], 'warnings' => $compatibility['warnings'] ], 422); } // ✅ Обновляем сборку DB::transaction(function () use ($build, $validated) { $build->update([ 'name' => $validated['name'], 'description' => $validated['description'] ?? null, 'is_ai_generated' => $validated['is_ai_generated'] ?? $build->is_ai_generated, 'ai_prompt' => $validated['ai_prompt'] ?? $build->ai_prompt, ]); $build->components()->sync($validated['component_ids']); }); return response()->json([ 'message' => 'Сборка обновлена.', 'build' => $build->load('components'), 'compatibility' => $compatibility // опционально ]); } /** * @OA\Delete( * path="/api/builds/{id}", * summary="Удалить сборку", * tags={"PC Builds"}, * @OA\Parameter(name="id", in="path", required=true, @OA\Schema(type="integer")), * @OA\Response(response=200, description="Сборка удалена"), * @OA\Response(response=403, description="Запрещено") * ) */ public function destroy($id) { $build = PCBuild::findOrFail($id); if ($build->user_id !== auth()->id()) { return response()->json([ 'message' => 'Вы не можете удалить эту сборку.' ], 403); } $build->delete(); return response()->json([ 'message' => 'Сборка удалена.' ]); } }