| 1 |
<?php |
| 2 |
|
| 3 |
namespace Yatra\Migration; |
| 4 |
|
| 5 |
use Yatra\Database\Tables\EnquiriesTable; |
| 6 |
use Yatra\Migration\MigrationProgress; |
| 7 |
use Yatra\Utils\Logger; |
| 8 |
|
| 9 |
/** |
| 10 |
* EnquiryMigration - Migrates enquiries from old Yatra custom table to new table. |
| 11 |
* |
| 12 |
* Old system: wp_yatra_tour_enquiries |
| 13 |
* Columns: id, tour_id, fullname, email, country, phone_number, |
| 14 |
* number_of_adults, number_of_childs, message, subject, |
| 15 |
* additional_fields, ip_address, created_at |
| 16 |
* |
| 17 |
* New system: wp_yatra_enquiries |
| 18 |
* Columns: id, trip_id, name, email, phone, message, travel_date, |
| 19 |
* metadata (JSON), travelers_count, status, response_notes, |
| 20 |
* responded_at, responded_by, ip_address, user_agent, source, |
| 21 |
* created_at, updated_at, created_by |
| 22 |
* |
| 23 |
* Field mapping: |
| 24 |
* tour_id → trip_id (looked up via _migrated_to_trip_id post meta) |
| 25 |
* fullname → name |
| 26 |
* email → email |
| 27 |
* phone_number → phone |
| 28 |
* number_of_adults + number_of_childs → travelers_count + metadata.traveler_categories |
| 29 |
* country → metadata.country |
| 30 |
* subject → metadata.subject |
| 31 |
* additional_fields → metadata.additional_fields |
| 32 |
* ip_address → ip_address |
| 33 |
* created_at → created_at / updated_at |
| 34 |
*/ |
| 35 |
class EnquiryMigration extends BaseMigration |
| 36 |
{ |
| 37 |
public function __construct(MigrationProgress $service) |
| 38 |
{ |
| 39 |
parent::__construct($service); |
| 40 |
} |
| 41 |
|
| 42 |
public function run(): array |
| 43 |
{ |
| 44 |
$migrated = 0; |
| 45 |
$skipped = 0; |
| 46 |
$failed = 0; |
| 47 |
|
| 48 |
$oldTable = $this->wpdb->prefix . 'yatra_tour_enquiries'; |
| 49 |
|
| 50 |
// Bail early if the old table does not exist. |
| 51 |
if ($this->wpdb->get_var("SHOW TABLES LIKE '{$oldTable}'") !== $oldTable) { |
| 52 |
Logger::info('No old yatra_tour_enquiries table found — skipping enquiry migration.', [ |
| 53 |
'source' => 'migration', |
| 54 |
]); |
| 55 |
return compact('migrated', 'skipped', 'failed'); |
| 56 |
} |
| 57 |
|
| 58 |
$oldEnquiries = $this->wpdb->get_results("SELECT * FROM {$oldTable} ORDER BY id ASC"); |
| 59 |
$total = count($oldEnquiries); |
| 60 |
|
| 61 |
if ($total === 0) { |
| 62 |
Logger::info('Old enquiry table exists but is empty.', ['source' => 'migration']); |
| 63 |
return compact('migrated', 'skipped', 'failed'); |
| 64 |
} |
| 65 |
|
| 66 |
Logger::info("Found {$total} enquiries to migrate.", [ |
| 67 |
'source' => 'migration', |
| 68 |
'count' => $total, |
| 69 |
]); |
| 70 |
|
| 71 |
$newTable = EnquiriesTable::getTableName(); |
| 72 |
|
| 73 |
foreach ($oldEnquiries as $enquiry) { |
| 74 |
try { |
| 75 |
$oldId = (int) $enquiry->id; |
| 76 |
$email = sanitize_email($enquiry->email ?? ''); |
| 77 |
|
| 78 |
if (empty($email)) { |
| 79 |
$skipped++; |
| 80 |
Logger::debug("Enquiry #{$oldId} skipped — no email address.", ['source' => 'migration']); |
| 81 |
$this->updateProgress('enquiries', 'running', $migrated, $skipped, $failed, $total, null, null); |
| 82 |
continue; |
| 83 |
} |
| 84 |
|
| 85 |
// ---------- Resolve new trip_id ---------- |
| 86 |
$oldTourId = (int) ($enquiry->tour_id ?? 0); |
| 87 |
$newTripId = null; |
| 88 |
|
| 89 |
if ($oldTourId > 0) { |
| 90 |
$newTripId = $this->getMigratedTripId($oldTourId); |
| 91 |
|
| 92 |
// Fallback: look up by the old post title in the new trips table. |
| 93 |
if (!$newTripId) { |
| 94 |
$oldTourTitle = $this->wpdb->get_var($this->wpdb->prepare( |
| 95 |
"SELECT post_title FROM {$this->wpdb->posts} |
| 96 |
WHERE ID = %d AND post_type = 'tour' LIMIT 1", |
| 97 |
$oldTourId |
| 98 |
)); |
| 99 |
|
| 100 |
if ($oldTourTitle) { |
| 101 |
$newTripId = $this->wpdb->get_var($this->wpdb->prepare( |
| 102 |
"SELECT id FROM " . \Yatra\Database\Tables\TripsTable::getTableName() . " |
| 103 |
WHERE title = %s LIMIT 1", |
| 104 |
$oldTourTitle |
| 105 |
)); |
| 106 |
} |
| 107 |
} |
| 108 |
} |
| 109 |
|
| 110 |
// ---------- Duplicate detection ---------- |
| 111 |
// Consider an enquiry a duplicate when the same email submitted for |
| 112 |
// the same (possibly null) trip_id on the same day. |
| 113 |
$createdDate = !empty($enquiry->created_at) |
| 114 |
? date('Y-m-d', strtotime($enquiry->created_at)) |
| 115 |
: null; |
| 116 |
|
| 117 |
if (!$this->isForceMigration()) { |
| 118 |
if ($newTripId) { |
| 119 |
$existingId = $this->wpdb->get_var($this->wpdb->prepare( |
| 120 |
"SELECT id FROM {$newTable} |
| 121 |
WHERE email = %s AND trip_id = %d AND DATE(created_at) = %s |
| 122 |
LIMIT 1", |
| 123 |
$email, |
| 124 |
$newTripId, |
| 125 |
$createdDate ?? date('Y-m-d') |
| 126 |
)); |
| 127 |
} else { |
| 128 |
$existingId = $this->wpdb->get_var($this->wpdb->prepare( |
| 129 |
"SELECT id FROM {$newTable} |
| 130 |
WHERE email = %s AND trip_id IS NULL AND DATE(created_at) = %s |
| 131 |
LIMIT 1", |
| 132 |
$email, |
| 133 |
$createdDate ?? date('Y-m-d') |
| 134 |
)); |
| 135 |
} |
| 136 |
|
| 137 |
if ($existingId) { |
| 138 |
$skipped++; |
| 139 |
Logger::debug("Enquiry #{$oldId} skipped — already migrated (new ID {$existingId}).", [ |
| 140 |
'source' => 'migration', |
| 141 |
]); |
| 142 |
$this->updateProgress('enquiries', 'running', $migrated, $skipped, $failed, $total, null, null); |
| 143 |
continue; |
| 144 |
} |
| 145 |
} |
| 146 |
|
| 147 |
// ---------- Traveler counts ---------- |
| 148 |
$adults = (int) ($enquiry->number_of_adults ?? 0); |
| 149 |
$children = (int) ($enquiry->number_of_childs ?? 0); |
| 150 |
$travelersCount = $adults + $children; |
| 151 |
|
| 152 |
// ---------- Build metadata JSON ---------- |
| 153 |
$metadata = []; |
| 154 |
|
| 155 |
if (!empty($enquiry->country)) { |
| 156 |
$metadata['country'] = sanitize_text_field($enquiry->country); |
| 157 |
} |
| 158 |
|
| 159 |
if (!empty($enquiry->subject)) { |
| 160 |
$metadata['subject'] = sanitize_text_field($enquiry->subject); |
| 161 |
} |
| 162 |
|
| 163 |
if ($adults > 0 || $children > 0) { |
| 164 |
$metadata['traveler_categories'] = []; |
| 165 |
if ($adults > 0) { |
| 166 |
$metadata['traveler_categories'][] = [ |
| 167 |
'title' => 'Adult', |
| 168 |
'count' => $adults, |
| 169 |
]; |
| 170 |
} |
| 171 |
if ($children > 0) { |
| 172 |
$metadata['traveler_categories'][] = [ |
| 173 |
'title' => 'Child', |
| 174 |
'count' => $children, |
| 175 |
]; |
| 176 |
} |
| 177 |
} |
| 178 |
|
| 179 |
if (!empty($enquiry->additional_fields)) { |
| 180 |
$parsed = maybe_unserialize($enquiry->additional_fields); |
| 181 |
$metadata['additional_fields'] = is_array($parsed) |
| 182 |
? $parsed |
| 183 |
: $enquiry->additional_fields; |
| 184 |
} |
| 185 |
|
| 186 |
$metadataJson = !empty($metadata) ? wp_json_encode($metadata) : null; |
| 187 |
|
| 188 |
// ---------- Normalise created_at ---------- |
| 189 |
$createdAt = null; |
| 190 |
if (!empty($enquiry->created_at)) { |
| 191 |
$ts = strtotime($enquiry->created_at); |
| 192 |
$createdAt = $ts ? date('Y-m-d H:i:s', $ts) : current_time('mysql'); |
| 193 |
} else { |
| 194 |
$createdAt = current_time('mysql'); |
| 195 |
} |
| 196 |
|
| 197 |
// ---------- Build insert row ---------- |
| 198 |
$insertData = [ |
| 199 |
'trip_id' => $newTripId ?: null, |
| 200 |
'name' => sanitize_text_field($enquiry->fullname ?? ''), |
| 201 |
'email' => $email, |
| 202 |
'phone' => sanitize_text_field($enquiry->phone_number ?? ''), |
| 203 |
'message' => wp_kses_post($enquiry->message ?? ''), |
| 204 |
'travel_date' => null, |
| 205 |
'metadata' => $metadataJson, |
| 206 |
'travelers_count' => $travelersCount > 0 ? $travelersCount : null, |
| 207 |
'status' => 'new', |
| 208 |
'ip_address' => sanitize_text_field($enquiry->ip_address ?? ''), |
| 209 |
'source' => 'website', |
| 210 |
'created_at' => $createdAt, |
| 211 |
'updated_at' => $createdAt, |
| 212 |
]; |
| 213 |
|
| 214 |
$inserted = $this->wpdb->insert($newTable, $insertData); |
| 215 |
|
| 216 |
if ($inserted) { |
| 217 |
$migrated++; |
| 218 |
Logger::debug("Enquiry #{$oldId} migrated as new enquiry #{$this->wpdb->insert_id}.", [ |
| 219 |
'source' => 'migration', |
| 220 |
'old_tour_id' => $oldTourId, |
| 221 |
'new_trip_id' => $newTripId, |
| 222 |
'email' => $email, |
| 223 |
]); |
| 224 |
} else { |
| 225 |
$failed++; |
| 226 |
Logger::error("Failed to insert enquiry #{$oldId}: {$this->wpdb->last_error}", [ |
| 227 |
'source' => 'migration', |
| 228 |
'old_id' => $oldId, |
| 229 |
'email' => $email, |
| 230 |
'error' => $this->wpdb->last_error, |
| 231 |
]); |
| 232 |
} |
| 233 |
|
| 234 |
$this->updateProgress('enquiries', 'running', $migrated, $skipped, $failed, $total, null, null); |
| 235 |
|
| 236 |
} catch (\Throwable $e) { |
| 237 |
$failed++; |
| 238 |
Logger::error("Exception migrating enquiry #{$enquiry->id}: {$e->getMessage()}", [ |
| 239 |
'source' => 'migration', |
| 240 |
'old_id' => $enquiry->id ?? 'unknown', |
| 241 |
'error' => $e->getMessage(), |
| 242 |
]); |
| 243 |
$this->updateProgress('enquiries', 'running', $migrated, $skipped, $failed, $total, null, null); |
| 244 |
} |
| 245 |
} |
| 246 |
|
| 247 |
Logger::info("Enquiry migration complete.", [ |
| 248 |
'source' => 'migration', |
| 249 |
'migrated' => $migrated, |
| 250 |
'skipped' => $skipped, |
| 251 |
'failed' => $failed, |
| 252 |
]); |
| 253 |
|
| 254 |
return compact('migrated', 'skipped', 'failed'); |
| 255 |
} |
| 256 |
} |
| 257 |
|