I am trying to build a web app in Laravel / Filament, where users are able to import data from a CSV file. However, I want to build a 2 step form in my filament resource with the wizard.
I am building an ImportResource which will make users able to upload a csv file, add a name and then go to the next step, which is the CSV preview.
I have tried all sorts of things, building a Filament component, building a Livewire component, passing the data directly. But I really don’t know how to do this…
What I have right now, my ImportResource:
namespace AppFilamentResources;
use AppFilamentResourcesImportResourcePages;
use AppModelsImport;
use FilamentForms;
use FilamentResourcesResource;
use FilamentTables;
use FilamentFormsForm;
use FilamentTablesTable;
use IlluminateDatabaseEloquentBuilder;
use IlluminateSupportFacadesStorage;
class ImportResource extends Resource
{
protected static ?string $model = Import::class;
protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack';
public static function form(Form $form): Form
{
return $form
->schema([
FormsComponentsWizard::make([
FormsComponentsWizardStep::make('Import Details')
->columnSpan('full')
->columns('1')
->description('Upload your csv file')
->schema([
FormsComponentsTextInput::make('name')
->required()
->label('Import Name'),
FormsComponentsFileUpload::make('file')
->label('CSV File')
->acceptedFileTypes(['text/csv', 'text/plain'])
->directory('uploads/imports')
->getUploadedFileNameForStorageUsing(function ($file): string {
return now()->timestamp . '-' . $file->getClientOriginalName();
})
->maxSize(51200)
->required()
->afterStateUpdated(function ($state, $set, $get) {
if ($state) {
$tmpPath = $state->getRealPath();
// Read CSV file and set preview data
$csv = Reader::createFromPath($tmpPath, 'r');
$csv->setHeaderOffset(0);
$headers = $csv->getHeader(); // Get the header of the CSV file
$records = $csv->getRecords(); // Get all records of the CSV file
$rows = [];
foreach ($records as $record) {
$rows[] = $record;
if (count($rows) >= 5) break; // Take the first 5 rows
}
$set('csv_preview_headers', $headers);
$set('csv_preview_rows', $rows);
// Prepare the final path
$path = 'uploads/imports/' . now()->timestamp . '-' . $state->getClientOriginalName();
$set('file_path', $path);
}
}),
]),
FormsComponentsWizardStep::make('CSV Preview')
->description('Review your upload')
->schema([
FormsComponentsView::make('components.import-preview')
->extraAttributes([
'headers' => fn ($get) => $get('csv_preview_headers'),
'rows' => fn ($get) => $get('csv_preview_rows'),
]),
]),
])
]);
}
public static function table(Table $table): Table
{
return $table
->columns([
TablesColumnsTextColumn::make('name')->label('Import Name'),
TablesColumnsTextColumn::make('status')->label('Status'),
TablesColumnsTextColumn::make('rows_processed')->label('Rows Processed'),
TablesColumnsTextColumn::make('created_at')->dateTime()->label('Created At'),
])
->filters([
//
])
->actions([
TablesActionsEditAction::make(),
TablesActionsDeleteAction::make(),
])
->bulkActions([
TablesActionsBulkActionGroup::make([
TablesActionsDeleteBulkAction::make(),
]),
]);
}
public static function getRelations(): array
{
return [
//
];
}
public static function getPages(): array
{
return [
'index' => PagesListImports::route('/'),
'create' => PagesCreateImport::route('/create'),
'edit' => PagesEditImport::route('/{record}/edit'),
];
}
}
Now, I would think that this part:
$set('csv_preview_headers', $headers);
$set('csv_preview_rows', $rows);
Would make the csv_preview_headers available to pass to a livewire component and render this with FormsComponentsView::make('components.import-preview')
.
This is my import-preview.blade.php file:
@props(['headers' => [, 'rows' => []])
@if ($headers && $rows)
<table>
<thead>
<tr>
@foreach ($headers as $header)
<th>{{ $header }}</th>
@endforeach
</tr>
</thead>
<tbody>
@foreach ($rows as $row)
<tr>
@foreach ($row as $cell)
<td>{{ $cell }}</td>
@endforeach
</tr>
@endforeach
</tbody>
</table>
@else
<p>No data to preview.</p>
@endif
But, the file upload works. And then I click the next button to go to the next step, and it keeps showing “No data to preview”.
I have no idea how to fix this, been at this for hours.
Anyone that knows how I can use the data in my uploaded .csv (which does not exist on the server yet, because the file has not been saved) to preview it with a component in step 2 of the form?
Kind regards,
Syb.