service = new ItineraryService(new ItineraryRepository()); } /** * Register routes */ public function register_routes(): void { $namespace = 'yatra/v1'; $base = 'itinerary'; // Collection routes register_rest_route($namespace, '/' . $base, [ [ 'methods' => \WP_REST_Server::CREATABLE, 'callback' => [$this, 'create_item'], 'permission_callback' => [$this, 'check_permission'], ], [ 'methods' => \WP_REST_Server::DELETABLE, 'callback' => [$this, 'bulk_delete_items'], 'permission_callback' => [$this, 'check_permission'], ], [ 'methods' => \WP_REST_Server::READABLE, 'callback' => [$this, 'get_items_by_trip'], 'permission_callback' => [$this, 'check_permission'], ], ]); // Single item routes register_rest_route($namespace, '/' . $base . '/(?P[\d]+)', [ [ 'methods' => \WP_REST_Server::READABLE, 'callback' => [$this, 'get_item'], 'permission_callback' => [$this, 'check_permission'], ], [ 'methods' => \WP_REST_Server::EDITABLE, 'callback' => [$this, 'update_item'], 'permission_callback' => [$this, 'check_permission'], ], [ 'methods' => \WP_REST_Server::DELETABLE, 'callback' => [$this, 'delete_item'], 'permission_callback' => [$this, 'check_permission'], ], ]); // Get day entry ID by day_id register_rest_route($namespace, '/' . $base . '/day-entry-by-day-id/(?P[\d]+)', [ [ 'methods' => \WP_REST_Server::READABLE, 'callback' => [$this, 'get_day_entry_id_by_day_id'], 'permission_callback' => [$this, 'check_permission'], ], ]); } /** * Get items by trip ID */ public function get_items_by_trip(WP_REST_Request $request) { try { $trip_id = (int) $request->get_param('trip_id'); if (!$trip_id) { return $this->error_response('Trip ID is required', 400); } $items = $this->service->getByTripId($trip_id); // Debug logging $prepared_items = array_map(function ($item) use ($request) { $prepared = $this->prepare_item_for_response($item, $request); return $prepared; }, $items); $response = $this->success_response($prepared_items); return $response; } catch (\Exception $e) { return $this->error_response($e->getMessage(), 500); } } /** * Get item */ public function get_item(WP_REST_Request $request) { try { $id = (int) $request->get_param('id'); $mode = $request->get_param('mode') ?: 'activity'; // Default to activity mode // Use mode to determine which method to call if ($mode === 'day') { // Get day entry (from days table) $item = $this->service->find($id); } else { // Get activity entry (from entries table) $item = $this->service->findActivity($id); } if (!$item) { return $this->error_response('Itinerary entry not found', 404); } $prepared = $this->prepare_item_for_response($item, $request); return $this->success_response($prepared); } catch (\Exception $e) { return $this->error_response($e->getMessage(), 500); } } /** * Create item */ public function create_item(WP_REST_Request $request) { try { $data = $request->get_json_params(); $id = $this->service->create($data); return $this->success_response([ 'id' => $id, 'message' => __('Itinerary entry created successfully', 'yatra'), ], 201); } catch (\InvalidArgumentException $e) { return $this->error_response($e->getMessage(), 400); } catch (\Exception $e) { return $this->error_response($e->getMessage(), 500); } } /** * Update item */ public function update_item(WP_REST_Request $request) { try { $id = (int) $request->get_param('id'); $mode = $request->get_param('mode') ?: 'activity'; // Default to activity mode $data = $request->get_json_params(); $result = $this->service->update($id, $data, $mode); if (!$result) { return $this->error_response('Failed to update itinerary entry', 500); } return $this->success_response([ 'message' => __('Itinerary entry updated successfully', 'yatra'), ]); } catch (\InvalidArgumentException $e) { return $this->error_response($e->getMessage(), 400); } catch (\Exception $e) { return $this->error_response($e->getMessage(), 500); } } /** * Delete item */ public function delete_item(WP_REST_Request $request) { try { $id = (int) $request->get_param('id'); $mode = $request->get_param('mode') ?: 'activity'; // Default to activity mode $result = $this->service->delete($id, $mode); if (!$result) { return $this->error_response('Failed to delete itinerary entry', 500); } return $this->success_response([ 'message' => __('Itinerary entry deleted successfully', 'yatra'), ]); } catch (\Exception $e) { return $this->error_response($e->getMessage(), 500); } } /** * Bulk delete items */ public function bulk_delete_items(WP_REST_Request $request) { try { $data = $request->get_json_params(); $ids = $data['ids'] ?? []; if (empty($ids) || !is_array($ids)) { return $this->error_response('No IDs provided for bulk delete', 400); } $result = $this->service->bulkDelete($ids); return $this->success_response([ 'deleted' => $result['deleted'], 'failed' => $result['failed'], 'message' => sprintf( __('%d item(s) deleted successfully. %d item(s) failed to delete.', 'yatra'), $result['deleted'], $result['failed'] ), ]); } catch (\Exception $e) { return $this->error_response($e->getMessage(), 500); } } /** * Get day entry ID by day_id */ public function get_day_entry_id_by_day_id(WP_REST_Request $request) { try { $dayId = (int) $request->get_param('day_id'); $repository = new ItineraryRepository(); $dayEntryId = $repository->getDayEntryIdByDayId($dayId); return $this->success_response([ 'day_entry_id' => $dayEntryId, ]); } catch (\Exception $e) { return $this->error_response($e->getMessage(), 500); } } /** * Prepare item for response */ protected function prepare_item_for_response($item, WP_REST_Request $request): array { // Parse included_items and excluded_items from JSON if they're strings $includedItems = $item->included_items ?? []; if (is_string($includedItems)) { $decoded = json_decode($includedItems, true); $includedItems = is_array($decoded) ? $decoded : []; } $excludedItems = $item->excluded_items ?? []; if (is_string($excludedItems)) { $decoded = json_decode($excludedItems, true); $excludedItems = is_array($decoded) ? $decoded : []; } return [ 'id' => (int) $item->id, 'trip_id' => (int) $item->trip_id, 'day_id' => (int) $item->day_id, 'day' => isset($item->day_number) ? (int) $item->day_number : null, 'day_title' => $item->day_title ?? null, 'day_description' => $item->day_description ?? null, 'title' => $item->title ?? '', 'description' => $item->description ?? '', 'time' => $item->time ?? null, 'start_time' => $item->start_time ?? null, 'end_time' => $item->end_time ?? null, 'time_type' => $item->time_type ?? 'exact', 'location' => $item->location ?? null, 'location_latitude' => $item->location_latitude ?? null, 'location_longitude' => $item->location_longitude ?? null, 'duration' => $item->duration ?? null, 'cost' => isset($item->cost) ? (float) $item->cost : null, 'cost_per_person' => isset($item->cost_per_person) ? (bool) $item->cost_per_person : false, 'notes' => $item->notes ?? null, 'item_type_id' => ($item->item_type_id !== null && $item->item_type_id !== '') ? (int) $item->item_type_id : null, 'item_id' => ($item->item_id !== null && $item->item_id !== '') ? (int) $item->item_id : null, 'item_type_name' => $item->item_type_name ?? null, 'item_name' => $item->item_name ?? null, 'item_type_icon' => $item->item_type_icon ?? null, 'included_items' => $includedItems, 'excluded_items' => $excludedItems, 'status' => $item->status ?? 'draft', 'images' => $item->images ?? [], 'gallery' => $this->decodeGallery($item->gallery ?? null), 'video_url' => $item->video_url ?? null, 'order' => (int) ($item->order ?? 0), 'created_at' => $item->created_at ?? null, 'updated_at' => $item->updated_at ?? null, ]; } /** * Decode gallery JSON from database and convert attachment IDs to URLs */ private function decodeGallery(?string $galleryJson): array { if (empty($galleryJson)) { return []; } $gallery = json_decode($galleryJson, true); if (!is_array($gallery)) { return []; } // Convert attachment IDs to URLs for frontend compatibility foreach ($gallery as &$item) { if (isset($item['attachment_id']) && $item['attachment_id'] > 0) { // Get attachment URL from WordPress $attachment_url = wp_get_attachment_url($item['attachment_id']); if ($attachment_url) { $item['url'] = $attachment_url; } // Get thumbnail URL for images if (isset($item['type']) && $item['type'] === 'image') { $thumbnail_url = wp_get_attachment_image_src($item['attachment_id'], 'medium'); if ($thumbnail_url) { $item['thumbnail_url'] = $thumbnail_url[0]; } } } } return $gallery; } }