id) ->orderByDesc('is_default') ->orderByDesc('last_used_at') ->orderByDesc('updated_at'); if ($role = $request->query('role')) { if (in_array($role, [AddressBookEntry::ROLE_SENDER, AddressBookEntry::ROLE_RECEIVER], true)) { $query->ofRole($role); } } if ($search = trim((string) $request->query('search', ''))) { $query->where(function ($q) use ($search) { $q->where('name', 'like', "%{$search}%") ->orWhere('label', 'like', "%{$search}%") ->orWhere('phone', 'like', "%{$search}%") ->orWhere('city', 'like', "%{$search}%"); }); } $entries = $query->get(); return response()->json([ 'success' => true, 'data' => $entries->map(fn (AddressBookEntry $e) => $this->formatEntry($e)), 'count' => $entries->count(), 'max_entries' => AddressBookEntry::MAX_ENTRIES_PER_USER, ]); } /** * افزودن آدرس جدید * POST /api/v1/addresses */ public function store(Request $request): JsonResponse { $validated = $this->validateEntry($request); $user = Auth::user(); $count = AddressBookEntry::forUser($user->id)->count(); if ($count >= AddressBookEntry::MAX_ENTRIES_PER_USER) { return response()->json([ 'success' => false, 'message' => 'تعداد آدرس‌های ذخیره‌شده شما به حداکثر مجاز (' . AddressBookEntry::MAX_ENTRIES_PER_USER . ') رسیده است. لطفاً آدرس‌های قدیمی را حذف کنید.', ], 422); } // جلوگیری از رکورد تکراری (همان نقش + تلفن + آدرس) → بروزرسانی رکورد موجود $duplicate = AddressBookEntry::findDuplicate( $user->id, $validated['role'], $validated['phone'], $validated['address'] ); if ($duplicate) { $duplicate->update(collect($validated)->except(['role'])->all()); if (!empty($validated['is_default'])) { $duplicate->makeDefault(); } return response()->json([ 'success' => true, 'message' => 'آدرس مشابه از قبل ذخیره شده بود و بروزرسانی شد.', 'data' => $this->formatEntry($duplicate->refresh()), 'updated_existing' => true, ]); } $entry = AddressBookEntry::create([ ...$validated, 'user_id' => $user->id, ]); if (!empty($validated['is_default'])) { $entry->makeDefault(); } return response()->json([ 'success' => true, 'message' => 'آدرس با موفقیت ذخیره شد.', 'data' => $this->formatEntry($entry), ], 201); } /** * ویرایش آدرس * PUT/PATCH /api/v1/addresses/{entry} */ public function update(Request $request, AddressBookEntry $address): JsonResponse { $user = Auth::user(); if ($address->user_id !== $user->id) { return response()->json([ 'success' => false, 'message' => 'دسترسی غیرمجاز.', ], 403); } $validated = $this->validateEntry($request, $address->id); // نقش رکورد قابل تغییر نیست (جلوگیری از ناسازگاری با فرم) unset($validated['role']); $address->update($validated); if (!empty($validated['is_default'])) { $address->makeDefault(); } return response()->json([ 'success' => true, 'message' => 'آدرس بروزرسانی شد.', 'data' => $this->formatEntry($address->refresh()), ]); } /** * حذف آدرس * DELETE /api/v1/addresses/{entry} */ public function destroy(Request $request, AddressBookEntry $address): JsonResponse { $user = Auth::user(); if ($address->user_id !== $user->id) { return response()->json([ 'success' => false, 'message' => 'دسترسی غیرمجاز.', ], 403); } $address->delete(); return response()->json([ 'success' => true, 'message' => 'آدرس حذف شد.', ]); } /** * تنظیم به‌عنوان پیش‌فرض * POST /api/v1/addresses/{address}/default */ public function setDefault(Request $request, AddressBookEntry $address): JsonResponse { $user = Auth::user(); if ($address->user_id !== $user->id) { return response()->json([ 'success' => false, 'message' => 'دسترسی غیرمجاز.', ], 403); } $address->makeDefault(); return response()->json([ 'success' => true, 'message' => 'این آدرس به‌عنوان پیش‌فرض تنظیم شد.', 'data' => $this->formatEntry($address->refresh()), ]); } // === Helpers === private function validateEntry(Request $request, ?int $ignoreId = null): array { try { return $request->validate([ 'role' => ['required', Rule::in([AddressBookEntry::ROLE_SENDER, AddressBookEntry::ROLE_RECEIVER])], 'label' => ['nullable', 'string', 'max:100'], 'name' => ['required', 'string', 'max:255'], 'company' => ['nullable', 'string', 'max:255'], 'phone' => ['required', 'string', 'max:50'], 'email' => ['nullable', 'email', 'max:255'], 'state' => ['nullable', 'string', 'max:255'], 'city' => ['nullable', 'string', 'max:255'], 'postal_code' => ['nullable', 'string', 'max:20'], 'national_id' => ['nullable', 'string', 'max:50'], 'address' => ['required', 'string', 'max:1000'], 'is_default' => ['nullable', 'boolean'], ]); } catch (ValidationException $e) { abort(response()->json([ 'success' => false, 'message' => 'خطا در اعتبارسنجی اطلاعات آدرس', 'errors' => $e->errors(), ], 422)); } } private function formatEntry(AddressBookEntry $e): array { return [ 'id' => $e->id, 'role' => $e->role, 'label' => $e->label, 'display_label' => $e->display_label, 'name' => $e->name, 'company' => $e->company, 'phone' => $e->phone, 'email' => $e->email, 'state' => $e->state, 'city' => $e->city, 'postal_code' => $e->postal_code, 'national_id' => $e->national_id, 'address' => $e->address, 'is_default' => $e->is_default, 'use_count' => $e->use_count, 'last_used_at' => $e->last_used_at?->toIso8601String(), 'form_fields' => $e->toFormFieldData(), 'updated_at' => $e->updated_at->toIso8601String(), ]; } }