Enrollment Policy Extensions
Enrollment Policy Extensions
Section titled “Enrollment Policy Extensions”KoAkademy’s enrollment engine executes declarative, version-controlled blueprints. Policies store stable handler keys and JSON configuration data—they never store executable PHP class names, closures, formulas, or raw scripts.
This architecture allows institutional rules to be simulated, versioned, exported, and rolled back safely.
Extension Contracts
Section titled “Extension Contracts”Custom extension points live under app/Contracts/Enrollment/:
| Interface | Purpose | Method to Implement |
|---|---|---|
EnrollmentRuleHandler | Evaluates student eligibility or condition | evaluate(EnrollmentContext $context, array $config): RuleResult |
EnrollmentActionHandler | Executes an idempotent side-effect | execute(EnrollmentContext $context, array $config, string $idempotencyKey): ActionResult |
EnrollmentAssignmentStrategy | Determines subject or class section assignment | recommend(EnrollmentContext $context, array $config): array |
EnrollmentBillingStrategy | Calculates custom fees or tuition structures | calculate(EnrollmentContext $context, array $config): array |
EnrollmentOperatorSchemaProvider | (Optional) Exposes configuration fields to the visual no-code blueprint editor | operatorSchema(): array |
Writing an Enrollment Rule Extension
Section titled “Writing an Enrollment Rule Extension”Here is a complete, production-grade example of a custom rule that checks whether a student has an approved scholarship:
<?php
declare(strict_types=1);
namespace App\Enrollment\Rules;
use App\Contracts\Enrollment\EnrollmentOperatorSchemaProvider;use App\Contracts\Enrollment\EnrollmentRuleHandler;use App\Data\Enrollment\EnrollmentContext;use App\Data\Enrollment\RuleResult;
final readonly class ScholarshipEligibilityRule implements EnrollmentRuleHandler, EnrollmentOperatorSchemaProvider{ public function key(): string { return 'eligibility.scholarship'; }
public function metadata(): array { return [ 'key' => $this->key(), 'label' => 'Scholarship Eligibility', 'category' => 'eligibility', ]; }
public function configurationSchema(): array { return $this->operatorSchema(); }
public function operatorSchema(): array { return [ 'description' => 'Verify minimum scholarship percentage before enrollment.', 'what_it_does' => 'Checks the approved scholarship grant on the student record.', 'impact' => 'Students without the minimum grant cannot proceed through this blueprint.', 'example' => 'Enter 50 to require at least a 50% tuition scholarship.', 'docs_anchor' => 'enrollment-policies/availability-eligibility-documents', 'fields' => [ [ 'key' => 'minimum_percentage', 'label' => 'Minimum Scholarship %', 'control' => 'number', 'required' => true, 'min' => 1, 'max' => 100, 'recommended' => 50, ], ], ]; }
public function evaluate(EnrollmentContext $context, array $configuration): RuleResult { $minPercentage = (int) ($configuration['minimum_percentage'] ?? 0);
// Access student facts from the immutable context $scholarshipPercentage = (int) ($context->facts['scholarship_percentage'] ?? 0);
if ($scholarshipPercentage < $minPercentage) { return RuleResult::fail( "Student scholarship ({$scholarshipPercentage}%) is below the required {$minPercentage}%.", ['actual' => $scholarshipPercentage, 'required' => $minPercentage], ); }
return RuleResult::pass(['scholarship_percentage' => $scholarshipPercentage]); }}Registering Extensions
Section titled “Registering Extensions”Register custom rules, actions, and strategies with EnrollmentPolicyRegistry inside a service provider:
<?php
declare(strict_types=1);
namespace App\Providers;
use App\Enrollment\EnrollmentPolicyRegistry;use App\Enrollment\Rules\ScholarshipEligibilityRule;use Illuminate\Support\ServiceProvider;
final class CustomEnrollmentServiceProvider extends ServiceProvider{ public function boot(): void { $this->app->afterResolving( EnrollmentPolicyRegistry::class, function (EnrollmentPolicyRegistry $registry): void { $registry->registerRule(new ScholarshipEligibilityRule()); }, ); }}Extension Safety Guidelines
Section titled “Extension Safety Guidelines”- Idempotency: Actions must execute safely when re-run with the same
$idempotencyKey. - Stateless Registry: Do not store request instances, users, or active models inside the handler object; the registry is shared by long-running workers (Horizon / Octane).
- JSON Compatibility: Schema definitions must use serializable JSON structures.
- Testing: Include Pest feature tests covering evaluation success, evaluation failure, missing facts, and edge-case values.