bayareawebpro / laravel-dom-pipeline
A DOM Pipeline Utility for Modifying HTML
Package info
github.com/bayareawebpro/laravel-dom-pipeline
pkg:composer/bayareawebpro/laravel-dom-pipeline
v1.0.8
2026-03-19 06:01 UTC
Requires
- php: ^8.2
- ext-dom: *
- ext-libxml: *
- illuminate/pipeline: ^12.0|^13.0
- illuminate/support: ^12.0|^13.0
Requires (Dev)
- nunomaduro/larastan: ^2.0|^3.0
- orchestra/testbench: ^10.0|^11.0
- phpunit/phpunit: ^11.0|^12.0
README
composer require bayareawebpro/laravel-dom-pipeline
https://packagist.org/packages/bayareawebpro/laravel-dom-pipeline
Laravel DOM Pipeline allows you to pipe HTML content through a series of classes
which can be helpful with sanitization and server-side enhancement / modification of page
elements. The pipeline will not return the <body> tag or any other tags outside the
body scope.
Usage:
use BayAreaWebPro\DomPipeline\DomPipeline; use My\Pipes\{ LazyLoadImageTags, LazyLoadVideoTags, BuildTableOfContents }; $modified = DomPipeline::make($html, [ LazyLoadImageTags::class, LazyLoadVideoTags::class, BuildTableOfContents::class, ]);
Example Dom Pipe Class
LazyLoad Images
<?php declare(strict_types=1); namespace App\Services\Html\Formatters; use Closure; use DOMElement; use DOMDocument; class LazyLoadImages { public function handle(DOMDocument $dom, Closure $next) { foreach ($dom->getElementsByTagName('img') as $node) { $this->lazyLoad($node); } return $next($dom); } protected function lazyLoad(DOMElement $node): void { if (!$node->hasAttribute('loading')) { $node->setAttribute('loading', 'lazy'); } } }
Element to VueComponent
Convert an Iframe into a Vue Component extracting the video ID from the URL.
<?php declare(strict_types=1); namespace App\Services\Html\Formatters; use Closure; use DOMElement; use DOMDocument; class LazyLoadVideos { public function handle(DOMDocument $dom, Closure $next) { $xpath = new \DOMXPath($dom); foreach($xpath->query('//iframe[@class="media"]') as $node){ $this->lazyLoad($dom, $node); } return $next($dom); } protected function lazyLoad(DOMDocument $dom, DOMElement $node): void { if(is_null($node->parentNode)) return; // Match the YouTube Video ID. // https://stackoverflow.com/questions/2936467/parse-youtube-video-id-using-preg-match preg_match('%(?:youtube(?:-nocookie)?\.com/(?:[^/]+/.+/|(?:v|e(?:mbed)?)/|.*[?&]v=)|youtu\.be/)([^"&?/ ]{11})%i', (string) $node->getAttribute('src'), $matches ); if(isset($matches[1])){ // Create a new HTML fragment. $fragment = $dom->createDocumentFragment(); $fragment->appendXML(<<<HTML <v-video id="$matches[1]" label="Click to play..." :show-image="true"></v-video> HTML); // Replace Self with Fragment. $node->parentNode->replaceChild($fragment, $node); } } }
Table of Contents
<?php declare(strict_types=1); namespace App\Services\Html\Formatters; use DOMElement; use Closure; use StdClass; use DOMDocument; use Illuminate\Support\Str; use Illuminate\Support\Collection; use Illuminate\Support\Facades\View; class TableOfContents { public function handle(DOMDocument $dom, Closure $next) { $nodes = Collection::make(); $xpath = new \DOMXPath($dom); foreach ($xpath->query('//h1|//h2|//h3|//h4|//h5|//h6') as $node) { $text = strip_tags(html_entity_decode((string) $node->nodeValue)); $nodes->push($this->makeBookmark($node, $text)); } if($nodes->count() > 5){ View::share('tableOfContents', $nodes->take(25)); } return $next($dom); } protected function makeBookmark(DOMElement $node, string $text): StdClass { $bookmark = (object)[ 'anchor' => Str::slug($text), 'text' => Str::title(Str::replaceLast('.', '', $text)), ]; $node->setAttribute('id', $bookmark->anchor); return $bookmark; } }