PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.16
Yatra – Travel Booking & Tour Operator Software v3.0.16
3.0.16 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 All 84 releases
← All changes | app/Repositories/ItineraryRepository.php +54 -79 3.0.6 → 3.0.16 View file →
@@ -33,8 +33,17 @@
33 33 * @throws \InvalidArgumentException If day exists and $allowExisting is false
34 34 */
35 35 public function getOrCreateDay(int $tripId, int $dayNumber, ?string $dayTitle = null, ?string $dayDescription = null, bool $allowExisting = true): int
36 36 {
37 + // The method body uses $wpdb and $tableDays directly for the day
38 + // update/insert below. The cached-query closures declare their own copies,
39 + // but the body never did — so $wpdb was null ("Call to a member function
40 + // update() on null") and $tableDays was empty ("Incorrect table name '')".
41 + // This broke saving an activity onto an existing, titled day and silently
42 + // mis-created new days. Declare both here.
43 + global $wpdb;
44 + $tableDays = TripItineraryDaysTable::getTableName();
45 +
37 46 // Use QueryCache for caching day existence checks
38 47 $cacheKey = Cache::KEY_DAY_EXISTS . "_{$tripId}_day_{$dayNumber}";
39 48
40 49 $existingDay = $this->cacheQueryResult($cacheKey, function() use ($tripId, $dayNumber) {
@@ -883,8 +892,16 @@
883 892
884 893 return $result !== false;
885 894 }
886 895
896 + // Explicit 'day' mode must never fall through to delete an activity.
897 + // Day ids and activity ids come from different tables and can collide
898 + // numerically; if the day no longer exists (e.g. already removed in
899 + // another tab), bail rather than silently deleting a same-id activity.
900 + if ($mode === 'day') {
901 + return false;
902 + }
903 +
887 904 // Check if this is an activity entry ID (from entries table)
888 905 $activityEntry = $wpdb->get_row(
889 906 $wpdb->prepare("SELECT * FROM `{$tableEntries}` WHERE id = %d", $id)
890 907 );
@@ -903,100 +920,58 @@
903 920 return false;
904 921 }
905 922
906 923 /**
907 - * Bulk delete entries
908 - * @param array $ids Array of entry IDs to delete
924 + * Bulk delete itinerary days and/or activity entries.
925 + *
926 + * Days and activities live in two different tables with independent
927 + * auto-increment id spaces, so a single flat list of ids is ambiguous
928 + * (the same number can be a valid day id *and* a valid activity id). The
929 + * caller therefore tells us which is which: `$dayIds` are day-table ids
930 + * (deleting one removes the day row and all of its activities) and `$ids`
931 + * are activity-entry ids. Each id is routed through the single-item
932 + * {@see self::delete()} with an explicit mode so the correct table is
933 + * always used.
934 + *
935 + * @param array $ids Activity entry ids (day_entry table)
936 + * @param array $dayIds Day ids (days table); the day and its activities are removed
909 937 * @return array ['deleted' => count, 'failed' => count]
910 938 */
911 - public function bulkDelete(array $ids): array
939 + public function bulkDelete(array $ids, array $dayIds = []): array
912 940 {
913 - global $wpdb;
914 - $tableEntries = $this->getTableName();
915 -
916 - $tableDays = TripItineraryDaysTable::getTableName();
917 -
918 - if (empty($ids)) {
919 - return ['deleted' => 0, 'failed' => 0];
920 - }
921 -
922 - // Sanitize IDs
923 - $ids = array_map('intval', $ids);
924 - $ids = array_filter($ids, function($id) {
925 - return $id > 0;
926 - });
927 -
928 - if (empty($ids)) {
929 - return ['deleted' => 0, 'failed' => 0];
930 - }
931 -
932 941 $deleted = 0;
933 942 $failed = 0;
934 - $processedDayIds = []; // Track days we've already processed
935 943
936 - foreach ($ids as $id) {
944 + // Delete whole days first — this also removes every activity that
945 + // belongs to the day, so any of those activity ids that also appear in
946 + // $ids become harmless no-ops below.
947 + $dayIds = array_unique(array_filter(array_map('intval', $dayIds), static function ($id) {
948 + return $id > 0;
949 + }));
950 + foreach ($dayIds as $dayId) {
937 951 try {
938 - // Get the entry to check if it's a day entry
939 - $entry = $wpdb->get_row(
940 - $wpdb->prepare("SELECT day_id, item_type_id, item_id FROM `{$tableEntries}` WHERE id = %d", $id)
941 - );
942 -
943 - if (!$entry) {
952 + if ($this->delete($dayId, 'day')) {
953 + $deleted++;
954 + } else {
944 955 $failed++;
945 - continue;
946 956 }
957 + } catch (\Throwable $e) {
958 + $failed++;
959 + }
960 + }
947 961
948 - $dayId = (int) $entry->day_id;
949 - $isDayEntry = ($entry->item_type_id === null || $entry->item_type_id === 0) &&
950 - ($entry->item_id === null || $entry->item_id === 0);
951 -
952 - // If this is a day entry, delete all entries for this day
953 - if ($isDayEntry) {
954 - // Skip if we've already processed this day
955 - if (in_array($dayId, $processedDayIds)) {
956 - continue;
957 - }
958 -
959 - $processedDayIds[] = $dayId;
960 -
961 - // Get all entry IDs for this day
962 - $dayEntryIds = $wpdb->get_col(
963 - $wpdb->prepare("SELECT id FROM `{$tableEntries}` WHERE day_id = %d", $dayId)
964 - );
965 -
966 - if (!empty($dayEntryIds)) {
967 - // Delete images for all entries
968 - $placeholders = implode(',', array_fill(0, count($dayEntryIds), '%d'));
969 - $wpdb->query(
970 - $wpdb->prepare(
971 - "DELETE FROM `{$tableImages}` WHERE entry_id IN ($placeholders)",
972 - ...$dayEntryIds
973 - )
974 - );
975 -
976 - // Delete all entries for this day
977 - $wpdb->delete($tableEntries, ['day_id' => $dayId], ['%d']);
978 -
979 - // Delete the day itself
980 - $wpdb->delete($tableDays, ['id' => $dayId], ['%d']);
981 - }
982 -
962 + // Delete standalone activity entries.
963 + $ids = array_unique(array_filter(array_map('intval', $ids), static function ($id) {
964 + return $id > 0;
965 + }));
966 + foreach ($ids as $id) {
967 + try {
968 + if ($this->delete($id, 'activity')) {
983 969 $deleted++;
984 970 } else {
985 - // For activity entries, just delete the entry and its images
986 - // Delete related images
987 - $wpdb->delete($tableImages, ['entry_id' => $id], ['%d']);
988 -
989 - // Delete entry
990 - $result = $wpdb->delete($tableEntries, ['id' => $id], ['%d']);
991 -
992 - if ($result !== false) {
993 - $deleted++;
994 - } else {
995 - $failed++;
996 - }
971 + $failed++;
997 972 }
998 - } catch (\Exception $e) {
973 + } catch (\Throwable $e) {
999 974 $failed++;
1000 975 }
1001 976 }
1002 977