Import action multiple models
Hi, I am working with an importer class. My use case is that one csv is converted to multiple models.
For example one row contains a card number. I first want to insert the card to retrieve a card_id.
I have the following models which need to be inserted based on this csv:
- cards
- cars
- stations
- countries
- and the main one which are rows, containing foreign keys to all the above.
I know I can use the relationship method column but the problem is that for example cars is build up from license_plate. It is only needed in the cars relationship.
I am currently trying this approach, but was wondering if I am doing it wrong:
5 Replies
However this approach is not working for me, because it is trying to insert to tank_rows based on the getColumn definition instead of the new and returned $tankRow...
Column not found: 1054 Unknown column 'issuer_nr' in 'field list' (Connection: mysql, SQL: insert into
tank_rows
(car_id
, card_id
, product_id
, station_id
, amount
, price
, mileage
, issuer_nr
, authority_nr
, serial_nr
, license_plate
, datetime
, station_nr
, station_name
, product_code
, product_description
, vat
, country_code
, updated_at
, created_at
) values (...)
I dont understand where it is retrieving these columns from, because I am specifically returning a new TankRow model object
When I look in the log, it shows
As the result of the Log.
When I dive a bit deeper into the Import action, it shows that the result of resolveRecord is used directly to save the record. But then for me it does not make sense why it still uses all the ImportColumns to try and insert the fields, instead of the record
My second thought was to only make ImportColumns for the actual fields I need for the tankrow, but in order to for example generate the Card instance, I need to have license plate. I cant find any solution on how to access the data from one ImportColumn in another ImportColumn.
Any help or ideas would really be appreciateddid you find a solution to this ?
Currently I have 2 imports, one for my questions model and another for my answers model
I would LOVE to be able to import questions and answers all from one CSV
questionText, questionImage, answerAText, answerAImage,answerACorrect, answerBText, answerBImage,answerBCorrect, answerCText, answerCImage,answerCCorrect, answerDText, answerDImage,answerDCorrect
I would like the importer to create a question record first and then create 4 answer records that link to the new question record
so users can create only 1 excel sheet, one simple CSV and no need to worry about resolving question or knowing question ID for answer import
I am just starting on this, so as per docs
I will try with
protected function afterSave(): void
{
// Runs after a record is saved to the database.
}
and then use $this->data, $this->record and $this->originalData
https://filamentphp.com/docs/3.x/actions/prebuilt-actions/import#lifecycle-hooks
I got it to work, - creating and saving other records / child records when importing
I have additional columns in my csv, but only the required/main model columns in the getColumns method - I have also added a couple of OPTIONS on the upload modal itself
the options are used to add additional info to the main record for the importer
I use afterFill for that
and then I am using afterSave to create child records from other models and records and get data of the additional columns from $this->originalData object
below is some code - I have removed unnecessary stuff - so may not be directly usable, but it conveys the message - maybe error and I have not worked at UPDATING the original model or its child records - so that is an exercise for later
class QuestionImporter extends Importer
{
protected static ?string $model = Question::class;
public static function getColumns(): array
{
return [
ImportColumn::make('text')
->requiredMapping()
->rules(['required'])
->guess(['Question-Text']),
ImportColumn::make('neverShuffleOptions')
->requiredMapping()
->boolean()
->rules(['required', 'boolean'])
->guess(['Shuffle-Options']),
];
}
public static function getOptionsFormComponents(): array
{
return [
Select::make('quiz_id')
->relationship('quiz', 'name')
->label('Quiz'),
];
}
protected function afterFill(): void
{
$this->record->user_id = auth()->user()->id;
$this->record->quiz_id = $this->options['quiz_id'];
}
protected function afterSave(): void
{
$newAnswer = Answer::create([
'text' => $this->originalData['Answer-Text'],
'image' => $this->originalData['Answer-Image'],
'question_id' => $this->record->id,
]);
}
Awesome! I will try this tomorrow
Now I have solved it with unsetting the values from this->data and returning null in the resolve record. It works but your solution seems a lot nices