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

H5P Bundle Laravel Package

jorisdugue/h5p-bundle

Laravel bundle for integrating H5P interactive content into PHP applications. Manage H5P libraries, content types, and editor/viewer assets, making it easier to create, embed, and serve rich interactive exercises and quizzes.

View on GitHub
Deep Wiki
Context7

Getting Started

What this package actually is. jorisdugue/h5p-bundle is a Symfony bundle that wires the official h5p/h5p-core and h5p/h5p-editor PHP libraries into a Symfony app: service definitions, routes/controllers for the editor and AJAX endpoints, Doctrine entities for content/libraries, and a filesystem abstraction for the h5p/ directory tree. There is no Laravel equivalent here — as a Laravel developer you have three realistic options:

  1. Sidecar service — run a small Symfony app with this bundle as an internal "H5P authoring + delivery" service, and talk to it from Laravel over HTTP (iframe embeds + a thin JSON API you write).
  2. Port the wiring — depend on h5p/h5p-core + h5p/h5p-editor directly and reimplement what the bundle does (container bindings, routes, storage, migrations) as a Laravel package. The bundle is a good reference implementation for this.
  3. Borrow the design — copy the config shape, storage layout and entity schema, and pair it with the JS-only H5PStandalone player if you only need playback, not authoring.

Where to look first (in vendor/jorisdugue/h5p-bundle):

Path Why
src/DependencyInjection/Configuration.php The full config surface (h5p_bundle: keys) — storage paths, public URLs, DB, assets
src/Resources/config/ Service wiring; shows which H5P core classes are constructed and with what
src/Controller/ The route contract: editor, ajax, content, library upload/download
src/Entity/ The Doctrine schema you'd translate to Laravel migrations
README.md Install order (bundle registration → routes → config → migrations → assets)

Minimal steps in a Symfony host:

composer require jorisdugue/h5p-bundle
// config/bundles.php
Jorisdugue\H5PBundle\H5PBundle::class => ['all' => true],
# config/routes.yaml
h5p:
    resource: '@H5PBundle/Resources/config/routes.yaml'
    prefix: /h5p
# config/packages/h5p_bundle.yaml
h5p_bundle:
    storage:
        path: '%kernel.project_dir%/var/h5p'      # libraries/, content/, files/, cachedassets/
        public_url: '/h5p-assets'                  # MUST be web-served
    database:
        driver: doctrine

Then: run the migrations that create h5p_content, h5p_libraries, h5p_libraries_libraries, h5p_libraries_language, h5p_content_dependencies, h5p_content_user_data; symlink (or route-serve) var/h5p to public/h5p-assets; bin/console assets:install.

First use case. Log into the admin area, hit the bundle's editor route, upload a content type .h5p library (e.g. Course Presentation or Interactive Video), create one piece of content, then embed it:

{{-- Laravel / Blade side --}}
<div class="h5p-content" data-content-id="{{ $h5pContentId }}"></div>
<script src="https://your-symfony-host/h5p-assets/h5p-core/js/h5p.js"></script>
<script>
  H5PIntegration = {!! $integrationJson !!};  // returned by the host's content endpoint
</script>

That "create a quiz in the editor, embed via H5PIntegration" loop is the whole day-to-day of this package.

Implementation Patterns

