Academy is a professional-grade Learning Management System (LMS) package for the Anchor Framework. It provides a robust engine for creating, managing, and monetizing educational content, from simple video courses to complex corporate training portals with automated assessments and certifications.
- Hierarchical Content: Organize learning into Programs, Modules, and Lessons.
- Fluent Content Builder: Create and structure programs using a developer-friendly fluent API.
- Advanced Assessment Engine: Build quizzes with multiple question types (MCQ, True/False, Essay) and automated grading.
- Learner Lifecycle Management: Handle enrolments, progress tracking, and milestone achievements.
- Monetization & Instalments: Sell individual courses or recurring subscription access via deep integration with Pay, Wave, and Wallet.
- Gamification: Reward learners with automated badges and rank-based achievements.
- Certification: Generate, issue, and verify secure PDF certificates upon program completion.
- Community Learning: Integrated discussion forums per program or lesson via the Hub package.
- CLI Maintenance: Dedicated commands for certificate verification, payment syncing, and data pruning.
- Universal RefIDs: Secure, public-facing identifiers for all Academy entities.
Academy uses a universal refid system for public-facing identifiers. This ensures that internal database IDs are never exposed in URLs or APIs, enhancing security and preventing enumeration attacks.
All Academy models implement the HasRefid trait and have a unique prefix (e.g., prg_ for programs, enr_ for enrolments).
// Retrieve a program by its public refid
$program = AcademyProgram::where('refid', 'prg_abc123')->first();
// Generate a new refid manually if needed
$newRefid = AcademyProgram::generateRefid();| Entity | Prefix | Example |
|---|---|---|
| Program | prg_ |
prg_x8k2... |
| Module | mod_ |
mod_y1p9... |
| Lesson | les_ |
les_z4q0... |
| Enrolment | enr_ |
enr_a2b3... |
| Assessment | asm_ |
asm_c4d5... |
| Question | que_ |
que_e6f7... |
| Choice | cho_ |
cho_g8h9... |
| Submission | sub_ |
sub_i0j1... |
| Certificate | crt_ |
crt_k2l3... |
| Badge | bdg_ |
bdg_m4n5... |
| Rating | rat_ |
rat_p6q7... |
| Discussion | dsc_ |
dsc_r8s9... |
| Note | not_ |
not_t0u1... |
| Resource | res_ |
res_v2w3... |
Academy is an optional package that requires installation.
php dock package:install Academy --packagesThis command will:
- Publish the
App/Config/academy.phpconfiguration file. - Register the
AcademyServiceProvider. - Run the required database migrations.
- Enable the global
academy()helper andAcademyfacade.
Edit App/Config/academy.php to define your LMS defaults:
return [
'currency' => 'USD',
// Feature Toggles (Deep Integrations)
'integrations' => [
'wave' => true, // Subscriptions
'wallet' => true, // Credit-based learning
'hub' => true, // Course discussions
'blish' => true, // Newsletter sync
'audit' => true, // Activity logging
],
// Certification Rules
'certificates' => [
'enabled' => true,
'prefix' => 'CERT-',
'template' => 'default',
],
// Gamification
'rewards' => [
'enabled' => true,
'points_per_lesson' => 10,
'points_per_assessment' => 50,
],
];To build effectively with Academy, it's important to understand the hierarchy of entities and the service-oriented architecture.
- Programs: The container for the entire course (e.g., "Fullstack Web Development").
- Modules: Logical groupings within a program (e.g., "Module 1: Introduction to PHP").
- Lessons: The actual learning units (Text, Video, or Quiz).
- Assessments: Quizzes or exams attached to modules or lessons to validate learning.
Everything in Academy revolves around the Enrolment. When a user joins a program, an AcademyEnrolment record is created. This record serves as the "source of truth" for:
- Status: (Pending, Active, Completed, Suspended)
- Progress: A percentage dynamically calculated based on completed lessons.
- Milestones: Tracked badges, grades, and eventual certification.
use Academy\Academy;
$program = Academy::program()
->titled('Mastering the Anchor Framework')
->described('Become a professional developer with Anchor.')
->withContent('This program covers everything from core to advanced packages.')
->create();Tip
Instructor Auto-Assignment: When create() is called, the system automatically assigns the current authenticated user as an instructor if no instructor_id is provided in the data array.
To enrol a user in a program, you can use the quick-access method on the facade:
$enrolment = Academy::enrol($user->id, $program->id);
// Activate the enrolment (e.g., after payment confirmation)
Academy::enrolments()->activate($enrolment);Note
Upon enrolment, a unique Admission Number (e.g., ADM-2026-0001) is automatically generated and assigned to the enrolment record.
Academy::progress()->completeLesson($enrolment->id, $lesson->id, [
'time_spent' => 3600, // seconds
]);
$currentProgress = $enrolment->fresh()->progress_percent; // e.g., 25%// 1. Drip Content: Release Module 2 after 7 days of enrolment
$module->update(['metadata' => ['drip_delay' => 7]]);
// 2. Prerequisites: Lesson B requires Lesson A to be completed
$lessonB->update(['metadata' => ['prerequisite_lesson_id' => $lessonA->id]]);
// 3. Validation
$canAccess = Academy::programs()->canAccess($userId, $lessonB->id); // returns false if Lesson A is not completed
### Data Aggregation & Deep Fetching
Academy provides "Deep Fetching" methods to retrieve a full tree of data for common entities. All methods support both integer IDs and string `refid`s.
| Method | Returns | Included Associations |
| :------------------------ | :------ | :----------------------------------------------------------- |
| `getProgramDetails(id)` | `array` | Modules, Lessons, Instructors, Resources, Announcements, Plans. |
| `getLessonDetails(id)` | `array` | Parent Module/Program, Resources, Assessment, Live Session. |
| `getEnrolmentDetails(id)` | `array` | Program Tree, Progress, Payment Instalments, Certificate, Notes. |
#### Example Usage
```php
// Fetching a Program via RefID
$details = Academy::getProgramDetails('prg_abc123');
// Accessing nested data
$modules = $details['modules'];
$instructors = $details['instructors'];- Instructor Override: Any user assigned as an instructor to a program via
addMember()has full access to all lessons, bypassing drip-delay and prerequisites. - Specific Learner Access: You can restrict a lesson to specific users by adding an
allowed_user_idsarray to the lesson'smetadata. - Multiple Instructors: Programs support multiple instructors. If no
instructor_idsare provided duringcreate(), the creator is assigned as the primary instructor by default.
// 1. Fluent Builder (Multiple Instructors)
$program = Academy::program()
->titled('Advanced Laravel Architecture')
->withInstructors([$leadId, $assistantId])
->create();
// 2. Manual Assignment (Program Service)
Academy::programs()->addMember($program->id, $user->id, 'instructor');
// 3. Metadata Restrictions (per Lesson)
$lesson->update(['metadata' => ['allowed_user_ids' => [1, 5, 8]]]);The Assessment Engine allows you to create complex quizzes and exams with automated feedback loops.
Assessments can be attached to modules or lessons.
$assessment = Academy::assessments()->create([
'title' => 'Core Architecture Quiz',
'passing_score' => 80, // percentage
'attempts_allowed' => 3,
]);$question = Academy::assessments()->addQuestion($assessment->id, [
'content' => 'What is the entry point of an Anchor request?',
'type' => 'mcq',
'points' => 10,
]);
Academy::assessments()->addChoice($question->id, 'index.php', isCorrect: true);
Academy::assessments()->addChoice($question->id, 'kernel.php', isCorrect: false);// 1. Start an attempt
$submission = Academy::assessments()->startAttempt($assessment->id, $enrolment->id);
// 2. Process submission (from a DTO or Request)
Academy::assessments()->submit($submission, [
$question1->id => ['choice_id' => $choice1->id],
$question2->id => ['choice_id' => $choice2->id],
]);
// 3. Check results
if ($submission->is_passing) {
echo "Learner passed with " . $submission->percent_score . "%";
}For high-volume content creation and grading, Academy supports bulk operations.
Use bulkAddQuestions to efficiently create multiple questions and their respective choices in a single transaction.
Academy::assessments()->bulkAddQuestions($assessmentId, [
[
'text' => 'Question 1',
'type' => 'mcq',
'points' => 5,
'choices' => [
['text' => 'Choice 1', 'is_correct' => true],
['text' => 'Choice 2', 'is_correct' => false],
],
],
[
'text' => 'Question 2',
'type' => 'mcq',
'points' => 10,
'choices' => [...],
],
]);The submit method natively accepts an array of answers, allowing for a single-request submission of an entire assessment.
Academy::assessments()->submit($submission, [
$q1_id => ['choice_id' => $c1_id],
$q2_id => ['choice_id' => $c2_id, 'content' => 'Optional essay text'],
$q3_id => ['file_path' => 'path/to/upload.jpg'], // For file-upload types
]);For essay-heavy programs, you can batch process grades.
$grades = [
['submission_id' => 1, 'score' => 85, 'feedback' => 'Great job!'],
['submission_id' => 2, 'score' => 45, 'feedback' => 'Please rewrite.']
];
foreach ($grades as $g) {
$submission = AcademySubmission::find($g['submission_id']);
Academy::assessments()->manualGrade($submission, $g['score'], $g['feedback']);
}Academy thrives by leveraging the Anchor ecosystem to handle complex financial logic.
You can define payment plans that allow learners to pay in instalments.
$plan = Academy::payments()->createPlan([
'name' => 'Extended BootCamp',
'price' => 120000, // $1,200.00
'instalment_count' => 3,
'instalment_interval' => 30, // days
]);
// Initialize instalments for a new enrolment
Academy::payments()->initializeInstalments($enrolment);Gate entire programs behind recurring subscription plans.
$program = Academy::program()
->titled('VIP Learning Pass')
->create();
// Gating via metadata (Wave looks for these Plan IDs during enrolment)
$program->update(['metadata' => ['required_plan_ids' => ['pro-monthly', 'enterprise']]]);Allow learners to deposit funds and pay for course milestones using their internal balance.
// Pay an instalment specifically via wallet
Academy::payments()->initializePayment($instalment, driver: 'wallet');
// Deposit funds to wallet for academy use
Academy::payments()->depositToWallet($user, 50000); // $500.00Keep learners engaged with rewards and professional credentials.
Automate badge awards based on learning triggers.
// Award a badge manually
Academy::badges()->award($user->id, $badge->id, $program->id);
// Or via triggers in App/Config/academy.php
'triggers' => [
'lesson_completed' => ['Fast Learner Badge'],
'program_completed' => ['Anchor Certified Pro'],
],Learners can rate programs and provide feedback to help others.
// Submit a rating
Academy::ratings()->submit($userId, $programId, rating: 5, review: 'Excellent content!');
// Get average rating
$average = Academy::ratings()->getAverageRating($programId);
// Get featured / top reviews
$reviews = Academy::ratings()->getFeaturedReviews($programId, limit: 10);Academy generates secure, verifiable credentials.
// Issue certificate upon completion
$certificate = Academy::certificates()->issue($enrolment);
// Get a secure, signed sharing URL (Linked to the Link package)
$shareUrl = Academy::certificates()->getSharingUrl($certificate);Academy automatically manages public-facing landing pages for your programs, optimized for conversion and search engines.
Landing pages are dynamically generated based on the program's slug and status.
// Get comprehensive data for a landing page
$pageData = Academy::landingPages()->getPageData('mastering-anchor');
// Returns:
// [
// 'program' => (AcademyProgram),
// 'instructors' => (Collection of staff),
// 'modules_count' => 12,
// 'lessons_count' => 45
// ]If the Rank package is installed and enabled in academy.php, SEO metadata (Title, Description, Graph Images) is automatically injected into the page state when getPageData() is called.
Beyond static content, Academy supports synchronized live sessions for interactive teaching.
Sessions can be linked to lessons of type live.
$session = Academy::liveSessions()->schedule([
'lesson_id' => $liveLesson->id,
'provider' => 'zoom',
'meeting_url' => 'https://zoom.us/j/123456789',
'starts_at' => '2026-03-01 10:00:00',
]);Academy provides a robust, polymorphic attendance system to track engagement across live sessions or any other attendable entity.
// 1. Using the dedicated Attendance Service
$attendance = Academy::attendance()->record($session, $enrolment->id);
// 2. Recording leave (auto-calculates duration)
Academy::attendance()->recordLeave($session, $enrolment->id);
// 3. Automated tracking via LiveSessionService
Academy::liveSessions()->recordAttendance($session->id, $enrolment->id);
Academy::liveSessions()->recordLeave($session->id, $enrolment->id);The AcademyAnalyticsService provides deep insights into learner attendance and engagement during sessions.
Retrieve a list of users currently participating in an ongoing session.
$active = Academy::analytics()->getActiveAttendees($session);// Returns an array of objects: // [ // (object) ['user_id' => 1, 'refid' => 'USR-01', 'name' => 'Alice', 'joined_at' => '2026-03-01 10:05:00'], // ... // ]
Determine if learners met a minimum "seat time" requirement (in minutes). Useful for issuing certificates.
$compliance = Academy::analytics()->getAttendanceCompliance($session, requiredMinutes: 45);// Sample Data Structure: // [ // 'compliant' => [ // ['user_id' => 1, 'refid' => 'USR-01', 'name' => 'Alice', 'duration_seconds' => 3000, 'duration_minutes' => 50], // ], // 'non_compliant' => [ // ['user_id' => 2, 'refid' => 'USR-02', 'name' => 'Bob', 'duration_seconds' => 1200, 'duration_minutes' => 20], // ], // 'summary' => [ // 'total_compliant' => 1, // 'total_non_compliant' => 1, // 'required_minutes' => 45, // ] // ]
Visualize when learners lose interest and leave the session, grouped into time intervals (buckets).
$trend = Academy::analytics()->getDropOffTrend($session, intervalMinutes: 5);// Sample Data Structure: // [ // 'labels' => ['0-5 min', '5-10 min', '10-15 min'], // 'values' => [2, 5, 1], // Number of drop-offs in each bucket // 'summary' => [ // 'total_dropoffs' => 8, // 'active_users' => 12 // Users who haven't dropped off yet // ] // ]
Engagement is boosted through integrated discussions, either standalone or linked to the Hub community package.
// Post a new topic
$topic = Academy::discussions()->post([
'program_id' => $program->id,
'user_id' => $user->id,
'content' => 'How do I handle dependency injection?',
]);
// Reply to a topic
Academy::discussions()->post([
'parent_id' => $topic->id,
'user_id' => $admin->id,
'content' => 'Use the resolve() helper!',
]);Pin important discussions or resolve support-style queries.
Academy::discussions()->pin($topic);
Academy::discussions()->resolve($topic);Academy provides powerful reporting tools for learners and educators to track performance and results.
Transcripts provide a consolidated record of all assessments and grades for an enrolment.
$transcript = Academy::reports()->getTranscript($enrolmentId);
// Sample Data Structure:
// [
// 'learner_id' => 12,
// 'learner_refid' => 'USR-ABCD-1234',
// 'learner_name' => 'John Doe',
// 'program_title' => 'Mastering Anchor',
// 'status' => 'active',
// 'progress' => 45,
// 'assessments' => [
// [
// 'title' => 'Core Quiz',
// 'score' => 95,
// 'is_passing' => true,
// 'submitted_at' => '2026-02-20',
// 'is_late' => false
// ]
// ]
// ]Visualize a learner's performance over time.
$performance = Academy::analytics()->getLearnerPerformance($enrolmentId);
// Sample Data Structure:
// [
// 'labels' => ['02-15', '02-18', '02-20'], // graded_at dates
// 'values' => [85, 90, 75] // percent scores
// ]Break down lesson completion with historical logs.
$report = Academy::reports()->getProgressReport($enrolmentId);
// Sample Data Structure:
// [
// 'total_lessons' => 20,
// 'completed_count' => 9,
// 'percentage' => 45,
// 'lessons' => [
// ['title' => 'Intro to PHP', 'completed_at' => '2026-02-10 14:00', 'time_spent' => 1200],
// ['title' => 'Data Structures', 'completed_at' => '2026-02-12 10:30', 'time_spent' => 3600]
// ]
// ]A unified log of all engagement events.
$history = Academy::reports()->getLifecycleHistory($enrolmentId);
// Sample Data Structure:
// [
// ['type' => 'lesson', 'event' => 'Completed lesson: Intro', 'date' => '2026-02-10'],
// ['type' => 'assessment', 'event' => 'Submitted assessment: Core Quiz', 'date' => '2026-02-12', 'score' => 95]
// ]Monitor program health and revenue via the analytics service.
$metrics = Academy::analytics()->getProgramMetrics($program->id);
// Sample Data Structure:
// [
// 'total_enrolments' => 1250,
// 'completion_rate' => 68,
// 'average_progress' => 74,
// 'active_students' => 450
// ]// Trends for 'enrolments', 'revenue', or 'submissions'
$trends = Academy::analytics()->getHistory('enrolments', '30d');
// Sample Data Structure:
// [
// 'labels' => ['Feb 01', 'Feb 02', ...],
// 'values' => [12, 15, 8, 20, ...]
// ]For enterprise-level tracking and reporting, you can retrieve heavily detailed and aggregated metrics for individual learners and entire cohorts.
Fetch a massive unified nested array detailing every facet of a learner's journey, including their attendance, transcripts, and overall performance.
$metrics = Academy::analytics()->getLearnerMetrics($enrolmentId);
// Sample Data Structure:
// [
// 'transcript' => [
// 'learner_name' => 'John Doe',
// 'program_title' => 'Mastering Anchor',
// 'assessments' => [...]
// ],
// 'progress' => [
// 'total_lessons' => 20,
// 'completed_count' => 9,
// 'percentage' => 45
// ],
// 'performance' => [
// 'labels' => ['02-15', '02-18'],
// 'values' => [85, 90]
// ],
// 'attendance' => [
// 'sessions' => [...],
// 'summary' => ['compliance_rate' => 70]
// ],
// 'engagement_score' => 82
// ]Identify whether a learner has met the required duration for live sessions to be considered "compliant", or retrieve a comprehensive history of attendance records filtered by any type (including the new AcademyOfflineClass).
use Academy\Models\AcademyLiveSession;
use Academy\Models\AcademyOfflineClass;
// 1. Live Session Compliance (Default)
$attendance = Academy::analytics()->getLearnerAttendanceReport($enrolmentId, AcademyLiveSession::class, requiredMinutes: 45);
// 2. Filter by specific type (e.g. Offline Classes)
$offline = Academy::analytics()->getLearnerAttendanceReport($enrolmentId, AcademyOfflineClass::class);
// 3. See ALL attendance records
$all = Academy::analytics()->getLearnerAttendanceReport($enrolmentId, attendableType: null); {
"sessions": [
{"session_id": 1, "duration_minutes": 50, "is_compliant": true}
],
"summary": {
"total_sessions": 10,
"attended_sessions": 8,
"compliant_sessions": 7,
"compliance_rate": 70
}
} {
"records": [
{
"id": 1,
"type": "Academy\\Models\\AcademyLiveSession",
"target_id": 101,
"joined_at": "2026-03-01 10:00:00",
"left_at": "2026-03-01 11:00:00",
"duration_minutes": 60
}
],
"summary": {
"total_count": 1,
"total_duration_minutes": 60,
"filtered_by": "all"
}
}Generate a robust grid of all learners within a specific program, ideal for an instructor's dashboard.
$grid = Academy::analytics()->getProgramLearnersReport($programId);
// Sample Data Structure:
// [
// [
// 'user_id' => 12,
// 'refid' => 'USR-ABCD-1234',
// 'name' => 'John Doe',
// 'status' => 'active',
// 'progress_percent' => 85,
// 'average_score' => 92,
// 'attendance_compliance_rate' => 100
// ],
// ...
// ]Instructors can also identify program bottlenecks (where users drop off), flag at-risk learners, calculate overall engagement scores, and see which assessment questions have high failure rates.
// Find which lessons users get stuck on the most
$bottlenecks = Academy::analytics()->getProgramBottlenecks($programId);
// Find active learners with < 20% progress who haven't logged activity in 14 days
$atRisk = Academy::analytics()->getAtRiskLearners($programId, maxProgress: 20, idleDays: 14);
// Get failure rates for specific quiz questions
$questionDifficulty = Academy::analytics()->getAssessmentQuestionMetrics($assessmentId);
// Calculate a 0-100 universal engagement score for a learner
$engagement = Academy::analytics()->getLearnerEngagementScore($enrolmentId);[
{
"lesson_id": 5,
"title": "Advanced SQL Joins",
"stalled_learners": 45,
"stall_rate_percent": 12.5
}
][
{
"user_id": 101,
"refid": "USR-XYZ-987",
"name": "John Doe",
"progress_percent": 15,
"last_activity": "2026-02-10",
"days_inactive": 18
}
][
{
"question_id": 12,
"content": "What is the primary key of...?",
"total_attempts": 150,
"failure_rate_percent": 75.5
}
]82 // Unified 0-100 scoreAcademy provides a powerful deep-search engine that matches queries against programs, contents, resources, and announcements.
// 1. Deep Search across all entities
$results = Academy::programs()->search('Database Optimization');
// Matches program titles, lesson content, resource titles, and announcements.
// 2. Filtered Search (e.g., only featured programs)
$featured = Academy::programs()->search('PHP', ['is_featured' => true]);
// 3. Dedicated Entity Search
$instructors = Academy::programs()->searchInstructors('John');
$learners = Academy::enrolments()->searchLearners('ADM-2026');Engage learners and showcase program quality with internal social metrics.
// Get top 10 learners by progress
$leaderboard = Academy::analytics()->getLeaderboard($programId, limit: 10);
// Get a summary of all ratings for a program
$ratings = Academy::analytics()->getRatingSummary($programId);
// Sample Ratings Summary:
// [
// 'average' => 4.5,
// 'total' => 150,
// 'breakdown' => [5 => 100, 4 => 30, 3 => 10, 2 => 5, 1 => 5]
// ]Learners can take personal notes on specific lessons or save programs for later.
// Add a note
Academy::progress()->saveNote($enrolmentId, $lessonId, 'This is an important concept!');
// Retrieve all notes for an enrolment
$allNotes = Academy::progress()->getNotes($enrolmentId);// Add to wishlist
Academy::enrolments()->addToWishlist($userId, $programId);
// Get user wishlist
$wishlist = Academy::enrolments()->getWishlist($userId);Academy includes built-in tools for keeping learners informed and engaged.
Send broadcast messages to all enrolled learners.
$announcement = AcademyAnnouncement::create([
'program_id' => $program->id,
'user_id' => $instructor->id,
'title' => 'New Module Released!',
'content' => 'Check out the new module on Advanced SQL.',
'send_email' => true, // Triggers background email delivery
]);Discounts are handled via the Wave package. You can create coupons and apply them during the checkout/enrolment process.
use Wave\Wave;
// Create a 20% discount coupon
$coupon = Wave::coupons()->create([
'code' => 'ACADEMY20',
'percent_off' => 20,
'duration' => 'once',
]);Images in questions and options are typically handled by:
- Embedding HTML: Standard
<img>tags in thetextfield. - Media Library: Using Anchor's internal media service to attach assets to
AcademyQuestionrecords.
Academy allows attaching external files, videos, and links to lessons as supplemental material.
Resources are linked to specific lessons and can be managed via the AcademyResource model.
use Academy\Models\AcademyResource;
use Academy\Enums\ResourceType;
use Academy\Enums\VideoProvider;
$resource = AcademyResource::create([
'lesson_id' => $lesson->id,
'title' => 'Advanced SQL Cheat Sheet',
'type' => ResourceType::FILE,
'path' => 'uploads/docs/sql-cheat-sheet.pdf',
]);
// For external videos
$video = AcademyResource::create([
'lesson_id' => $lesson->id,
'title' => 'Database Optimization',
'type' => ResourceType::VIDEO_EXTERNAL,
'provider' => VideoProvider::YOUTUBE,
'path' => 'https://youtube.com/watch?v=123',
]);file: Locally uploaded documents or media.video_external: Videos hosted on providers like YouTube, Vimeo, Wistia, or Bunny.link: Direct URLs to external websites or documents.embed: Raw HTML/Iframe embed codes.
// Get all resources for a specific program
$programResources = Academy::programs()->getResources($programId);
// Get resources specifically attached to a lesson
$lessonResources = Academy::programs()->getResources(lessonId: $lessonId);Manage deadlines and flexible learning paths.
Academy::enrolments()->extend($enrolmentId, 10); // Extend access by 10 daysFor cohort imports or enterprise onboarding, use the optimal bulkEnrol method.
$userIds = [101, 102, 103];
Academy::enrolments()->bulkEnrol($userIds, $programId);Academy::assessments()->grantExtension($submission, '2026-03-30 23:59:59');Academy includes robust maintenance tools to handle background tasks.
| Command | Description |
|---|---|
academy:verify-cert {num} |
Verify a learner's certificate manually. |
academy:credentials:issue |
Bulk issue credentials to learners who completed courses. |
academy:payments:sync |
Pull payment statuses from external gateways. |
academy:prune:expired |
Clean up expired enrolments and stale waitlists. |
Production Tip: Automate these tasks via Cron.
0 0 * * * php dock academy:prune:expired
0 * * * * php dock academy:payments:syncAcademy is designed primarily as a Self-Paced, Duration-Based system.
- Self-Paced: Learners can enrol at any time and progress at their own speed.
- Duration-Based: Access is controlled via the
expires_atproperty on the enrolment. If a program is part of a subscription, Wave manages the "heartbeat" of this access.
Tip: For Cohort/Batch-based learning, use the metadata column on a Program to store start_date and end_date, and use a custom middleware to gate access based on these dates.
| Method | Returns | Description |
|---|---|---|
program() |
ProgramBuilder |
Start a fluent program definition. |
enrol(...) |
AcademyEnrolment |
Quick enrolment shortcut. |
gradeQuiz(...) |
void |
Auto-grade an MCQ submission. |
programs() |
ProgramManagerService |
Access core program management. |
enrolments() |
EnrolmentManagerService |
Access learner lifecycle methods. |
assessments() |
AssessmentService |
Access quiz/exam engine. |
analytics() |
AcademyAnalyticsService |
Access metrics and trends. |
discussions() |
DiscussionService |
Access community threads. |
liveSessions() |
LiveSessionService |
Manage Zoom/Meet sessions. |
ratings() |
RatingService |
Access program reviews and ratings. |
badges() |
BadgeService |
Manage learner achievements. |
attendance() |
AttendanceService |
Access polymorphic attendance tracking. |
landingPages() |
LandingPageService |
Manage SEO-optimized landing pages. |
reports() |
ReportingService |
Access transcripts and detailed reports. |
getDefaultCurrency() |
string |
Get the configured currency (e.g. USD). |
getConfig(...) |
mixed |
Retrieve raw academy configuration. |
enrol(userId, programId, ?planId, ?refCode)bulkEnrol(userIds, programId, ?planId)activate(enrolment)isEnrolled(userId, programId)bulkIssueCredentials(?programId)pruneExpiredEnrolments()pruneExpiredWaitlists()extend(enrolment, days)getWishlist(userId)addToWishlist(userId, programId)searchLearners(query): Find students by Name, Email, or Admission ID.generateAdmissionNumber(enrolmentId): Generates a uniqueADM-2026-0001format ID and saves it to theadmission_idcolumn. (Note: This is called automatically duringenrol()).
initializeInstalments(enrolment)initializePayment(instalment, ?driver)processPayment(enrolment, reference, amount)syncExternalPayments()getDefaulters(?programId): Returns overdue pending instalments.getOutstandingBalance(enrolmentId): Total remaining debt.getBalance(enrolmentId): Alias forgetOutstandingBalance.getOverdue(enrolmentId): Returns specific overdue items.depositToWallet(user, amount): Fund learner wallet for academy use.
create(data)update(program, data)addMember(program, userId, role)publish(program)getMembers(programId, ?role)getResources(?programId, ?lessonId)getAnnouncements(programId)getDripContent(programId, enrolmentId)canAccess(userId, lessonId): Checks for Drip Content and Prerequisites.search(query, filters): Matches Programs, Modules, Lessons, Resources, and Announcements.searchInstructors(query): Find instructors by name or email.
completeLesson(enrolmentId, lessonId, ?time)saveNote(enrolmentId, lessonId, content)getNotes(enrolmentId, ?lessonId)updateOverallProgress(enrolmentId): Force a recalculation of progress.
-
startAttempt(assessmentId, enrolmentId) -
submit(submission, answers) -
autoGrade(submission) -
manualGrade(submission, score, feedback) -
grantExtension(submission, date): Grant more time for an assessment. -
canTake(userId, assessmentId): Check attempt limits and prerequisites. -
getAssessmentLeaderboard(assessmentId, ?limit) -
getRatingSummary(programId) -
getHistory(metric, range)
record(attendable, enrolmentId): Track entry/join event for any model.recordLeave(attendable, enrolmentId): Track exit/leave event and calculate duration.getAttendance(attendable): Fetch all records for an entity.
Academy provides granular authorization checks for various user roles.
| Method | Role | Description |
|---|---|---|
canView(userId, programId) |
Learner/Instructor | Check if user can see program contents. |
canAccess(userId, lessonId) |
Learner/Instructor | Check if user can take a specific lesson (Drip/Prereq). |
canManage(userId, programId) |
Instructor | Check if user can edit or moderate a program. |
canTake(userId, assessmentId) |
Learner/Instructor | Check if user can start an assessment. |
canGrade(userId, submissionId) |
Instructor | Check if user can grade a specific submission. |
// Gating a Management UI
if (Academy::programs()->canManage($user->id, $programId)) {
// Show Edit/Grade buttons
}
// Starting an Assessment
if (Academy::assessments()->canTake($user->id, $assessmentId)) {
return Academy::assessments()->startAttempt($assessmentId, $enrolmentId);
}