PHPackages                             blackpig-creatif/epitre - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. blackpig-creatif/epitre

ActiveLibrary

blackpig-creatif/epitre
=======================

A filamentPHP plugin to manage email templates for Mailables

v1.0.1(6d ago)08↓100%MITPHPPHP ^8.2|^8.3|^8.4CI passing

Since Mar 17Pushed 6d agoCompare

[ Source](https://github.com/Blackpig/epitre)[ Packagist](https://packagist.org/packages/blackpig-creatif/epitre)[ Docs](https://github.com/blackpig/epitre)[ GitHub Sponsors](https://github.com/BlackpigCreatif)[ RSS](/packages/blackpig-creatif-epitre/feed)WikiDiscussions main Synced today

READMEChangelog (2)Dependencies (15)Versions (4)Used By (0)

Epitre
======

[](#epitre)

[![Latest Version on Packagist](https://camo.githubusercontent.com/d4084b9aa68e62005188e1f394b6ffc892858652e4f9386030668674b4bca3d3/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f626c61636b7069672f6570697472652e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/blackpig/epitre)[![GitHub Tests Action Status](https://camo.githubusercontent.com/47fdfc512fbdb34655ab60880409ccf00129f7acc47b6a802fa28e9a9ba19892/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f626c61636b7069672f6570697472652f72756e2d74657374732e796d6c3f6272616e63683d6d61696e266c6162656c3d7465737473267374796c653d666c61742d737175617265)](https://github.com/blackpig/epitre/actions?query=workflow%3Arun-tests+branch%3Amain)[![GitHub Code Style Action Status](https://camo.githubusercontent.com/1a07bbb0aef982e61dacd19d21248fbad15ab499ab1e60bedfd46c6e799f6a66/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f626c61636b7069672f6570697472652f6669782d7068702d636f64652d7374796c652d6973737565732e796d6c3f6272616e63683d6d61696e266c6162656c3d636f64652532307374796c65267374796c653d666c61742d737175617265)](https://github.com/blackpig/epitre/actions?query=workflow%3A%22Fix+PHP+code+styling%22+branch%3Amain)[![Total Downloads](https://camo.githubusercontent.com/124593723badedd11e481dc735a0ea966b994dfb130c12771695026434b4474a/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f626c61636b7069672f6570697472652e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/blackpig/epitre)

An editable email copy layer for Laravel Mailables, managed through a Filament v5 panel.

Epitre sits between your Mailable classes and their output. Each template has a Blade view as its default. Editors can override the subject and body per locale through the Filament panel without touching code. If no DB record exists, the Blade view is used as-is.

Requirements
------------

[](#requirements)

- PHP 8.2+
- Laravel 11+
- Filament 5.0+
- `spatie/laravel-translatable`
- `lara-zeus/spatie-translatable` (Filament translatable plugin)

---

Installation
------------

[](#installation)

```
composer require blackpig-creatif/epitre
```

Run the migration:

```
php artisan migrate
```

Register the plugin in your `PanelProvider`:

```
use BlackpigCreatif\Epitre\EpitrePlugin;

public function panel(Panel $panel): Panel
{
    return $panel
        ->plugins([
            EpitrePlugin::make(),
        ]);
}
```

Epitre will register `SpatieTranslatablePlugin` automatically if it is not already present. It reads your locale list from `config('app.locales')` if that key exists (expected format: `['en' => 'English', 'fr' => 'Francais']`), falling back to `app()->getLocale()`.

---

Quick Start
-----------

[](#quick-start)

### 1. Generate a template class

[](#1-generate-a-template-class)

```
php artisan epitre:make-template ContactConfirmation
```

This creates:

- `app/BlackpigCreatif/Epitre/Templates/ContactConfirmationTemplate.php`
- `resources/views/mail/epitre/contact-confirmation.blade.php`

### 2. Define tokens and resolution logic

[](#2-define-tokens-and-resolution-logic)

Open the generated class and fill in the tokens your template uses:

```
namespace App\BlackpigCreatif\Epitre\Templates;

use BlackpigCreatif\Epitre\Support\EpitreTemplate;

class ContactConfirmationTemplate extends EpitreTemplate
{
    protected string $key = 'contact-confirmation';

    protected string $label = 'Contact Confirmation';

    protected string $view = 'mail.epitre.contact-confirmation';

    /** @var array */
    protected array $tokens = [
        '{name}'    => 'The recipient\'s name',
        '{message}' => 'The message they submitted',
    ];

    /** @return array */
    public function resolve(array $data): array
    {
        return [
            '{name}'    => $data['name'],
            '{message}' => $data['message'],
        ];
    }
}
```

### 3. Register the template in your service provider

[](#3-register-the-template-in-your-service-provider)

```
use BlackpigCreatif\Epitre\Facades\Epitre;
use App\BlackpigCreatif\Epitre\Templates\ContactConfirmationTemplate;

public function boot(): void
{
    Epitre::register(ContactConfirmationTemplate::class);
}
```

### 4. Wire up your Mailable

[](#4-wire-up-your-mailable)

Add the `HasEpitreTemplate` trait and implement the two required members:

```
use BlackpigCreatif\Epitre\Concerns\HasEpitreTemplate;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Attachment;

class ContactConfirmation extends Mailable
{
    use HasEpitreTemplate;

    protected string $epitreKey = 'contact-confirmation';

    public function __construct(
        public string $name,
        public string $message,
    ) {}

    public function epitreData(): array
    {
        return [
            'name'    => $this->name,
            'message' => $this->message,
        ];
    }

    public function attachments(): array
    {
        return [];
    }
}
```

The trait provides `envelope()` and `content()`. Do not implement those methods yourself.

---

Converting an Existing Mailable
-------------------------------

[](#converting-an-existing-mailable)

If you have a Mailable already in production, the migration is straightforward.

**Before:**

```
class OrderShipped extends Mailable
{
    public function __construct(public Order $order) {}

    public function envelope(): Envelope
    {
        return new Envelope(subject: 'Your order has shipped');
    }

    public function content(): Content
    {
        return new Content(view: 'mail.order-shipped');
    }

    public function attachments(): array
    {
        return [];
    }
}
```

**Steps:**

**1.** Generate the template class, pointing at your existing Blade view:

```
php artisan epitre:make-template OrderShipped
```

Open the generated class and set `$view` to your existing view, define any tokens, and implement `resolve()`:

```
class OrderShippedTemplate extends EpitreTemplate
{
    protected string $key = 'order-shipped';
    protected string $label = 'Order Shipped';
    protected string $view = 'mail.order-shipped'; // your existing view, unchanged

    protected array $tokens = [
        '{order_number}' => 'The order reference number',
        '{customer_name}' => 'The recipient\'s name',
    ];

    public function resolve(array $data): array
    {
        return [
            '{order_number}'  => $data['order_number'],
            '{customer_name}' => $data['customer_name'],
        ];
    }
}
```

**2.** Register the template in your service provider:

```
Epitre::register(OrderShippedTemplate::class);
```

**3.** Update the Mailable. Remove `envelope()` and `content()`, add the trait and the two required members:

```
use BlackpigCreatif\Epitre\Concerns\HasEpitreTemplate;

class OrderShipped extends Mailable
{
    use HasEpitreTemplate;

    protected string $epitreKey = 'order-shipped';

    public function __construct(public Order $order) {}

    public function epitreData(): array
    {
        return [
            'order'          => $this->order, // still available in the Blade view
            'order_number'   => $this->order->reference,
            'customer_name'  => $this->order->customer->name,
        ];
    }

    public function attachments(): array
    {
        return [];
    }
}
```

Your existing Blade view continues to work unchanged since no DB record exists yet. Editors can customise the copy through the panel at any point, and the Blade view remains the fallback if they haven't.

---

How Resolution Works
--------------------

[](#how-resolution-works)

When a Mailable using `HasEpitreTemplate` is sent, Epitre resolves the content in this order:

**Subject:** If a DB record exists with a subject for the current locale, it is used with tokens replaced. Otherwise, the template `$label` is used as the subject.

**Body:** If a DB record exists with a body for the current locale, it is rendered as an HTML string with tokens replaced. If a `$layout` is set on the template, the resolved HTML is passed to the layout view as `$body` instead. Otherwise the body is returned as a raw `htmlString` with no wrapping. If no DB record exists, the Blade view is rendered via `Content(view: ...)` with `epitreData()` passed as view data.

This means your Blade view is always the working default. Editors only override when they want to.

---

Template Classes
----------------

[](#template-classes)

All template classes extend `EpitreTemplate`:

```
abstract class EpitreTemplate
{
    protected string  $key;     // unique dot-notation or kebab identifier
    protected string  $label;   // displayed in the Filament panel
    protected string  $view;    // Blade view path for the default content
    protected ?string $layout;  // optional layout view for DB-stored content
    protected array   $tokens;  // '{token}' => 'Description for editors'

    abstract public function resolve(array $data): array;
}
```

The `$tokens` array is informational only. It is displayed in the Filament editor sidebar so editors know what substitutions are available. The `resolve()` method maps token strings to their runtime values given the data array from `epitreData()`.

### Layouts

[](#layouts)

When editors save content through the Filament panel, Epitre renders it as HTML with tokens replaced. Without a layout, this is returned as a raw `htmlString` — no wrapping, no styling.

If your emails use a mail layout (Laravel's ``, a custom component, or any Blade view), set `$layout` on the template to a Blade view path. Epitre will render that view with two variables available: `$body` (the resolved HTML) and everything from `epitreData()`.

```
class OrderShippedTemplate extends EpitreTemplate
{
    protected string  $view   = 'mail.order-shipped';
    protected ?string $layout = 'mail.layouts.epitre';
    // ...
}
```

Create the layout view:

```
{{-- resources/views/mail/layouts/epitre.blade.php --}}

{!! $body !!}

```

Epitre renders layout views through Laravel's mail rendering pipeline, which is the only way `` and related components are available. This pipeline also inlines CSS from your mail theme into the output, which is necessary for email client compatibility. Your stored HTML body is output raw via `{!! $body !!}` — nothing is converted to or processed as Markdown. The pipeline name is a Laravel implementation detail you do not need to think about.

The `$layout` only applies when a DB record exists. The `$view` default continues to use its own layout as normal.

---

Filament Resource
-----------------

[](#filament-resource)

Registering `EpitrePlugin` adds an **Email Templates** resource to your panel.

**List view** shows all registered templates with their current status:

StatusMeaningUsing defaultNo DB record exists, Blade view is activeCustomisedA DB record overrides the subject and/or body**Edit view** lets editors set the subject and body per locale. The sidebar shows the available tokens for that template. Saving creates or updates the DB record. Leaving a field empty falls back to the Blade view default.

**Reset to default** (visible only when a DB record exists) deletes the record and restores the Blade view default. Requires confirmation.

---

Translation
-----------

[](#translation)

Subject and body are stored as translatable JSON columns via `spatie/laravel-translatable`. The Filament editor uses the locale switcher from `lara-zeus/spatie-translatable` to manage per-locale content.

Configure your available locales in `config/app.php`:

```
'locales' => [
    'en' => 'English',
    'fr' => 'Francais',
],
```

Epitre reads this at boot time to configure the translatable plugin. If you are already registering `SpatieTranslatablePlugin` yourself with locales set, Epitre will not overwrite them.

---

Testing
-------

[](#testing)

```
composer test
```

Changelog
---------

[](#changelog)

Please see [CHANGELOG](CHANGELOG.md) for recent changes.

Credits
-------

[](#credits)

- [Blackpig Creatif](https://github.com/blackpig)
- [All Contributors](../../contributors)

License
-------

[](#license)

MIT. See [LICENSE](LICENSE.md) for details.

###  Health Score

43

—

FairBetter than 91% of packages

Maintenance98

Actively maintained with recent releases

Popularity7

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity53

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

2

Last Release

6d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/e094a144c7d16d2115fac66e49bf5013829a545f111c618ff22fe11ddf8c7bf4?d=identicon)[Blackpig](/maintainers/Blackpig)

---

Top Contributors

[![Blackpig](https://avatars.githubusercontent.com/u/1029317?v=4)](https://github.com/Blackpig "Blackpig (6 commits)")

---

Tags

laravelfilamentfilament-pluginfilamentphpBlackpigCreatifepitre

###  Code Quality

TestsPest

Static AnalysisPHPStan, Rector

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/blackpig-creatif-epitre/health.svg)

```
[![Health](https://phpackages.com/badges/blackpig-creatif-epitre/health.svg)](https://phpackages.com/packages/blackpig-creatif-epitre)
```

###  Alternatives

[a909m/filament-statefusion

Filament StateFusion is a powerful FilamentPHP plugin that seamlessly integrates Spatie Laravel Model States into the Filament admin panel. This package provides an intuitive way to manage model states, transitions, and filtering within Filament, enhancing the user experience and developer productivity.

295.8k1](/packages/a909m-filament-statefusion)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
