When building admin panels using Laravel Filament, the Repeater field is a powerful way to collect dynamic sets of data — such as specifications, tags, or features. Often, these repeater entries are stored as a JSON array in the database. But what if you want to convert those JSON entries into individual rows in another table — for analytics, reporting, or normalization?
In this article, we’ll walk through how to:
- Collect data using a Repeater field (stored as JSON)
- Add a button to your Filament admin to insert each entry as a row in another table
- Do this on-demand, without preloading data from the related table
The Use Case
Let’s assume you’re managing products with technical specifications.
- You store specifications in a
specifications
JSON column of theproducts
table using a FilamentRepeater
. - When a button is clicked (e.g., “Insert Repeater Entries”), each specification should be copied into a
product_specifications
table, with one row per entry.
Step-by-Step Guide
Step 1: Set Up the Repeater Field
Add a specifictions
repeater field with multiple fields in product
form as follows:
Repeater::make('specifications')
->schema([
TextInput::make('key'),
TextInput::make('value'),
TextInput::make('unit'),
TextInput::make('description'),
TextInput::make('notes'),
])
This will create specifications repeater field in which you can add multiple rows of specifications for any product. This data is stored in the product_pecifications
column of your products
table as a JSON array.
Step 2: Create the Target Table
Create a migration for new table to hold individual specification entries using this command:
php artisan make:model ProductSpecification -m
It will create 2 files as follows:
- Model File:
app/Models/ProductSpecification.php
- Migration File:
database/migrations/xxxx_xx_xx_xxxxxx_create_product_specifications_table.php
Step 3: Run the Migration File
Update the migration file as per your repeater field entry as follows:
Schema::create('product_specifications', function (Blueprint $table) {
$table->id();
$table->foreignId('product_id')->constrained()->onDelete('cascade');
$table->string('key')->nullable();
$table->string('value')->nullable();
$table->string('unit')->nullable();
$table->text('description')->nullable();
$table->text('notes')->nullable();
$table->timestamps();
});
You can now run the migration using migrate command.
php artisan migrate
It will create a product_specifictions
table in the database.
Step 4: Add a Button to Trigger the Insert
In your ProductResource\Pages\ViewProduct
or EditProduct
, add a custom action to generate specifications entries from the repeater field in product form.
use Filament\Actions\Action;
public function getHeaderActions(): array
{
return [
Action::make('Insert Repeater Entries')
->requiresConfirmation()
->action(function () {
$specs = $this->record->specifications ?? [];
if (!is_array($specs)) {
$specs = json_decode($specs, true) ?? [];
}
foreach ($specs as $spec) {
\App\Models\ProductSpecification::create([
'product_id' => $this->record->id,
'key' => $spec['key'] ?? null,
'value' => $spec['value'] ?? null,
'unit' => $spec['unit'] ?? null,
'description' => $spec['description'] ?? null,
'notes' => $spec['notes'] ?? null,
]);
}
$this->notify('success', 'Specifications inserted successfully.');
}),
];
}
You can name the button anything, such as “Sync Specifications” or “Publish to Table”. It will manually extract the JSON data and insert it as rows in the product_specifications
table.
Benefits of This Approach
- Keeps your form simple and user friendly by storing repeater data in JSON.
- Normalizes data later when needed — perfect for one-time inserts or batch operations.
- Doesn’t require eager loading or nested relationship editing.
Avoid Duplicate Inserts
You can prevent duplicate imports by checking if rows already exists by adding the following check before adding the specifications in above code:
if ($this->record->productSpecifications()->exists()) {
$this->notify('warning', 'Specifications already exist.');
return;
}
Conclusion
Using repeater field gives you flexibility in how you manage structured, dynamic data in Laravel Filament. You can let users manage repeater fields easily, while keeping your database clean and relational by syncing data to separate tables on demand.
Whether for analytics, reporting, or integration, separating repeater entries into rows gives you the best of both worlds: JSON-based forms with relational data power.