Skip to content

A comprehensive Laravel mail client package for managing email accounts, messages, and folders with support for IMAP, SMTP, and various email providers.

License

Notifications You must be signed in to change notification settings

turahe/mail-client

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

47 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Turahe Mail Client

Latest Version on Packagist GitHub Tests Action Status Total Downloads License PHP Version Require Test Coverage

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

  • πŸ” 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' => '[email protected]',
    '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('[email protected]')
    ->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' => '[email protected]',
    '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' => '[email protected]',
    'connection_type' => ConnectionType::GMAIL,
    'access_token' => $accessToken,
    'refresh_token' => $refreshToken,
]);

Outlook Account

$outlookAccount = EmailAccount::create([
    'email' => '[email protected]', 
    '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('[email protected]')
    ->subject('Meeting Tomorrow')
    ->text('Don\'t forget our meeting at 2 PM tomorrow.')
    ->send();

// Rich HTML email
$message = $client->compose()
    ->to('[email protected]')
    ->cc('[email protected]')
    ->bcc('[email protected]')
    ->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('[email protected]')
    ->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' => '[email protected]',
    '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('[email protected]')
    ->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' => '[email protected]'
]);

// 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' => '[email protected]'
    ]);

πŸ“– 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

  1. Fork the repository
  2. Create feature branch (git checkout -b feature/amazing-feature)
  3. Write tests for your changes
  4. Ensure all tests pass (vendor/bin/phpunit)
  5. Commit your changes (git commit -m 'Add amazing feature')
  6. Push to branch (git push origin feature/amazing-feature)
  7. 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


πŸ“„ 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

⭐ Star us on GitHub β€’ πŸ“¦ View on Packagist

About

A comprehensive Laravel mail client package for managing email accounts, messages, and folders with support for IMAP, SMTP, and various email providers.

Resources

License

Contributing

Stars

Watchers

Forks

Packages

No packages published

Languages