turahe / mailclient
A comprehensive Laravel mail client package for managing email accounts, messages, and folders
v1.2.1
2025-09-05 04:04 UTC
Requires
- php: ^8.4
- ext-pdo: *
- ddeboer/imap: ^1.19
- kub-at/php-simple-html-dom-parser: ^1.9
- pelago/emogrifier: ^7.2.0
- turahe/core: ^1.0
- turahe/media: ^3.0
- willdurand/email-reply-parser: ^2.10
- zbateson/mail-mime-parser: ^3.0.3
Requires (Dev)
- enlightn/security-checker: ^1.0
- friendsofphp/php-cs-fixer: ^3.0
- laravel/pint: ^1.17
- mockery/mockery: ^1.6
- orchestra/testbench: ^10.0
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^12.3
README
A comprehensive Laravel mail client package for managing email accounts, messages, and folders with support for IMAP, SMTP, Gmail, Outlook, and various email providers.
๐ Table of Contents
- Features
- Requirements
- Quick Start
- Configuration
- Basic Usage
- Advanced Features
- Models & Architecture
- Testing
- API Reference
- Troubleshooting
- Contributing
- Support
โจ Features
- ๐ Multi-Provider Support - IMAP, SMTP, Gmail API, Outlook/Exchange
- ๐ง Complete Email Management - Send, receive, organize, and track emails
- ๐ Folder Hierarchy - Nested folder structures with full CRUD operations
- ๐ Email Templates - Predefined and reusable email templates
- โฐ Scheduled Emails - Queue and schedule emails for future delivery
- ๐ Link Tracking - Track email link clicks and analytics
- ๐ Sync Management - Intelligent email synchronization
- ๐ ULID Support - Modern, sortable unique identifiers
- ๐งช 100% Test Coverage - 173 tests, 566 assertions, rock-solid reliability
๐ Requirements
Component | Version |
---|---|
PHP | 8.4+ |
Laravel | 12.0+ |
Database | MySQL 8.0+ / PostgreSQL 13+ / SQLite 3.35+ |
โก Quick Start
1. Install Package
composer require turahe/mailclient
2. Publish & Migrate
php artisan vendor:publish --provider="Turahe\MailClient\MailClientServiceProvider"
php artisan migrate
3. Create Your First Email Account
use Turahe\MailClient\Models\EmailAccount; use Turahe\MailClient\Enums\ConnectionType; $account = EmailAccount::create([ 'email' => 'user@example.com', 'password' => 'your_password', 'connection_type' => ConnectionType::IMAP, 'imap_server' => 'imap.example.com', 'imap_port' => 993, 'smtp_server' => 'smtp.example.com', 'smtp_port' => 587, ]);
4. Send Your First Email
$client = $account->createClient(); $message = $client->compose() ->to('recipient@example.com') ->subject('Hello World!') ->html('<h1>Hello from Laravel Mail Client!</h1>') ->send();
โ๏ธ Configuration
Environment Setup
# Gmail OAuth GMAIL_CLIENT_ID=your_gmail_client_id GMAIL_CLIENT_SECRET=your_gmail_client_secret GMAIL_REDIRECT_URL=your_callback_url # Outlook OAuth OUTLOOK_CLIENT_ID=your_outlook_client_id OUTLOOK_CLIENT_SECRET=your_outlook_client_secret OUTLOOK_REDIRECT_URL=your_callback_url
Package Configuration
// config/mail-client.php return [ 'default_connection_type' => 'imap', 'sync_batch_size' => 50, 'max_attachment_size' => 25 * 1024 * 1024, // 25MB 'allowed_attachment_types' => ['pdf', 'doc', 'docx', 'jpg', 'png'], ];
๐ง Basic Usage
๐ฅ Email Account Management
Creating Different Account Types
IMAP/SMTP Account
$account = EmailAccount::create([ 'email' => 'user@example.com', 'password' => 'secure_password', 'connection_type' => ConnectionType::IMAP, 'imap_server' => 'imap.example.com', 'imap_port' => 993, 'imap_encryption' => 'ssl', 'smtp_server' => 'smtp.example.com', 'smtp_port' => 587, 'smtp_encryption' => 'tls', ]);
Gmail Account
$gmailAccount = EmailAccount::create([ 'email' => 'user@gmail.com', 'connection_type' => ConnectionType::GMAIL, 'access_token' => $accessToken, 'refresh_token' => $refreshToken, ]);
Outlook Account
$outlookAccount = EmailAccount::create([ 'email' => 'user@outlook.com', 'connection_type' => ConnectionType::OUTLOOK, 'access_token' => $accessToken, 'refresh_token' => $refreshToken, ]);
Account Operations
// Test connection if ($account->testConnection()) { echo "โ Connection successful!"; } // Sync management $account->enableSync(); $account->disableSync(); // Get statistics $stats = $account->getStats(); echo "๐ง Total: {$stats['total_messages']}"; echo "๐ต Unread: {$stats['unread_messages']}";
๐ Folder Management
Creating and Organizing Folders
// Create main folder $folder = EmailAccountFolder::create([ 'email_account_id' => $account->id, 'name' => 'Important', 'display_name' => 'Important Messages', 'syncable' => true, ]); // Create subfolder $subFolder = EmailAccountFolder::create([ 'email_account_id' => $account->id, 'parent_id' => $folder->id, 'name' => 'Urgent', 'display_name' => 'Urgent Items', ]); // Browse hierarchy $rootFolders = $account->folders()->whereNull('parent_id')->get(); foreach ($rootFolders as $folder) { echo "๐ {$folder->name}\n"; foreach ($folder->children as $child) { echo " โโ ๐ {$child->name}\n"; } }
๐จ Sending Emails
Basic Email Composition
$client = $account->createClient(); // Simple email $message = $client->compose() ->to('user@example.com') ->subject('Meeting Tomorrow') ->text('Don\'t forget our meeting at 2 PM tomorrow.') ->send(); // Rich HTML email $message = $client->compose() ->to('user@example.com') ->cc('manager@example.com') ->bcc('archive@example.com') ->subject('Project Update') ->html(' <h2>Project Status Update</h2> <p>The project is <strong>on track</strong> for completion.</p> <ul> <li>โ Phase 1: Complete</li> <li>๐ Phase 2: In Progress</li> <li>โณ Phase 3: Planned</li> </ul> ') ->attach('/path/to/report.pdf') ->send();
Using Templates
$template = PredefinedMailTemplate::find(1); $message = $client->compose() ->to('customer@example.com') ->fromTemplate($template) ->variables([ 'customer_name' => 'John Doe', 'order_number' => 'ORD-12345', 'delivery_date' => '2025-01-15' ]) ->send();
๐ฌ Managing Messages
Reading Messages
// Get recent unread messages $messages = $account->messages() ->unread() ->orderBy('date', 'desc') ->take(10) ->get(); foreach ($messages as $message) { echo "๐ง {$message->subject}\n"; echo "๐ค From: {$message->from_address}\n"; echo "๐ Date: {$message->date}\n\n"; }
Message Operations
$message = EmailAccountMessage::find($messageId); // Status operations $message->markAsRead(); $message->markAsUnread(); // Organization $message->moveToFolder($importantFolder); $message->addToFolders([$folder1, $folder2]); // Lifecycle management $message->archive(); $message->trash(); $message->restore(); $message->purge(); // Permanent delete
Working with Content
// Get message content $htmlBody = $message->getHtmlBody(); $textBody = $message->getTextBody(); // Handle attachments foreach ($message->attachments as $attachment) { echo "๐ {$attachment->name} ({$attachment->size} bytes)\n"; // Download attachment $content = $attachment->getContent(); file_put_contents("/downloads/{$attachment->name}", $content); }
๐ Advanced Features
๐ Email Templates
Creating Templates
$template = PredefinedMailTemplate::create([ 'name' => 'Order Confirmation', 'subject' => 'Order {{order_number}} Confirmed', 'html_body' => ' <h1>Order Confirmed! ๐</h1> <p>Hello {{customer_name}},</p> <p>Your order <strong>#{{order_number}}</strong> has been confirmed.</p> <p>Expected delivery: {{delivery_date}}</p> ', 'text_body' => 'Hello {{customer_name}}, your order #{{order_number}} is confirmed. Delivery: {{delivery_date}}', 'is_shared' => true, ]);
Using Template Variables
$processedTemplate = $template->process([ 'customer_name' => 'Sarah Johnson', 'order_number' => 'ORD-789', 'delivery_date' => 'January 20, 2025' ]); // Result: // Subject: "Order ORD-789 Confirmed" // Body: "Hello Sarah Johnson, your order #ORD-789 is confirmed..."
โฐ Scheduled Emails
Scheduling Emails
$scheduledEmail = ScheduledEmail::create([ 'email_account_id' => $account->id, 'to' => 'customer@example.com', 'subject' => 'Weekly Newsletter', 'html_body' => '<h1>This Week in Tech</h1>...', 'scheduled_at' => Carbon::now()->addWeek(), 'data' => ['newsletter_id' => 456], ]);
Processing Scheduled Emails
// In your scheduled job (e.g., daily) $dueEmails = ScheduledEmail::dueForSend()->get(); foreach ($dueEmails as $email) { try { $email->send(); echo "โ Sent: {$email->subject}\n"; } catch (Exception $e) { echo "โ Failed: {$e->getMessage()}\n"; } }
๐ Link Tracking
Automatic Link Tracking
// Links in emails are automatically tracked $message = $client->compose() ->to('user@example.com') ->subject('Check out our new features!') ->html(' <p>Visit our <a href="https://example.com/features">new features page</a></p> <p>Read the <a href="https://example.com/blog/update">latest blog post</a></p> ') ->send();
Analyzing Click Data
$message = EmailAccountMessage::find($messageId); // Get all clicks $clicks = $message->linkClicks; foreach ($clicks as $click) { echo "๐ URL: {$click->url}\n"; echo "๐ Clicked: {$click->clicked_at}\n"; echo "๐ IP: {$click->ip_address}\n"; echo "๐ป Browser: {$click->user_agent}\n\n"; } // Statistics $totalClicks = $message->linkClicks()->count(); $uniqueClicks = $message->linkClicks()->distinct('ip_address')->count(); echo "๐ Total clicks: {$totalClicks} | Unique: {$uniqueClicks}";
๐ Bulk Operations
Mass Message Management
// Get messages to process $messages = $account->messages() ->where('subject', 'like', '%newsletter%') ->get(); // Bulk operations $account->moveMessagesToFolder($messages, $newsletterFolder); $account->markMessagesAsRead($messages); $account->deleteMessages($messages);
Sync Management
use Turahe\MailClient\Services\EmailAccountMessageSyncService; $syncService = app(EmailAccountMessageSyncService::class); // Full account sync $syncService->syncAccount($account); // Incremental sync (faster) $lastSync = $account->last_synced_at; $syncService->syncAccountIncremental($account, $lastSync); // Sync specific folder only $syncService->syncFolder($folder);
๐๏ธ Models & Architecture
Core Models Overview
Model | Purpose | Key Features |
---|---|---|
EmailAccount | Email account management | Multi-provider support, OAuth, sync settings |
EmailAccountFolder | Folder organization | Hierarchical structure, sync control |
EmailAccountMessage | Message storage | Rich content, attachments, metadata |
EmailAccountMessageAddress | Email addresses | From, To, CC, BCC tracking |
EmailAccountMessageHeader | Email headers | Technical metadata storage |
PredefinedMailTemplate | Email templates | Variable substitution, sharing |
ScheduledEmail | Email scheduling | Queue management, retry logic |
MessageLinksClick | Link analytics | Click tracking, user behavior |
Key Relationships
// EmailAccount (1:N) $account->folders; // All folders $account->messages; // All messages $account->scheduledEmails; // Scheduled emails // EmailAccountMessage (N:M) $message->folders; // Associated folders $message->addresses; // Email addresses $message->headers; // Technical headers $message->linkClicks; // Click analytics // EmailAccountFolder (Tree) $folder->parent; // Parent folder $folder->children; // Child folders $folder->messages; // Folder messages
๐งช Testing
Running Tests
# All tests (173 tests, 566 assertions) vendor/bin/phpunit # Specific test suites vendor/bin/phpunit --testsuite=Unit # Unit tests only vendor/bin/phpunit --testsuite=Feature # Feature tests only # With coverage report vendor/bin/phpunit --coverage-html coverage
Using Test Factories
use Turahe\MailClient\Tests\Factories\EmailAccountFactory; // Create test account $account = EmailAccountFactory::new()->create([ 'email' => 'test@example.com' ]); // Create account with related data $account = EmailAccountFactory::new() ->withMessages(10) // 10 messages ->withFolders(5) // 5 folders ->create(); // Create specific message $message = EmailAccountMessageFactory::new() ->forAccount($account) ->create([ 'subject' => 'Test Message', 'from_address' => 'sender@example.com' ]);
๐ API Reference
EmailAccount Methods
// Connection management $account->testConnection(): bool $account->createClient(): Client // Sync control $account->enableSync(): void $account->disableSync(): void $account->isSyncDisabled(): bool // Statistics $account->getStats(): array $account->getUnreadCount(): int // Token management (OAuth accounts) $account->refreshAccessToken(): void $account->isTokenExpired(): bool
EmailAccountMessage Methods
// Status management $message->markAsRead(): void $message->markAsUnread(): void $message->isRead(): bool // Organization $message->moveToFolder(EmailAccountFolder $folder): void $message->addToFolders(array $folders): void $message->removeFromFolder(EmailAccountFolder $folder): void // Lifecycle $message->archive(): void $message->trash(): void $message->restore(): void $message->purge(): void // Content access $message->getHtmlBody(): string $message->getTextBody(): string $message->hasAttachments(): bool
PredefinedMailTemplate Methods
// Template processing $template->process(array $variables): array $template->getProcessedSubject(array $variables): string $template->getProcessedBody(array $variables): string // Sharing $template->makeShared(): void $template->makePrivate(): void $template->isShared(): bool
๐ Troubleshooting
Connection Issues
Problem: Connection timeout errors
// Solution: Increase timeout settings 'imap_timeout' => 60, // seconds 'smtp_timeout' => 30, // seconds
Problem: SSL certificate errors
// Solution: Disable SSL verification (development only) 'imap_options' => [ 'ssl' => [ 'verify_peer' => false, 'verify_peer_name' => false, ] ]
Memory Issues
Problem: Memory exhaustion with large attachments
// Solution 1: Increase memory limit ini_set('memory_limit', '512M'); // Solution 2: Use streaming $attachment->streamToFile('/path/to/destination'); // Solution 3: Process in chunks $account->messages()->chunk(50, function ($messages) { // Process 50 messages at a time });
OAuth Token Issues
Problem: Expired access tokens
// Solution: Automatic token refresh if ($account->isTokenExpired()) { $account->refreshAccessToken(); } // Or handle in exception try { $client->getMessages(); } catch (UnauthorizedException $e) { $account->refreshAccessToken(); $client->getMessages(); // Retry }
๐ ๏ธ Contributing
We welcome contributions! Here's how to get started:
Development Setup
# Clone and setup git clone https://github.com/turahe/mail-client.git cd mail-client composer install # Prepare testing cp phpunit.xml.dist phpunit.xml vendor/bin/phpunit # Start coding!
Contribution Process
- Fork the repository
- Create feature branch (
git checkout -b feature/amazing-feature
) - Write tests for your changes
- Ensure all tests pass (
vendor/bin/phpunit
) - Commit your changes (
git commit -m 'Add amazing feature'
) - Push to branch (
git push origin feature/amazing-feature
) - Open a Pull Request
Code Standards
- โ PHP 8.4+ type declarations
- โ PSR-12 coding standards
- โ 100% test coverage for new features
- โ PHPStan level 8 compliance
- โ Clear documentation for public methods
๐ Support
- ๐ง Email: wachid@outlook.com
- ๐ Issues: GitHub Issues
- ๐ Wiki: Documentation
- ๐ฌ Discussions: GitHub Discussions
๐ License
This package is open-sourced software licensed under the MIT license.
๐ Changelog
See CHANGELOG.md for version history and updates.
Built with โค๏ธ for the Laravel community