PostResource.php
TLDR
This file defines the PostResource
class, which is a resource class for managing blog posts in a Filament application. It includes static methods for handling forms, tables, infolists, and sub-navigation, as well as methods for defining global search functionality.
Methods
form
This method defines the form schema for creating and editing blog posts. It includes fields for the post's title, slug, content, author, category, published date, and tags.
table
This method defines the table schema for displaying a list of blog posts. It includes columns for the post's image, title, slug, author, status, category, published date, and comment authors. It also includes filters for searching by published date.
infolist
This method defines the infolist schema for displaying a detailed view of a blog post. It includes sections for displaying the post's title, slug, published date, author, category, tags, and content.
getRecordSubNavigation
This method returns an array of sub-navigation items for a specific blog post. It includes links to view the post, edit the post, and manage post comments.
getRelations
This method returns an array of relations for the Post
model. It is currently empty.
getPages
This method returns an array of pages for the PostResource
. It includes pages for listing posts, creating a new post, managing post comments, editing a post, and viewing a post.
getGlobalSearchEloquentQuery
This method returns an Eloquent query for performing a global search on the Post
model. It includes eager loading the author
and category
relations.
getGloballySearchableAttributes
This method returns an array of attributes that can be searched globally. It includes the post's title, slug, author's name, and category's name.
getGlobalSearchResultDetails
This method returns an array of additional details to display in the global search results for a specific blog post. It includes the post's author name and category name.
Classes
There are no classes defined in this file.
<?php
namespace App\Filament\Resources\Blog;
use App\Filament\Resources\Blog\PostResource\Pages;
use App\Models\Blog\Post;
use Filament\Forms;
use Filament\Forms\Components\SpatieTagsInput;
use Filament\Forms\Form;
use Filament\Infolists\Components;
use Filament\Infolists\Infolist;
use Filament\Notifications\Notification;
use Filament\Pages\SubNavigationPosition;
use Filament\Resources\Pages\Page;
use Filament\Resources\Resource;
use Filament\Tables;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Carbon;
use Illuminate\Support\Str;
class PostResource extends Resource
{
protected static ?string $model = Post::class;
protected static ?string $slug = 'blog/posts';
protected static ?string $recordTitleAttribute = 'title';
protected static ?string $navigationGroup = 'Blog';
protected static ?string $navigationIcon = 'heroicon-o-document-text';
protected static ?int $navigationSort = 0;
protected static SubNavigationPosition $subNavigationPosition = SubNavigationPosition::Top;
public static function form(Form $form): Form
{
return $form
->schema([
Forms\Components\Section::make()
->schema([
Forms\Components\TextInput::make('title')
->required()
->live(onBlur: true)
->maxLength(255)
->afterStateUpdated(fn (string $operation, $state, Forms\Set $set) => $operation === 'create' ? $set('slug', Str::slug($state)) : null),
Forms\Components\TextInput::make('slug')
->disabled()
->dehydrated()
->required()
->maxLength(255)
->unique(Post::class, 'slug', ignoreRecord: true),
Forms\Components\MarkdownEditor::make('content')
->required()
->columnSpan('full'),
Forms\Components\Select::make('blog_author_id')
->relationship('author', 'name')
->searchable()
->required(),
Forms\Components\Select::make('blog_category_id')
->relationship('category', 'name')
->searchable()
->required(),
Forms\Components\DatePicker::make('published_at')
->label('Published Date'),
SpatieTagsInput::make('tags'),
])
->columns(2),
Forms\Components\Section::make('Image')
->schema([
Forms\Components\FileUpload::make('image')
->image()
->hiddenLabel(),
])
->collapsible(),
]);
}
public static function table(Table $table): Table
{
return $table
->columns([
Tables\Columns\ImageColumn::make('image')
->label('Image'),
Tables\Columns\TextColumn::make('title')
->searchable()
->sortable(),
Tables\Columns\TextColumn::make('slug')
->searchable()
->sortable()
->toggleable(isToggledHiddenByDefault: true),
Tables\Columns\TextColumn::make('author.name')
->searchable()
->sortable()
->toggleable(),
Tables\Columns\BadgeColumn::make('status')
->getStateUsing(fn (Post $record): string => $record->published_at?->isPast() ? 'Published' : 'Draft')
->colors([
'success' => 'Published',
]),
Tables\Columns\TextColumn::make('category.name')
->searchable()
->sortable()
->toggleable(isToggledHiddenByDefault: true),
Tables\Columns\TextColumn::make('published_at')
->label('Published Date')
->date(),
Tables\Columns\TextColumn::make('comments.customer.name')
->label('Comment Authors')
->listWithLineBreaks()
->limitList(2)
->expandableLimitedList(),
])
->filters([
Tables\Filters\Filter::make('published_at')
->form([
Forms\Components\DatePicker::make('published_from')
->placeholder(fn ($state): string => 'Dec 18, ' . now()->subYear()->format('Y')),
Forms\Components\DatePicker::make('published_until')
->placeholder(fn ($state): string => now()->format('M d, Y')),
])
->query(function (Builder $query, array $data): Builder {
return $query
->when(
$data['published_from'] ?? null,
fn (Builder $query, $date): Builder => $query->whereDate('published_at', '>=', $date),
)
->when(
$data['published_until'] ?? null,
fn (Builder $query, $date): Builder => $query->whereDate('published_at', '<=', $date),
);
})
->indicateUsing(function (array $data): array {
$indicators = [];
if ($data['published_from'] ?? null) {
$indicators['published_from'] = 'Published from ' . Carbon::parse($data['published_from'])->toFormattedDateString();
}
if ($data['published_until'] ?? null) {
$indicators['published_until'] = 'Published until ' . Carbon::parse($data['published_until'])->toFormattedDateString();
}
return $indicators;
}),
])
->actions([
Tables\Actions\ViewAction::make(),
Tables\Actions\EditAction::make(),
Tables\Actions\DeleteAction::make(),
])
->groupedBulkActions([
Tables\Actions\DeleteBulkAction::make()
->action(function () {
Notification::make()
->title('Now, now, don\'t be cheeky, leave some records for others to play with!')
->warning()
->send();
}),
]);
}
public static function infolist(Infolist $infolist): Infolist
{
return $infolist
->schema([
Components\Section::make()
->schema([
Components\Split::make([
Components\Grid::make(2)
->schema([
Components\Group::make([
Components\TextEntry::make('title'),
Components\TextEntry::make('slug'),
Components\TextEntry::make('published_at')
->badge()
->date()
->color('success'),
]),
Components\Group::make([
Components\TextEntry::make('author.name'),
Components\TextEntry::make('category.name'),
Components\SpatieTagsEntry::make('tags'),
]),
]),
Components\ImageEntry::make('image')
->hiddenLabel()
->grow(false),
])->from('lg'),
]),
Components\Section::make('Content')
->schema([
Components\TextEntry::make('content')
->prose()
->markdown()
->hiddenLabel(),
])
->collapsible(),
]);
}
public static function getRecordSubNavigation(Page $page): array
{
return $page->generateNavigationItems([
Pages\ViewPost::class,
Pages\EditPost::class,
Pages\ManagePostComments::class,
]);
}
public static function getRelations(): array
{
return [];
}
public static function getPages(): array
{
return [
'index' => Pages\ListPosts::route('/'),
'create' => Pages\CreatePost::route('/create'),
'comments' => Pages\ManagePostComments::route('/{record}/comments'),
'edit' => Pages\EditPost::route('/{record}/edit'),
'view' => Pages\ViewPost::route('/{record}'),
];
}
public static function getGlobalSearchEloquentQuery(): Builder
{
return parent::getGlobalSearchEloquentQuery()->with(['author', 'category']);
}
public static function getGloballySearchableAttributes(): array
{
return ['title', 'slug', 'author.name', 'category.name'];
}
public static function getGlobalSearchResultDetails(Model $record): array
{
/** @var Post $record */
$details = [];
if ($record->author) {
$details['Author'] = $record->author->name;
}
if ($record->category) {
$details['Category'] = $record->category->name;
}
return $details;
}
}