From 3e2dcaccb548b04f5fa110663ecf2b31fb0f4a63 Mon Sep 17 00:00:00 2001 From: bernardhanna Date: Tue, 1 Jul 2025 10:55:07 +0100 Subject: [PATCH 1/4] Add example Excel file and importer update --- app/Imports/GenericEventsImport.php | 168 ++++++++++------------------ 1 file changed, 62 insertions(+), 106 deletions(-) diff --git a/app/Imports/GenericEventsImport.php b/app/Imports/GenericEventsImport.php index eb49cf8c9..4caaab1fd 100644 --- a/app/Imports/GenericEventsImport.php +++ b/app/Imports/GenericEventsImport.php @@ -15,49 +15,24 @@ 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); - // 2) required fields + // required fields if ( empty($row['activity_title']) || empty($row['name_of_organisation']) || @@ -68,132 +43,113 @@ public function model(array $row): ?Model empty($row['start_date']) || empty($row['end_date']) ) { - Log::error('Missing required fields in row, skipping.'); + Log::error('Missing required fields in row, skipping.', $row); return null; } - // 3) resolve creator_id if present + // resolve creator_id $creatorId = null; if (isset($row['creator_id']) && $row['creator_id'] !== '') { - $creatorId = is_int($row['creator_id']) - ? $row['creator_id'] + $creatorId = is_numeric($row['creator_id']) + ? (int)$row['creator_id'] : User::where('email', trim($row['creator_id']))->value('id'); } - // 4) build attribute array + // build a clean 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' => $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' => $row['longitude'] ?? '', - 'latitude' => $row['latitude'] ?? '', - 'language' => isset($row['language']) - ? strtolower(explode('_', trim($row['language']))[0]) - : 'en', + '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' => trim($row['address'] ?? 'online'), + 'event_url' => trim($row['organiser_website'] ?? ''), + 'user_email' => trim($row['contact_email'] ?? ''), + 'creator_id' => $creatorId, + 'country_iso' => strtoupper(trim($row['country'])), + 'picture' => trim($row['image_path'] ?? ''), + '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' => (!empty($row['latitude']) && !empty($row['longitude'])) + ? "{$row['latitude']},{$row['longitude']}" + : '', + 'longitude' => trim($row['longitude'] ?? ''), + 'latitude' => trim($row['latitude'] ?? ''), + 'language' => isset($row['language']) + ? strtolower(explode('_', trim($row['language']))[0]) + : 'en', ]; - // 5) optional singles & counts + // optional single/boolean/count fields 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]; + $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]); } } - // 7) multi‐choice arrays + // multi-choice 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); } - // 8) contact_person from contact_email, if the column exists - if (Schema::hasColumn('events','contact_person') - && !empty($row['contact_email']) - ) { + // contact_person fallback + 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); + // bypass fillable: assign property‐by‐property + $event = new Event; + foreach ($attrs as $key => $value) { + $event->$key = $value; + } $event->save(); - // audiences - if (! empty($row['audience_comma_separated_ids'])) { + // pivot attaches + if (!empty($row['audience_comma_separated_ids'])) { $aud = array_filter( - array_unique( - array_map('trim', explode(',', $row['audience_comma_separated_ids'])) - ), - fn($i) => is_numeric($i) && $i > 0 && $i <= 100 + array_unique(explode(',', $row['audience_comma_separated_ids'])), + fn($i)=> is_numeric($i) && $i > 0 && $i <= 100 ); $event->audiences()->attach($aud); } - - // themes - if (! empty($row['theme_comma_separated_ids'])) { + if (!empty($row['theme_comma_separated_ids'])) { $themes = $this->validateThemes($row['theme_comma_separated_ids']); $event->themes()->attach($themes); } return $event; + } catch (\Exception $e) { - Log::error('Event import failed: '.$e->getMessage()); - return null; + // at least now you’ll see the real exception in your log + Log::error('Event import failed', [ + 'message' => $e->getMessage(), + 'row' => $row, + 'attrs' => $attrs, + 'trace' => $e->getTraceAsString(), + ]); + return null; } } } - From 805a0a2493666fa5dfce9927e5655fe2e1242e44 Mon Sep 17 00:00:00 2001 From: bernardhanna Date: Tue, 1 Jul 2025 11:03:31 +0100 Subject: [PATCH 2/4] Add example Excel file and importer update --- app/Imports/GenericEventsImport.php | 155 ++++++++++++++++++---------- 1 file changed, 99 insertions(+), 56 deletions(-) diff --git a/app/Imports/GenericEventsImport.php b/app/Imports/GenericEventsImport.php index 4caaab1fd..8e9ce84f7 100644 --- a/app/Imports/GenericEventsImport.php +++ b/app/Imports/GenericEventsImport.php @@ -15,10 +15,21 @@ class GenericEventsImport extends BaseEventsImport implements ToModel, WithCustomValueBinder, WithHeadingRow { + protected function normalizeRow(array $row): array + { + foreach ($row as $k => $v) { + if (is_float($v) && floor($v) === $v) { + $row[$k] = (int) $v; + } + } + return $row; + } + 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'); @@ -30,6 +41,7 @@ public function parseDate($value) public function model(array $row): ?Model { + $row = $this->normalizeRow($row); Log::info('Importing row:', $row); // required fields @@ -47,109 +59,140 @@ public function model(array $row): ?Model return null; } - // resolve creator_id + // + // 1) resolve creator_id — only by explicit ID or by local-part of contact_email + // $creatorId = null; - if (isset($row['creator_id']) && $row['creator_id'] !== '') { - $creatorId = is_numeric($row['creator_id']) - ? (int)$row['creator_id'] - : User::where('email', trim($row['creator_id']))->value('id'); + + // 1a) explicit numeric + if (isset($row['creator_id']) && is_int($row['creator_id'])) { + $creatorId = $row['creator_id']; + } + // 1b) explicit email in that column + elseif (!empty($row['creator_id']) && filter_var($row['creator_id'], FILTER_VALIDATE_EMAIL)) { + $creatorId = User::where('email', trim($row['creator_id']))->value('id'); } + // 1c) contact_email—only match on the local-part before the “@” + elseif (!empty($row['contact_email']) && filter_var($row['contact_email'], FILTER_VALIDATE_EMAIL)) { + [$local,] = explode('@', trim($row['contact_email']), 2); + // look for any user whose email starts with that same local-part + $creatorId = User::where('email', 'like', $local.'@%')->value('id'); + } + + // leave $creatorId null if no match — you said that’s OK - // build a clean attribute array + // + // 2) build attributes + // $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' => trim($row['address'] ?? 'online'), - 'event_url' => trim($row['organiser_website'] ?? ''), - 'user_email' => trim($row['contact_email'] ?? ''), - 'creator_id' => $creatorId, - 'country_iso' => strtoupper(trim($row['country'])), - 'picture' => trim($row['image_path'] ?? ''), - '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' => (!empty($row['latitude']) && !empty($row['longitude'])) - ? "{$row['latitude']},{$row['longitude']}" - : '', - 'longitude' => trim($row['longitude'] ?? ''), - 'latitude' => trim($row['latitude'] ?? ''), - 'language' => isset($row['language']) - ? strtolower(explode('_', trim($row['language']))[0]) - : 'en', + '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' => trim($row['address'] ?? 'online'), + 'event_url' => trim($row['organiser_website'] ?? ''), + 'user_email' => trim($row['contact_email'] ?? ''), + 'creator_id' => $creatorId, + 'country_iso' => strtoupper(trim($row['country'])), + 'picture' => trim($row['image_path'] ?? ''), + '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' => (! empty($row['latitude']) && ! empty($row['longitude'])) + ? "{$row['latitude']},{$row['longitude']}" + : '', + 'longitude' => trim($row['longitude'] ?? ''), + 'latitude' => trim($row['latitude'] ?? ''), + 'language' => isset($row['language']) + ? strtolower(explode('_', trim($row['language']))[0]) + : 'en', + 'pub_date' => now(), + 'created' => now(), + 'updated' => now(), ]; - // optional single/boolean/count fields + // 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]; } } + + // 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 + // 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 fallback - if (Schema::hasColumn('events','contact_person') && !empty($row['contact_email'])) { + if (Schema::hasColumn('events','contact_person') && ! empty($row['contact_email'])) { $attrs['contact_person'] = trim($row['contact_email']); } + // persist & attach try { - // bypass fillable: assign property‐by‐property $event = new Event; - foreach ($attrs as $key => $value) { - $event->$key = $value; + foreach ($attrs as $k => $v) { + $event->$k = $v; } $event->save(); - // pivot attaches - if (!empty($row['audience_comma_separated_ids'])) { + if (! empty($row['audience_comma_separated_ids'])) { $aud = array_filter( array_unique(explode(',', $row['audience_comma_separated_ids'])), - fn($i)=> is_numeric($i) && $i > 0 && $i <= 100 + fn($i) => is_numeric($i) && $i > 0 && $i <= 100 ); $event->audiences()->attach($aud); } - if (!empty($row['theme_comma_separated_ids'])) { + + if (! empty($row['theme_comma_separated_ids'])) { $themes = $this->validateThemes($row['theme_comma_separated_ids']); $event->themes()->attach($themes); } return $event; - } catch (\Exception $e) { - // at least now you’ll see the real exception in your log - Log::error('Event import failed', [ - 'message' => $e->getMessage(), - 'row' => $row, - 'attrs' => $attrs, - 'trace' => $e->getTraceAsString(), - ]); - return null; + Log::error('Event import failed: '.$e->getMessage(), $attrs); + return null; } } } From 06a3432fce2351cd6ab5b7ba514718d767efa33e Mon Sep 17 00:00:00 2001 From: bernardhanna Date: Tue, 1 Jul 2025 11:13:29 +0100 Subject: [PATCH 3/4] Add example Excel file and importer update --- app/Imports/GenericEventsImport.php | 68 ++++++++++++++++------------- 1 file changed, 38 insertions(+), 30 deletions(-) diff --git a/app/Imports/GenericEventsImport.php b/app/Imports/GenericEventsImport.php index 8e9ce84f7..c92c5d252 100644 --- a/app/Imports/GenericEventsImport.php +++ b/app/Imports/GenericEventsImport.php @@ -15,6 +15,9 @@ class GenericEventsImport extends BaseEventsImport implements ToModel, WithCustomValueBinder, WithHeadingRow { + /** + * Cast floats with no fractional part back to int (so “3.0” → 3). + */ protected function normalizeRow(array $row): array { foreach ($row as $k => $v) { @@ -25,6 +28,9 @@ protected function normalizeRow(array $row): array return $row; } + /** + * Turn Excel serials or strings into Y-m-d H:i:s + */ public function parseDate($value) { if (is_numeric($value)) { @@ -39,51 +45,53 @@ public function parseDate($value) } } + /** + * Map a row to an Event model (or skip/return null). + */ public function model(array $row): ?Model { + // 1) normalize decimals → ints $row = $this->normalizeRow($row); + Log::info('Importing row:', $row); - // required fields + // 2) required fields guard if ( - empty($row['activity_title']) || + empty($row['activity_title']) || empty($row['name_of_organisation']) || - empty($row['description']) || + empty($row['description']) || empty($row['type_of_organisation']) || - empty($row['activity_type']) || - empty($row['country']) || - empty($row['start_date']) || + empty($row['activity_type']) || + empty($row['country']) || + empty($row['start_date']) || empty($row['end_date']) ) { - Log::error('Missing required fields in row, skipping.', $row); + Log::error('Missing required fields, skipping row.', $row); return null; } - // - // 1) resolve creator_id — only by explicit ID or by local-part of contact_email - // + // 3) resolve creator_id $creatorId = null; - - // 1a) explicit numeric + // a) explicit integer if (isset($row['creator_id']) && is_int($row['creator_id'])) { $creatorId = $row['creator_id']; } - // 1b) explicit email in that column + // b) explicit email in creator_id column elseif (!empty($row['creator_id']) && filter_var($row['creator_id'], FILTER_VALIDATE_EMAIL)) { $creatorId = User::where('email', trim($row['creator_id']))->value('id'); } - // 1c) contact_email—only match on the local-part before the “@” + // c) fallback: match on local part of contact_email elseif (!empty($row['contact_email']) && filter_var($row['contact_email'], FILTER_VALIDATE_EMAIL)) { [$local,] = explode('@', trim($row['contact_email']), 2); - // look for any user whose email starts with that same local-part $creatorId = User::where('email', 'like', $local.'@%')->value('id'); } - // leave $creatorId null if no match — you said that’s OK + // d) final fallback to your chosen ID + if ($creatorId === null) { + $creatorId = 315958; + } - // - // 2) build attributes - // + // 4) build attribute array $attrs = [ 'status' => 'APPROVED', 'title' => trim($row['activity_title']), @@ -104,11 +112,11 @@ public function model(array $row): ?Model 'mass_added_for' => 'Excel', 'start_date' => $this->parseDate($row['start_date']), 'end_date' => $this->parseDate($row['end_date']), - 'geoposition' => (! empty($row['latitude']) && ! empty($row['longitude'])) + 'geoposition' => (!empty($row['latitude']) && !empty($row['longitude'])) ? "{$row['latitude']},{$row['longitude']}" : '', - 'longitude' => trim($row['longitude'] ?? ''), - 'latitude' => trim($row['latitude'] ?? ''), + 'longitude' => trim($row['longitude'] ?? ''), + 'latitude' => trim($row['latitude'] ?? ''), 'language' => isset($row['language']) ? strtolower(explode('_', trim($row['language']))[0]) : 'en', @@ -117,7 +125,7 @@ public function model(array $row): ?Model 'updated' => now(), ]; - // optional singles & counts + // 5) optional singles & counts if (isset($row['recurring_event'])) { $attrs['recurring_event'] = $this->validateSingleChoice( $row['recurring_event'], @@ -130,14 +138,14 @@ public function model(array $row): ?Model } } - // optional booleans + // 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'], @@ -163,12 +171,12 @@ public function model(array $row): ?Model ); } - // contact_person fallback + // 8) contact_person fallback if the column exists if (Schema::hasColumn('events','contact_person') && ! empty($row['contact_email'])) { $attrs['contact_person'] = trim($row['contact_email']); } - // persist & attach + // 9) Persist + attach pivots try { $event = new Event; foreach ($attrs as $k => $v) { @@ -176,7 +184,7 @@ public function model(array $row): ?Model } $event->save(); - if (! empty($row['audience_comma_separated_ids'])) { + if (!empty($row['audience_comma_separated_ids'])) { $aud = array_filter( array_unique(explode(',', $row['audience_comma_separated_ids'])), fn($i) => is_numeric($i) && $i > 0 && $i <= 100 @@ -184,14 +192,14 @@ public function model(array $row): ?Model $event->audiences()->attach($aud); } - if (! empty($row['theme_comma_separated_ids'])) { + if (!empty($row['theme_comma_separated_ids'])) { $themes = $this->validateThemes($row['theme_comma_separated_ids']); $event->themes()->attach($themes); } return $event; } catch (\Exception $e) { - Log::error('Event import failed: '.$e->getMessage(), $attrs); + Log::error('Event import failed: '.$e->getMessage(), $row); return null; } } From f8086c5ee8671e3e1b69b955f6a4da9083fdb7d1 Mon Sep 17 00:00:00 2001 From: bernardhanna Date: Tue, 1 Jul 2025 11:19:39 +0100 Subject: [PATCH 4/4] Add example Excel file and importer update --- app/Imports/GenericEventsImport.php | 72 ++++++++++++++++------------- 1 file changed, 39 insertions(+), 33 deletions(-) diff --git a/app/Imports/GenericEventsImport.php b/app/Imports/GenericEventsImport.php index c92c5d252..1fcac8fe7 100644 --- a/app/Imports/GenericEventsImport.php +++ b/app/Imports/GenericEventsImport.php @@ -15,21 +15,24 @@ class GenericEventsImport extends BaseEventsImport implements ToModel, WithCustomValueBinder, WithHeadingRow { + /** Fallback when no creator_id can be determined */ + protected const FALLBACK_CREATOR_ID = 315958; + /** - * Cast floats with no fractional part back to int (so “3.0” → 3). + * Turn 3.0 → 3, 42.0 → 42, etc. */ protected function normalizeRow(array $row): array { foreach ($row as $k => $v) { if (is_float($v) && floor($v) === $v) { - $row[$k] = (int) $v; + $row[$k] = (int)$v; } } return $row; } /** - * Turn Excel serials or strings into Y-m-d H:i:s + * Handle Excel‐serial or string dates. */ public function parseDate($value) { @@ -38,7 +41,8 @@ public function parseDate($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; @@ -46,52 +50,55 @@ public function parseDate($value) } /** - * Map a row to an Event model (or skip/return null). + * Map each row into an Event or skip (return null). */ public function model(array $row): ?Model { - // 1) normalize decimals → ints + // 1) normalize floats $row = $this->normalizeRow($row); Log::info('Importing row:', $row); - // 2) required fields guard - if ( - empty($row['activity_title']) || - empty($row['name_of_organisation']) || - empty($row['description']) || - empty($row['type_of_organisation']) || - empty($row['activity_type']) || - empty($row['country']) || - empty($row['start_date']) || - empty($row['end_date']) - ) { - Log::error('Missing required fields, skipping row.', $row); - return null; + // 2) required fields + foreach ([ + 'activity_title', + 'name_of_organisation', + 'description', + 'type_of_organisation', + 'activity_type', + 'country', + 'start_date', + 'end_date' + ] as $required) { + if (empty($row[$required])) { + Log::error("Missing required field “{$required}”, skipping.", $row); + return null; + } } // 3) resolve creator_id $creatorId = null; + // a) explicit integer if (isset($row['creator_id']) && is_int($row['creator_id'])) { $creatorId = $row['creator_id']; } - // b) explicit email in creator_id column + // b) explicit email elseif (!empty($row['creator_id']) && filter_var($row['creator_id'], FILTER_VALIDATE_EMAIL)) { $creatorId = User::where('email', trim($row['creator_id']))->value('id'); } - // c) fallback: match on local part of contact_email + // c) fallback from contact_email local‐part elseif (!empty($row['contact_email']) && filter_var($row['contact_email'], FILTER_VALIDATE_EMAIL)) { [$local,] = explode('@', trim($row['contact_email']), 2); - $creatorId = User::where('email', 'like', $local.'@%')->value('id'); + $creatorId = User::where('email', 'like', "{$local}@%")->value('id'); } - // d) final fallback to your chosen ID - if ($creatorId === null) { - $creatorId = 315958; + // d) final fallback + if (empty($creatorId)) { + $creatorId = self::FALLBACK_CREATOR_ID; } - // 4) build attribute array + // 4) build attributes $attrs = [ 'status' => 'APPROVED', 'title' => trim($row['activity_title']), @@ -125,14 +132,14 @@ public function model(array $row): ?Model 'updated' => now(), ]; - // 5) optional singles & counts + // 5) optional single‐choice & counts if (isset($row['recurring_event'])) { $attrs['recurring_event'] = $this->validateSingleChoice( $row['recurring_event'], Event::RECURRING_EVENTS ); } - foreach (['males_count','females_count','other_count'] as $c) { + foreach (['males_count', 'females_count', 'other_count'] as $c) { if (isset($row[$c])) { $attrs[$c] = (int)$row[$c]; } @@ -171,12 +178,12 @@ public function model(array $row): ?Model ); } - // 8) contact_person fallback if the column exists - if (Schema::hasColumn('events','contact_person') && ! empty($row['contact_email'])) { + // 8) contact_person fallback + if (Schema::hasColumn('events', 'contact_person') && !empty($row['contact_email'])) { $attrs['contact_person'] = trim($row['contact_email']); } - // 9) Persist + attach pivots + // 9) persist & attach try { $event = new Event; foreach ($attrs as $k => $v) { @@ -191,7 +198,6 @@ public function model(array $row): ?Model ); $event->audiences()->attach($aud); } - if (!empty($row['theme_comma_separated_ids'])) { $themes = $this->validateThemes($row['theme_comma_separated_ids']); $event->themes()->attach($themes); @@ -199,7 +205,7 @@ public function model(array $row): ?Model return $event; } catch (\Exception $e) { - Log::error('Event import failed: '.$e->getMessage(), $row); + Log::error('Event import failed: ' . $e->getMessage(), $row); return null; } }