Skip to content

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.

Custom extension points live under app/Contracts/Enrollment/:

InterfacePurposeMethod to Implement
EnrollmentRuleHandlerEvaluates student eligibility or conditionevaluate(EnrollmentContext $context, array $config): RuleResult
EnrollmentActionHandlerExecutes an idempotent side-effectexecute(EnrollmentContext $context, array $config, string $idempotencyKey): ActionResult
EnrollmentAssignmentStrategyDetermines subject or class section assignmentrecommend(EnrollmentContext $context, array $config): array
EnrollmentBillingStrategyCalculates custom fees or tuition structurescalculate(EnrollmentContext $context, array $config): array
EnrollmentOperatorSchemaProvider(Optional) Exposes configuration fields to the visual no-code blueprint editoroperatorSchema(): array

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]);
}
}

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());
},
);
}
}
  • 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.