Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Filament Slug Laravel Package

novius/laravel-filament-slug

Adds a Slug form field for Laravel Filament. Generate slugs from a TextInput, optionally conditionally, while keeping TextInput features like validation rules and unique checks. Requires PHP 8.2+, Laravel 11+, Filament 4.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require novius/laravel-filament-slug
    

    Ensure your project meets the requirements: PHP 8.2+, Laravel 11+, and Filament 4+.

  2. First Use Case: Add the Slug field to a Filament Resource form. For example, in a blog post resource:

    use Novius\FilamentSlug\Slug;
    
    public static function form(Form $form): Form
    {
        return $form->schema([
            TextInput::make('title')->required(),
            Slug::make('slug')
                ->fromField($title) // Auto-generate slug from 'title'
                ->required()
                ->unique(BlogPost::class, 'slug', ignoreRecord: true),
        ]);
    }
    
  3. Key Files to Review:

    • README.md for basic usage.
    • Slug.php for extending functionality (e.g., custom validation rules).

Implementation Patterns

Usage Patterns

  1. Basic Slug Generation:

    Slug::make('slug')
        ->fromField(TextInput::make('title'))
        ->required();
    
    • Automatically generates a slug from the title field using Laravel’s Str::slug().
  2. Conditional Slug Generation:

    Slug::make('slug')
        ->fromField($title, fn (Get $get) => ! $get('is_draft'))
        ->required();
    
    • Skips slug generation if is_draft is true.
  3. Validation Rules: Leverage TextInput methods for validation:

    Slug::make('slug')
        ->fromField($title)
        ->regex('/^[a-z0-9-]+$/') // Custom regex
        ->maxLength(100)
        ->unique(YourModel::class, 'slug', ignoreRecord: true);
    
  4. Dynamic Source Fields:

    $description = TextInput::make('description');
    Slug::make('slug')
        ->fromField($description); // Generate slug from 'description' instead
    
  5. Integration with Filament Actions: Use the Slug field in bulk edit or import actions:

    public static function table(Table $table): Table
    {
        return $table
            ->columns([
                // ...
            ])
            ->actions([
                Tables\Actions\EditAction::make(),
            ]);
    }
    

Workflows

  1. New Resource Creation:

    • Slugs are auto-generated from a source field (e.g., title) during form submission.
    • Example: A blog post titled "How to Build a SaaS" becomes /how-to-build-a-saas.
  2. Existing Record Updates:

    • Slugs update dynamically when the source field changes (e.g., editing a title).
    • Use ignoreRecord: true in unique() to avoid conflicts during updates.
  3. Bulk Operations:

    • Combine with Filament’s table actions (e.g., EditInPlace) to update slugs for multiple records at once.
  4. Custom Logic:

    • Extend the Slug class to add custom slug generation logic (e.g., multi-field sources):
      use Novius\FilamentSlug\Slug as BaseSlug;
      
      class CustomSlug extends BaseSlug
      {
          protected static string $slugSeparator = '_'; // Custom separator
      
          public static function make(string $name): static
          {
              return parent::make($name);
          }
      }
      

Integration Tips

  1. Database Constraints: Add a unique constraint to your database table to enforce slug uniqueness:

    Schema::table('blog_posts', function (Blueprint $table) {
        $table->string('slug')->unique();
    });
    
  2. SEO Optimization:

    • Use Str::of($title)->slug('-') for custom separators (e.g., hyphens).
    • Limit slug length (e.g., ->maxLength(100)) to avoid URL truncation issues.
  3. Localization: For multilingual slugs, handle translations in the source field (e.g., title) or use a package like spatie/laravel-translatable to manage localized slugs.

  4. Performance:

    • Disable live updates for slugs in large forms:
      Slug::make('slug')
          ->fromField($title)
          ->live(false); // Disable real-time updates
      
    • Use afterStateUpdated to defer slug generation until form submission.
  5. Testing: Test slug generation with edge cases (e.g., special characters, empty fields, duplicates):

    public function test_slug_generation()
    {
        $this->create('BlogPost', ['title' => 'Test Title']);
        $this->assertDatabaseHas('blog_posts', ['slug' => 'test-title']);
    }
    

