Create a Simple Settings Page with Filament
Store settings as keyed rows, then let Filament build the edit form per type.
Recently, I had to create a simple settings page to manage general settings for a website with Filament. Filament is a Laravel-based admin panel that provides an intuitive interface for managing your application's data.
In this post, I'll walk you through the steps I took to create the settings page, including how to use migrations to store the data, how to update the settings from the Filament admin panel, and how to dynamically build the edit form depending on the setting type.
I.Creating the settings table
First, we need to create a settings table to store the data. We'll
use migrations to create the table with the necessary columns. Instead
of using an id column, we'll use key as a primary column, which
will make it easier to identify the settings. We'll also include
columns for the label, value, type, and attributes, and create a few
settings to test with:
<?php
use App\Models\Setting;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('settings', function (Blueprint $table) {
$table->string('key')->primary();
$table->string('label');
$table->text('value')->nullable();
$table->json('attributes')->nullable();
$table->string('type');
$table->timestamps();
});
Setting::create([
'key' => 'site_name',
'label' => 'Site Name',
'value' => null,
'type' => 'text',
]);
Setting::create([
'key' => 'basic_plan_price',
'label' => 'Basic Plan Price',
'value' => 1000,
'type' => 'number',
]);
Setting::create([
'key' => 'pro_plan_price',
'label' => 'Pro Plan Price',
'value' => 2000,
'type' => 'number',
]);
Setting::create([
'key' => 'environment',
'label' => 'Environment',
'value' => 'production',
'type' => 'select',
'attributes' => [
'options' => [
'production' => 'Production',
'staging' => 'Staging',
'local' => 'Local',
],
],
]);
}
public function down(): void
{
Schema::dropIfExists('settings');
}
};II.Creating the Setting model
Next, we need a Setting model to interact with the settings table.
Since we're using a non-standard column as the primary key, we need to
customize the model a bit. We set $primaryKey to key and
$incrementing to false so Laravel doesn't try to auto-increment it.
We also cast attributes as an array since it's a JSON column:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Setting extends Model
{
protected $primaryKey = 'key';
public $incrementing = false;
protected $fillable = [
'key',
'label',
'value',
'type',
'attributes',
];
protected $casts = [
'attributes' => 'array',
];
}III.Creating the Filament resource
With the table and model in place, we can create a Filament resource to manage our settings:
php artisan make:filament-resource Settings --simple
We use the --simple option to manage our settings on one page using
modals. This creates two files: ManageSettings.php and
SettingsResource.php.
In ManageSettings.php, we remove the getActions() method because
we don't need it in our case.
<?php
namespace App\Filament\Resources\SettingsResource\Pages;
use App\Filament\Resources\SettingsResource;
use Filament\Resources\Pages\ManageRecords;
class ManageSettings extends ManageRecords
{
protected static string $resource = SettingsResource::class;
}In SettingsResource.php, we update the $model attribute with the
Setting model. We also override canCreate() to return false
because we don't want to create settings from the control panel:
<?php
namespace App\Filament\Resources;
use App\Models\Setting;
use Filament\Resources\Resource;
class SettingsResource extends Resource
{
protected static ?string $model = Setting::class;
protected static ?string $navigationIcon = 'heroicon-o-collection';
public static function canCreate(): bool
{
return false;
}
//...
}In the table() method, we define the columns we want to display. We
make the label and value columns sortable and searchable, and use
formatStateUsing() to show Empty when the value is null. We also
customize the EditAction with a dynamic form: depending on the
setting type, we return the correct input.
<?php
namespace App\Filament\Resources;
use App\Filament\Resources\SettingsResource\Pages;
use App\Models\Setting;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
use Filament\Resources\Resource;
use Filament\Resources\Table;
use Filament\Tables;
class SettingsResource extends Resource
{
//...
public static function table(Table $table): Table
{
return $table
->columns([
Tables\Columns\TextColumn::make('label')
->sortable()
->searchable(),
Tables\Columns\TextColumn::make('value')
->formatStateUsing(fn ($state) => $state === null ? 'Empty' : $state)
->sortable()
->searchable(),
])
->actions([
Tables\Actions\EditAction::make()
->form(function (Setting $record) {
return match ($record->type) {
'select' => [
Select::make('value')
->label($record->label)
->options($record->attributes['options'])
],
'number' => [
TextInput::make('value')
->label($record->label)
->type('number')
],
default => [
TextInput::make('value')
->label($record->label)
]
};
}),
]);
}
//...
}IV.The final result
With migrations to create the settings table, the Setting model to
interact with it, and a Filament resource to manage the records, we
can easily update settings from the admin panel. And because the edit
form is built dynamically from the setting type, the interface stays
intuitive as new settings are added.