Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

raobilal4822's avatar

Chat system in filament

I want to build a messaging/chat system within a Filament PHP application based on the following detailed specifications. Please provide a step-by-step guide to implement this system, including the necessary Filament resources, models, database schema, relationships, and UI components. The response should cover backend logic, front-end implementation, and any relevant Filament-specific features (e.g., resources, forms, tables, actions). Ensure the solution is modular, follows Laravel/Filament best practices, and handles all the described functionality. Provide code snippets where necessary and explain how to integrate each feature.

Messaging Module Specifications:

Features and Purpose:
    Built-in Messaging Inbox: A central hub to view recent and past conversations.
    Message Threads by Record: Group chats under related tenancy, property, or task records.
    Manual Messaging: Initiate 1:1 or bulk messages directly from the UI.
    Email Integration (2-way): Emails sent/received are logged in the message thread.
    SMS Integration: Messages sent as SMS are stored in the same thread; supports two-way replies if enabled.
    Mobile Messaging: View/respond to messages via mobile with push notification alerts.
    Message Templates: Save reusable content for quick replies (e.g., rent reminders).
    Attachments Support: Add files (e.g., PDFs, images) to messages.
    Read/Seen Status: Indicators to show if the recipient has viewed the message.
    History Search: A search bar to filter messages by body, recipient, or subject.
    Notification Sync: New message alerts sync with the Notifications Module.
Message Types:
    Individual Message:
        Sent from tenancy, applicant, or property page.
        User manually selects the recipient.
        Requires subject and message body.
        Supports file attachments.
        Delivery options: Email, SMS.
        UI Fields:
            To: Recipient dropdown.
            Relation: Auto-populated based on context (e.g., tenancy).
            Subject: Required input.
            Message: Text body field.
            Attachments: File upload icon.
        Label: Compose New Individual Message.
    Group Message:
        Sent from “Post Message” or tenant/property filter.
        Group selection via dropdown (e.g., "All tenants in Property X").
        Messages sent individually to each user (BCC behavior).
        Replies are isolated, not visible to the entire group.
        Delivery options: Email, SMS.
        SMS usage tied to credits (140 characters = 1 credit, max 456 characters = 3 credits).
        UI Components:
            Select Group: Dropdown.
            Recipients Field: Auto-filled after group selection.
            Subject: Input field.
            Message: Textarea.
            Attach Files: Option.
            Send Email/SMS toggles.
            SMS credit info displayed when SMS is selected.
        Label: Compose New Group Message.
Message Flow & Delivery Pipeline:
    In-App:
        Messages appear in “My Messages.”
        Threads tied to records (e.g., Tenancy ID, Property ID).
        Seen/read status tracked per recipient.
    Email:
        Sends message body and subject to recipient’s email.
        Replies are logged in the thread.
        Supports HTML formatting and attachments.
    SMS:
        Sends message body to recipient’s mobile number.
        Shortens content if over 140 characters.
        Two-way replies (if enabled) are routed back to the system.
        Delivery failures (e.g., invalid number) are logged.
Interface Layout (Messages Hub Page):
    + New Message: Modal for 1:1 message creation.
    + Group Message: Modal with group selector and SMS/email toggles.
    Search Bar: Filters past messages by subject, body, or contact.
    Latest Messages: Displays recent conversations (click to open full thread).
    “See All” Button: Redirects to detailed message center or logs per record.
Message History & Logging:
    Logged Fields:
        Message ID: Unique identifier.
        Sender: User who sent the message.
        Recipient(s): Users or groups addressed.
        Channel: In-App, Email, SMS.
        Delivery Status: Sent, Delivered, Failed, Read.
        Timestamp: Date and time sent.
        Linked Record: Tenancy, Property, Task, or Applicant.
        Subject + Body: Message contents.
        Attachments: File names and types.
Configuration & Customization:
    Default Channel: Toggle between Email and SMS.
    Template Creation: Admins can define reusable subject + body combinations.
    SMS Credit Management: Billing panel for SMS purchase/usage tracking.
    Message Archiving: Archive old conversations or auto-archive after 6 months.
    Seen/Unread Indicator: Toggle visibility of read status.

Requirements:

Provide a database schema for the messaging system (tables, columns, relationships).
Suggest Filament resources (e.g., MessageResource, ThreadResource) and their configurations.
Explain how to implement the UI for individual and group message composition using Filament forms and modals.
Describe how to handle email and SMS integration (e.g., using Laravel Mail, Twilio for SMS).
Provide code for key components, such as:
    Models (e.g., Message, Thread, Template).
    Filament resources for managing messages and threads.
    Form schemas for individual and group messages.
    Logic for SMS credit calculation and delivery status tracking.
    Notification sync with Laravel’s notification system.