1. Editor vs. player must be separated. The editor routes (/h5p/editor/*, library upload) are privileged — a user who can reach them can upload PHP-bearing .h5p packages. Put them behind your admin firewall (ROLE_ADMIN, or a dedicated H5P author role via the bundle's voter if present). Player routes (/h5p/content/{id}, /h5p/ajax) only need to be reachable by learners. In the sidecar model, expose the player route publicly and the editor route only on your internal network.

2. Storage abstraction is the seam you'll actually touch. H5P writes four sibling directories under one root: libraries/, content/, files/, cachedassets/. Configure a single Flysystem adapter and make sure the same root is reachable by both PHP (write) and the web server (read). Typical Laravel-side translation:

// config/filesystems.php
'h5p' => ['driver' => 'local', 'root' => storage_path('h5p'), 'url' => env('APP_URL').'/h5p-assets'],

and a Route::get('/h5p-assets/{path}') ->where('path', '.*') controller that streams from that disk with long Cache-Control — or just a public/h5p-assets symlink. Do not use S3 as the primary root: H5P core does direct file_put_contents/scandir in places, so a local root plus a CDN in front is the low-friction path.

3. Content is two columns, not a blob. The model is content_id + parameters (JSON of the content's settings) + library (e.g. H5P.InteractiveVideo 1.24). Treat parameters as opaque; never hand-parse or mutate it. Versioned library strings are how H5P decides to run upgrade scripts on load — keep old library versions on disk after an upgrade or existing content breaks.

4. Build the H5PIntegration payload once, cache it. The player needs a JS settings object listing core scripts, content dependencies, contents and ajaxPath. Compute it per content type, cache it (Redis) keyed by library + content type version, and inject it into your Blade/iframe wrapper. Regenerating it per request is the main source of slow first paint.

5. Capture results through the AJAX contract. Learner answers, xAPI statements, and content-user-data (resume state) all stream through the ajax endpoint. Two integration tips:

  • Keep an eye on that route's CSRF behaviour — H5P core posts raw payloads; if your Symfony firewall blocks them you'll see silent 403s in the Network tab.
  • Persist h5p_content_user_data for resume/attempt history; it's the closest thing to a "gradebook" the bundle gives you out of the box. Map it to a Laravel h5p_results table if you're building reporting.

6. Iframe is the safest embed. Rather than shipping core JS/CSS into your Laravel layout, render the H5P player in a same-origin iframe on the Symfony host. It isolates H5P's global H5PIntegration/H5P namespace, its CSS resets, and its jQuery assumptions from your app. Communicate via postMessage for xAPI/completed events.

7. Porting checklist if you go the Laravel route. Reimplement, in order: config + Flysystem root → migrations for the six core tables (utf8mb4, content_id unsigned bigint, long indexes on library/name) → the editor/player/AJAX controllers → asset route → a H5PEditorStorage/H5PEditorAjaxInterface bridge backed by Eloquent. Budget a week; H5P core's interfaces are global-state-heavy.

Gotchas and Tips

Vendor risk is real. 11 stars, 0 dependents, one maintainer, first stable-ish release Feb 2025. Pin the exact version ("jorisdugue/h5p-bundle": "1.x.y"), commit your composer.lock, and be prepared to fork. If you're doing the sidecar approach, treat the whole Symfony app as disposable — it should be reproducible from config.

Path/public-URL mismatch is the #1 failure mode. Symptom: iframe renders blank, console shows 404s for h5p-core/js/*.js or content .h5p dependencies. storage.path (filesystem) and the URL the web server serves must be the same directory. Check with curl -I on a known asset before debugging anything else.

Permissions. libraries/, content/, files/, cachedassets/ must be writable by the PHP user and readable by nginx/Apache. cachedassets/ in particular gets rewritten on library changes; on containerised deploys put it on a writable volume or accept cold-cache rebuilds.

.h5p uploads are zip files containing JS and PHP-checkable content. Always validate against H5P's own library validation, restrict upload to admins, and never serve the libraries/ directory with PHP execution enabled.

CSRF, sessions, and AJAX. If H5P editor saves fail with a generic "error" toast, look at /h5p/ajax responses in the Network tab — most often it's a firewall/CSRF rule rejecting the POST or a session user not being resolved. Loosen only that path.

Library upgrades don't migrate content backwards. Upgrading H5P.CoursePresentation leaves old content pinned to the old library. Keep previous versions in libraries/ and test a sample content item after every bump. There is no "downgrade" path.

Debugging ladder: (1) Network tab for 4xx/5xx on /h5p/ajax; (2) var/log/dev.log for the PHP exception behind it; (3) inspect the JSON in h5p_content.parameters — a malformed or empty parameters renders an empty player with no error; (4) var/h5p/cachedassets/ — clear it if CSS/JS look stale after a deploy.

Extension points. Prefer decorating the bundle's controllers/services over forking: swap the storage adapter via the service alias, add a Doctrine listener on the content entity for audit/versioning, or wrap the AJAX controller to log xAPI statements into your own analytics table. The bundle's entities are plain Doctrine — a postPersist listener is the cheapest place to push "content created" events into your Laravel app.

Don't fight H5P's globals. If you're porting, accept that H5PCore, H5PEditor, and H5PIntegration are not PSR/DI-friendly. Wrap them in one adapter class behind a Laravel contract; don't try to inject them everywhere or you'll be debugging bootstrap order forever.

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.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect