bayareawebpro/laravel-dom-pipeline

A DOM Pipeline Utility for Modifying HTML

Maintainers

Package info

github.com/bayareawebpro/laravel-dom-pipeline

pkg:composer/bayareawebpro/laravel-dom-pipeline

Statistics

Installs: 1 330

Dependents: 0

Suggesters: 0

Stars: 5

Open Issues: 0

v1.0.8 2026-03-19 06:01 UTC

This package is auto-updated.

Last update: 2026-03-19 06:09:36 UTC


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

Docs: https://www.php.net/manual/en/book.dom.php

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;
    }
}