Explain how to implement message threads tied to records (e.g., tenancy, property).
Suggest how to handle file attachments and store them securely.
Provide guidance on implementing search functionality and read/seen status.
Include mobile responsiveness and push notification integration.
Ensure the solution is scalable and follows Laravel/Filament best practices.

Additional Notes:

The system should integrate with existing Filament resources (e.g., TenancyResource, PropertyResource).
Assume a Laravel application with Filament v3 installed.
For SMS, assume a third-party service like Twilio is available.
Provide code snippets in PHP/Laravel/Filament syntax.
If any clarification is needed about specific features, suggest logical assumptions based on the provided details.

Please structure the response as a step-by-step guide with clear sections for each component (e.g., Database Schema, Models, Filament Resources, UI, Integrations). Include code snippets and explanations tailored to Filament PHP.

0 likes
1 reply
LaryAI's avatar

Certainly! Here’s a comprehensive, step-by-step guide to building a modular, scalable messaging/chat system in a Laravel + Filament v3 application, covering all your requirements.


1. Database Schema

Migration Overview

You’ll need the following tables:

  • threads: Conversation containers, linked to records (tenancy, property, etc.)
  • messages: Individual messages in a thread.
  • message_recipients: Pivot for message delivery/read status per user.
  • message_templates: Saved reusable templates.
  • attachments: Files linked to messages.
  • sms_credits: Tracks SMS usage/purchase (if needed).

Example Migrations

threads Table

php artisan make:migration create_threads_table
Schema::create('threads', function (Blueprint $table) {
    $table->id();
    $table->string('subject');
    $table->foreignId('created_by')->constrained('users');
    $table->nullableMorphs('linked_record'); // e.g., tenancy, property, etc.
    $table->timestamps();
    $table->softDeletes();
});

messages Table

Schema::create('messages', function (Blueprint $table) {
    $table->id();
    $table->foreignId('thread_id')->constrained();
    $table->foreignId('sender_id')->constrained('users');
    $table->enum('channel', ['in_app', 'email', 'sms']);
    $table->text('body');
    $table->timestamps();
});

message_recipients Table

Schema::create('message_recipients', function (Blueprint $table) {
    $table->id();
    $table->foreignId('message_id')->constrained();
    $table->foreignId('recipient_id')->constrained('users');
    $table->enum('status', ['sent', 'delivered', 'failed', 'read'])->default('sent');
    $table->timestamp('read_at')->nullable();
    $table->timestamps();
});

message_templates Table

Schema::create('message_templates', function (Blueprint $table) {
    $table->id();
    $table->string('name');
    $table->string('subject');
    $table->text('body');
    $table->foreignId('created_by')->constrained('users');
    $table->timestamps();
});

attachments Table

Schema::create('attachments', function (Blueprint $table) {
    $table->id();
    $table->foreignId('message_id')->constrained();
    $table->string('file_path');
    $table->string('file_name');
    $table->string('mime_type');
    $table->timestamps();
});

sms_credits Table (Optional)

Schema::create('sms_credits', function (Blueprint $table) {
    $table->id();
    $table->foreignId('user_id')->constrained();
    $table->integer('credits')->default(0);
    $table->timestamps();
});

2. Models & Relationships

Thread

class Thread extends Model
{
    use SoftDeletes;

    protected $fillable = ['subject', 'created_by', 'linked_record_type', 'linked_record_id'];

    public function messages() {
        return $this->hasMany(Message::class);
    }

    public function linkedRecord() {
        return $this->morphTo();
    }

    public function creator() {
        return $this->belongsTo(User::class, 'created_by');
    }
}

Message

class Message extends Model
{
    protected $fillable = ['thread_id', 'sender_id', 'channel', 'body'];

    public function thread() {
        return $this->belongsTo(Thread::class);
    }

    public function sender() {
        return $this->belongsTo(User::class, 'sender_id');
    }

    public function recipients() {
        return $this->hasMany(MessageRecipient::class);
    }

    public function attachments() {
        return $this->hasMany(Attachment::class);
    }
}

MessageRecipient

class MessageRecipient extends Model
{
    protected $fillable = ['message_id', 'recipient_id', 'status', 'read_at'];

    public function message() {
        return $this->belongsTo(Message::class);
    }

    public function recipient() {
        return $this->belongsTo(User::class, 'recipient_id');
    }
}

MessageTemplate

class MessageTemplate extends Model
{
    protected $fillable = ['name', 'subject', 'body', 'created_by'];

    public function creator() {
        return $this->belongsTo(User::class, 'created_by');
    }
}

Attachment

class Attachment extends Model
{
    protected $fillable = ['message_id', 'file_path', 'file_name', 'mime_type'];

    public function message() {
        return $this->belongsTo(Message::class);
    }
}

3. Filament Resources