Gotchas and Tips

Pitfalls

  1. Duplicate Slugs:

    • The package does not auto-increment slugs (e.g., -2 for duplicates). Handle this with:
      • Database constraints (recommended).
      • Custom validation logic:
        ->unique(YourModel::class, 'slug', ignoreRecord: true)
        ->afterStateUpdated(fn (Set $set, ?string $state) => {
            if (YourModel::where('slug', $state)->exists()) {
                $set('slug', $state . '-2');
            }
        })
        
  2. Conditional Logic Quirks:

    • The closure in fromField() must return a boolean. Incorrect logic (e.g., returning a string) will cause errors.
    • Example of a working closure:
      ->fromField($title, fn (Get $get) => filled($get('title')))
      
  3. Regex Validation:

    • Default regex may not cover all use cases. Customize with:
      ->regex('/^[a-z0-9-]+$/i') // Case-insensitive, alphanumeric + hyphen
      
  4. Filament Version Mismatches:

    • The package is tied to Filament 4. If upgrading Filament, test compatibility or fork the package.
  5. Live Updates:

    • Real-time slug updates (live(true)) can trigger unnecessary validation or database queries. Disable for performance:
      ->live(false)
      
  6. Source Field Changes:

    • If the source field (e.g., title) is updated after slug generation, the slug may not reflect the latest value. Use afterStateUpdated to sync them:
      $title->afterStateUpdated(fn (Set $set, ?string $state) => {
          $this->form->fill('slug', Str::slug($state));
      });
      

Debugging

  1. Slug Not Updating:

    • Ensure the source field (e.g., title) is bound to the form and its value is changing.
    • Check for JavaScript errors if using live updates.
  2. Validation Errors:

    • Validate the source field’s value before slug generation (e.g., required() on title).
    • Use ->dehydrateStateUsing() to customize slug output:
      ->dehydrateStateUsing(fn (?string $state) => Str::lower($state))
      
  3. Unique Constraint Conflicts:

    • If ignoreRecord: true doesn’t work, manually check for duplicates in the closure:
      ->afterStateUpdated(fn (Set $set, ?string $state) => {
          if (YourModel::where('slug', $state)->whereNot('id', $this->record?->id)->exists()) {
              $set('slug', $state . '-duplicate');
          }
      })
      

Tips

  1. Reusable Slug Logic: Create a trait or helper for consistent slug generation across resources:

    trait UsesSlugs
    {
        protected function makeSlugField(string $name, string $sourceField): Slug
        {
            return Slug::make($name)
                ->fromField($this->getSourceField($sourceField))
                ->required()
                ->unique(static::class, $name, ignoreRecord: true);
        }
    }
    
  2. Client-Side Previews: Use Alpine.js or Livewire to preview slugs as users type:

    <x-filament::input.wire:model="title" />
    <x-filament::input wire:model="slug" readonly />
    
    public $title, $slug;
    
    protected function rules(): array
    {
        return [
            'title' => 'required',
            'slug' => 'required|unique:blog_posts,slug',
        ];
    }
    
    protected function updatedTitle(): void
    {
        $this->slug = Str::slug($this->title);
    }
    
  3. **Custom

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
calmfox/watch-sylius
damienfern/grpc-symfony-bundle
atoolo/index-bundle
atoolo/genai-bundle
coprotoai/laravel-ticket
davidjln/llm-carbon-bundle
cryonighter/valid-request-bundle
coolms/taxonomy-bundle
coolms/field-bundle
articulate-orm/symfony
aaix/laravel-tall-architect
ephoto/akeneo-connector
emmanuelballery/eb-plantumlbundle
emielburgman/symfony-visitor-beacon
emielburgman/symfony-visit-storage
emielburgman/symfony-security-headers
emielburgman/symfony-log-viewer
emarref/xdebug-bundle
emarref/pubnub-bundle
elriseio/finance-money-bundle