functions.php

PHP

Path: src/Validator/functions.php

<?php

namespace mini;

use mini\Validator\Validator;
use mini\Validator\ValidatorStore;
use mini\Validator\AttributeValidatorFactory;
use mini\Validator\Purpose;

// Register Validator services
Mini::$mini->addService(Validator::class, Lifetime::Transient, fn() => new Validator());
Mini::$mini->addService(ValidatorStore::class, Lifetime::Singleton, fn() => new ValidatorStore());
Mini::$mini->addService(AttributeValidatorFactory::class, Lifetime::Singleton, fn() => new AttributeValidatorFactory());

/**
 * Get or create a Validator instance
 *
 * With no arguments: Returns a new Validator for building validation rules.
 * With class name: Returns the core validator (auto-built from class attributes).
 * With class name + purpose: Returns the purpose-specific validator.
 *
 * ## Examples
 *
 * ```php
 * // New validator for building rules
 * $v = validator()->type('string')->minLength(5);
 *
 * // Core validator (auto-built from class attributes)
 * $v = validator(User::class);
 *
 * // Purpose-specific validators
 * $v = validator(User::class, Purpose::Create);
 * $v = validator(User::class, Purpose::Update);
 *
 * // Custom purpose (string)
 * $v = validator(User::class, 'password-reset');
 * ```
 *
 * ## Validation Flow
 *
 * Purpose validation is done in the application layer:
 * ```php
 * // In controller/service
 * if ($error = validator(User::class, Purpose::Create)->isInvalid($user)) {
 *     throw new ValidationException($error);
 * }
 * ```
 *
 * Core validation is done in the repository layer:
 * ```php
 * // In repository
 * if ($error = validator(User::class)->isInvalid($user)) {
 *     throw new ValidationException($error);
 * }
 * ```
 *
 * @param class-string|string|null $classOrName Class name or custom identifier
 * @param Purpose|string|null $purpose Optional purpose scope (Create, Update, or custom string)
 * @return Validator Validator instance
 * @throws \InvalidArgumentException If identifier not found and not a valid class
 */
function validator(?string $classOrName = null, Purpose|string|null $purpose = null): Validator {
    // No argument: return new validator
    if ($classOrName === null) {
        return Mini::$mini->get(Validator::class);
    }

    $store = Mini::$mini->get(ValidatorStore::class);

    // Get from store (auto-builds from attributes if class/interface and no purpose)
    $validator = $store->get($classOrName, $purpose);

    if ($validator === null) {
        // Standard purposes (Purpose enum) are opt-in: return empty validator
        if ($purpose instanceof Purpose) {
            return Mini::$mini->get(Validator::class);
        }

        // Custom string purposes and core validators: throw if not found
        $msg = $purpose !== null
            ? "Validator '$classOrName' with purpose '$purpose' not found. Register it in ValidatorStore."
            : "Validator '$classOrName' not found. Register it in ValidatorStore or ensure the class exists.";
        throw new \InvalidArgumentException($msg);
    }

    // Return clone to allow modifications without affecting cached version
    return clone $validator;
}