ThreadResource

  • List threads (inbox)
  • Show thread details (messages, participants)
  • Modal to compose new message (individual/group)
php artisan make:filament-resource Thread

ThreadResource Table

public static function table(Table $table): Table
{
    return $table
        ->columns([
            TextColumn::make('subject')->searchable(),
            TextColumn::make('linked_record_type'),
            TextColumn::make('created_by')->label('Started By'),
            TextColumn::make('messages_count')->counts('messages')->label('Messages'),
            TextColumn::make('created_at')->dateTime(),
        ])
        ->filters([
            // Add filters for record type, participants, etc.
        ])
        ->actions([
            Tables\Actions\ViewAction::make(),
            Tables\Actions\Action::make('compose')
                ->label('Compose New Message')
                ->action(fn () => $this->openComposeModal()),
        ]);
}

ThreadResource Form (Compose Modal)

public static function form(Form $form): Form
{
    return $form
        ->schema([
            Select::make('recipients')
                ->label('To')
                ->multiple()
                ->options(User::all()->pluck('name', 'id'))
                ->required(),
            Select::make('linked_record')
                ->label('Relation')
                ->options([
                    // Populate with tenancy, property, etc.
                ])
                ->required(),
            TextInput::make('subject')->required(),
            RichEditor::make('body')->required(),
            FileUpload::make('attachments')
                ->multiple()
                ->directory('message-attachments'),
            Toggle::make('send_email')->label('Send as Email'),
            Toggle::make('send_sms')->label('Send as SMS'),
        ]);
}

MessageTemplateResource

  • CRUD for templates (admin only)
php artisan make:filament-resource MessageTemplate

4. UI: Individual & Group Message Composition

  • Use Filament modals/forms for both.
  • For group: select group, auto-fill recipients, show SMS credit info.

Group Message Form Example


5. Email & SMS Integration

Email (Laravel Mailables)

Mail::to($recipient->email)->send(new MessageMailable($message));
  • Create a MessageMailable class for formatting.
  • Log email delivery status in message_recipients.

SMS (Twilio Example)

use Twilio\Rest\Client;

$twilio = new Client($sid, $token);
$message = $twilio->messages->create(
    $recipient->phone,
    [
        'from' => $fromNumber,
        'body' => $body,
    ]
);

// Log status
MessageRecipient::create([
    'message_id' => $messageId,
    'recipient_id' => $recipient->id,
    'status' => $message->status,
]);
  • Handle incoming SMS via webhook and route to the correct thread/message.

6. Key Logic: SMS Credit Calculation

function calculateSmsCredits($body)
{
    $length = strlen($body);
    return min(ceil($length / 140), 3);
}
  • Deduct credits from sms_credits table before sending.

7. Notification Sync

  • Use Laravel Notifications for in-app and push.
$user->notify(new NewMessageNotification($message));
  • In NewMessageNotification, implement toDatabase, toMail, and (optionally) toBroadcast for push.

8. Message Threads Tied to Records

  • Use morphTo on Thread for linked_record.
  • When composing from a tenancy/property/task, auto-populate linked_record_type and linked_record_id.

9. File Attachments

  • Use Filament’s FileUpload field.
  • Store files in a secure directory (storage/app/message-attachments).
  • In Attachment model, generate secure download URLs.

10. Search Functionality

  • In ThreadResource, add a search bar:
TextInput::make('search')
    ->placeholder('Search messages...')
    ->reactive()
    ->afterStateUpdated(fn ($state, callable $set) => {
        // Filter threads/messages by subject, body, recipient
    });
  • Or use Filament’s built-in searchable columns.

11. Read/Seen Status

  • Update message_recipients.status to read and set read_at when a user views a message.
  • Show unread counts/badges in the UI.

12. Mobile Responsiveness & Push Notifications

  • Filament v3 is responsive by default.
  • For push: use Laravel Echo + Pusher or Firebase Cloud Messaging.
  • In NewMessageNotification, implement toBroadcast and listen on the client.

13. Best Practices & Scalability

  • Use policies for authorization (who can view/send messages).
  • Use queues for sending emails/SMS.
  • Archive old threads via scheduled jobs.
  • Use eager loading to reduce N+1 queries.

14. Integration with Existing Resources

  • Add a “Messages” relation manager to TenancyResource, PropertyResource, etc., showing related threads.
  • Allow composing messages from these pages with context auto-filled.

15. Summary

This modular approach ensures:

  • Scalability: Separate tables for threads, messages, recipients, attachments.
  • Flexibility: Supports multiple channels, group/individual messaging, templates.
  • Integration: Works with Filament’s resource system and Laravel’s notification/mail/queue features.
  • Security: Attachments stored securely, access controlled by policies.

If you need code for a specific component (e.g., a full Filament resource, notification class, or SMS webhook), let me know!

Please or to participate in this conversation.