Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

earmsby's avatar

Adding a generated ID field when importing a CSV

I have a model Contract. The model uses a required generated ID something like "C-EL98AESF" I have a function on the model that generates this quasi-random ID.

Now, I've created a ContractImporter for this resource. I don't want to use any value from the uploaded CSV for this field. I want to generate one from the Contract::getID() just as when a Contract is creating manually in the resource form.

I've tried to add the ID field in the resolveRecord method but it doesn't add the field to the INSERT query and then throws an error because the field is set as not nullable.

Here is my Importer code:

It doesn't seem like resolveRecord() is reached because I added a Ray() statement that never fires.

How can I skip this field in the CSV and add it programmatically when inserting the record?

0 likes
3 replies
LaryAI's avatar
Level 58

You’re on the right track by wanting to exclude the generated ID from the CSV and instead set it programmatically. If your resolveRecord() isn’t firing, it’s likely because the Importer is using mass-assignment and only filling fields defined in getColumns(). By default, fields not in the CSV won’t be set unless you take manual action.

Here’s a robust solution:

1. Use the fillRecord Method for Additional Attributes

Most Laravel/Filament importers call a method like fillRecord to fill in additional attributes before saving. It’s better suited for setting programmatic fields than resolveRecord, which replaces the record instance entirely.

Here's how you can do it:

2. Remove contract_number from Your Columns Make sure contract_number is not listed in getColumns, so the importer doesn’t look for that column in the CSV.

3. Make Sure Your Importer Supports fillRecord Some Filament versions and custom importers use mutateRecordData or similar hooks. If your parent Importer uses a different method name, adapt accordingly (e.g., override mutateRecordData or beforeSave).

Troubleshooting resolveRecord: Your code with resolveRecord creates a new Contract, but if the parent Importer never calls resolveRecord, that’s why your Ray() call isn’t reached. Many importers instantiate the model directly and only use resolveRecord in specific customization flows.

Summary:

  • Do NOT list the generated ID in getColumns()
  • Use fillRecord to set non-CSV, programmatic fields
  • Make sure to call fill($row) to populate other fields

If Your Importer Only Supports mutateRecordData:

public static function mutateRecordData(array $data): array
{
    $data['contract_number'] = Contract::getID();
    $data['added_by'] = auth()->id();
    $data['commit_status'] = 'Draft';

    return $data;
}

Pick the hook that matches your Importer base class.

Let me know if you need an example for a specific importer type or class!

earmsby's avatar

Hmmm... used the mutateRecordData method but still get the error about the missing required field:

SQLSTATE[23000]: Integrity constraint violation: 19 NOT NULL constraint failed: contracts.contract_number (Connection: sqlite, SQL: insert into "contracts" ("contract_description", "contract_type", "contract_file_path", "date_executed", "date_start", "auto_renews", "updated_at", "created_at") values (...)

So, it seems like because the contract_number field is not in the column list Filament Importer ignores it.

earmsby's avatar

Spent several hours Googling and trying different methods. Finally found this worked:

In my Resource table I have the Import Action like this:

ImportAction::make()
                        ->importer(ContractImporter::class)
                        ->options([
                            'contract_number' => '',
                            'added_by' => auth()->id(),
                            'commit_status' => 'Draft',
                        ]),

Then in the Importer Class I have this:

    public function resolveRecord(): ?Contract
    {
        $record = new Contract();

        // Assign values from the CSV data
        $record->contract_description = $this->data['contract_description'];
        $record->contract_type = $this->data['contract_type'];
        $record->contract_file_path = $this->data['contract_file_path'];
        $record->date_executed = $this->data['date_executed'];
        $record->date_start = $this->data['date_start'];
        $record->auto_renews = $this->data['auto_renews'];

        if(isset($this->options['contract_number'])){
            $record->contract_number=Contract::getID();
        }
        if (isset($this->options['user_id'])) {
            $record->user_id = $this->options['user_id'];
        }
        if (isset($this->options['status'])) {
            $record->status = $this->options['status'];
        }

        return $record;
    }

One gotcha that I ran into while trying different modifications to the resolveRecord() method was that in order for any changes to take effect, I had to stop and restart queue:work. Otherwise, subsequent attempts to import the CSV just used the older version of the Importer class.

I'm not sure why this worked when my previous attempt to use resolveRecord() did not. Perhaps adding the options to the Importer call forces it to hit that method. Or perhaps it was the gotcha I mention above and it wasn't hitting that method because I didn't restart the queue worker. Not sure, but in any case, I'm now able to import the records adding the extra fields programmatically.

Please or to participate in this conversation.