feat: add validation and use opencart logger

This commit is contained in:
2025-08-16 16:59:21 +03:00
parent 9bcf32841e
commit 9f35acf399
28 changed files with 2416 additions and 143 deletions

View File

@@ -8,6 +8,7 @@ use Cart\Currency;
use Cart\Tax;
use Openguru\OpenCartFramework\ImageTool\ImageTool;
use Openguru\OpenCartFramework\ImageTool\ImageToolInterface;
use Openguru\OpenCartFramework\Logger\OpenCartLogAdapter;
$sysLibPath = rtrim(DIR_SYSTEM, '/') . '/library/oc_telegram_shop';
$basePath = rtrim(DIR_APPLICATION, '/') . '/..';
@@ -26,6 +27,9 @@ class Controllerextensiontgshophandle extends Controller
{
public function index(): void
{
$this->load->model('checkout/order');
$this->session->data['language'] = $this->config->get('config_language');
$app = ApplicationFactory::create([
'app_enabled' => filter_var($this->config->get('module_tgshop_status'), FILTER_VALIDATE_BOOLEAN),
'oc_config_tax' => $this->config->get('config_tax'),
@@ -69,40 +73,22 @@ class Controllerextensiontgshophandle extends Controller
'cache_products_main' => 60 * 10,
]);
$app->bind(Url::class, function () {
return $this->url;
});
$app->bind(Currency::class, function () {
return $this->currency;
});
$app->bind(Tax::class, function () {
return $this->tax;
});
$app->bind(OcModelCatalogProductAdapter::class, function () {
$this->load->model('catalog/product');
return new OcModelCatalogProductAdapter($this->model_catalog_product);
});
$app->bind(ImageToolInterface::class, function () {
return new ImageTool(DIR_IMAGE, HTTPS_SERVER);
});
$app->bind(Url::class, fn () => $this->url);
$app->bind(Currency::class, fn () => $this->currency);
$app->bind(Tax::class, fn () => $this->tax);
$app->bind(ImageToolInterface::class, fn () => new ImageTool(DIR_IMAGE, HTTPS_SERVER));
$app->bind(Cart::class, fn () => $this->cart);
$app->bind(OcRegistryDecorator::class, fn () => new OcRegistryDecorator($this->registry));
$app->singleton(Log::class, fn () => $this->log);
$app->bind(Cart::class, function () {
return $this->cart;
});
$app->bind(OcRegistryDecorator::class, function () {
return new OcRegistryDecorator($this->registry);
});
$this->load->model('checkout/order');
$this->session->data['language'] = $this->config->get('config_language');
$app->bootAndHandleRequest();
$app
->withLogger(fn () => new OpenCartLogAdapter($this->log, 'TeleCart'))
->bootAndHandleRequest();
}
function extractPureJs($input)

View File

@@ -3,7 +3,8 @@
"autoload": {
"psr-4": {
"Openguru\\OpenCartFramework\\": "framework/",
"App\\": "src/"
"App\\": "src/",
"Tests\\": "tests/"
},
"files": [
"framework/Support/helpers.php"
@@ -21,13 +22,13 @@
"psr/container": "^2.0",
"ext-json": "*",
"intervention/image": "^2.7",
"rakit/validation": "^1.4",
"vlucas/phpdotenv": "^5.6",
"guzzlehttp/guzzle": "^7.9",
"symfony/cache": "^5.4"
},
"require-dev": {
"roave/security-advisories": "dev-latest",
"phpstan/phpstan": "^2.1"
"phpstan/phpstan": "^2.1",
"phpunit/phpunit": "^9.6"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -2,6 +2,7 @@
namespace Openguru\OpenCartFramework;
use Closure;
use Dotenv\Dotenv;
use InvalidArgumentException;
use Openguru\OpenCartFramework\Config\Settings;
@@ -9,6 +10,7 @@ use Openguru\OpenCartFramework\Container\Container;
use Openguru\OpenCartFramework\Http\JsonResponse;
use Openguru\OpenCartFramework\Http\Request;
use Openguru\OpenCartFramework\Logger\Logger;
use Openguru\OpenCartFramework\Logger\LoggerInterface;
use Openguru\OpenCartFramework\Router\Router;
use Openguru\OpenCartFramework\Support\ExecutionTimeProfiler;
@@ -18,6 +20,16 @@ class Application extends Container
private static array $events = [];
private array $serviceProviders = [];
private array $middlewareStack = [];
private LoggerInterface $logger;
public function __construct(array $config)
{
parent::__construct($config);
// Fallback logger
$path = rtrim($this->getConfigValue('logs.path'), '/') . '/oc_telegram_shop.log';
$this->logger = new Logger($path);
}
/**
* @var ExecutionTimeProfiler
@@ -42,10 +54,7 @@ class Application extends Container
return $container;
});
$this->singleton(Logger::class, function (Container $container) {
$path = $container->getConfigValue('logs.path') . '/oc_telegram_shop.log';
return new Logger($path, Logger::LEVEL_INFO);
});
$this->singleton(LoggerInterface::class, fn () => $this->logger);
$this->singleton(Settings::class, function (Container $container) {
return new Settings($container->getConfigValue());
@@ -55,7 +64,7 @@ class Application extends Container
$dotenv->load();
$errorHandler = new ErrorHandler(
$this->get(Logger::class),
$this->get(LoggerInterface::class),
$this,
);
@@ -89,17 +98,17 @@ class Application extends Container
[$controller, $method] = $action;
if (!class_exists($controller) || !method_exists($controller, $method)) {
if (! class_exists($controller) || ! method_exists($controller, $method)) {
throw new InvalidArgumentException('Invalid action: ' . $controller . '->' . $method);
}
$this->profiler->addCheckpoint('Handle Middlewares.');
$next = fn($req) => $this->call($controller, $method);
$next = fn ($req) => $this->call($controller, $method);
foreach (array_reverse($this->middlewareStack) as $class) {
$instance = $this->get($class);
$next = static fn($req) => $instance->handle($req, $next);
$next = static fn ($req) => $instance->handle($req, $next);
}
$response = $next($request);
@@ -130,4 +139,11 @@ class Application extends Container
return $this;
}
public function withLogger(Closure $closure): Application
{
$this->logger = $closure();
return $this;
}
}

View File

@@ -7,7 +7,7 @@ use Openguru\OpenCartFramework\Contracts\ExceptionHandlerInterface;
use Openguru\OpenCartFramework\Exceptions\NonLoggableExceptionInterface;
use Openguru\OpenCartFramework\Http\JsonResponse;
use Openguru\OpenCartFramework\Http\Response;
use Openguru\OpenCartFramework\Logger\Logger;
use Openguru\OpenCartFramework\Logger\LoggerInterface;
use Throwable;
/**
@@ -15,10 +15,10 @@ use Throwable;
*/
class ErrorHandler
{
private $logger;
private LoggerInterface $logger;
private Application $app;
public function __construct(Logger $logger, Application $application)
public function __construct(LoggerInterface $logger, Application $application)
{
$this->logger = $logger;
$this->app = $application;

View File

@@ -4,24 +4,20 @@ namespace Openguru\OpenCartFramework\Logger;
use Throwable;
class Logger
class Logger implements LoggerInterface
{
private $logFile;
private $logLevel;
private $maxFileSize; // Максимальный размер файла в байтах
public const LEVEL_INFO = 1;
public const LEVEL_WARNING = 2;
public const LEVEL_ERROR = 3;
public function __construct($logFile, $logLevel = self::LEVEL_INFO, $maxFileSize = 1048576)
public function __construct($logFile, $logLevel = LoggerInterface::LEVEL_INFO, $maxFileSize = 1048576)
{
$this->logFile = $logFile;
$this->logLevel = $logLevel;
$this->maxFileSize = $maxFileSize;
}
public function log($message, $level = self::LEVEL_INFO)
public function log($message, $level = LoggerInterface::LEVEL_INFO)
{
if ($level < $this->logLevel) {
return; // Не логируем, если уровень ниже установленного
@@ -39,11 +35,11 @@ class Logger
private function getLevelString($level): string
{
switch ($level) {
case self::LEVEL_INFO:
case LoggerInterface::LEVEL_INFO:
return 'INFO';
case self::LEVEL_WARNING:
case LoggerInterface::LEVEL_WARNING:
return 'WARNING';
case self::LEVEL_ERROR:
case LoggerInterface::LEVEL_ERROR:
return 'ERROR';
default:
return 'UNKNOWN';
@@ -52,17 +48,17 @@ class Logger
public function info(string $message): void
{
$this->log($message, self::LEVEL_INFO);
$this->log($message, LoggerInterface::LEVEL_INFO);
}
public function warning(string $message): void
{
$this->log($message, self::LEVEL_WARNING);
$this->log($message, LoggerInterface::LEVEL_WARNING);
}
public function error(string $message): void
{
$this->log($message, self::LEVEL_ERROR);
$this->log($message, LoggerInterface::LEVEL_ERROR);
}
private function rotateLogs(): void

View File

@@ -0,0 +1,22 @@
<?php
namespace Openguru\OpenCartFramework\Logger;
use Throwable;
interface LoggerInterface
{
public const LEVEL_INFO = 1;
public const LEVEL_WARNING = 2;
public const LEVEL_ERROR = 3;
public function log($message, $level = self::LEVEL_INFO);
public function info(string $message): void;
public function warning(string $message): void;
public function error(string $message): void;
public function logException(Throwable $exception): void;
}

View File

@@ -0,0 +1,72 @@
<?php
namespace Openguru\OpenCartFramework\Logger;
use Log;
use Throwable;
class OpenCartLogAdapter implements LoggerInterface
{
private Log $ocLogger;
private string $namespace;
public function __construct(Log $ocLogger, string $namespace)
{
$this->ocLogger = $ocLogger;
$this->namespace = $namespace;
}
public function log($message, $level = self::LEVEL_INFO): void
{
$this->ocLogger->write(
sprintf(
"[%s] [%s] %s\n",
$this->namespace,
$this->getLevelString($level),
$message
)
);
}
public function info(string $message): void
{
$this->log($message);
}
public function warning(string $message): void
{
$this->log($message, self::LEVEL_WARNING);
}
public function error(string $message): void
{
$this->log($message, self::LEVEL_ERROR);
}
public function logException(Throwable $exception): void
{
$this->error(
sprintf(
"Fatal error %s in %s on line %d\n%s",
$exception->getMessage(),
$exception->getFile(),
$exception->getLine(),
$exception->getTraceAsString()
)
);
}
private function getLevelString($level): string
{
switch ($level) {
case LoggerInterface::LEVEL_INFO:
return 'INFO';
case LoggerInterface::LEVEL_WARNING:
return 'WARNING';
case LoggerInterface::LEVEL_ERROR:
return 'ERROR';
default:
return 'UNKNOWN';
}
}
}

View File

@@ -3,17 +3,14 @@
namespace Openguru\OpenCartFramework\Telegram;
use GuzzleHttp\Client;
use Openguru\OpenCartFramework\Logger\Logger;
class TelegramService
{
private Logger $logger;
private Client $client;
private ?string $botToken;
public function __construct(Logger $logger, ?string $botToken = null)
public function __construct(?string $botToken = null)
{
$this->logger = $logger;
$this->botToken = $botToken;
$this->client = $this->createGuzzleClient("https://api.telegram.org/bot{$botToken}/");
}

View File

@@ -4,7 +4,6 @@ namespace Openguru\OpenCartFramework\Telegram;
use Openguru\OpenCartFramework\Application;
use Openguru\OpenCartFramework\Container\ServiceProvider;
use Openguru\OpenCartFramework\Logger\Logger;
class TelegramServiceProvider extends ServiceProvider
{
@@ -12,10 +11,7 @@ class TelegramServiceProvider extends ServiceProvider
{
$this->container->singleton(TelegramService::class, function (Application $app) {
$botToken = $app->getConfigValue('telegram.bot_token');
return new TelegramService(
$app->get(Logger::class),
$botToken,
);
return new TelegramService($botToken);
});
$this->container->singleton(SignatureValidator::class, function (Application $app) {

View File

@@ -0,0 +1,43 @@
<?php
namespace Openguru\OpenCartFramework\Validator;
class ErrorBag
{
private array $errors;
public function __construct(array $errors = [])
{
$this->errors = $errors;
}
public function count(): int
{
return count($this->errors);
}
public function put(string $field, string $message): void
{
$this->errors[$field][] = $message;
}
public function first(): array
{
foreach ($this->errors as $error) {
return $error;
}
}
public function firstOfAll(): array
{
$result = [];
foreach ($this->errors as $field => $errors) {
if (count($errors) > 0) {
$result[$field] = $errors[0];
}
}
return $result;
}
}

View File

@@ -0,0 +1,9 @@
<?php
namespace Openguru\OpenCartFramework\Validator;
use Exception;
class ValidationRuleNotFoundException extends Exception
{
}

View File

@@ -0,0 +1,17 @@
<?php
namespace Openguru\OpenCartFramework\Validator\ValidationRules;
use Closure;
class Email implements ValidationRuleInterface
{
public function validate(string $field, array $input, Closure $fail): void
{
$email = $input[$field] ?? '';
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$fail('Email is not valid');
}
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace Openguru\OpenCartFramework\Validator\ValidationRules;
use Closure;
class Required implements ValidationRuleInterface
{
public function validateRequired($value): bool
{
if (is_null($value)) {
return false;
}
if (is_string($value) && trim($value) === '') {
return false;
}
if (is_countable($value) && count($value) < 1) {
return false;
}
return true;
}
public function validate(string $field, array $input, Closure $fail): void
{
$value = $input[$field] ?? null;
if ($this->validateRequired($value) === false) {
$fail(':field is required');
}
}
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Openguru\OpenCartFramework\Validator\ValidationRules;
use Closure;
interface ValidationRuleInterface
{
public function validate(string $field, array $input, Closure $fail): void;
}

View File

@@ -2,23 +2,89 @@
namespace Openguru\OpenCartFramework\Validator;
class Validator
{
private $input;
private $rules;
use Openguru\OpenCartFramework\Validator\ValidationRules\ValidationRuleInterface;
public function __construct(array $input, array $rules)
class Validator implements ValidatorInterface
{
private array $input;
private array $rules;
private ErrorBag $errors;
private array $customMessages;
private array $fieldNames;
public function __construct(array $validationRules = [], array $customMessages = [])
{
$this->validationRules = $validationRules;
$this->customMessages = $customMessages;
}
public function make(array $input, array $rules, array $fieldNames = []): Validator
{
$this->input = $input;
$this->rules = $rules;
$this->errors = new ErrorBag();
$this->fieldNames = $fieldNames;
return $this;
}
public function validate(): bool
/**
* @throws ValidationRuleNotFoundException
*/
private function validate(): void
{
foreach ($this->rules as $name => $rule) {
$components = explode('|', $rule);
foreach ($this->rules as $field => $rule) {
$parts = $this->extractParts($rule);
foreach ($parts as $part) {
$validationRule = $this->resolveRuleClass($part);
$validationRule->validate($field, $this->input, function ($message) use ($part, $field) {
$this->errors->put($field, $this->formatMessage($message, $part, $field));
});
}
}
}
return true;
/**
* @throws ValidationRuleNotFoundException
*/
public function fails(): bool
{
$this->validate();
return $this->errors->count() > 0;
}
private function extractParts($rule): array
{
return explode('|', $rule);
}
/**
* @throws ValidationRuleNotFoundException
*/
private function resolveRuleClass($part): ValidationRuleInterface
{
$lazyClass = $this->validationRules[$part] ?? null;
if ($lazyClass === null) {
throw new ValidationRuleNotFoundException('Unknown rule "' . $part);
}
return $lazyClass();
}
public function getErrors(): ErrorBag
{
return $this->errors;
}
private function formatMessage(string $message, string $rule, string $field): string
{
$message = $this->customMessages[$rule] ?? $message;
$field = $this->fieldNames[$field] ?? $field;
return str_replace(':field', $field, $message);
}
}

View File

@@ -0,0 +1,15 @@
<?php
namespace Openguru\OpenCartFramework\Validator;
interface ValidatorInterface
{
public function make(array $input, array $rules, array $fieldNames = []): ValidatorInterface;
/**
* @throws ValidationRuleNotFoundException
*/
public function fails(): bool;
public function getErrors(): ErrorBag;
}

View File

@@ -0,0 +1,32 @@
<?php
namespace Openguru\OpenCartFramework\Validator;
use Openguru\OpenCartFramework\Container\ServiceProvider;
use Openguru\OpenCartFramework\Validator\ValidationRules\Email;
use Openguru\OpenCartFramework\Validator\ValidationRules\Required;
class ValidatorServiceProvider extends ServiceProvider
{
protected function rules(): array
{
return [
'required' => fn () => $this->container->get(Required::class),
'email' => fn () => $this->container->get(Email::class),
];
}
public function register(): void
{
$this->container->bind(ValidatorInterface::class, function () {
$langCode = $this->container->getConfigValue('', 'ru');
$translationsFile = __DIR__ . "/translations/$langCode.php";
if (! file_exists($translationsFile)) {
$translationsFile = __DIR__ . "/lang/en.php";
}
return new Validator($this->rules(), require $translationsFile);
});
}
}

View File

@@ -0,0 +1,6 @@
<?php
return [
'required' => ':field обязательно для заполнения.',
'email' => 'Неверный e-mail.',
];

View File

@@ -10,6 +10,7 @@ use Openguru\OpenCartFramework\Router\RouteServiceProvider;
use Openguru\OpenCartFramework\Support\Arr;
use Openguru\OpenCartFramework\Telegram\TelegramServiceProvider;
use Openguru\OpenCartFramework\Telegram\TelegramValidateInitDataMiddleware;
use Openguru\OpenCartFramework\Validator\ValidatorServiceProvider;
class ApplicationFactory
{
@@ -24,6 +25,7 @@ class ApplicationFactory
RouteServiceProvider::class,
AppServiceProvider::class,
TelegramServiceProvider::class,
ValidatorServiceProvider::class,
])
->withMiddlewares([
TelegramValidateInitDataMiddleware::class,

View File

@@ -3,12 +3,18 @@
namespace App\Decorators;
use Cart\Cart;
use Cart\Currency;
use Config;
use Loader;
use Registry;
use Session;
/**
* @property Loader $load
* @property Cart $cart
* @property Session $session
* @property Currency $currency
* @property Config $config
* @property \ModelCatalogProduct $model_catalog_product
*/
class OcRegistryDecorator

View File

@@ -2,7 +2,7 @@
namespace App\Exceptions;
use Rakit\Validation\ErrorBag;
use Openguru\OpenCartFramework\Validator\ErrorBag;
use RuntimeException;
use Throwable;

View File

@@ -9,16 +9,16 @@ use Exception;
use Openguru\OpenCartFramework\Config\Settings;
use Openguru\OpenCartFramework\Http\JsonResponse;
use Openguru\OpenCartFramework\Http\Request;
use Openguru\OpenCartFramework\Logger\Logger;
use Openguru\OpenCartFramework\Logger\LoggerInterface;
use RuntimeException;
class ProductsHandler
{
private Settings $settings;
private ProductsService $productsService;
private Logger $logger;
private LoggerInterface $logger;
public function __construct(Settings $settings, ProductsService $productsService, Logger $logger)
public function __construct(Settings $settings, ProductsService $productsService, LoggerInterface $logger)
{
$this->settings = $settings;
$this->productsService = $productsService;

View File

@@ -1,16 +1,19 @@
<?php
declare(strict_types=1);
namespace App\Services;
use App\Decorators\OcRegistryDecorator;
use App\Exceptions\OrderValidationFailedException;
use Exception;
use Openguru\OpenCartFramework\Config\Settings;
use Openguru\OpenCartFramework\Logger\Logger;
use Openguru\OpenCartFramework\Logger\LoggerInterface;
use Openguru\OpenCartFramework\QueryBuilder\Connections\ConnectionInterface;
use Openguru\OpenCartFramework\Support\Arr;
use Openguru\OpenCartFramework\Telegram\TelegramService;
use Rakit\Validation\Validator;
use Openguru\OpenCartFramework\Validator\ValidationRuleNotFoundException;
use Openguru\OpenCartFramework\Validator\ValidatorInterface;
use RuntimeException;
class OrderCreateService
@@ -20,7 +23,8 @@ class OrderCreateService
private OcRegistryDecorator $oc;
private Settings $settings;
private TelegramService $telegramService;
private Logger $logger;
private LoggerInterface $logger;
private ValidatorInterface $validator;
public function __construct(
ConnectionInterface $database,
@@ -28,7 +32,8 @@ class OrderCreateService
OcRegistryDecorator $registry,
Settings $settings,
TelegramService $telegramService,
Logger $logger
LoggerInterface $logger,
ValidatorInterface $validator
) {
$this->database = $database;
$this->cartService = $cartService;
@@ -36,11 +41,16 @@ class OrderCreateService
$this->settings = $settings;
$this->telegramService = $telegramService;
$this->logger = $logger;
$this->validator = $validator;
}
public function create(array $data, array $meta = []): void
{
try {
$this->validate($data);
} catch (ValidationRuleNotFoundException $e) {
throw new RuntimeException($e->getMessage());
}
$now = date('Y-m-d H:i:s');
$storeId = $this->settings->get('oc_store_id');
@@ -170,23 +180,22 @@ class OrderCreateService
$this->sendNotifications($orderData, $data['tgData']);
}
/**
* @throws ValidationRuleNotFoundException
*/
private function validate(array $data): void
{
$validator = new Validator();
$validation = $validator->make($data, [
'firstName' => 'required',
'lastName' => 'required',
'email' => 'required|email',
'phone' => 'required',
'address' => 'required',
'comment' => 'required',
$v = $this->validator->make($data, $this->makeValidationRulesFromSettings(), [
'firstName' => 'Имя',
'lastName' => 'Фамилия',
'email' => 'E-mail',
'phone' => 'Номер телефона',
'address' => 'Адрес доставки',
'comment' => 'Комментарий',
]);
$validation->validate();
if ($validation->fails()) {
throw new OrderValidationFailedException($validation->errors());
if ($v->fails()) {
throw new OrderValidationFailedException($v->getErrors());
}
}
@@ -232,4 +241,16 @@ class OrderCreateService
}
}
}
private function makeValidationRulesFromSettings(): array
{
return [
'firstName' => 'required',
'lastName' => 'required',
'email' => 'required|email',
'phone' => 'required',
'address' => 'required',
'comment' => 'required',
];
}
}

View File

@@ -1,7 +1,6 @@
<?php
return [
'config_timezone' => 'UTC',
'lang' => 'en-gb',

View File

@@ -0,0 +1,19 @@
<?php
namespace Tests;
use Openguru\OpenCartFramework\Application;
class TestCase extends \PHPUnit\Framework\TestCase
{
protected Application $app;
protected function setUp(): void
{
parent::setUp();
$config = [];
$this->app = new Application($config);
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace Tests\Validator;
use Openguru\OpenCartFramework\Validator\ErrorBag;
use Tests\TestCase;
class ErrorBagTest extends TestCase
{
public function testFirstOfAll(): void
{
$errorBag = new ErrorBag([
'foo' => ['one', 'two'],
'bar' => ['three', 'four'],
]);
$expected = [
'foo' => 'one',
'bar' => 'three',
];
$this->assertEquals($expected, $errorBag->firstOfAll());
}
}

View File

@@ -0,0 +1,145 @@
<?php
namespace Tests\Validator;
use Openguru\OpenCartFramework\Validator\ValidationRuleNotFoundException;
use Openguru\OpenCartFramework\Validator\ValidationRules\Required;
use Openguru\OpenCartFramework\Validator\Validator;
use Tests\TestCase;
/**
* @coversDefaultClass Validator
*/
class ValidatorTest extends TestCase
{
/**
* @covers Validator::fails()
* @covers Validator::make()
*/
public function testValidateBasic(): void
{
$validator = new Validator();
$this->assertFalse($validator->make([], [])->fails());
}
/**
* @covers Validator::fails()
*/
public function testThrowExceptionIfRuleNotFound(): void
{
$validator = new Validator();
$v = $validator->make([], [
'field' => 'not_exists_rule',
]);
$this->expectException(ValidationRuleNotFoundException::class);
$v->fails();
}
public function testRequiredForNonExistentField(): void
{
$validator = new Validator([
'required' => fn () => new Required(),
]);
$v = $validator->make(['xyz' => 'abc'], ['foo' => 'required']);
$this->assertTrue($v->fails());
$this->assertEquals(1, $v->getErrors()->count());
$this->assertEquals(['foo is required'], $v->getErrors()->first());
}
public function testRequiredForEmptyField(): void
{
$validator = new Validator([
'required' => fn () => new Required(),
]);
$v = $validator->make(['foo' => ''], ['foo' => 'required']);
$this->assertTrue($v->fails());
$this->assertEquals(1, $v->getErrors()->count());
$this->assertEquals(['foo is required'], $v->getErrors()->first());
}
public function testRequiredForFalseValue(): void
{
$validator = new Validator([
'required' => fn () => new Required(),
]);
$v = $validator->make(['foo' => false], ['foo' => 'required']);
$this->assertFalse($v->fails());
}
public function testRequiredForNullValue(): void
{
$validator = new Validator([
'required' => fn () => new Required(),
]);
$v = $validator->make(['foo' => null], ['foo' => 'required']);
$this->assertTrue($v->fails());
$this->assertEquals(1, $v->getErrors()->count());
$this->assertEquals(['foo is required'], $v->getErrors()->first());
}
public function testRequiredForZero(): void
{
$validator = new Validator([
'required' => fn () => new Required(),
]);
$v = $validator->make(['foo' => 0], ['foo' => 'required']);
$this->assertFalse($v->fails());
}
public function testRequiredForZeroString(): void
{
$validator = new Validator([
'required' => fn () => new Required(),
]);
$v = $validator->make(['foo' => "0"], ['foo' => 'required']);
$this->assertFalse($v->fails());
}
public function testCustomValidationMessages(): void
{
$customMessages = [
'required' => ':field is very required',
];
$validator = new Validator([
'required' => fn () => new Required(),
], $customMessages);
$v = $validator->make(['foo' => ''], ['foo' => 'required']);
$this->assertTrue($v->fails());
$this->assertEquals(1, $v->getErrors()->count());
$this->assertEquals(['foo is very required'], $v->getErrors()->first());
}
public function testCustomFieldNames(): void
{
$validator = new Validator([
'required' => fn () => new Required(),
]);
$v = $validator->make(['foo' => ''], [
'foo' => 'required'
], [
'foo' => 'My Field'
]);
$this->assertTrue($v->fails());
$this->assertEquals(1, $v->getErrors()->count());
$this->assertEquals(['My Field is required'], $v->getErrors()->first());
}
}