isapp/tinyeditor-picture-tag
Install Dependencies
composer require isapp/tinyeditor-picture-tag spatie/laravel-medialibrary
Ensure spatie/laravel-medialibrary is properly configured (follow Spatie’s docs).
Publish Config
php artisan vendor:publish --provider="Isapp\TinyEditorPictureTag\TinyEditorPictureTagServiceProvider"
Configure config/tinyeditor-picture-tag.php with:
picture_tag_config (e.g., ['webp', 'avif'] for formats)model_observers (array of observed models)tiny_mce_content_field (default: content)Register Observer
Add the observer to your model (e.g., Post.php):
use Isapp\TinyEditorPictureTag\Observers\PictureTagObserver;
class Post extends Model
{
protected static function booted()
{
static::observe(PictureTagObserver::class);
}
}
First Use Case
Insert an <img> via TinyMCE into a model’s content field. After saving, the observer converts it to a <picture> tag with responsive sources (e.g., WebP/AVIF fallbacks).
TinyMCE Integration
image plugin to insert <img> tags (e.g., src="/storage/123.jpg").saved() or updated().Observer Logic
<img> tags from the content field (configurable).<picture> tags using Spatie’s media library to generate responsive sources:
<picture>
<source srcset="/storage/123.webp" type="image/webp">
<source srcset="/storage/123.avif" type="image/avif">
<img src="/storage/123.jpg" alt="...">
</picture>
alt, title, and class attributes.Configuration-Driven
picture_tag_config (e.g., ['webp', 'avif', 'jpg']).<picture> structure via picture_tag_template in config.Bulk Processing
PictureTagHelper facade to manually process content:
use Isapp\TinyEditorPictureTag\Facades\PictureTagHelper;
$processedContent = PictureTagHelper::process($content);
hasMany(Media::class)).src paths compatible with Spatie’s storage (e.g., /storage/filename.jpg).static::observe(PictureTagObserver::class)->unless(fn () => app()->environment('import'));
<img> fallback in picture_tag_template if formats fail to load.Observer Timing
creating/updating events with manual processing:
protected static function booted()
{
static::saving(function ($model) {
$model->content = PictureTagHelper::process($model->content);
});
}
Media Library Paths
srcset paths break if Spatie’s storage disk isn’t configured correctly.config/filesystems.php and config/medialibrary.php match TinyMCE’s output paths.HTML Sanitization
srcset). Use PictureTagHelper::process() with htmlspecialchars_decode() if needed:
$content = htmlspecialchars_decode($content);
$content = PictureTagHelper::process($content);
Performance
content fields with many <img> tags may slow down saves.static::saved(function ($model) {
PictureTagHelper::process($model->content)->later();
});
'debug' => env('APP_DEBUG', false),
php artisan tinyeditor-picture-tag:test (if provided) or manually test with:
$content = '<img src="/storage/test.jpg" alt="Test">';
dd(PictureTagHelper::process($content));
Custom Templates
Override the <picture> template in config:
'picture_tag_template' => '<picture class="custom-class">
<source srcset="{{ srcset }}" type="{{ type }}">
<img src="{{ src }}" alt="{{ alt }}" class="{{ class }}">
</picture>',
Dynamic Formats
Fetch formats dynamically (e.g., from browser Accept header):
use Isapp\TinyEditorPictureTag\PictureTagHelper;
$formats = request()->header('Accept') ? ['webp', 'avif'] : ['jpg'];
$content = PictureTagHelper::process($content, $formats);
Exclude Fields Skip processing for specific fields by extending the observer:
class CustomPictureTagObserver extends PictureTagObserver
{
protected $skipFields = ['content', 'excerpt'];
}
WebP Fallback
Add a <img> fallback for unsupported browsers:
'picture_tag_template' => '<picture>
<source srcset="{{ srcset }}" type="{{ type }}">
<img src="{{ src }}" alt="{{ alt }}" class="{{ class }}">
<img src="{{ src }}" alt="{{ alt }}" class="fallback" style="display: none;">
</picture>',
How can I help you explore Laravel packages today?