Feat: Complete import wizard overhaul — reverse mapping UI, relation FK fixes, Eloquent events, Translatable support, and performance optimizations.
Issue #1
Resolve them.
split to 7 tasks from Issue #1, delivering a complete overhaul of the import wizard with a reversed mapping UI, critical bug fixes for relation foreign keys, Eloquent lifecycle support, Spatie Translatable integration, and performance optimizations for large datasets.
🔄 Task 1: Reverse Mapping UI
- Replaced header-first loop with model-field-first layout in
mapping.blade.php - Added
$modelFieldMappings,$relationFieldMappings,$fieldTypesproperties - Added
initializeModelFieldMappings()with snake_case auto-matching (e.g.,Title→title,CategoryId→category_id) - Added
syncMappingsToColumnMappings()to convert back to$columnMappingsformat forProcessImportChunk - Added
getUnmappedCsvColumns()to show leftover CSV headers - Added
toggleMergeTranslation()for translatable field multi-column support - Added merge translation checkbox column in mapping table for translatable fields
- Auto-detects locale from header names (e.g.,
title_en,name_ar)
🐛 Task 2: Fix Relation Foreign Key Not Saved
- Fixed
fillablecheck that skipped relations when model uses$guarded = [] - Added
resolveFillable()helper that falls back toSchema::getColumnListing() - Replaced
wire:model.livewithwire:change+ hidden inputs for relation selects to avoid Livewire v3 dot-notation issues
🐛 Task 3: Fix SQL VALUES Length Mismatch
- Added
normalizeRecordKeys()to ensure all records have consistent column keys before bulk insert - Prevents
VALUES lists must all be the same lengtherrors when rows have varying columns
🐛 Task 4: Fix Model Boot/Observers Not Firing
- Added
insertRecordsAsModels()that uses Eloquentsave()inside a transaction - Added
addTimestampsToRecords()for upsert path - Non-upsert inserts now fire
boot(), observers, and lifecycle hooks
✨ Task 5: Support Spatie Translatable / Composite Types
- Replaced MySQL-specific
where("field->locale", value)with Laravel'swhereJsonContains() - Removed Spatie dependency; now detects JSON columns from database schema
- Native JSON storage:
$model->{$fieldName} = [$locale => $value] - Added
getTranslatableFields()detection via schema inspection
🔧 Task 6: Excel Column Trimming
- Added
$cellIterator->setIterateOnlyExistingCells(true)inparseExcel() - Strips trailing empty Excel columns (no more 256-column UI)
⚡ Task 7: General Code Optimizations
- 7.1: Added
$cachedRelationsand$cachedGroupedColumnsto avoid duplicate reflection loops - 7.2: Added BOM stripping in
parseCsv()usingpreg_replace('/^\xEF\xBB\xBF/', '', $h) - 7.3: Added column count guard in
loadCsvChunk()beforearray_combine() - 7.4: Wrapped fallback chunked insert in try/catch with
completed_with_errorsstatus - 7.5: Changed
Log::warningtoLog::debugfor production - 7.6: Added
completed_with_errorsUI handling inimport.blade.php+ translation key