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

Scorm Laravel Package

escolalms/scorm

Laravel package for integrating SCORM e-learning content into your application, providing backend support for uploading, playing, and tracking SCORM packages and learner progress. Part of the EscolaLMS ecosystem for building online learning platforms.

Deep Wiki
Context7

Getting Started

  1. Install and migrate.

    composer require escolalms/scorm
    php artisan vendor:publish --provider="EscolaLms\Scorm\Providers\ScormServiceProvider"
    php artisan migrate
    

    The package is part of the EscolaLMS ecosystem, so it expects escolalms/courses and escolalms/auth to already be installed. Confirm the exact service-provider class name from composer.json / src/Providers — it may be ScormServiceProvider or ScormServiceProvider.

  2. Where to look first.

    • config/scorm.php — storage disk, allowed mime types, package size limit, route prefix/middleware.
    • database/migrations/* — the scorms table and how it links to courses (likely course_id or a polymorphic topicable/model_id relationship, since EscolaLMS content is topic-based).
    • routes/api.php — the upload, launch, and tracking endpoints.
    • src/Models/ — the Scorm model and its relationships (course(), topic(), user progress).
    • src/Http/Controllers/ — upload and "launch" controllers are the two you will touch most.
  3. First end-to-end use case. Attach a SCORM 1.2 ZIP to a course and play it:

    • Authenticate as a user enrolled in the course (the package relies on escolalms/courses enrollment checks; without enrollment the launch/tracking endpoints will 403).
    • Upload the ZIP via the API endpoint (POST /api/scorm or similar).
    • Poll or wait for the parse job to populate the manifest fields (entry_point, title, version).
    • Call the launch endpoint to get the iframe URL, then load it in your frontend player.
    • Watch the tracking callbacks (initialize/commit/terminate) land in the scorm_user_progress (or equivalently named) table, and confirm a completed status triggers the topic/course progress update.

Implementation Patterns

Upload → parse → attach flow. Use the package's upload endpoint (or dispatch its parsing job yourself if you have a separate admin UI). The ZIP is stored on the configured disk, then a job extracts it and parses imsmanifest.xml to derive the schema version (1.2 vs 2004), the entry file, and the organization title. Keep this asynchronous: a 50 MB package with hundreds of assets will blow past the default 30s HTTP timeout.

$scorm = Scorm::create([
    'course_id' => $course->id,
    'title'     => $request->file('package')->getClientOriginalName(),
]);
$scorm->addMediaFromRequest('package')->toMediaCollection('package');
ScormParserJob::dispatch($scorm); // verify class name in src/Jobs

Launching content. Treat the launch response as a contract your frontend consumes: it should return the extracted package's base URL plus the entry_point from the manifest. Your player mounts that in an <iframe> and defines the SCORM API adapter on window before the iframe loads:

<script>
  window.API = {
    LMSInitialize:   () => "true",
    LMSGetValue:      (k) => fetch(`/api/scorm/${id}/get?key=${k}`).then(...),
    LMSSetValue:      (k, v) => { pending[k] = v; return "true"; },
    LMSCommit:        () => postCommit(pending),
    LMSFinish:        () => postCommit(pending, true),
    LMSGetLastError:  () => "0",
    LMSGetErrorString: () => "",
    LMSGetDiagnostic: () => "",
  };
</script>
<iframe src="{{ $launchUrl }}" sandbox="allow-scripts allow-same-origin allow-forms"></iframe>

Tracking and completion. The commit and terminate endpoints are the heart of the integration. Buffer cmi.* writes on the client and flush them on LMSSetValue batches to avoid one request per field. On terminate, map cmi.core.lesson_status (1.2) or cmi.completion_status + cmi.success_status (2004) onto your course progress model. If your app listens to an EscolaLMS event (TopicCompleted, CourseProgressUpdated), hook into it here rather than duplicating logic in a controller.

Authorization. Every launch and tracking call must re-check enrollment and that the requesting user owns the progress row. Do not trust a user_id in the payload — derive it from Auth::user(), or tracking gets forgeable by any authenticated learner.

Integration tips.

  • If you expose the player in a Vue/React SPA, proxy tracking calls through your app's API rather than the SCORM endpoint directly, so you can add retries and offline buffering.
  • For S3/GCS disks, prefer a streaming controller over signed URLs; SCORM packages use relative asset paths and presigned URLs break imsmanifest.xml-relative navigation.
  • Use the package's model events (saved, deleted) to cascade cleanup of extracted assets, otherwise orphaned directories accumulate on disk.

Gotchas and Tips

  • Version detection is non-negotiable. SCORM 1.2 expects window.API; SCORM 2004 expects window.API_1484_11 with different method names (Initialize, GetValue, SetValue, Commit, Terminate). Serving a 1.2 adapter to a 2004 package silently disables tracking. Confirm the package parses schemaversion from the manifest and lets the player pick the adapter.
  • Zip extraction is a security boundary. Verify the package rejects entries with ../ or absolute paths (Zip Slip). If the package doesn't, add your own validation before dispatch — ZipArchive::extractTo follows paths blindly.
  • imsmanifest.xml namespaces. Parsing with SimpleXML requires registering the SCORM namespaces (http://www.imsproject.org/xsd/imscp_rootv1p1p2, etc.); a naive $xml->organizations->organization query returns empty. Check the parser handles both namespace-qualified and bare queries.
  • Upload limits. SCORM packages are routinely 20–100 MB. Raise upload_max_filesize, post_max_size, and max_execution_time, and set the queue timeout on the parse job; the default 60s worker timeout is a common silent failure.
  • Storage permissions. Extracted content must be web-servable. On local disk make sure the storage/app/... path is either linked to public/ or served through a controller — a 404 on the entry point is almost always permissions or a wrong disk.
  • Iframe sandbox. allow-scripts alone blocks the SCORM API because the content can't reach the parent. Use allow-scripts allow-same-origin (or serve from the same origin) if you need the adapter injected via window.parent.API.
  • Completion semantics. lesson_status can be not attempted, incomplete, completed, passed, failed. Do not collapse passed/completed into one flag — courses that gate on success will be wrong. For 2004, completion_status and success_status are separate.
  • Progress row bloat. cmi data is stored as a JSON blob and updated on every commit. Prune or truncate on terminate with the package's pruning command (check src/Console); otherwise the table balloons.
  • Re-uploading a package typically creates a new Scorm record rather than replacing. Old progress rows won't migrate, so version packages explicitly and keep the scorm_id on progress rows stable if you care about resuming learners mid-course.
  • Testing. Unit tests mock the parser; but the only way to catch real adapter bugs is a fixture package per version (a tiny 1.2 and 2004 sample with a single index.html SCO). Commit these to tests/fixtures and run them through the full launch → initialize → set → finish → verify-progress path in a feature test.
  • Route caching. If you use php artisan route:cache in production, verify the package's routes are registered via the service provider and not a bootstrap file, or your SCORM endpoints vanish after cache warmup.
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.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin