This document contains important notes for developers working with or extending the private messaging system.
- This project requires PHP 8.4+ as specified in
composer.json - The messaging system was developed with PHP 8.3.6 available in the testing environment
- All code is compatible with PHP 8.3+ but deployment requires PHP 8.4+
- ✅ All PHP files pass syntax validation
- ✅ 24 comprehensive test cases written
⚠️ Tests not executed due to PHP version requirement in environment- ✅ Code review completed with all issues resolved
- ✅ CodeQL security scan passed
Decision: Encrypt message bodies at rest using Laravel's Crypt facade
Rationale:
- Provides strong AES-256-CBC encryption
- Built into Laravel, no additional dependencies
- Automatic key management via APP_KEY
- Easy to decrypt when needed for display
Implementation:
// Encryption (on save)
$encryptedBody = Crypt::encryptString($messageBody);
// Decryption (on retrieval)
$decryptedBody = Crypt::decryptString($message->body);Trade-offs:
- ✅ Strong security
- ✅ No additional infrastructure needed
- ❌ Cannot search encrypted messages without decrypting
- ❌ Key rotation requires re-encrypting all messages
Decision: Use Laravel Policies for message access control
Rationale:
- Standard Laravel pattern
- Centralized authorization logic
- Easy to test and maintain
- Works with Blade directives and middleware
Rules:
- Users can view messages they sent OR received
- Users can update messages they sent
- Users can delete messages they sent OR received
Decision: Separate API endpoints (Sanctum auth) from web routes (session auth)
Rationale:
- Follows Laravel best practices
- Enables both web UI and external API access
- Different authentication methods for different use cases
- Allows for future mobile app integration
Decision: Use Alpine.js instead of Vue.js or React
Rationale:
- Lightweight (no build step required for MVP)
- Integrates well with Blade templates
- Sufficient for messaging UI requirements
- Consistent with Livewire ecosystem
Future Consideration: Could migrate to Livewire components for better Laravel integration
Two indexes were added for performance:
-
Composite index on (sender_id, recipient_id)
- Optimizes conversation queries
- Speeds up the
between()scope
-
Single index on recipient_id
- Optimizes unread message queries
- Used frequently in the index view
Decision: Messages stored directly without a separate conversations table
Rationale:
- Simpler schema
- Fewer joins required
- Direct message queries are fast
- Conversations derived dynamically
Trade-off: If adding group messaging, would need to refactor to add conversations table
Instead of encrypting entire columns, each message body is encrypted separately:
- Allows for selective decryption
- Each message can have different encryption if needed
- Follows principle of least privilege
Authorization is checked in:
- Routes: Middleware ensures authentication
- Controller: Policy checks before operations
- Views: Blade directives for UI elements
This defense-in-depth approach prevents bypassing authorization.
Using @json() directive instead of raw {{ }} in JavaScript contexts:
// ✅ Safe
currentUserId: @json(Auth::id())
// ❌ Potentially unsafe
currentUserId: {{ Auth::id() }}All POST/PATCH/DELETE requests include CSRF token:
headers: {
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content
}- ✅ Database queries are optimized with proper indexes
- ✅ Eager loading used with
->with(['sender', 'recipient']) - ✅ Messages retrieved in batches, not individually
- Pagination: Large conversation histories should be paginated
- Caching: Unread counts could be cached
- Broadcasting: Use Laravel Echo for real-time updates
- Queue Jobs: Send email notifications via queue
To add WebSocket support:
- Install Laravel Echo and Pusher/Socket.io
- Create
MessageSentevent - Broadcast on message creation
- Listen in frontend with Echo
To support file sharing:
- Add
attachmentstable with polymorphic relation - Use Laravel's file storage system
- Add file upload endpoints
- Update UI to show attachments
To support group conversations:
- Create
conversationstable - Create
conversation_participantspivot table - Refactor Message to belong to Conversation
- Update UI for group member management
To add search functionality:
- Consider using Laravel Scout
- Decrypt messages for indexing (security trade-off)
- Or implement client-side search after loading
- Add search UI to conversation view
Tests are organized by concern:
- MessageTest.php: Model logic, relationships, scopes
- MessageApiTest.php: API endpoints, authorization, validation
# All tests
php artisan test
# Only message tests
php artisan test --filter=Message
# With coverage
php artisan test --coverageTests use migrate:fresh in beforeEach to ensure clean state. This is acceptable for small test suites but may need optimization for larger suites.
Cause: APP_KEY changed or missing Solution: Ensure APP_KEY in .env matches the key used to encrypt
Cause: Policy not registered
Solution: Policies are auto-discovered in Laravel 12, ensure file is in app/Policies/
Cause: Session expired or token not included Solution: Include token in all POST/PATCH/DELETE requests
Cause: Using {{ }} in JavaScript context
Solution: Use @json() directive for safe JSON encoding
- Keep methods focused on single responsibility
- Extract complex logic to service classes if needed
- Always return JSON for API endpoints
- Include proper HTTP status codes
- Use descriptive names (e.g.,
scopeBetween, notscopeGetBetween) - Include PHPDoc with parameter and return types
- Keep scopes simple and composable
- Separate layout from content
- Use Alpine.js for interactivity
- Keep JavaScript in script tags, not inline
- Use Tailwind utility classes
Before deploying to production:
- ✅ Run migrations:
php artisan migrate - ✅ Clear caches:
php artisan config:clear && php artisan cache:clear - ✅ Compile assets:
npm run build - ✅ Set APP_ENV to production
- ✅ Enable HTTPS for Sanctum cookies
- ✅ Configure CORS if using separate frontend
- ✅ Set up queue workers if adding notifications
- ✅ Configure mail settings for notifications
- ✅ Review and update rate limiting
- ✅ Set up monitoring for message delivery
- Monitor database size (messages table can grow large)
- Consider archiving old messages
- Review and rotate encryption keys periodically
- Monitor for failed message deliveries
Suggested metrics to track:
- Messages sent per day
- Average response time
- Unread message count per user
- Failed encryption/decryption attempts
- API endpoint response times
When extending this system:
- Write tests first (TDD approach recommended)
- Follow existing code style and patterns
- Update documentation (especially MESSAGING.md)
- Run code review before committing
- Ensure backward compatibility
- Add migration for schema changes
For questions about this implementation:
- Check MESSAGING.md for API documentation
- Review ARCHITECTURE.md for system design
- Check IMPLEMENTATION_SUMMARY.md for feature details
- Review test files for usage examples
Same as parent project (MIT)