Stars: 100
Forks: 20
Pull Requests: 12
Issues: 6
Watchers: 5
Last Updated: 2023-08-16 19:39:33
"Title With Slug" Input - Easy Permalink Slugs for Filament Forms (PHP / Laravel / Livewire)
License: MIT License
Languages: PHP, JavaScript, CSS, Blade
This package for FilamentPHP adds the form component TitleWithSlugInput
which allows to
edit titles and slugs easily.
It is inspired by the classic WordPress title & slug implementation.
The plugin is fully configurable. You can change all labels, use your own slugifier, use a route() to generate the " View" link, hide the host name, and many more. Read the full documentation
TitleWithSlugInput::make(
fieldTitle: 'title', // The name of the field in your model that stores the title.
fieldSlug: 'slug', // The name of the field in your model that will store the slug.
),
The output looks like this: (Watch » Demo Video «)
You can watch a short demo video of the packages below.
You can support my work with a donation.
Follow me on Twitter for DEV updates.
Support the package: Please give it a ⭐ Star on GitHub and on the official Filament plugin page, if it's helpful for you.
Visit the plugin's Composer Packagist page (The PHP Package Repository) for the current install count and more.
You can install the package via composer:
composer require camya/filament-title-with-slug
If needed, you can publish the config file with:
php artisan vendor:publish --tag="filament-title-with-slug-config"
If needed, you can publish the translation files with:
php artisan vendor:publish --tag="filament-title-with-slug-translations"
You'll find the published translations here: trans/vendor/filament-title-with-slug
This package is translated to:
You translated it too? Share your translation on our GitHub discussions page.
This package provides the custom InputField TitleWithSlugInput
for the Filament Form Builder.
Read the installation details for Filament here.
Below an example, where to put the new field inside your Filament Resource.
fieldTitle
: The name of the field in your model that stores the title.fieldSlug
: The name of the field in your model that will store the slug.use Camya\Filament\Forms\Components\TitleWithSlugInput;
class PostResource extends Resource
{
public static function form(Form $form): Form
{
return $form->schema([
TitleWithSlugInput::make(
fieldTitle: 'title',
fieldSlug: 'slug',
)
]);
}
}
Tip: To occupy the full width, use
TitleWithSlugInput::make()->columnSpan('full')
.
The output looks like this:
The package assumes, that you model fields are named title
and slug
.
You can easily change them according to your needs.
In the example below, the package now uses the database fields name
for the title and identifier
for the slug.
\Camya\Filament\Forms\Components\TitleWithSlugInput::make(
fieldTitle: 'name',
fieldSlug: 'identifier',
)
It's possible to change all labels on the fly.
In this example, we also add the base path /books/
.
\Camya\Filament\Forms\Components\TitleWithSlugInput::make(
urlPath: '/book/',
urlVisitLinkLabel: 'Visit Book',
titleLabel: 'Title',
titlePlaceholder: 'Insert the title...',
slugLabel: 'Link:',
)
Tip: You can translate the package completely.
The output looks like this:
You can hide the host part of the permalink preview.
\Camya\Filament\Forms\Components\TitleWithSlugInput::make(
urlHostVisible: false,
)
The output looks like this:
You can set the path and the host for the preview.
\Camya\Filament\Forms\Components\TitleWithSlugInput::make(
urlPath: '/category/',
urlHost: 'https://project.local',
)
The output looks like this:
You can use a named route, e.g. route('product.show', ['slug' => $record->slug])
, to generated the "Visit" link.
\Camya\Filament\Forms\Components\TitleWithSlugInput::make(
urlPath: '/product/',
urlHost: 'camya.com',
urlVisitLinkRoute: fn(?Model $record) => $record?->slug
? route('product.show', ['slug' => $record->slug])
: null,
)
Laravel documentation: Generating URLs To Named Routes
By default, the package concatenates the strings host + path + slug
to generate the "Visit" link.
Because the "Visit" link now is generated by an route, you can use partial hosts like urlHost: 'camya.com'
to shorten
the permalink preview.
The output looks like this:
You can remove the "Visit" link completely.
\Camya\Filament\Forms\Components\TitleWithSlugInput::make(
urlVisitLinkVisible: false,
)
In order to style the "title" input field, you can pass the attributes class
via titleExtraInputAttributes
parameter.
\Camya\Filament\Forms\Components\TitleWithSlugInput::make(
titleExtraInputAttributes: ['class' => 'italic'],
)
The output looks like this:
You can add additional validation rules by passing in the variables titleRules
or slugRules
.
In addition, a unique validation rule is applied to the slug field automatically. In order to modify the unique rule, read Custom unique validation rules for title (and slug).
\Camya\Filament\Forms\Components\TitleWithSlugInput::make(
titleRules: [
'required',
'string',
'min:3',
'max:12',
],
)
You can also customize the error messages.
You can customize the error messages in your EditModel and CreateModel filament resources by adding the $messages member variable.
protected $messages = [
'data.slug.regex' => 'Invalid Slug. Use only chars (a-z), numbers (0-9), and the dash (-).',
];
Unique validation rules can be modified only by using the parameters titleRuleUniqueParameters
and
the slugRuleUniqueParameters
counterpart.
This is needed in order to set Filament's "ignorable" parameter correctly.
\Camya\Filament\Forms\Components\TitleWithSlugInput::make(
titleRuleUniqueParameters: [
'callback' => fn(Unique $rule) => $rule->where('is_published', 1),
'ignorable' => fn(?Model $record) => $record,
],
)
This array is inserted into the input field's ->unique(...[$slugRuleUniqueParameters])
method.
Read Filament's documentation for the Unique method.
Available array keys:
'ignorable' (Model | Closure)
'callback' (?Closure)
'ignoreRecord' (bool)
'table' (string | Closure | null)
'column' (string | Closure | null)
This package uses Laravel's slugifier, Str::slug()
, but it is possible to replace it with one of your own.
The following generates a slug with only the characters a-z and validates them with a regex.
\Camya\Filament\Forms\Components\TitleWithSlugInput::make(
slugSlugifier: fn($string) => preg_replace( '/[^a-z]/', '', $string),
slugRuleRegex: '/^[a-z]*$/',
)
Note: You can customize the validation error, see Custom error messages.
The package supports Filaments dark mode. Dark mode output looks like this:
To set an empty slug, you must first remove the slug's required
rule. You can do this by overwriting the slugRules
array.
\Camya\Filament\Forms\Components\TitleWithSlugInput::make(
slugRules: [],
),
In the input field of the component's slug form, use the /
character to set the home page.
The
/
character is necessary to bypass the auto slug-regenerate that would be triggered if the slug field is an empty string.
The input looks like this:
You can use the TitleWithSlugInput inside a repeater with a database relation.
This example uses the Eloquent relationship "Post hasMany FAQEntries"
.
Read the Laravel Eloquent Relationship and the Filament Repeater docs for details.
\Filament\Forms\Components\Repeater::make('FAQEntries')
->relationship()
->collapsible()
->schema([
\Camya\Filament\Forms\Components\TitleWithSlugInput::make(
fieldTitle: 'title',
fieldSlug: 'slug',
urlPath: '/faq/',
urlHostVisible: false,
titleLabel: 'Title',
titlePlaceholder: 'Insert FAQ title...'
)
]),
The output looks like this:
It is possible to create a URL with the slug in the middle of the path.
Example: "/books/ slug /detail/"
It is important to add a urlVisitLinkRoute
closure to create a correct visit link. Please also read the "urlVisitLinkRoute with named route" documentation.
\Camya\Filament\Forms\Components\TitleWithSlugInput::make(
urlPath: '/books/',
urlVisitLinkRoute: fn (?Model $record) => $record?->slug
? '/books/'.$record->slug.'/detail'
: null,
slugLabelPostfix: '/detail/',
urlVisitLinkLabel: 'Visit Book Details'
),
The output looks like this:
You can use the package to create the subdomain part of a URL with the following setup.
Example: "https:// my-subdomain .camya.com"
It is important to add a urlVisitLinkRoute
closure to create a correct visit link. Also, you need to set the name of the Eloquent model field for the subdomain using slugField
.
Please also read the "urlVisitLinkRoute with named route" documentation.
\Camya\Filament\Forms\Components\TitleWithSlugInput::make(
fieldSlug: 'subdomain',
urlPath: '',
urlHostVisible: false,
urlVisitLinkLabel: 'Visit Domain',
urlVisitLinkRoute: fn (?Model $record) => $record?->slug
? 'https://'.$record->slug.'.camya.com'
: null,
slugLabel: 'Domain:',
slugLabelPostfix: '.camya.com',
),
The output looks like this:
This package comes with some default values that can be easily overridden programmatically.
If you have other defaults, you can publish the configuration file and change them globally.
php artisan vendor:publish --tag="filament-title-with-slug-config"
You'll find the published config here: config/filament-title-with-slug-config.php
The values can be programmatically overridden with: TitleWithSlugInput::make(fieldTitle: 'title')
[
'field_title' => 'title', // Overwrite with (fieldTitle: 'title')
'field_slug' => 'slug', // Overwrite with (fieldSlug: 'title')
'url_host' => env('APP_URL'), // Overwrite with (urlHost: 'https://www.camya.com/')
];
You can call TitleWithSlugInput without parameters, and it will work and use its default values.
In order to set parameters, you use PHP8's Named Arguments syntax.
\Camya\Filament\Forms\Components\TitleWithSlugInput::make(
fieldTitle: 'title',
fieldSlug: 'slug',
);
Below is an example with some defaults overridden.
\Camya\Filament\Forms\Components\TitleWithSlugInput::make(
// Model fields
fieldTitle: 'title',
fieldSlug: 'slug',
// Url
urlPath: '/blog/',
urlHost: 'https://www.camya.com',
urlHostVisible: true,
urlVisitLinkLabel: 'View',
urlVisitLinkRoute: fn(?Model $record) => $record?->slug
? route('post.show', ['slug' => $record->slug])
: null,
urlVisitLinkVisible: true,
// Title
titleLabel: 'The Title',
titlePlaceholder: 'Post Title',
titleExtraInputAttributes: ['class' => 'italic'],
titleRules: [
'required',
'string',
],
titleRuleUniqueParameters: [
'callback' => fn(Unique $rule) => $rule->where('is_published', 1),
'ignorable' => fn(?Model $record) => $record,
],
titleIsReadonly: fn($context) => $context !== 'create',
titleAutofocus: true,
titleAfterStateUpdated: function ($state) {},
// Slug
slugLabel: 'The Slug: ',
slugRules: [
'required',
'string',
],
slugRuleUniqueParameters: [
'callback' => fn(Unique $rule) => $rule->where('is_published', 1),
'ignorable' => fn(?Model $record) => $record,
],
slugIsReadonly: fn($context) => $context !== 'create',
slugSlugifier: fn($string) => Str::slug($string),
slugRuleRegex: '/^[a-z0-9\-\_]*$/',
slugAfterStateUpdated: function ($state) {},
slugLabelPostfix: null,
)->columnSpan('full'),
Please see the release changelog for more information on what has changed recently.
Want to implement a feature, fix a bug, or translate this package? Please see contributing for details.
Please review our security policy on how to report security vulnerabilities.
FilamentPHP is based on Laravel, Livewire, AlpineJS, and TailwindCSS. (aka Tall Stack)
This package was inspired by a package by awcodes and the work of spatie. Thanks also to ralphjsmit for his blueprint that I used to implement the Filament Component Pest Tests.
The MIT License (MIT). Please see License File for more information.