Dynamic ExportColumns based on JSON field in Exporter, possible?

Hi, is it possible to define extra dynamic export columns based on a JSON array from a owner record in a relation manager? My use case: I've got a Course resource with a CourseRegistration (HasMany) relation manager. The Course resource contains a Builder field that is used to insert extra form fields for when people sign up for a course. For example:
[
{
"data": {"id": "dN268g", "label": "Question", "data_type": "string", "is_required": true},
"type": "text_input"
},
//...
]
[
{
"data": {"id": "dN268g", "label": "Question", "data_type": "string", "is_required": true},
"type": "text_input"
},
//...
]
When a person fills in the sign up form, the extra fields get saved in CourseRegistration in a DB column like so:
{"Question": "Answer", //...}
{"Question": "Answer", //...}
I've added an Exporter to the CourseRegistration relation manager, and I want to create extra columns for every extra form field, so that the csv/xlsx for all signups doesn't contain one column with above data. Is this possible? Is there a way to get a hold of the owner record inside the public static function getColumns() method inside an Exporter? Basically I need a way to pass the owner record to the exporter class.
2 Replies
Arjen
ArjenOP2w ago
I've now tried using sessions, but that didn't work because the export job doesn't use the current session. It worked when mapping the columns.
Tables\Actions\ExportAction::make()
->beforeFormFilled(function () {
session(['export_parent_record_id' => $this->getOwnerRecord()->id]);
})
->exporter(CourseRegistrationExporter::class);

// CourseRegistrationExporter::class
public static function getColumns(): array
{
session('export_parent_record_id');

return [
// ExportColumns
];
}
Tables\Actions\ExportAction::make()
->beforeFormFilled(function () {
session(['export_parent_record_id' => $this->getOwnerRecord()->id]);
})
->exporter(CourseRegistrationExporter::class);

// CourseRegistrationExporter::class
public static function getColumns(): array
{
session('export_parent_record_id');

return [
// ExportColumns
];
}
I found a workaround that works for my use case. I added the ID to the ExportAction->options([]) array and I've overridden the getCachedColumns method to not get the columns from the static getColumns method, but from a different method that can read the options. I've kept the session from before, because you can't read the non-static options in the static getColumns method.
Tables\Actions\ExportAction::make()
->exporter(CourseRegistrationExporter::class)
->beforeFormFilled(function () {
// Used for the column mapping form.
session(['export_parent_record_id' => $this->getOwnerRecord()->id]);
})
->options([
// Used when exporting (no access to session).
'export_parent_record_id' => $this->getOwnerRecord()->id,
])
Tables\Actions\ExportAction::make()
->exporter(CourseRegistrationExporter::class)
->beforeFormFilled(function () {
// Used for the column mapping form.
session(['export_parent_record_id' => $this->getOwnerRecord()->id]);
})
->options([
// Used when exporting (no access to session).
'export_parent_record_id' => $this->getOwnerRecord()->id,
])
class CourseRegistrationExporter extends Exporter
{
protected static ?string $model = CourseRegistration::class;

public static function getColumnsBefore(): array
{
return [
ExportColumn::make('id')
->label('ID'),
// More columns...
];
}

public static function getColumnsAfter(): array
{
return [
// More columns...
ExportColumn::make('created_at')
->label('Datum aanmelding'),
];
}

public static function getExtraColumns($id): array
{
$extraColumns = [];
$course = Course::find($id);
if ($course && $course->extra_fields) {
foreach ($course->extra_fields as $field) {
$label = $field['data']['label'];
$extraColumns[] = ExportColumn::make('extra_fields.'.$label)
->label($label);
}
}

return $extraColumns;
}

public static function getColumns(): array
{
return [
...static::getColumnsBefore(),
...static::getExtraColumns(session('export_parent_record_id')),
...static::getColumnsAfter(),
];
}

public function getColumnsForJob(): array
{
return [
...static::getColumnsBefore(),
...static::getExtraColumns(data_get($this->getOptions(), 'export_parent_record_id', null)),
...static::getColumnsAfter(),
];
}

/**
* @return array<ExportColumn>
*/
public function getCachedColumns(): array
{
return $this->cachedColumns ??= array_reduce($this->getColumnsForJob(), function (array $carry, ExportColumn $column): array {
$carry[$column->getName()] = $column->exporter($this);

return $carry;
}, []);
}
}
class CourseRegistrationExporter extends Exporter
{
protected static ?string $model = CourseRegistration::class;

public static function getColumnsBefore(): array
{
return [
ExportColumn::make('id')
->label('ID'),
// More columns...
];
}

public static function getColumnsAfter(): array
{
return [
// More columns...
ExportColumn::make('created_at')
->label('Datum aanmelding'),
];
}

public static function getExtraColumns($id): array
{
$extraColumns = [];
$course = Course::find($id);
if ($course && $course->extra_fields) {
foreach ($course->extra_fields as $field) {
$label = $field['data']['label'];
$extraColumns[] = ExportColumn::make('extra_fields.'.$label)
->label($label);
}
}

return $extraColumns;
}

public static function getColumns(): array
{
return [
...static::getColumnsBefore(),
...static::getExtraColumns(session('export_parent_record_id')),
...static::getColumnsAfter(),
];
}

public function getColumnsForJob(): array
{
return [
...static::getColumnsBefore(),
...static::getExtraColumns(data_get($this->getOptions(), 'export_parent_record_id', null)),
...static::getColumnsAfter(),
];
}

/**
* @return array<ExportColumn>
*/
public function getCachedColumns(): array
{
return $this->cachedColumns ??= array_reduce($this->getColumnsForJob(), function (array $carry, ExportColumn $column): array {
$carry[$column->getName()] = $column->exporter($this);

return $carry;
}, []);
}
}
mumutik
mumutik2w ago
yes, it's entirely possible to efine extra dynamic export columns based on a json field, and you've alreay made a good attempt with your workaround

Did you find this page helpful?