PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.2.8
Yatra – Travel Booking & Tour Operator Software v3.0.2.8
3.0.15 3.0.14 3.0.14.1 3.0.14.2 3.0.12 3.0.13 3.0.11 3.0.10 3.0.9 3.0.8 3.0.7 3.0.6 3.0.5 3.0.5.1 3.0.4 3.0.3 3.0.2.9 3.0.2.7 3.0.2.8 3.0.2.6 trunk 1.0.0 2.0.0 2.0.1 2.0.10 All 83 releases
yatra / app / Repositories / SampleDataRepository.php

SampleDataRepository.php in Yatra – Travel Booking & Tour Operator Software 3.0.2.8, at app/Repositories/SampleDataRepository.php

804 lines 32.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Yatra\Repositories;
4
5 use Yatra\Database\Tables\TripsTable;
6 use Yatra\Database\Tables\ClassificationsTable;
7 use Yatra\Database\Tables\TripClassificationsTable;
8 use Yatra\Database\Tables\DiscountsTable;
9 use Yatra\Database\Tables\TripAvailabilityDatesTable;
10 use Yatra\Database\Tables\TripAvailabilityRulesTable;
11 use Yatra\Database\Tables\TripItineraryDaysTable;
12 use Yatra\Database\Tables\TripItineraryDayEntryTable;
13 use Yatra\Database\Tables\TripContentTable;
14
15 /**
16 * Sample Data Repository
17 *
18 * Handles database operations for sample data import.
19 * Inserts data into all required tables matching the exact structure
20 * that the real UI/API/Service layer uses.
21 */
22 class SampleDataRepository
23 {
24 private $wpdb;
25
26 private $trips_table;
27 private $classifications_table;
28 private $trip_classifications_table;
29 private $discounts_table;
30 private $availability_dates_table;
31 private $availability_rules_table;
32 private $itinerary_days_table;
33 private $itinerary_entries_table;
34 private $trip_content_table;
35
36 /**
37 * Tracks inserted classification type:slug => id mapping.
38 * Uses composite key to avoid slug collisions across types.
39 */
40 private $classification_ids = [];
41
42 /**
43 * Simple slug => id mapping (last wins) for backward compat.
44 */
45 private $slug_to_id = [];
46
47 public function __construct()
48 {
49 global $wpdb;
50 $this->wpdb = $wpdb;
51
52 $this->trips_table = TripsTable::getTableName();
53 $this->classifications_table = ClassificationsTable::getTableName();
54 $this->trip_classifications_table = TripClassificationsTable::getTableName();
55 $this->discounts_table = DiscountsTable::getTableName();
56 $this->availability_dates_table = TripAvailabilityDatesTable::getTableName();
57 $this->availability_rules_table = TripAvailabilityRulesTable::getTableName();
58 $this->itinerary_days_table = TripItineraryDaysTable::getTableName();
59 $this->itinerary_entries_table = TripItineraryDayEntryTable::getTableName();
60 $this->trip_content_table = TripContentTable::getTableName();
61 }
62
63 /**
64 * Get sample data directory path
65 */
66 private function get_sample_data_dir()
67 {
68 return YATRA_ABSPATH . 'sample-data/';
69 }
70
71 /**
72 * Read JSON file
73 */
74 private function read_json_file($filename)
75 {
76 $file_path = $this->get_sample_data_dir() . $filename;
77
78 if (!file_exists($file_path)) {
79 error_log('Yatra Sample Data: File not found: ' . $file_path);
80 return [];
81 }
82
83 $json_content = file_get_contents($file_path);
84 $data = json_decode($json_content, true);
85
86 if (json_last_error() !== JSON_ERROR_NONE) {
87 error_log('Yatra Sample Data: JSON decode error in ' . $filename . ': ' . json_last_error_msg());
88 return [];
89 }
90
91 return $data;
92 }
93
94 /**
95 * Get all sample data from JSON files
96 */
97 public function get_all_sample_data()
98 {
99 return [
100 'categories' => $this->read_json_file('categories.json'),
101 'activities' => $this->read_json_file('activities.json'),
102 'destinations' => $this->read_json_file('destinations.json'),
103 'difficulty_levels' => $this->read_json_file('difficulty-levels.json'),
104 'attributes' => $this->read_json_file('attributes.json'),
105 'item_types' => $this->read_json_file('item-types.json'),
106 'traveler_categories' => $this->read_json_file('traveler-categories.json'),
107 'items' => $this->read_json_file('items.json'),
108 'discounts' => $this->read_json_file('discounts.json'),
109 'trips' => $this->read_json_file('trips.json'),
110 'trip_classifications' => $this->read_json_file('trip-classifications.json'),
111 'availability_dates' => $this->read_json_file('availability-dates.json'),
112 'availability_rules' => $this->read_json_file('availability-rules.json'),
113 'itinerary_days' => $this->read_json_file('itinerary-days.json'),
114 'itinerary_entries' => $this->read_json_file('itinerary-entries.json'),
115 ];
116 }
117
118 /**
119 * Get the tracked classification IDs (slug => id)
120 */
121 public function get_classification_ids()
122 {
123 return $this->classification_ids;
124 }
125
126 /**
127 * Insert classifications and track their IDs by slug.
128 * Returns the count of inserted records.
129 */
130 public function insert_classifications($data)
131 {
132 $inserted = 0;
133 $now = current_time('mysql');
134 $uid = get_current_user_id();
135
136 foreach ($data as $item) {
137 // Track the slug before inserting
138 $slug = $item['slug'] ?? '';
139
140 // Set defaults for required fields
141 $item['created_at'] = $item['created_at'] ?? $now;
142 $item['updated_at'] = $item['updated_at'] ?? $now;
143 $item['created_by'] = $item['created_by'] ?? $uid;
144 $item['updated_by'] = $item['updated_by'] ?? $uid;
145 $item['parent_id'] = $item['parent_id'] ?? null;
146 $item['level'] = $item['level'] ?? 0;
147 $item['sorting'] = $item['sorting'] ?? 0;
148 $item['is_featured'] = $item['is_featured'] ?? 0;
149
150 // Convert metadata to JSON if it's an array
151 if (isset($item['metadata']) && is_array($item['metadata'])) {
152 $item['metadata'] = json_encode($item['metadata']);
153 }
154
155 $result = $this->wpdb->insert($this->classifications_table, $item);
156
157 if ($result && $this->wpdb->insert_id) {
158 $inserted++;
159 $id = $this->wpdb->insert_id;
160 $type = $item['type'] ?? '';
161 // Track by composite key (type:slug) and simple slug
162 if ($slug) {
163 $this->classification_ids[$type . ':' . $slug] = $id;
164 $this->slug_to_id[$slug] = $id;
165 }
166 } else {
167 error_log('Yatra Sample Data: Failed to insert classification: ' . $slug . ' - ' . $this->wpdb->last_error);
168 }
169 }
170
171 return $inserted;
172 }
173
174 /**
175 * Insert items (type='item') with parent_id resolved from item_type slugs.
176 * Items use parent_slug to reference their item_type.
177 */
178 public function insert_items($data)
179 {
180 $inserted = 0;
181 $now = current_time('mysql');
182 $uid = get_current_user_id();
183
184 foreach ($data as $item) {
185 $slug = $item['slug'] ?? '';
186 $parent_slug = $item['parent_slug'] ?? '';
187 unset($item['parent_slug']);
188
189 // Resolve parent_id from the item_type slug using composite key
190 $parent_key = 'item_type:' . $parent_slug;
191 if ($parent_slug && isset($this->classification_ids[$parent_key])) {
192 $item['parent_id'] = $this->classification_ids[$parent_key];
193 } else {
194 $item['parent_id'] = null;
195 if ($parent_slug) {
196 error_log('Yatra Sample Data: Item type not found for item parent_slug: ' . $parent_slug);
197 }
198 }
199
200 $item['created_at'] = $item['created_at'] ?? $now;
201 $item['updated_at'] = $item['updated_at'] ?? $now;
202 $item['created_by'] = $item['created_by'] ?? $uid;
203 $item['updated_by'] = $item['updated_by'] ?? $uid;
204 $item['level'] = $item['level'] ?? 0;
205 $item['sorting'] = $item['sorting'] ?? 0;
206 $item['is_featured'] = $item['is_featured'] ?? 0;
207
208 if (isset($item['metadata']) && is_array($item['metadata'])) {
209 $item['metadata'] = json_encode($item['metadata']);
210 }
211
212 $result = $this->wpdb->insert($this->classifications_table, $item);
213
214 if ($result && $this->wpdb->insert_id) {
215 $inserted++;
216 $id = $this->wpdb->insert_id;
217 if ($slug) {
218 $this->classification_ids['item:' . $slug] = $id;
219 $this->slug_to_id[$slug] = $id;
220 }
221 }
222 }
223
224 return $inserted;
225 }
226
227 /**
228 * Insert trip-classification pivot records.
229 * Links trips to their categories, activities, destinations, difficulty, traveler_types.
230 */
231 public function insert_trip_classifications($data, $trip_ids)
232 {
233 $inserted = 0;
234 $now = current_time('mysql');
235
236 foreach ($data as $mapping) {
237 $trip_slug = $mapping['trip_slug'] ?? '';
238 if (!isset($trip_ids[$trip_slug])) {
239 continue;
240 }
241 $trip_id = $trip_ids[$trip_slug];
242
243 $classifications = $mapping['classifications'] ?? [];
244
245 // Track index per classification type so first of each type gets 'primary'
246 // This matches how TripRepository::saveDestinations/saveActivities works
247 $type_counters = [];
248
249 foreach ($classifications as $classification) {
250 $cls_slug = $classification['slug'] ?? '';
251 $cls_type = $classification['type'] ?? '';
252
253 if (!$cls_slug || !$cls_type) {
254 continue;
255 }
256
257 // Look up the classification ID using composite key (type:slug)
258 $composite_key = $cls_type . ':' . $cls_slug;
259 if (!isset($this->classification_ids[$composite_key])) {
260 // Try to find it in DB if not tracked (might already exist)
261 $existing = $this->wpdb->get_var($this->wpdb->prepare(
262 "SELECT id FROM {$this->classifications_table} WHERE slug = %s AND type = %s LIMIT 1",
263 $cls_slug, $cls_type
264 ));
265 if ($existing) {
266 $this->classification_ids[$composite_key] = (int) $existing;
267 } else {
268 error_log('Yatra Sample Data: Classification not found: ' . $composite_key);
269 continue;
270 }
271 }
272
273 $classification_id = $this->classification_ids[$composite_key];
274
275 // Per-type index for relationship_type and sort_order
276 if (!isset($type_counters[$cls_type])) {
277 $type_counters[$cls_type] = 0;
278 }
279 $type_index = $type_counters[$cls_type];
280 $type_counters[$cls_type]++;
281
282 $metadata_json = null;
283 if ($cls_type === 'attribute') {
284 if (!empty($classification['metadata']) && is_array($classification['metadata'])) {
285 $metadata_json = wp_json_encode($classification['metadata']);
286 } elseif (array_key_exists('value', $classification) || !empty($classification['field_type'])) {
287 $metadata_json = wp_json_encode([
288 'field_type' => $classification['field_type'] ?? 'text_field',
289 'value' => $classification['value'] ?? '',
290 ]);
291 }
292 } elseif (!empty($classification['metadata'])) {
293 $meta = $classification['metadata'];
294 $metadata_json = is_string($meta) ? $meta : wp_json_encode($meta);
295 }
296
297 $result = $this->wpdb->insert(
298 $this->trip_classifications_table,
299 [
300 'trip_id' => $trip_id,
301 'classification_id' => $classification_id,
302 'classification_type' => $cls_type,
303 'relationship_type' => $type_index === 0 ? 'primary' : 'secondary',
304 'metadata' => $metadata_json,
305 'sort_order' => $type_index,
306 'is_featured' => $type_index === 0 ? 1 : 0,
307 'is_active' => 1,
308 'created_at' => $now,
309 'updated_at' => $now,
310 ],
311 ['%d', '%d', '%s', '%s', '%s', '%d', '%d', '%d', '%s', '%s']
312 );
313
314 if ($result) {
315 $inserted++;
316 }
317 }
318 }
319
320 return $inserted;
321 }
322
323 /**
324 * Insert discounts
325 */
326 public function insert_discounts($data)
327 {
328 $inserted = 0;
329 $now = current_time('mysql');
330 $future = date('Y-m-d H:i:s', strtotime('+6 months'));
331 $uid = get_current_user_id();
332
333 foreach ($data as $item) {
334 $item['created_at'] = $item['created_at'] ?? $now;
335 $item['updated_at'] = $item['updated_at'] ?? $now;
336 $item['created_by'] = $item['created_by'] ?? $uid;
337 $item['updated_by'] = $item['updated_by'] ?? $uid;
338 $item['usage_count'] = $item['usage_count'] ?? 0;
339 $item['usage_limit_per_customer'] = $item['usage_limit_per_customer'] ?? 0;
340 $item['applicable_to'] = $item['applicable_to'] ?? 'all';
341 $item['first_time_customer_only'] = $item['first_time_customer_only'] ?? 0;
342 $item['is_group_discount'] = $item['is_group_discount'] ?? 0;
343 $item['discount_mode'] = $item['discount_mode'] ?? 'both';
344 $item['valid_from'] = $item['valid_from'] ?? $now;
345 $item['expiry_date'] = $item['expiry_date'] ?? $future;
346
347 $result = $this->wpdb->insert($this->discounts_table, $item);
348 if ($result) {
349 $inserted++;
350 }
351 }
352
353 return $inserted;
354 }
355
356 /**
357 * Insert trips. Returns slug => id mapping.
358 */
359 public function insert_trips($data)
360 {
361 $trip_ids = [];
362 $now = current_time('mysql');
363 $uid = get_current_user_id();
364
365 foreach ($data as $item) {
366 $trip_slug = $item['slug'];
367
368 $item['created_at'] = $item['created_at'] ?? $now;
369 $item['updated_at'] = $item['updated_at'] ?? $now;
370 $item['created_by'] = $item['created_by'] ?? $uid;
371 $item['updated_by'] = $item['updated_by'] ?? $uid;
372
373 // Resolve price_types: convert traveler_category slugs to category_id
374 // and map field names (price→original_price, sale_price→discounted_price)
375 if (!empty($item['price_types'])) {
376 $price_types_raw = $item['price_types'];
377
378 // Decode if it's a JSON string
379 if (is_string($price_types_raw)) {
380 $price_types_raw = json_decode($price_types_raw, true);
381 }
382
383 if (is_array($price_types_raw) && !empty($price_types_raw)) {
384 $resolved_price_types = [];
385 foreach ($price_types_raw as $pt) {
386 $resolved = [];
387
388 // Resolve traveler_category slug to numeric category_id
389 // Composite key uses 'traveler_type:' (matches type field in classifications table)
390 if (isset($pt['traveler_category'])) {
391 $slug = $pt['traveler_category'];
392 $composite_key = 'traveler_type:' . $slug;
393 if (isset($this->classification_ids[$composite_key])) {
394 $resolved['category_id'] = $this->classification_ids[$composite_key];
395 } elseif (isset($this->slug_to_id[$slug])) {
396 $resolved['category_id'] = $this->slug_to_id[$slug];
397 } else {
398 // Keep slug as label fallback
399 $resolved['category_id'] = null;
400 $resolved['label'] = ucfirst(str_replace('-', ' ', $slug));
401 }
402 } elseif (isset($pt['category_id'])) {
403 $resolved['category_id'] = $pt['category_id'];
404 }
405
406 // Map field names: price → original_price, sale_price → discounted_price
407 $resolved['original_price'] = (float) ($pt['original_price'] ?? $pt['price'] ?? 0);
408 $resolved['discounted_price'] = (float) ($pt['discounted_price'] ?? $pt['sale_price'] ?? 0);
409
410 if (isset($resolved['label'])) {
411 // Keep label for fallback display
412 }
413
414 $resolved_price_types[] = $resolved;
415 }
416
417 $item['price_types'] = wp_json_encode($resolved_price_types);
418
419 // Auto-set pricing_type to traveler_based when price_types exist
420 if (empty($item['pricing_type']) || $item['pricing_type'] === 'regular') {
421 $item['pricing_type'] = 'traveler_based';
422 }
423 } else {
424 $item['price_types'] = null;
425 }
426 }
427
428 // Ensure other JSON fields are properly encoded
429 foreach (['included_items', 'excluded_items', 'frontend_tabs', 'custom_fields'] as $jsonField) {
430 if (isset($item[$jsonField]) && is_array($item[$jsonField])) {
431 $item[$jsonField] = wp_json_encode($item[$jsonField]);
432 }
433 }
434
435 $result = $this->wpdb->insert($this->trips_table, $item);
436
437 if ($result && $this->wpdb->insert_id) {
438 $trip_ids[$trip_slug] = $this->wpdb->insert_id;
439 } else {
440 error_log('Yatra Sample Data: Failed to insert trip: ' . $trip_slug . ' - ' . $this->wpdb->last_error);
441 }
442 }
443
444 return $trip_ids;
445 }
446
447 /**
448 * Insert availability dates
449 */
450 public function insert_availability_dates($data, $trip_ids)
451 {
452 $inserted = 0;
453
454 foreach ($data as $item) {
455 $trip_slug = $item['trip_slug'];
456 unset($item['trip_slug']);
457
458 if (!isset($trip_ids[$trip_slug])) {
459 continue;
460 }
461 $item['trip_id'] = $trip_ids[$trip_slug];
462
463 if (empty($item['return_date']) && !empty($item['arrival_date'])) {
464 $item['return_date'] = $item['arrival_date'];
465 }
466
467 $result = $this->wpdb->insert($this->availability_dates_table, $item);
468 if ($result) {
469 $inserted++;
470 }
471 }
472
473 return $inserted;
474 }
475
476 /**
477 * Insert availability rules
478 */
479 public function insert_availability_rules($data, $trip_ids)
480 {
481 $inserted = 0;
482
483 foreach ($data as $item) {
484 $trip_slug = $item['trip_slug'];
485 unset($item['trip_slug']);
486
487 if (!isset($trip_ids[$trip_slug])) {
488 continue;
489 }
490 $item['trip_id'] = $trip_ids[$trip_slug];
491
492 // Convert arrays to JSON
493 if (isset($item['days_of_week']) && is_array($item['days_of_week'])) {
494 $item['days_of_week'] = json_encode($item['days_of_week']);
495 }
496 if (isset($item['recurrence_pattern']) && is_array($item['recurrence_pattern'])) {
497 $item['recurrence_pattern'] = json_encode($item['recurrence_pattern']);
498 }
499
500 $result = $this->wpdb->insert($this->availability_rules_table, $item);
501 if ($result) {
502 $inserted++;
503 }
504 }
505
506 return $inserted;
507 }
508
509 /**
510 * Insert itinerary days. Returns composite key => day_id mapping.
511 */
512 public function insert_itinerary_days($data, $trip_ids)
513 {
514 $day_ids = [];
515 $now = current_time('mysql');
516 $uid = get_current_user_id();
517
518 foreach ($data as $item) {
519 $trip_slug = $item['trip_slug'];
520 unset($item['trip_slug']);
521
522 if (!isset($trip_ids[$trip_slug])) {
523 continue;
524 }
525 $item['trip_id'] = $trip_ids[$trip_slug];
526 $item['created_at'] = $item['created_at'] ?? $now;
527 $item['updated_at'] = $item['updated_at'] ?? $now;
528 $item['created_by'] = $item['created_by'] ?? $uid;
529 $item['updated_by'] = $item['updated_by'] ?? $uid;
530
531 $result = $this->wpdb->insert($this->itinerary_days_table, $item);
532
533 if ($result && $this->wpdb->insert_id) {
534 $key = $trip_slug . '_day_' . $item['day_number'];
535 $day_ids[$key] = $this->wpdb->insert_id;
536 }
537 }
538
539 return $day_ids;
540 }
541
542 /**
543 * Insert itinerary entries with proper item_type_id and item_id resolution.
544 * Uses item_type slug from JSON to look up real classification IDs.
545 */
546 public function insert_itinerary_entries($data, $trip_ids, $day_ids)
547 {
548 $inserted = 0;
549 $now = current_time('mysql');
550 $uid = get_current_user_id();
551
552 // Build a map of item_type slug => {id, name, icon} from classifications
553 $item_type_map = $this->build_item_type_map();
554 // Build a map of item slug => {id, name, icon, parent_id} from classifications
555 $item_map = $this->build_item_map();
556
557 foreach ($data as $item) {
558 $trip_slug = $item['trip_slug'];
559 $day_number = $item['day_number'];
560 unset($item['trip_slug'], $item['day_number']);
561
562 if (!isset($trip_ids[$trip_slug])) {
563 continue;
564 }
565 $day_key = $trip_slug . '_day_' . $day_number;
566 if (!isset($day_ids[$day_key])) {
567 continue;
568 }
569
570 $item['trip_id'] = $trip_ids[$trip_slug];
571 $item['day_id'] = $day_ids[$day_key];
572
573 // Resolve item_type field to real item_type_id, item_name, item_icon
574 // Note: item_type is also a real DB column (varchar(50)) so we keep it
575 $item_type_slug = $item['item_type'] ?? null;
576 if ($item_type_slug && isset($item_type_map[$item_type_slug])) {
577 $typeInfo = $item_type_map[$item_type_slug];
578 $item['item_type_id'] = $typeInfo['id'];
579 $item['item_name'] = $item['item_name'] ?? $typeInfo['name'];
580 $item['item_icon'] = $item['item_icon'] ?? $typeInfo['icon'];
581
582 // Try to find a matching item for this item_type
583 $matched_item = $this->find_best_item_match($item_type_slug, $item['title'] ?? '', $item_map);
584 if ($matched_item) {
585 $item['item_id'] = $matched_item['id'];
586 $item['item_name'] = $matched_item['name'];
587 $item['item_icon'] = $matched_item['icon'] ?: ($item['item_icon'] ?? null);
588 }
589 }
590
591 $item['created_at'] = $item['created_at'] ?? $now;
592 $item['updated_at'] = $item['updated_at'] ?? $now;
593 $item['created_by'] = $item['created_by'] ?? $uid;
594 $item['updated_by'] = $item['updated_by'] ?? $uid;
595
596 // Convert JSON fields if arrays
597 foreach (['included_items', 'excluded_items', 'gallery'] as $jsonField) {
598 if (isset($item[$jsonField]) && is_array($item[$jsonField])) {
599 $item[$jsonField] = json_encode($item[$jsonField]);
600 }
601 }
602
603 $result = $this->wpdb->insert($this->itinerary_entries_table, $item);
604 if ($result) {
605 $inserted++;
606 } else {
607 error_log('Yatra Sample Data: Failed to insert itinerary entry: ' . ($item['title'] ?? 'unknown') . ' - ' . $this->wpdb->last_error);
608 }
609 }
610
611 return $inserted;
612 }
613
614 /**
615 * Build item_type slug => info map from DB
616 */
617 private function build_item_type_map()
618 {
619 $map = [];
620 $rows = $this->wpdb->get_results(
621 "SELECT id, slug, name, icon FROM {$this->classifications_table} WHERE type = 'item_type'"
622 );
623 foreach ($rows as $row) {
624 $map[$row->slug] = [
625 'id' => (int) $row->id,
626 'name' => $row->name,
627 'icon' => $row->icon,
628 ];
629 }
630 return $map;
631 }
632
633 /**
634 * Build item slug => info map from DB (type='item')
635 */
636 private function build_item_map()
637 {
638 $map = [];
639 $rows = $this->wpdb->get_results(
640 "SELECT id, slug, name, icon, parent_id FROM {$this->classifications_table} WHERE type = 'item'"
641 );
642 foreach ($rows as $row) {
643 $map[$row->slug] = [
644 'id' => (int) $row->id,
645 'name' => $row->name,
646 'icon' => $row->icon,
647 'parent_id' => (int) $row->parent_id,
648 ];
649 }
650 return $map;
651 }
652
653 /**
654 * Find the best matching item for an itinerary entry based on item_type and title
655 */
656 private function find_best_item_match($item_type_slug, $entry_title, $item_map)
657 {
658 // Get the item_type_id for this slug using composite key
659 $item_type_id = $this->classification_ids['item_type:' . $item_type_slug] ?? null;
660 if (!$item_type_id) {
661 return null;
662 }
663
664 // Filter items that belong to this item_type (via parent_id)
665 $candidates = [];
666 foreach ($item_map as $slug => $info) {
667 if ($info['parent_id'] === $item_type_id) {
668 $candidates[$slug] = $info;
669 }
670 }
671
672 if (empty($candidates)) {
673 return null;
674 }
675
676 // Simple keyword matching
677 $title_lower = strtolower($entry_title);
678 foreach ($candidates as $slug => $info) {
679 $name_lower = strtolower($info['name']);
680 $slug_words = explode('-', $slug);
681
682 // Check if any word from slug appears in the entry title
683 foreach ($slug_words as $word) {
684 if (strlen($word) > 2 && strpos($title_lower, $word) !== false) {
685 return $info;
686 }
687 }
688 // Check if item name appears in title
689 if (strpos($title_lower, $name_lower) !== false) {
690 return $info;
691 }
692 }
693
694 // Return first candidate as fallback
695 return reset($candidates);
696 }
697
698 /**
699 * Cleanup all sample data
700 */
701 public function cleanup_sample_data()
702 {
703 $results = [];
704
705 // Get trip IDs first for cleaning pivot tables
706 $trip_slugs = [
707 'swiss-alps-mountain-trek', 'maldives-beach-escape', 'kyoto-cultural-journey',
708 'serengeti-wildlife-safari', 'paris-city-explorer', 'bali-island-adventure',
709 'iceland-northern-lights', 'new-zealand-adventure', 'peru-machu-picchu-trek',
710 'norway-fjords-cruise', 'grand-canyon-day-tour', 'paris-city-highlights-tour',
711 'nyc-helicopter-liberty-tour', 'tokyo-cultural-food-tour'
712 ];
713 $slugs_string = "'" . implode("','", $trip_slugs) . "'";
714
715 // Get trip IDs before deleting
716 $trip_ids = $this->wpdb->get_col(
717 "SELECT id FROM {$this->trips_table} WHERE slug IN ({$slugs_string})"
718 );
719
720 if (!empty($trip_ids)) {
721 $trip_ids_string = implode(',', array_map('intval', $trip_ids));
722
723 // Clean pivot table
724 $this->wpdb->query(
725 "DELETE FROM {$this->trip_classifications_table} WHERE trip_id IN ({$trip_ids_string})"
726 );
727
728 // Clean itinerary entries
729 $this->wpdb->query(
730 "DELETE FROM {$this->itinerary_entries_table} WHERE trip_id IN ({$trip_ids_string})"
731 );
732
733 // Clean itinerary days
734 $this->wpdb->query(
735 "DELETE FROM {$this->itinerary_days_table} WHERE trip_id IN ({$trip_ids_string})"
736 );
737
738 // Clean availability dates
739 $this->wpdb->query(
740 "DELETE FROM {$this->availability_dates_table} WHERE trip_id IN ({$trip_ids_string})"
741 );
742
743 // Clean availability rules
744 $this->wpdb->query(
745 "DELETE FROM {$this->availability_rules_table} WHERE trip_id IN ({$trip_ids_string})"
746 );
747
748 // Clean trip content
749 $this->wpdb->query(
750 "DELETE FROM {$this->trip_content_table} WHERE trip_id IN ({$trip_ids_string})"
751 );
752 }
753
754 // Delete trips
755 $result = $this->wpdb->query(
756 "DELETE FROM {$this->trips_table} WHERE slug IN ({$slugs_string})"
757 );
758 $results['trips'] = $result !== false;
759
760 // All classification slugs including items
761 $classification_slugs = [
762 'adventure-tours', 'beach-island', 'cultural-tours', 'wildlife-safari',
763 'city-tours', 'trekking-hiking', 'cruise-tours', 'food-wine',
764 'hiking', 'snorkeling', 'city-walking-tour', 'wildlife-viewing',
765 'kayaking', 'photography', 'camping', 'rock-climbing', 'cycling', 'cooking-class',
766 'swiss-alps', 'maldives', 'kyoto-japan', 'serengeti-tanzania', 'paris-france',
767 'bali-indonesia', 'iceland', 'new-zealand', 'peru', 'norway',
768 'easy', 'moderate', 'challenging', 'difficult', 'extreme', 'expert',
769 'group-size', 'age-restriction', 'fitness-level', 'accommodation-type',
770 'meal-plan', 'transportation', 'guide-language', 'season',
771 'accommodation', 'activity', 'meal', 'sightseeing', 'free-time',
772 'adult', 'child', 'infant', 'student', 'senior', 'group-leader', 'family', 'solo-traveler',
773 'mountain-hut', '5-star-hotel', 'beach-resort', 'safari-lodge', 'tented-camp',
774 'guided-hiking', 'snorkeling-trip', 'temple-visit', 'game-drive',
775 'city-walking-tour-item', 'cooking-class-item',
776 'breakfast', 'lunch', 'dinner', 'welcome-dinner',
777 'airport-transfer', 'coach-transfer', 'boat-transfer',
778 'landmark-visit', 'scenic-viewpoint',
779 'rest-and-explore', 'shopping-time'
780 ];
781
782 $class_slugs_string = "'" . implode("','", $classification_slugs) . "'";
783 $result = $this->wpdb->query(
784 "DELETE FROM {$this->classifications_table} WHERE slug IN ({$class_slugs_string})"
785 );
786 $results['classifications'] = $result !== false;
787
788 // Clean discounts
789 $discount_codes = ['SUMMER2024', 'EARLYBIRD', 'GROUP10', 'WELCOME50',
790 'FAMILY20', 'LASTMINUTE', 'LOYAL100', 'WINTER2024'];
791 $codes_string = "'" . implode("','", $discount_codes) . "'";
792 $result = $this->wpdb->query(
793 "DELETE FROM {$this->discounts_table} WHERE code IN ({$codes_string})"
794 );
795 $results['discounts'] = $result !== false;
796
797 // Clear import flags
798 delete_option('yatra_sample_data_imported');
799 delete_option('yatra_sample_data_import_date');
800
801 return $results;
802 }
803 }
804