There is a great YouTube video about how to manage Laravel Comments, a package by Spatie, using Filament Admin.
I could not find the source code anywhere else, that's why I wrote the code following the video. If anybody else may need the code, here it is:
Create a Comment
filament resource:
php artisan filament:resource Comment --view --simple
Create a User
filament resource
php artisan filament:resource User --generate
Create a Comments
widget:
php artisan filament:widget Comments
Content of the CommentResource.php:
<?php
namespace App\Filament\Resources;
use App\Filament\Resources\CommentResource\Pages;
use App\Filament\Resources\CommentResource\RelationManagers;
use App\Models\User;
use Filament\Facades\Filament;
use Filament\Forms\Components\DatePicker;
use Filament\Forms\Components\MarkdownEditor;
use Filament\Resources\Form;
use Filament\Resources\Resource;
use Filament\Resources\Table;
use Filament\Tables;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\HtmlString;
use Spatie\Comments\Models\Comment;
use Spatie\Comments\Models\Concerns\HasComments;
use Spatie\Comments\Models\Concerns\Interfaces\CanComment;
class CommentResource extends Resource
{
protected static ?string $model = Comment::class;
protected static ?string $navigationIcon = 'heroicon-o-collection';
public static function form(Form $form): Form
{
return $form
->schema([
MarkdownEditor::make('original_text')
->label('Comment')
->columnSpanFull()
]);
}
public static function table(Table $table): Table
{
return $table
->columns([
Tables\Columns\TextColumn::make('commentable.name')->getStateUsing(
function (Comment $record): string {
/** @var HasComments $commentable */
$commentable = $record->topLevel()->commentable;
return $commentable?->commentableName() ?? 'Deleted';
}
)->url(fn(Comment $record): string => $record->commentUrl())
->openUrlInNewTab(),
// ->searchable([static::getCommentableTitleAttribute()]),
Tables\Columns\TextColumn::make('commentator.name')->getStateUsing(
function (Comment $record): string {
/** @var CanComment $commentator */
$commentator = $record->commentator;
return $commentator?->commentatorProperties()?->name ?? 'Guest';
}
)->url(function (Comment $record): ?string {
$resource = match ($record->commentator::class) {
User::class => UserResource::class,
};
if (!$resource) {
return null;
}
return $resource::getUrl('edit', ['record' => $record->commentator]);
}),
Tables\Columns\BadgeColumn::make('status')->getStateUsing(function (Comment $record): string {
return $record->isApproved() ? 'Approved' : 'Pending';
})->colors([
'success' => 'Approved',
'warning' => 'Pending'
]),
Tables\Columns\TextColumn::make('created_at')->dateTime()->sortable()
])->filters([
Tables\Filters\TernaryFilter::make('approved_at')
->label('Approval status')
->trueLabel('Approved')
->falseLabel('Pending')
->placeholder('All')
->nullable(),
Tables\Filters\Filter::make('created_at')->form([
DatePicker::make('created_from'),
DatePicker::make('created_until'),
])->query(function (Builder $query, array $data) {
return $query->when(
$data['created_from'],
fn(Builder $query) => $query->whereDate('created_at', '>=', $data['created_from'])
)->when(
$data['created_until'],
fn(Builder $query) => $query->whereDate('created_at', '<=', $data['created_until'])
);
})
])
->actions([
Tables\Actions\Action::make('approve')->action(function (Comment $record) {
$record->approve();
Filament::notify('success', 'Approved');
})->requiresConfirmation()->hidden(fn(Comment $record): bool => $record->isApproved())->color('success')
->icon('heroicon-s-check'),
Tables\Actions\Action::make('reject')->action(function (Comment $record) {
$record->reject();
Filament::notify('success', 'Rejected');
})->requiresConfirmation()->visible(fn(Comment $record): bool => $record->isApproved())->color('danger')
->icon('heroicon-s-x'),
Tables\Actions\ViewAction::make()->modalContent(fn(Comment $record): HtmlString => new HtmlString($record->text)),
Tables\Actions\EditAction::make()->form([]),
])
->bulkActions([
Tables\Actions\BulkAction::make('approve')->action(function (Collection $records) {
$records->each->approve();
Filament::notify('success', 'Approved');
})->requiresConfirmation()->color('success')
->icon('heroicon-s-check'),
Tables\Actions\BulkAction::make('reject')->action(function (Collection $records) {
$records->each->reject();
Filament::notify('success', 'Rejected');
})->requiresConfirmation()->color('danger')
->icon('heroicon-s-x'),
]);
}
public static function canEdit(Model $record): bool
{
return true;
}
public static function getPages(): array
{
return [
'index' => Pages\ManageComments::route('/'),
];
}
}
Add this to PostResource.php:
public static function getWidgets(): array
{
return [
PostResource\Widgets\Comments::class
];
}
Content of PostResource/Widgets/Comments.php:
<?php
namespace App\Filament\Resources\PostResource\Widgets;
use Filament\Widgets\Widget;
use Illuminate\Database\Eloquent\Model;
class Comments extends Widget
{
protected static string $view = 'filament.resources.post-resource.widgets.comments';
public Model $record;
protected int|string|array $columnSpan = 'full';
}
Add this code to PostResource/Pages/EditPost.php:
protected function getFooterWidgets(): array
{
return [
PostResource\Widgets\Comments::class
];
}
Content of comments.blade.php:
<x-filament::widget>
<x-filament::card>
<livewire:comments :model="$record" />
</x-filament::card>
</x-filament::widget>
Add this to boot
method of AppServiceProvider.php:
Filament::registerRenderHook(
'styles.start',
fn(): string => \Blade::render('<x-comments::styles />')
);
Filament::registerRenderHook(
'scripts.start',
fn(): string => \Blade::render('<x-comments::scripts />')
);
That's it, you should now be able to manage your Laravel Comments using Filament Admin.