zedmagdy/filament-chat
Filament v4+ chat plugin for Laravel: configurable chat sources, 1:1 and group conversations, text + file attachments via Spatie Media Library, read/unread tracking, search, and real-time updates via polling or broadcasting (Reverb/Pusher).
Installation:
composer require zedmagdy/filament-chat
php artisan vendor:publish --tag="filament-chat-migrations"
php artisan migrate
php artisan vendor:publish --provider="Spatie\MediaLibrary\MediaLibraryServiceProvider" --tag="medialibrary-migrations"
php artisan migrate
Add HasChats trait to your User model:
use ZEDMagdy\FilamentChat\Traits\HasChats;
Create a chat source (quickest via Artisan):
php artisan make:chat-source Staff --model=User
Register the plugin in your PanelProvider:
->plugin(FilamentChatPlugin::make()->sources([StaffChatSource::class]))
Create a staff-to-staff chat system:
make:chat-source for a quick start (e.g., StaffChat).FilamentChatPlugin::make()
->sources([
StaffChatSource::class,
SupportChatSource::class,
])
->aggregates([AllMessagesAggregateChatSource::class])
php artisan vendor:publish --tag="filament-chat-views".filament-chat::components.chat-sidebar).$conversation = Conversation::create(['source' => 'staff', 'type' => 'direct']);
Participant::create([...]); // Add participants
$group = Conversation::create(['source' => 'staff', 'type' => 'group', 'name' => 'Team']);
Message::create([
'conversation_id' => $conversation->id,
'senderable_id' => $user->id,
'body' => 'Hello!',
]);
$message->addMedia($file)->toMediaCollection('chat-attachments');
config/filament-chat.php:
'attachments' => [
'accepted_types' => ['image/jpeg', 'application/pdf', ...],
],
.env:
FILAMENT_CHAT_REALTIME_MODE=polling
FILAMENT_CHAT_POLLING_INTERVAL=5s
broadcasting and ensure Laravel broadcasting is configured.
Events (MessageSent, MessagesRead) auto-broadcast to chat.conversation.{id}.public function getNavigationGroup(): ?string { return 'Communication'; }
public function getNavigationSort(): ?int { return 1; }
->middleware([...])
->permission('view-chats')
HasChats trait for user-specific queries:
$user->conversations()->where('source', 'staff')->get();
$user->sentMessages()->latest()->take(10)->get();
ChatSource and Message models.$this->actingAs($user)
->get('/admin/chat/staff')
->assertSee('Chat');
Migration Conflicts:
filament-chat migrations after Spatie Media Library migrations.config/filament-chat.php:
'table_prefix' => 'custom_chat_',
Real-Time Issues:
10s) may feel laggy; test with 5s or lower.BroadcastFailed events.Attachment Limits:
4 (configurable in config/filament-chat.php).// Example: Check file size before upload
if (file.size > 10 * 1024 * 1024) { // 10MB
alert('File too large');
}
Participant Filtering:
getAvailableParticipantsQuery() excludes the current user automatically, but ensure your query logic accounts for this:
// Bad: May return the current user
User::where('role', 'staff')->get();
// Good: Explicitly exclude
User::where('role', 'staff')->where('id', '!=', auth()->id())->get();
Group Chat Quirks:
allowsGroupChats() to return true.public function rules(): array {
return [
'name' => ['required', Rule::unique(Conversation::class)->where('source', $this->source)->where('type', 'group')],
];
}
Log Chat Events:
Add a registering callback to models for debugging:
Message::observe(function ($message) {
\Log::debug('Message created', ['id' => $message->id, 'body' => $message->body]);
});
Check Broadcast Channels:
Verify channels are authorized in app/Providers/BroadcastServiceProvider:
Broadcast::channel('chat.conversation.{id}', function ($user, $conversation) {
return $conversation->participants()->where('participantable_id', $user->id)->exists();
});
UI Glitches:
php artisan filament:cache:clear
MIX_APP_URL=http://localhost:8000
Custom Message Types:
Extend the Message model to add metadata (e.g., is_urgent):
class CustomMessage extends Message {
protected $casts = [
'is_urgent' => 'boolean',
];
}
Update config:
'models' => [
'message' => CustomMessage::class,
],
Custom Chat Sources: Add logic to filter conversations dynamically:
public function getConversationsQuery(): Builder {
return parent::getConversationsQuery()->whereHas('messages', function ($query) {
$query->where('body', 'like', '%urgent%');
});
}
Real-Time Extensions: Listen to custom events in your frontend:
import Echo from 'laravel-echo';
const echo = new Echo({
broadcaster: 'pusher',
key: process.env.MIX_PUSHER_APP_KEY,
});
echo.private(`chat.conversation.${conversationId}`)
.listen('CustomMessageEvent', (data) => {
console.log('Custom event:', data);
});
API Endpoints: Expose chat data via Laravel API routes:
Route::get('/api/chats/{source}', [ChatController::class, 'index']);
Use the same Conversation and Message models for consistency.
Webhook Triggers: Dispatch events when messages are sent:
Message::created(function ($message) {
event(new MessageSent($message));
});
Listen
How can I help you explore Laravel packages today?