motivo/filament-title-with-slug
Installation
composer require motivo/filament-title-with-slug
Publish the config (optional):
php artisan vendor:publish --provider="Motivo\FilamentTitleWithSlug\FilamentTitleWithSlugServiceProvider"
Basic Usage
Replace a standard TextInput or Textarea in your Filament form with:
use Motivo\FilamentTitleWithSlug\Components\TitleWithSlugInput;
TitleWithSlugInput::make()
->titleField('title') // Model field for the title
->slugField('slug') // Model field for the slug
->columnSpanFull(), // Optional: Full-width layout
First Use Case
Add to a Filament Form or Table edit/create page:
// In a Filament Form component
TitleWithSlugInput::make()
->titleField('post_title')
->slugField('post_slug')
->label('Post Title & Slug'),
Model Integration Use the component with Eloquent models:
TitleWithSlugInput::make()
->titleField('name')
->slugField('url')
->rules(['slug' => 'unique:pages,url'])
->required(),
Dynamic Slug Generation Override the default slugifier (e.g., for custom rules):
TitleWithSlugInput::make()
->slugifier(fn(string $title): string => Str::slug($title, '-').'-custom')
->titleField('heading')
->slugField('path'),
Link Customization Configure the "Visit" link (e.g., for a custom route):
TitleWithSlugInput::make()
->visitRoute(fn($record) => route('admin.pages.show', $record))
->titleField('title')
->slugField('slug'),
Multi-Field Forms Combine with other Filament components:
use Filament\Forms\Components\Select;
TitleWithSlugInput::make()
->titleField('title')
->slugField('slug')
->afterStateUpdated(fn($state, $set) => $set('status', 'draft'))
->columnSpan('full'),
Select::make('status')
->options(['draft', 'published'])
->default('draft'),
Table Columns Display slugs in Filament Tables:
use Motivo\FilamentTitleWithSlug\Columns\SlugColumn;
SlugColumn::make('slug')
->titleField('title') // Optional: Show title on hover
->searchable(),
Slug Uniqueness
TitleWithSlugInput::make()
->slugField('slug')
->rules(['slug' => 'unique:posts,slug,'.$record->id]),
Case Sensitivity
Post vs post).protected static function booted()
{
static::saving(fn($model) => $model->slug = Str::lower($model->slug));
}
Livewire State Persistence
dehydrateState() in the Filament form:
public function dehydrateState(): array
{
return array_merge(parent::dehydrateState(), [
'slug' => $this->slug,
]);
}
Slugifier Logic
->slugifier(fn($title) => {
logger()->debug('Slugifier input:', ['title' => $title]);
return Str::slug($title);
}),
Route Resolution
visitRoute closure:
->visitRoute(fn($record) => {
logger()->debug('Visit route params:', $record->toArray());
return route('admin.pages.show', $record);
}),
Custom Labels Override all translatable strings via config:
'labels' => [
'title' => 'Custom Title',
'slug' => 'Custom Slug',
'visit' => 'Preview',
'undo' => 'Reset Slug',
],
Dark Mode Ensure Tailwind classes are compatible:
->extraAttributes(['class' => 'dark:bg-gray-800']),
Conditional Rendering Show/hide based on model state:
TitleWithSlugInput::make()
->visible(fn($record) => $record->isDraft())
->titleField('title')
->slugField('slug'),
API Integration For API-only Filament apps, disable the "Visit" link:
->disableVisitLink(),
How can I help you explore Laravel packages today?