Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Content To Html Parser Laravel Package

roelofjan-elsinga/content-to-html-parser

A PHP package that converts structured content into clean HTML output. Define your content in a flexible format, then render it as ready-to-use markup in Laravel or any PHP application.

View on GitHub
Deep Wiki
Context7

Getting Started

The package wraps a Markdown-to-HTML converter so Laravel can transform Markdown strings, files, or Blade content without you hand-rolling a parser. As of 0.4.0 the engine is ParsedownExtra, not Parsedown — so you get Markdown Extra semantics (tables, footnotes, definition lists, fenced code with attributes) out of the box.

Minimal path:

  1. composer require the package, then confirm the service provider is discovered (Laravel 5.5+ auto-discovery) or register it in config/app.php.
  2. Publish the config if present: php artisan vendor:publish --tag=markdown (or check the README for the exact tag). Read config/markdown.php first — that's where escaping, breaks, and extension toggles live.
  3. First use case: a facade call in a controller or seeder.
use Markdown; // or the package's documented facade alias

$html = Markdown::parse('# Hello');
$html = markdown('# Hello'); // helper, if the package registers one

If your templates are the destination, the Blade directive is usually the shortest route:

@markdown($post->body)

Verify the output with {!! !!} semantics deliberately — see Gotchas.

Implementation Patterns

Rendering stored content. The common shape: a body (or body_markdown) column plus a cached body_html column rendered at save time. Parse on write, not on read — Markdown parsing is cheap but not free, and it keeps index/feed generation simple.

// in an observer or model mutator
protected static function booted(): void
{
    static::saving(function ($post) {
        $post->body_html = Markdown::parse($post->body);
    });
}

Blade directive for user-facing prose. Best for READMEs, policy pages, and CMS blocks. Keep the directive in an escaped container and only unescape the parsed output:

<div class="prose">{!! Markdown::parse($comment->body) !!}</div>

Config-driven parsing at the service boundary. Don't scatter Markdown::parse($x, true, false)-style flags through your codebase. Bind a thin wrapper service that reads config/markdown.php once, and inject that wrapper:

final class MarkdownRenderer
{
    public function __construct(private array $config) {}

    public function render(string $text): string
    {
        return Markdown::parse($text); // config already applied by the package
    }
}

This is the extension point that pays off later — when you need to post-process HTML (sanitisation, anchor injection, syntax highlighting) you change one class, not forty call sites.

Sanitisation as a mandatory second stage. ParsedownExtra does not sanitise. For anything user-submitted, pipe output through a dedicated HTML sanitiser (mews/purifier, symfony/html-sanitizer, or League's HTML Purifier) after parsing. Make it part of the wrapper so it cannot be forgotten:

public function render(string $text): string
{
    return $this->sanitizer->clean(Markdown::parse($text));
}

File and asset pipelines. Parse .md docs at build/seed time and store the HTML — e.g. an artisan command that walks resources/docs/*.md, parses each, and upserts into a pages table. This is a good fit for Markdown::parse() on file contents via File::get().

Testing. Golden-file tests are the pragmatic approach: a fixture .md paired with an expected .html, compared with assertSame. This pins your rendering behaviour so a future engine swap (the 0.4.0 Parsedown→ParsedownExtra move is exactly this class of change) surfaces as a failing test rather than a visual regression.

Gotchas and Tips

0.4.0 changes rendered output. ParsedownExtra is a superset of Parsedown, but it is not output-identical. Tables without leading/trailing pipes, footnote syntax, and some newline-before-block handling can render differently. If you render on write into a cached body_html column, you must re-render existing rows after upgrading — a one-off artisan command over your content table. Otherwise the site will contain a mix of two engines.

URL auto-linking and inline HTML differ subtly. ParsedownExtra enables markupEscaped = false behaviour by default in many configs, meaning inline HTML passes through. Combined with a missing sanitiser this is a stored-XSS path. Double-check config/markdown.php for safe_mode/markup_escaped/escape keys after the upgrade — key names may have shifted with the engine swap.

Escaped vs raw is the number-one bug source. In Blade, {{ Markdown::parse($x) }} shows literal <p> tags. You need {!! !!} or the package's @markdown directive. Conversely, wrapping {!! !!} around unparsed user input is the security hole. Keep the pairing explicit in your wrapper.

Facade alias collisions. The package typically registers an alias like Markdown, which can clash with an app class or another package. In config/app.php (or via class_alias avoidance) reference the full namespace, or bind your own MarkdownRenderer and never touch the alias directly.

StyleCI integration (0.4.0) is contributor-facing, not runtime. It formats the package's PHP, not yours. Don't expect it to reformat your Markdown or your app's code. What it does mean: if you've forked or vendored the package, your local modifications will get reflowed by their CI on PR — expect noisy diffs. If you maintain a fork, either adopt the same StyleCI preset (.styleci.yml) or exclude the vendor path.

Config caching. php artisan config:cache freezes config/markdown.php. If you're toggling parser options per-tenant via config()->set() at runtime, that breaks under cache — drive per-tenant behaviour through your wrapper's constructor instead.

Extension points to reach for: subclass your wrapper (not the vendor class) to post-process HTML; a Blade component (<x-markdown :text="$post->body" />) is cleaner than the directive for anything reused; and a MarkdownParsed-style event around your wrapper lets you bolt on TOC generation or heading-anchor IDs without touching call sites.

Debugging output that "looks wrong": dump the raw parse result before your sanitiser to isolate whether the problem is the parser or the sanitiser. Nine times out of ten a missing blank line before a fenced block or list is the culprit — Markdown Extra is stricter about block separation than you'd like, and CommonMark habits will bite you.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource