PHP Code
<?php
/**
* Deep inspection + demo data generator: AF Courses, Schedules, Units.
* Creates 50 realistic courses with future schedules (Term 1-4 2027),
* products, schedules and per-week units across levels/age groups.
*/
$iCoursesTarget = 50;
// ---------- 1. Inspect existing data ----------
echo "=== INSPECTION ===\n";
echo "Courses total: " . \Components\AF\Courses::getCount() . "\n";
echo "Schedules total: " . \Components\AF\Courses\Schedules::getCount() . "\n";
echo "Units total: " . \Components\AF\Courses\Units::getCount() . "\n";
echo "Sessions: " . \Components\AF\Courses\Configuration\Sessions::getCount() . "\n";
echo "Levels: " . \Components\AF\Courses\Configuration\Levels::getCount() . "\n";
echo "Products: " . \Components\Commerce\Products::getCount() . "\n\n";
// ---------- 2. Ensure future sessions exist (Term 2-4 2027) ----------
$arrNewSessions = [
['2027', 'Term 2 2027', '2027-04-19', '2027-06-19'],
['2027', 'Term 3 2027', '2027-07-12', '2027-09-11'],
['2027', 'Term 4 2027', '2027-10-04', '2027-12-04'],
];
$arrSessionIds = [51 => ['2027-02-01', '2027-03-28']]; // existing Term 1 2027
foreach ($arrNewSessions as $arrS)
{
$arrExisting = \Components\AF\Courses\Configuration\Sessions::getAll([
'filter' => [['session_name', '=', $arrS[1]], 'AND', ['session_year', '=', $arrS[0]]],
'limit' => 1,
]);
if (count($arrExisting))
{
$iSid = (int)$arrExisting[0]['session_id'];
}
else
{
$iSid = (int)\Components\AF\Courses\Configuration\Sessions::save([
'session_year' => $arrS[0],
'session_name' => $arrS[1],
'session_start_date' => $arrS[2],
'session_end_date' => $arrS[3],
'session_visible_public' => 1,
]);
}
$arrSessionIds[$iSid] = [$arrS[2], $arrS[3]];
}
echo "Sessions available: " . implode(', ', array_keys($arrSessionIds)) . "\n";
// ---------- 3. Course blueprint builder ----------
$arrTeachers = [1351, 1181, 1052, 1180, 1182];
// Rooms: CBD Campus(2): 7,8,9,23,24 | St Kilda(1): 3,4,5,6 | Online Zooms(17): 19,20,21
$arrRooms = [
'cbd' => [7, 8, 9, 23, 24],
'stkilda' => [3, 4, 5, 6],
'online' => [19, 20, 21],
];
$arrDayNames = [1 => 'Mon', 2 => 'Tue', 3 => 'Wed', 4 => 'Thu', 5 => 'Fri', 6 => 'Sat', 7 => 'Sun'];
$arrFullDayNames = [1 => 'monday', 2 => 'tuesday', 3 => 'wednesday', 4 => 'thursday', 5 => 'friday', 6 => 'saturday', 7 => 'sunday'];
$fnCourse = function($arrDef) {
return $arrDef;
};
// Build 50 course definitions: [group, level, levelName, ageGroup, type, pace, hoursPerClass, timeStart, timeEnd, days, campusKey]
$arrCourses = [];
// Adults - Standard progression (16)
foreach ([
['A1.1', 2], ['A1.2', 7], ['A1.3', 8], ['A1.4', 9],
['A2.1', 10], ['A2.2', 11], ['A2.3', 12], ['A2.4', 131],
['B1.1', 14], ['B1.2', 15], ['B1.3', 16],
['B2.1', 17], ['B2.2', 18], ['C1', 6], ['C2', 132], ['A2.5', 27],
] as $i => $arrLvl)
{
$iDay = ($i % 5) + 1;
$bEvening = ($i % 2 === 0);
$arrCourses[] = [
'level' => $arrLvl[0], 'level_id' => $arrLvl[1], 'age' => 1, 'type' => 2,
'pace' => 38, 'hpc' => 2.0, 'start' => $bEvening ? '18:00:00' : '09:30:00', 'end' => $bEvening ? '20:00:00' : '11:30:00',
'days' => [$iDay], 'campus' => ($i % 4 === 3) ? 'online' : 'cbd', 'teacher' => $arrTeachers[$i % 5],
];
}
// Adults - speciality (8)
$arrSpecial = [
['Conversation B1', 14, 29, 39, 1.5, '18:00:00', '19:30:00', 2],
['Conversation B2', 17, 29, 39, 1.5, '12:00:00', '13:30:00', 4],
['French for Travel A1', 2, 27, 39, 1.5, '17:30:00', '19:00:00', 5],
['Easy Going A2', 10, 30, 39, 1.5, '10:00:00', '11:30:00', 3],
['DELF B2 Prep', 17, 24, 38, 2.0, '17:00:00', '19:00:00', 6],
['Grammar Workshop B1', 14, 16, 44, 6.0, '09:30:00', '15:30:00', 6],
['Pronunciation Workshop A2', 10, 16, 44, 6.0, '09:30:00', '15:30:00', 6],
['TCF Exam Prep', 130, 24, 38, 2.0, '18:00:00', '20:00:00', 1],
];
foreach ($arrSpecial as $i => $a)
{
$arrCourses[] = [
'level' => $a[0], 'level_id' => $a[1], 'age' => 1, 'type' => $a[2],
'pace' => $a[3], 'hpc' => $a[4], 'start' => $a[5], 'end' => $a[6],
'days' => [$a[7]], 'campus' => ($i === 7) ? 'online' : 'stkilda', 'teacher' => $arrTeachers[($i + 2) % 5],
];
}
// Teens (10)
foreach ([['Year 7', 22], ['Year 8', 23], ['Year 9', 24], ['Year 10', 133], ['Year 11', 134], ['Year 12', 135]] as $i => $arrLvl)
{
$arrCourses[] = [
'level' => $arrLvl[0], 'level_id' => $arrLvl[1], 'age' => 2, 'type' => ($i % 3 === 0 ? 23 : 2),
'pace' => 38, 'hpc' => 2.0, 'start' => '16:30:00', 'end' => '18:30:00',
'days' => [($i % 4) + 1], 'campus' => ($i % 3 === 2) ? 'online' : 'cbd', 'teacher' => $arrTeachers[($i + 1) % 5],
];
}
foreach ([['Teens Conversation A2', 10, 29, 5], ['DELF Junior A2 Prep', 10, 24, 6], ['Taste of French Y9', 24, 23, 3], ['Taste of French Y11', 134, 23, 4]] as $i => $a)
{
$arrCourses[] = [
'level' => $a[0], 'level_id' => $a[1], 'age' => 2, 'type' => $a[2],
'pace' => 39, 'hpc' => 1.5, 'start' => '16:45:00', 'end' => '18:15:00',
'days' => [$a[3]], 'campus' => 'stkilda', 'teacher' => $arrTeachers[($i + 3) % 5],
];
}
// Children (10)
foreach ([['Prep', 136], ['Year 1', 137], ['Year 2', 138], ['Year 3', 139]] as $i => $arrLvl)
{
$arrCourses[] = [
'level' => $arrLvl[0], 'level_id' => $arrLvl[1], 'age' => 3, 'type' => 2,
'pace' => 39, 'hpc' => 1.5, 'start' => '10:00:00', 'end' => '11:30:00',
'days' => [6], 'campus' => 'stkilda', 'teacher' => $arrTeachers[$i % 5],
];
}
foreach ([['Taste of French Kids 1', 136, 22, 6], ['Taste of French Kids 2', 137, 22, 6], ['Kids Holiday Immersion A1', 2, 25, 2], ['Kids Holiday Immersion A2', 130, 25, 2], ['Kids Conversation B1', 14, 29, 3]] as $i => $a)
{
$arrCourses[] = [
'level' => $a[0], 'level_id' => $a[1], 'age' => 3, 'type' => $a[2],
'pace' => 39, 'hpc' => 1.5, 'start' => '13:00:00', 'end' => '14:30:00',
'days' => [$a[3]], 'campus' => ($i > 1 ? 'cbd' : 'stkilda'), 'teacher' => $arrTeachers[($i + 4) % 5],
];
}
// Toddlers (4)
foreach ([['French Play Group', 21, '09:30:00', '10:15:00'], ['Mum & Me', 19, '10:30:00', '11:15:00'], ['Story Time', 20, '11:30:00', '12:15:00'], ['French Play Group 2', 21, '09:30:00', '10:15:00']] as $i => $a)
{
$arrCourses[] = [
'level' => 'Pre-School', 'level_id' => 1, 'age' => 12, 'type' => $a[1],
'pace' => 37, 'hpc' => 0.75, 'start' => $a[2], 'end' => $a[3],
'days' => [($i % 2) + 5], 'campus' => 'stkilda', 'teacher' => $arrTeachers[$i % 5],
];
}
// Corporate + Intensive (2)
$arrCourses[] = [
'level' => 'A1.1 Corporate', 'level_id' => 2, 'age' => 4, 'type' => 28,
'pace' => 38, 'hpc' => 2.0, 'start' => '08:00:00', 'end' => '10:00:00',
'days' => [2, 4], 'campus' => 'cbd', 'teacher' => $arrTeachers[0],
];
$arrCourses[] = [
'level' => 'A1 Intensive', 'level_id' => 1, 'age' => 1, 'type' => 3,
'pace' => 45, 'hpc' => 4.0, 'start' => '09:00:00', 'end' => '13:00:00',
'days' => [1, 2, 3, 4, 5], 'campus' => 'online', 'teacher' => $arrTeachers[1],
];
$arrCourses[] = [
'level' => 'Conversation A2', 'level_id' => 10, 'age' => 1, 'type' => 29,
'pace' => 39, 'hpc' => 1.5, 'start' => '12:00:00', 'end' => '13:30:00',
'days' => [3], 'campus' => 'stkilda', 'teacher' => $arrTeachers[2],
];
$arrCourses = array_slice($arrCourses, 0, $iCoursesTarget);
echo "Course definitions: " . count($arrCourses) . "\n\n";
// ---------- 4. Create courses ----------
$arrSessionKeys = array_keys($arrSessionIds);
$iCreated = 0;
$arrErrors = [];
foreach ($arrCourses as $i => $a)
{
// Assign sessions round-robin, then a start date inside the session window
$iSessionId = $arrSessionKeys[$i % count($arrSessionKeys)];
list($sSessStart, $sSessEnd) = $arrSessionIds[$iSessionId];
$iDayNum = $a['days'][0];
$iOffset = ($i * 7) % 28; // vary start week
$oStart = new DateTime($sSessStart);
$oStart->modify('+' . $iOffset . ' days');
while ((int)$oStart->format('N') !== $iDayNum)
{
$oStart->modify('+1 day');
}
// 8 weekly classes per schedule
$oEnd = clone $oStart;
$oEnd->modify('+' . (count($a['days']) * 7) . ' days');
$sCourseCode = $a['level'] . '-' . $arrDayNames[$iDayNum] . '-' . substr(str_replace('-', '', $sSessStart), 0, 8) . '-' . ($i + 1);
// Product
if (!isset($iBrandId))
{
$arrBrand = \Components\Commerce\Products\Brands::getAll(['limit' => 1]);
if (count($arrBrand))
{
$iBrandId = (int)$arrBrand[0]['product_brand_id'];
}
else
{
$iBrandId = (int)\Components\Commerce\Products\Brands::save(['product_brand_title' => 'Alliance Française']);
}
echo "Using brand: {$iBrandId}\n";
}
$fHoursTotal = $a['hpc'] * 8;
$fPrice = round(($fHoursTotal * 55) / 10) * 10;
$iProductId = 0;
$arrProd = \Components\Commerce\Products::getAll(['filter' => [['product_code', '=', $sCourseCode]], 'limit' => 1]);
if (count($arrProd))
{
$iProductId = (int)$arrProd[0]['product_id'];
}
else
{
$iProductId = (int)\Components\Commerce\Products::save([
'product_title' => $a['level'] . ' ' . $arrDayNames[$iDayNum] . ' ' . ($iSessionId === 51 ? 'Term 1 2027' : '2027'),
'product_price_base' => $fPrice,
'product_code' => $sCourseCode,
'product_brand_id' => $iBrandId ?: 1,
'product_is_offline' => 1,
]);
}
$arrLocationIds = $arrRooms[$a['campus']];
$iLocationId = $arrLocationIds[$i % count($arrLocationIds)];
$sLocationType = ($a['campus'] === 'online') ? 'remote' : 'internal';
// Skip if the course already exists (idempotent re-run)
$arrExistingCourse = \Components\AF\Courses::getAll(['filter' => [['course_code', '=', $sCourseCode]], 'limit' => 1]);
if (count($arrExistingCourse))
{
echo "Skip (exists): {$sCourseCode}\n";
continue;
}
// Course
$arrCourse = [
'course_code' => $sCourseCode,
'course_type' => 'collective',
'course_start_date' => $oStart->format('Y-m-d'),
'course_start_time' => $a['start'],
'course_end_date' => $oEnd->format('Y-m-d'),
'course_end_time' => $a['end'],
'course_total_hours' => number_format($fHoursTotal, 2, '.', ''),
'course_objectives' => $a['level'] . ' objectives: develop listening and speaking skills, build core vocabulary and consolidate grammar through interactive activities.',
'session_id' => $iSessionId,
'age_group_id' => $a['age'],
'type_id' => $a['type'],
'pace_id' => $a['pace'],
'product_id' => $iProductId,
'location_ids_cache' => (string)$iLocationId,
'level_ids_cache' => (string)$a['level_id'],
'course_is_day_' . $arrFullDayNames[$iDayNum] => 1,
'course_hidden_in_selector' => 0,
'course_membership_option' => 'hidden',
];
$iCourseId = (int)\Components\AF\Courses::save($arrCourse);
if (!$iCourseId)
{
$arrErrors[] = "Course save failed: {$sCourseCode}";
continue;
}
// Level link
\Components\AF\Courses\Levels::save(['course_id' => $iCourseId, 'level_id' => $a['level_id']]);
// Schedule
$arrSchedule = [
'schedule_start_date' => $oStart->format('Y-m-d'),
'schedule_end_date' => $oEnd->format('Y-m-d'),
'schedule_start_time' => $a['start'],
'schedule_end_time' => $a['end'],
'schedule_location_type' => $sLocationType,
'location_id' => $iLocationId,
'teacher_id' => $a['teacher'],
'course_id' => $iCourseId,
'schedule_day' => $a['days'],
];
$iScheduleId = (int)\Components\AF\Courses\Schedules::save($arrSchedule);
// Units - one per class occurrence
$iUnits = 0;
$oUnitDate = clone $oStart;
for ($u = 0; $u < 8; $u++)
{
// step to the next configured weekday
while (!in_array((int)$oUnitDate->format('N'), $a['days']))
{
$oUnitDate->modify('+1 day');
}
\Components\AF\Courses\Units::save([
'unit_date' => $oUnitDate->format('Y-m-d'),
'unit_start_time' => $a['start'],
'unit_end_time' => $a['end'],
'unit_comment' => ($u === 0 ? 'Introduction and placement' : null),
'unit_billing_status' => 'unpaid',
'unit_location_type' => $sLocationType,
'location_id' => $iLocationId,
'teacher_id' => $a['teacher'],
'schedule_id' => $iScheduleId,
'course_id' => $iCourseId,
]);
$iUnits++;
$oUnitDate->modify('+1 day');
}
$iCreated++;
echo sprintf("#%02d course_id=%d sched=%d units=%d %s | %s %s-%s | session %d | lvl %s | product %d \$%.2f\n",
$iCreated, $iCourseId, $iScheduleId, $iUnits, $sCourseCode,
$oStart->format('d M Y'), $a['start'], substr($a['end'], 0, 5), $iSessionId, $a['level'], $iProductId, $fPrice);
\Components\Settings\Developer\Threads::setCurrentProgressStatus("Created {$iCreated} / " . count($arrCourses) . " courses…");
}
echo "\n=== SUMMARY ===\n";
echo "Created courses: {$iCreated}\n";
echo "Courses total now: " . \Components\AF\Courses::getCount() . "\n";
echo "Schedules total now: " . \Components\AF\Courses\Schedules::getCount() . "\n";
echo "Units total now: " . \Components\AF\Courses\Units::getCount() . "\n";
if (count($arrErrors))
{
echo "Errors:\n" . implode("\n", $arrErrors) . "\n";
}
else
{
echo "No errors.\n";
}