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

Oembed Bundle Laravel Package

markup/oembed-bundle

Symfony bundle that adds oEmbed support, turning media URLs from providers like YouTube, Vimeo, and Twitter into ready-to-use embed HTML via configurable services and Twig helpers.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a Symfony2 bundle, not a Laravel package. Treat it as a reference implementation to read and port, not a composer require away from working in a Laravel app.

  1. Read before installing. Open the README, then Resources/config/services.xml and the two interesting classes: the consumer (fetches oEmbed JSON for a URL) and the controller (serves your own content as an oEmbed provider). Those two files are 90% of the value; the Twig extension is a thin wrapper.
  2. Try to install only to inspect. composer require markup/oembed-bundle --no-scripts in a scratch Symfony2-era project. In a modern Laravel app the solver will almost certainly conflict (the bundle pins symfony/symfony 2.x components, which collide with Laravel's own symfony/* constraints). Don't burn time fighting it.
  3. First use case to replicate: given a YouTube/Vimeo URL, discover the provider endpoint, fetch the oEmbed JSON, cache it, and render the returned html in a Blade view. That's the "day-to-day" use — embedding user-pasted media links.
  4. Second use case: the inverse — expose your content (a video, a photo page, a rich article) as an oEmbed provider so Slack/WordPress/Discord unfurl your links. This is the bundle's distinguishing feature and the part most worth copying.

Implementation Patterns

Consumer pattern (embedding third-party media)

The bundle's flow is: URL → discovery → fetch → render. In Laravel the natural shape is a service + cache + Blade component:

// config/oembed.php
return ['providers' => [
    'youtube.com' => 'https://www.youtube.com/oembed',
    'vimeo.com'   => 'https://vimeo.com/api/oembed.json',
]];
class OEmbed
{
    public function forUrl(string $url): ?array
    {
        $host = parse_url($url, PHP_URL_HOST);
        $endpoint = config("oembed.providers.$host");
        if (!$endpoint) return null;

        return Cache::remember("oembed:$url", now()->addDay(), fn () =>
            Http::timeout(3)->get($endpoint, ['url' => $url, 'format' => 'json'])
                ->throw()->json()
        );
    }
}

Provider pattern (being embedded by others)

Mirror the bundle's controller: a route like GET /oembed?url=...&format=json&maxwidth=... that resolves your model from the URL and returns the oEmbed 1.0 payload:

{"version":"1.0","type":"video","provider_name":"…","provider_url":"…",
 "title":"…","html":"<iframe …></iframe>","width":640,"height":360}

Validate url against your own domain, honour maxwidth/maxheight, and return 404 for unknown URLs (the spec expects it). Discovery is a <link rel="alternate" type="application/json+oembed" href="…"> tag in your page <head> — that's how consumers find your endpoint, so add it to your layout too.

Rendering

The bundle exposes a Twig function/filter; the Laravel equivalent is a Blade component that swallows the "no oEmbed available" case:

<x-oembed :url="$post->media_url" />

Inside, fall back to a plain link when forUrl() returns null — that's the bundle's weakest spot and where you'll add the most value.

Integration tips

  • Keep the HTTP fetch out of the request path for bulk imports — dispatch a queued job that populates the cache.
  • Bind OEmbed as a singleton in a service provider and inject Illuminate\Http\Client\Factory so you can Http::fake() in tests.
  • Store the raw JSON, not the rendered HTML, so you can re-render when your markup changes.

Gotchas and Tips

  • Abandonment is the headline. Last release January 2020, Symfony2 has been EOL since 2018/2019, 3 stars, 0 dependents. No security patches, no PHP 8 support, no maintainer. Do not ship it to production. Port the ~150 lines or use a maintained, framework-agnostic library instead (embed/embed handles discovery, caching, and dozens of providers in one).
  • Dependency hell. Symfony2 bundles autoload via AppKernel, not Laravel's package discovery; there is no service provider. Even if the solver cooperates, nothing boots it.
  • XSS: never {!! $oembed['html'] !!} blindly. The html field is attacker-influenceable if you let users choose arbitrary endpoint URLs. Allowlist provider hosts by domain and sanitize the returned HTML.
  • SSRF: user-supplied URLs trigger server-side fetches. Validate the scheme (https only), resolve the host, and block private/link-local ranges — otherwise http://169.254.169.254/ becomes an oEmbed "provider".
  • Discovery is optional and inconsistent. Many providers only work via a direct endpoint; some reject HEAD, some require format=json explicitly (YouTube returns 401 without it), some return xml by default. Hardcode known endpoints for the top 5 providers and only fall back to <link> discovery otherwise.
  • Cache aggressively, respect failures. oEmbed responses are near-static. Cache successes for hours/days; cache negatives briefly (5–10 min) so a dead URL doesn't hammer the provider on every page view.
  • Debugging: log the raw JSON before rendering — 80% of "it renders blank" issues are a type you didn't handle (photo, link, rich vs video) or a missing html for link types. In Symfony you'd use the profiler; in Laravel, Http::fake() plus a dd of the decoded payload is faster.
  • Extension points worth copying: the bundle's separation of consumer (fetch) and renderer (Twig) is the right seam. Keep it — swap the renderer for a Blade component, and swap the HTTP client for Laravel's, without touching the other.
  • Don't over-engineer maxwidth. Honour it by scaling width/height proportionally on the provider side; most consumers ignore it anyway and CSS on your side is more reliable.
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.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package