diff --git a/app/Imports/GenericEventsImport.php b/app/Imports/GenericEventsImport.php index 399279758..eb49cf8c9 100644 --- a/app/Imports/GenericEventsImport.php +++ b/app/Imports/GenericEventsImport.php @@ -15,24 +15,49 @@ class GenericEventsImport extends BaseEventsImport implements ToModel, WithCustomValueBinder, WithHeadingRow { + /** + * Cast any float with no fractional part back to int. + */ + protected function normalizeRow(array $row): array + { + foreach ($row as $key => $value) { + if (is_float($value) && floor($value) === $value) { + $row[$key] = (int) $value; + } + } + return $row; + } + + /** + * Handle Excel‐serial or string dates. + */ public function parseDate($value) { if (is_numeric($value)) { - return Date::excelToDateTimeObject($value)->format('Y-m-d H:i:s'); + return Date::excelToDateTimeObject($value) + ->format('Y-m-d H:i:s'); } + try { - return Carbon::parse($value)->format('Y-m-d H:i:s'); + return Carbon::parse($value) + ->format('Y-m-d H:i:s'); } catch (\Exception $e) { Log::warning("Invalid date: {$value}"); return null; } } + /** + * Map a row from the sheet into an Event model. + */ public function model(array $row): ?Model { + // 1) normalize 3.0 → 3, etc. + $row = $this->normalizeRow($row); + Log::info('Importing row:', $row); - // required fields + // 2) required fields if ( empty($row['activity_title']) || empty($row['name_of_organisation']) || @@ -47,102 +72,121 @@ public function model(array $row): ?Model return null; } - // resolve creator_id (if that column exists) + // 3) resolve creator_id if present $creatorId = null; if (isset($row['creator_id']) && $row['creator_id'] !== '') { - $creatorId = is_numeric($row['creator_id']) - ? (int) $row['creator_id'] + $creatorId = is_int($row['creator_id']) + ? $row['creator_id'] : User::where('email', trim($row['creator_id']))->value('id'); } + // 4) build attribute array $attrs = [ - 'status' => 'APPROVED', - 'title' => trim($row['activity_title']), - 'slug' => str_slug(trim($row['activity_title'])), - 'organizer' => trim($row['name_of_organisation']), - 'description' => trim($row['description']), - 'organizer_type' => trim($row['type_of_organisation']), - 'activity_type' => trim($row['activity_type']), - 'location' => isset($row['address']) ? trim($row['address']) : 'online', - 'event_url' => isset($row['organiser_website']) ? trim($row['organiser_website']) : '', - 'user_email' => isset($row['contact_email']) ? trim($row['contact_email']) : '', - 'creator_id' => $creatorId, - 'country_iso' => strtoupper(trim($row['country'])), - 'picture' => isset($row['image_path']) ? trim($row['image_path']) : '', - 'pub_date' => now(), - 'created' => now(), - 'updated' => now(), + 'status' => 'APPROVED', + 'title' => trim($row['activity_title']), + 'slug' => str_slug(trim($row['activity_title'])), + 'organizer' => trim($row['name_of_organisation']), + 'description' => trim($row['description']), + 'organizer_type' => trim($row['type_of_organisation']), + 'activity_type' => trim($row['activity_type']), + 'location' => $row['address'] ?? 'online', + 'event_url' => $row['organiser_website'] ?? '', + 'user_email' => $row['contact_email'] ?? '', + 'creator_id' => $creatorId, + 'country_iso' => strtoupper(trim($row['country'])), + 'picture' => $row['image_path'] ?? '', + 'pub_date' => now(), + 'created' => now(), + 'updated' => now(), 'participants_count' => isset($row['participants_count']) ? (int) $row['participants_count'] : null, - 'mass_added_for' => 'Excel', - 'start_date' => $this->parseDate($row['start_date']), - 'end_date' => $this->parseDate($row['end_date']), - 'geoposition' => (isset($row['latitude'], $row['longitude']) && $row['latitude'] !== '' && $row['longitude'] !== '') - ? $row['latitude'] . ',' . $row['longitude'] - : '', - 'longitude' => isset($row['longitude']) ? trim($row['longitude']) : '', - 'latitude' => isset($row['latitude']) ? trim($row['latitude']) : '', - 'language' => isset($row['language']) - ? strtolower(explode('_', trim($row['language']))[0]) - : 'en', + 'mass_added_for' => 'Excel', + 'start_date' => $this->parseDate($row['start_date']), + 'end_date' => $this->parseDate($row['end_date']), + 'geoposition' => (isset($row['latitude'], $row['longitude']) && $row['latitude'] !== '' && $row['longitude'] !== '') + ? "{$row['latitude']},{$row['longitude']}" + : '', + 'longitude' => $row['longitude'] ?? '', + 'latitude' => $row['latitude'] ?? '', + 'language' => isset($row['language']) + ? strtolower(explode('_', trim($row['language']))[0]) + : 'en', ]; - // optional single‐choice / boolean / counts + // 5) optional singles & counts if (isset($row['recurring_event'])) { - $attrs['recurring_event'] = $this->validateSingleChoice($row['recurring_event'], Event::RECURRING_EVENTS); + $attrs['recurring_event'] = $this->validateSingleChoice( + $row['recurring_event'], + Event::RECURRING_EVENTS + ); } foreach (['males_count','females_count','other_count'] as $c) { if (isset($row[$c])) { $attrs[$c] = (int) $row[$c]; } } + + // 6) optional booleans foreach (['is_extracurricular_event','is_standard_school_curriculum','is_use_resource'] as $b) { if (isset($row[$b])) { $attrs[$b] = $this->parseBool($row[$b]); } } - // multi‐choice arrays + // 7) multi‐choice arrays if (isset($row['activity_format'])) { - $attrs['activity_format'] = $this->validateMultiChoice($row['activity_format'], Event::ACTIVITY_FORMATS); + $attrs['activity_format'] = $this->validateMultiChoice( + $row['activity_format'], + Event::ACTIVITY_FORMATS + ); } if (isset($row['ages'])) { - $attrs['ages'] = $this->validateMultiChoice($row['ages'], Event::AGES); + $attrs['ages'] = $this->validateMultiChoice( + $row['ages'], + Event::AGES + ); } if (isset($row['duration'])) { - $attrs['duration'] = $this->validateSingleChoice($row['duration'], Event::DURATIONS); + $attrs['duration'] = $this->validateSingleChoice( + $row['duration'], + Event::DURATIONS + ); } if (isset($row['recurring_type'])) { - $attrs['recurring_type'] = $this->validateSingleChoice($row['recurring_type'], Event::RECURRING_TYPES); + $attrs['recurring_type'] = $this->validateSingleChoice( + $row['recurring_type'], + Event::RECURRING_TYPES + ); } - // contact_person only if the *DB* has that column and we have a contact_email - if (Schema::hasColumn('events','contact_person') && isset($row['contact_email']) && $row['contact_email'] !== '') { + // 8) contact_person from contact_email, if the column exists + if (Schema::hasColumn('events','contact_person') + && !empty($row['contact_email']) + ) { $attrs['contact_person'] = trim($row['contact_email']); } + // 9) persist & attach try { $event = new Event($attrs); $event->save(); - // pivot attaches - if (isset($row['audience_comma_separated_ids'])) { + // audiences + if (! empty($row['audience_comma_separated_ids'])) { $aud = array_filter( array_unique( array_map('trim', explode(',', $row['audience_comma_separated_ids'])) ), - fn($id) => is_numeric($id) && $id > 0 && $id <= 100 + fn($i) => is_numeric($i) && $i > 0 && $i <= 100 ); - if ($aud) { - $event->audiences()->attach($aud); - } + $event->audiences()->attach($aud); } - if (isset($row['theme_comma_separated_ids'])) { + + // themes + if (! empty($row['theme_comma_separated_ids'])) { $themes = $this->validateThemes($row['theme_comma_separated_ids']); - if (count($themes)) { - $event->themes()->attach($themes); - } + $event->themes()->attach($themes); } return $event; @@ -152,3 +196,4 @@ public function model(array $row): ?Model } } } +