Tag blog posts and sign them with a member
Tags are named in every language and made from the page form; the byline is the member who signs a post on the site. The public API sends both, filters posts by tag and lists the tags in use. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -9,6 +9,7 @@ use App\Filament\Resources\Pages\PageResource;
|
||||
use App\Filament\Support\TranslatableTabs;
|
||||
use App\Filament\Support\TranslatedOptionLabel;
|
||||
use App\Models\Page;
|
||||
use App\Models\Tag;
|
||||
use App\Rules\UniqueTranslatedSlug;
|
||||
use App\Support\DuplicateTitles;
|
||||
use Filament\Forms\Components\DateTimePicker;
|
||||
@@ -126,6 +127,39 @@ class PageForm
|
||||
->searchable()
|
||||
->preload(),
|
||||
|
||||
// What the post is about, for the blog to be read by
|
||||
// subject. A missing one is made from here.
|
||||
Select::make('tags')
|
||||
->label(__('panel.tags.plural'))
|
||||
->relationship('tags', 'name->'.config('locales.default'))
|
||||
->getOptionLabelFromRecordUsing(TranslatedOptionLabel::using('name'))
|
||||
->multiple()
|
||||
->searchable()
|
||||
->preload()
|
||||
->createOptionForm([
|
||||
TextInput::make('name')
|
||||
->label(__('panel.tags.fields.name'))
|
||||
->required()
|
||||
->maxLength(80),
|
||||
])
|
||||
->createOptionUsing(function (array $data): int {
|
||||
$locale = (string) config('locales.default');
|
||||
|
||||
return Tag::query()->create([
|
||||
'name' => [$locale => $data['name']],
|
||||
'slug' => [$locale => Str::slug($data['name'])],
|
||||
])->getKey();
|
||||
})
|
||||
->createOptionAction(fn ($action) => $action->authorize('create', Tag::class)),
|
||||
|
||||
// Who signs it on the site, with a link to their profile.
|
||||
Select::make('byline_member_id')
|
||||
->label(__('panel.pages.fields.byline'))
|
||||
->helperText(__('panel.pages.fields.byline_hint'))
|
||||
->relationship('byline', 'name')
|
||||
->searchable()
|
||||
->preload(),
|
||||
|
||||
Select::make('author_id')
|
||||
->label(__('panel.pages.fields.author'))
|
||||
->relationship('author', 'name')
|
||||
|
||||
21
app/Filament/Resources/Tags/Pages/ManageTags.php
Normal file
21
app/Filament/Resources/Tags/Pages/ManageTags.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Resources\Tags\Pages;
|
||||
|
||||
use App\Filament\Resources\Tags\TagResource;
|
||||
use Filament\Actions\CreateAction;
|
||||
use Filament\Resources\Pages\ManageRecords;
|
||||
|
||||
class ManageTags extends ManageRecords
|
||||
{
|
||||
protected static string $resource = TagResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
CreateAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
108
app/Filament/Resources/Tags/TagResource.php
Normal file
108
app/Filament/Resources/Tags/TagResource.php
Normal file
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Resources\Tags;
|
||||
|
||||
use App\Filament\Resources\Tags\Pages\ManageTags;
|
||||
use App\Filament\Support\TranslatableTabs;
|
||||
use App\Models\Tag;
|
||||
use App\Rules\UniqueTranslatedSlug;
|
||||
use BackedEnum;
|
||||
use Filament\Actions\DeleteAction;
|
||||
use Filament\Actions\EditAction;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Schemas\Schema;
|
||||
use Filament\Support\Icons\Heroicon;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Str;
|
||||
use UnitEnum;
|
||||
|
||||
/**
|
||||
* Blog tags: what each post is about, named in every language.
|
||||
*/
|
||||
class TagResource extends Resource
|
||||
{
|
||||
protected static ?string $model = Tag::class;
|
||||
|
||||
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedHashtag;
|
||||
|
||||
protected static ?int $navigationSort = 3;
|
||||
|
||||
public static function getModelLabel(): string
|
||||
{
|
||||
return __('panel.tags.label');
|
||||
}
|
||||
|
||||
public static function getPluralModelLabel(): string
|
||||
{
|
||||
return __('panel.tags.plural');
|
||||
}
|
||||
|
||||
public static function getNavigationGroup(): string|UnitEnum|null
|
||||
{
|
||||
return __('panel.groups.content');
|
||||
}
|
||||
|
||||
public static function getRecordTitle(?Model $record): ?string
|
||||
{
|
||||
return $record?->getTranslation('name', app()->getLocale());
|
||||
}
|
||||
|
||||
public static function form(Schema $schema): Schema
|
||||
{
|
||||
return $schema->components([
|
||||
TranslatableTabs::make(fn (string $locale, bool $isDefault) => [
|
||||
TextInput::make("name.{$locale}")
|
||||
->label(__('panel.tags.fields.name'))
|
||||
->required($isDefault)
|
||||
->maxLength(80)
|
||||
->live(onBlur: true)
|
||||
->afterStateUpdated(function (?string $state, callable $get, callable $set) use ($locale): void {
|
||||
if (blank($get("slug.{$locale}"))) {
|
||||
$set("slug.{$locale}", Str::slug((string) $state));
|
||||
}
|
||||
}),
|
||||
|
||||
TextInput::make("slug.{$locale}")
|
||||
->label(__('panel.tags.fields.slug'))
|
||||
->required($isDefault)
|
||||
->maxLength(80)
|
||||
->rules(fn ($record) => [new UniqueTranslatedSlug(Tag::class, $record?->id)]),
|
||||
])->columnSpanFull(),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->modifyQueryUsing(fn ($query) => $query->withCount('pages'))
|
||||
->columns([
|
||||
TextColumn::make('name')
|
||||
->label(__('panel.tags.fields.name'))
|
||||
->searchable(),
|
||||
|
||||
TextColumn::make('slug')
|
||||
->label(__('panel.tags.fields.slug'))
|
||||
->color('gray'),
|
||||
|
||||
TextColumn::make('pages_count')
|
||||
->label(__('panel.tags.fields.pages'))
|
||||
->sortable(),
|
||||
])
|
||||
->recordActions([
|
||||
EditAction::make(),
|
||||
DeleteAction::make(),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => ManageTags::route('/'),
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user