# MailEclipse - Laravel Mail Editor ## Introduction MailEclipse is a comprehensive Laravel package that provides a web-based interface for creating, editing, and managing email mailables without requiring command-line access. It enables developers to build and modify email templates using a WYSIWYG editor, eliminating the need to manually write artisan commands or edit template files directly. The package is designed to make Laravel email development more accessible, particularly for beginners, while maintaining the full power and flexibility of Laravel's mailing system. The package offers over 20 pre-built email templates (both HTML and Markdown), automatic mailable generation, live preview functionality, and test email sending capabilities with fake data. It integrates seamlessly with Laravel's existing mailable system, automatically detecting mailables in the application, resolving dependencies with factory-generated fake data, and providing a unified interface for viewing and editing all email-related code. MailEclipse supports Laravel 9 and 10, with middleware-based access control and environment restrictions to ensure it's only accessible in development environments. ## Installation and Setup ### Installing the Package ```bash # Install via Composer composer require qoraiche/laravel-mail-editor # Publish configuration and assets php artisan laravel-mail-editor:install ``` ### Configuration Setup ```php // config/maileclipse.php return [ // URL path to access MailEclipse 'path' => 'maileclipse', // Directory where mailables are stored 'mailables_dir' => app_path('Mail/'), // Use factories for fake data generation 'factory' => true, // Display mailable subject instead of class name 'display_as_subject' => true, // Relationship loading depth (0-5) 'relations' => [ 'relation_depth' => env('MAILECLIPSE_RELATION_DEPTH', 2), 'model' => \Illuminate\Foundation\Auth\User::class, ], // Restrict to specific environments 'allowed_environments' => ['local', 'development', 'staging', 'testing'], // Route middleware 'middlewares' => [ 'web', // 'auth', // Uncomment to require authentication ], // Test email recipient 'test_mail' => 'your-test@email.com', ]; ``` ### Accessing the Interface ```php // Access MailEclipse in your browser // Default: http://your-app.local/maileclipse // The package automatically registers routes under the configured path // All routes use the middleware defined in config ``` ## Creating Mailables Programmatically ### Generate New Mailable via API ```php use Qoraiche\MailEclipse\Facades\MailEclipse; // Generate a simple mailable $response = MailEclipse::generateMailable(new \Illuminate\Http\Request([ 'name' => 'WelcomeUser', ])); // Returns: ['status' => 'ok', 'message' => 'Mailable Created
Reloading...'] // Generate mailable with markdown template $response = MailEclipse::generateMailable(new \Illuminate\Http\Request([ 'name' => 'OrderConfirmation', 'markdown' => 'emails.orders.confirmation', ])); // Generate with force overwrite $response = MailEclipse::generateMailable(new \Illuminate\Http\Request([ 'name' => 'ExistingMailable', 'force' => true, ])); ``` ### Using the CreateMailable Action ```php use Qoraiche\MailEclipse\Actions\CreateMailable; $action = new CreateMailable(); // Create a new mailable $result = $action->handle([ 'name' => 'invoice reminder', 'markdown' => 'emails.invoice', ]); // The action automatically: // - Converts name to proper case (InvoiceReminderMail) // - Validates the name isn't reserved ('mailable') // - Checks for duplicates unless force is set // - Calls artisan make:mail command // With force flag $result = $action->handle([ 'name' => 'invoice reminder', 'force' => true, ]); ``` ## Retrieving Mailables ### List All Mailables ```php use Qoraiche\MailEclipse\Facades\MailEclipse; // Get all mailables in the application $mailables = MailEclipse::getMailables(); // Returns a collection with detailed information: foreach ($mailables as $mailable) { echo $mailable['name']; // e.g., 'WelcomeUser' echo $mailable['namespace']; // e.g., 'App\Mail\WelcomeUserMail' echo $mailable['subject']; // Email subject echo $mailable['markdown']; // Markdown view name if applicable echo $mailable['view_path']; // Full path to template file echo $mailable['text_view_path']; // Path to plain text version echo $mailable['view_data']; // Array of data passed to view } // Mailables are automatically sorted by name $mailables = $mailables->sortBy('name'); ``` ### Get Specific Mailable ```php use Qoraiche\MailEclipse\Facades\MailEclipse; // Get mailable by name $mailable = MailEclipse::getMailable('name', 'WelcomeUser'); if (!$mailable->isEmpty()) { $data = $mailable->first(); // Access mailable properties echo $data['namespace']; // Full class name echo $data['subject']; // Email subject echo $data['view_path']; // Template file path // View data with fake values foreach ($data['view_data'] as $viewData) { echo $viewData['key']; // Variable name var_dump($viewData['value']); // Fake data value var_dump($viewData['data']); // Type information } } // Search by other criteria $mailable = MailEclipse::getMailable('namespace', 'App\\Mail\\WelcomeUserMail'); $mailable = MailEclipse::getMailable('subject', 'Welcome to Our Platform'); ``` ## Template Management ### Create New Template ```php use Qoraiche\MailEclipse\Facades\MailEclipse; use Illuminate\Http\Request; $request = new Request([ 'template_name' => 'Invoice Template', 'template_description' => 'Professional invoice email template', 'template_type' => 'html', // or 'markdown' 'template_view_name' => 'airmail', 'template_skeleton' => 'invoice', 'content' => '', 'plain_text' => 'Plain text version of the email', ]); $response = MailEclipse::createTemplate($request); // Returns JSON response: // Success: ['status' => 'ok', 'message' => 'Template created...', 'template_url' => '...'] // Error: ['status' => 'error', 'message' => 'Template not created'] // Template is saved to: resources/views/vendor/maileclipse/templates/ ``` ### List All Templates ```php use Qoraiche\MailEclipse\Facades\MailEclipse; // Get all custom templates $templates = MailEclipse::getTemplates(); foreach ($templates as $template) { echo $template->template_name; // Human-readable name echo $template->template_slug; // Slug for routing echo $template->template_description; // Description echo $template->template_type; // 'html' or 'markdown' echo $template->template_view_name; // Base skeleton name echo $template->template_skeleton; // Specific skeleton variant } // Templates are stored in: app/Mail/templates.json ``` ### Get Template Details ```php use Qoraiche\MailEclipse\Facades\MailEclipse; $templateSlug = 'invoiceTemplate'; $template = MailEclipse::getTemplate($templateSlug); if (!is_null($template)) { echo $template['template']; // Editor-formatted content echo $template['plain_text']; // Plain text version echo $template['slug']; // Template slug echo $template['name']; // Template name echo $template['description']; // Template description echo $template['template_type']; // 'html' or 'markdown' echo $template['template_view_name']; // Skeleton name echo $template['template_skeleton']; // Skeleton variant } ``` ### Update Template ```php use Qoraiche\MailEclipse\Facades\MailEclipse; use Illuminate\Http\Request; $request = new Request([ 'templateslug' => 'invoiceTemplate', 'title' => 'Updated Invoice Template', 'description' => 'Updated description', ]); $response = MailEclipse::updateTemplate($request); // Success response includes new template URL: // ['status' => 'ok', 'message' => 'Updated Successfully', 'template_url' => '...'] // Validation errors: // ['status' => 'failed', 'message' => 'Template name not valid'] // ['status' => 'failed', 'message' => 'Template name already exists'] ``` ### Delete Template ```php use Qoraiche\MailEclipse\Facades\MailEclipse; $templateSlug = 'oldTemplate'; $deleted = MailEclipse::deleteTemplate($templateSlug); if ($deleted) { echo "Template and associated view files deleted successfully"; } else { echo "Template not found or deletion failed"; } // This removes: // - Entry from templates.json // - Template blade file // - Plain text template file (if exists) ``` ## Previewing and Rendering Mailables ### Preview Mailable in Browser ```php use Qoraiche\MailEclipse\Facades\MailEclipse; // Render mailable with fake data $name = 'WelcomeUser'; $html = MailEclipse::renderMailable($name); // Returns fully rendered HTML with: // - Fake model data from factories // - Loaded relationships (up to configured depth) // - All view variables populated // - Complete email styling if ($html === false) { echo "Mailable data is empty or invalid"; } // The method automatically: // - Resolves mailable dependencies // - Generates fake data using factories // - Renders the view with all data // - Returns error view if template missing ``` ### Preview Markdown Content ```php use Qoraiche\MailEclipse\Facades\MailEclipse; use Illuminate\Http\Request; // Preview markdown in editor $request = new Request([ 'markdown' => '@component("mail::message")\n# Hello\n\nThis is a test.\n@endcomponent', 'name' => 'TestMailable', 'namespace' => 'App\\Mail\\TestMail', ]); $html = MailEclipse::previewMarkdownViewContent( false, // Simple view (false = use markdown parser) $request->markdown, // Markdown content $request->name, // Mailable name false, // Is template (false = mailable preview) $request->namespace // Mailable namespace ); // Returns rendered HTML for live preview echo $html; // For template preview (no variables) $html = MailEclipse::previewMarkdownViewContent( false, $markdown, 'templateName', true, // Is template null // No namespace needed for templates ); ``` ### Get Mailable Template Data ```php use Qoraiche\MailEclipse\Facades\MailEclipse; $mailableName = 'WelcomeUser'; $templateData = MailEclipse::getMailableTemplateData($mailableName); if ($templateData !== false) { // Template file content echo $templateData['template']; // Raw blade content echo $templateData['markdowned_template']; // Editor-formatted content echo $templateData['text_template']; // Plain text version // Metadata echo $templateData['template_name']; // View name echo $templateData['markdown']; // Markdown view (if applicable) echo $templateData['is_markdown']; // Boolean echo $templateData['view_path']; // Full file path echo $templateData['text_view_path']; // Plain text path // Mailable data var_dump($templateData['view_data']); // Variables passed to view echo $templateData['namespace']; // Mailable class } ``` ## Editing Mailable Templates ### Save Template Changes ```php use Qoraiche\MailEclipse\Facades\MailEclipse; // Content from WYSIWYG editor $editorContent = '@component("mail::message")\n# Order Confirmation\n\nThank you!\n@endcomponent'; // Convert editor format to blade and save $viewPath = '/path/to/resources/views/emails/orders/confirmation.blade.php'; $saved = MailEclipse::markdownedTemplateToView( true, // Save to file $editorContent, // Editor content $viewPath, // Target file path false // Is custom template ); if ($saved) { echo "Template saved successfully"; } // For custom templates (stored in vendor/maileclipse/templates) $saved = MailEclipse::markdownedTemplateToView( true, $editorContent, 'templateSlug', // Template slug instead of path true // Is custom template ); ``` ### Parse and Update via AJAX ```php // In MailablesController use Illuminate\Http\Request; use Qoraiche\MailEclipse\Facades\MailEclipse; public function parseTemplate(Request $request) { $template = $request->has('template') ? $request->template : false; $viewPath = $request->has('template') ? $request->viewpath : base64_decode($request->viewpath); // Convert HTML entities back (editor converts > to >) $bladeRenderable = preg_replace( '/((?!{{.*?-)(>)(?=.*?}}))/, '>', $request->markdown ); $saved = MailEclipse::markdownedTemplateToView( true, $bladeRenderable, $viewPath, $template ); return response()->json([ 'status' => $saved ? 'ok' : 'error', ]); } ``` ## Sending Test Emails ### Send Test Email with Fake Data ```php use Qoraiche\MailEclipse\Facades\MailEclipse; // Send test email to default recipient $mailableName = 'WelcomeUser'; $recipient = 'test@example.com'; MailEclipse::sendTest($mailableName, $recipient); // This will: // 1. Resolve the mailable instance // 2. Generate fake data using factories // 3. Set the recipient (clearing any cc/bcc) // 4. Send via Laravel's Mail facade // Uses config default if no recipient specified $defaultRecipient = config('maileclipse.test_mail'); MailEclipse::sendTest($mailableName, $defaultRecipient); ``` ### Send Test via HTTP Request ```php // POST route: /maileclipse/mailables/send-test use Illuminate\Http\Request; use Qoraiche\MailEclipse\Facades\MailEclipse; public function sendTest(Request $request) { $validatedData = $request->validate([ 'email' => 'email|nullable', 'name' => 'string|required', ]); // Use provided email or config default $email = $request->get('email') ?? config('maileclipse.test_mail'); $mailableName = $request->get('name'); MailEclipse::sendTest($mailableName, $email); // Returns nothing on success, throws exception on failure } ``` ## Working with Template Skeletons ### Available Template Skeletons ```php use Qoraiche\MailEclipse\Utils\TemplateSkeletons; // Get all available skeletons $skeletons = TemplateSkeletons::skeletons(); // Structure: // [ // 'html' => [ // 'airmail' => ['confirm', 'invite', 'invoice', 'ping', ...], // 'cerberus' => ['fluid', 'hybrid', 'responsive'], // 'cleave' => ['confirm', 'invite', 'invoice', ...], // // ... many more // ], // 'markdown' => [ // 'postmark' => ['blank', 'invoice', 'receipt', 'reset-password', ...] // ] // ] // Get specific skeleton $skeleton = TemplateSkeletons::get('html', 'airmail', 'invoice'); // Returns skeleton data including: // - Template HTML/Markdown content // - Skeleton metadata // - Preview image path ``` ### Creating Template from Skeleton ```php use Illuminate\Http\Request; use Qoraiche\MailEclipse\Facades\MailEclipse; use Qoraiche\MailEclipse\Utils\TemplateSkeletons; // Get skeleton content $skeleton = TemplateSkeletons::get('html', 'airmail', 'invoice'); // Create template from skeleton $request = new Request([ 'template_name' => 'My Invoice Template', 'template_description' => 'Custom invoice template', 'template_type' => 'html', 'template_view_name' => 'airmail', 'template_skeleton' => 'invoice', 'content' => $skeleton['content'], 'plain_text' => $skeleton['plain_text'] ?? '', ]); $response = MailEclipse::createTemplate($request); ``` ## Deleting Mailables ### Delete Mailable File ```php // Via HTTP (POST to /maileclipse/mailables/delete) use Illuminate\Http\Request; $request = new Request([ 'mailablename' => 'WelcomeUser', ]); $mailableFile = config('maileclipse.mailables_dir') . '/' . $request->mailablename . '.php'; if (file_exists($mailableFile)) { unlink($mailableFile); return response()->json([ 'status' => 'ok', ]); } return response()->json([ 'status' => 'error', ]); // Note: This deletes the mailable class file // Associated templates are NOT automatically deleted ``` ## Advanced Factory and Relationship Handling ### Automatic Dependency Resolution ```php // MailEclipse automatically handles mailable constructor dependencies namespace App\Mail; use App\Models\User; use App\Models\Order; use Illuminate\Mail\Mailable; class OrderShipped extends Mailable { public function __construct( public User $user, public Order $order, public array $items ) {} public function build() { return $this->view('emails.orders.shipped'); } } // When previewing or testing, MailEclipse will: // 1. Detect constructor parameters (User, Order, array) // 2. Generate fake User using UserFactory // 3. Generate fake Order using OrderFactory // 4. Provide empty array for $items // 5. Load relationships on models (up to configured depth) // 6. Pass all data to the view use Qoraiche\MailEclipse\Facades\MailEclipse; // This works automatically $html = MailEclipse::renderMailable('OrderShipped'); ``` ### Configure Relationship Loading ```php // config/maileclipse.php return [ 'relations' => [ // 0 = no relations loaded // 1 = direct relations only // 2-5 = nested relations depth 'relation_depth' => 2, // Base model to exclude methods from relation detection 'model' => \Illuminate\Foundation\Auth\User::class, ], // Disable factory usage (use empty models) 'factory' => false, ]; ``` ### Manual Dependency Handling ```php use Qoraiche\MailEclipse\Facades\MailEclipse; // Get mailable instance with resolved dependencies $namespace = 'App\\Mail\\OrderShipped'; $instance = MailEclipse::handleMailableViewDataArgs($namespace); if ($instance !== null) { // Instance created with fake data echo get_class($instance->user); // App\Models\User echo get_class($instance->order); // App\Models\Order var_dump($instance->items); // [] // Access loaded relationships echo $instance->user->profile->name; echo $instance->order->customer->email; } ``` ## Route Registration and Middleware ### Accessing MailEclipse Routes ```php // All routes are automatically registered under the configured prefix // Default: /maileclipse // Main routes: GET /maileclipse -> Redirect to mailables list GET /maileclipse/mailables -> List all mailables GET /maileclipse/mailables/view/{name} -> View mailable details GET /maileclipse/mailables/edit/template/{name} -> Edit mailable template GET /maileclipse/mailables/preview/{name} -> Preview mailable GET /maileclipse/mailables/new -> Create new mailable form POST /maileclipse/mailables/new -> Generate new mailable POST /maileclipse/mailables/delete -> Delete mailable POST /maileclipse/mailables/send-test -> Send test email POST /maileclipse/mailables/parse/template -> Save template changes // Template routes: GET /maileclipse/templates -> List all templates GET /maileclipse/templates/new -> Select template skeleton GET /maileclipse/templates/new/{type}/{name}/{skeleton} -> New template form GET /maileclipse/templates/edit/{slug} -> Edit template POST /maileclipse/templates/new -> Create template POST /maileclipse/templates/update -> Update template POST /maileclipse/templates/delete -> Delete template POST /maileclipse/templates/preview -> Preview template ``` ### Custom Middleware Configuration ```php // config/maileclipse.php return [ 'middlewares' => [ 'web', 'auth', // Require authentication 'can:access-maileclipse', // Custom authorization ], ]; // Service provider automatically applies middleware // Qoraiche\MailEclipse\MailEclipseServiceProvider: Route::middlewareGroup('maileclipse', config('maileclipse.middlewares', [])); ``` ### Environment Restrictions ```php // config/maileclipse.php return [ // Only allow in these environments 'allowed_environments' => ['local', 'development', 'staging', 'testing'], ]; // Controllers check environment on construction: public function __construct() { abort_unless( App::environment(config('maileclipse.allowed_environments', ['local'])), 403, 'Environment Not Allowed' ); } // This prevents access in production by default ``` ## Content Conversion Utilities ### Editor to Blade Conversion ```php use Qoraiche\MailEclipse\Utils\Replacer; // Convert WYSIWYG editor content to Blade syntax $editorContent = '[[component:mail::message]] # Hello World This is a test. [[/component]]'; $bladeContent = Replacer::toBlade($editorContent); // Result: // @component('mail::message') // # Hello World // This is a test. // @endcomponent // Handles all blade directives: // [[component:...]] -> @component('...') // [[extends:...]] -> @extends('...') // [[yield:...]] -> @yield('...') // [[section:...]] -> @section('...') // [[slot:...]] -> @slot('...') // etc. ``` ### Blade to Editor Conversion ```php use Qoraiche\MailEclipse\Utils\Replacer; // Convert Blade syntax to editor-friendly format $bladeContent = "@component('mail::message') # Hello World @endcomponent"; $editorContent = Replacer::toEditor($bladeContent); // Result: // [[component:mail::message]] // # Hello World // [[/component]] // Makes Blade directives visible and editable in WYSIWYG ``` ## Summary and Integration Patterns MailEclipse serves as a complete email development environment for Laravel applications, providing both a visual interface and programmatic API for email management. The primary use cases include rapid prototyping of email templates, enabling non-technical team members to edit email content, testing emails with realistic fake data, and maintaining a centralized view of all application mailables. It's particularly valuable during development when iterating on email designs, as it eliminates the build-deploy-test cycle by providing instant previews with factory-generated data. The package integrates seamlessly with existing Laravel applications by automatically discovering mailables in the configured directory, respecting Laravel's factory patterns for fake data generation, and working within the standard mailable architecture. Common integration patterns include using MailEclipse during development with `allowed_environments` restrictions, combining it with authentication middleware to limit access, leveraging the facade for programmatic mailable generation in seeders or tests, and using the template system to provide customer-facing email customization features. The package's automatic dependency resolution and relationship loading make it particularly powerful for complex mailables with multiple model dependencies, while its extensive template skeleton library accelerates the creation of professional-looking emails.