エラーハンドリング & 検証
「過ちて改めざる、これを過ちという」
—孔子『論語』(紀元前551-479年)
過ちの意味
Beフレームワークにおけるエラーハンドリングは例外をキャッチすることだけではありません—存在が失敗したときに意味を保持することです。
汎用的例外を超えて
従来のエラーハンドリングは意味を失います:
try {
$user = new User($name, $email, $age);
} catch (Exception $e) {
// 何が間違っていたのか?なぜ?どう修正するのか?
echo $e->getMessage(); // "検証に失敗しました"
}
意味的例外: 失敗における意味
すべての失敗は特定の存在論的意味を持ちます:
try {
$user = $becoming(new UserInput($name, $email, $age));
} catch (SemanticVariableException $e) {
foreach ($e->getErrors()->exceptions as $exception) {
echo get_class($exception) . ": " . $exception->getMessage();
// EmptyNameException: 名前は空にできません。
// InvalidEmailFormatException: メール形式が無効です。
// AgeTooYoungException: 年齢は最低13歳でなければなりません。
}
}
例外階層
ドメイン例外は意味のあるカテゴリーを形成します:
abstract class DomainException extends Exception {}
final readonly class EmptyNameException extends DomainException {}
final readonly class InvalidEmailFormatException extends DomainException
{
public function __construct(public string $invalidEmail)
{
parent::__construct("メール形式が無効です: {$invalidEmail}");
}
}
// 年齢関連の存在失敗
abstract class AgeException extends DomainException {}
final readonly class NegativeAgeException extends AgeException {}
final readonly class AgeTooHighException extends AgeException {}
多言語エラーメッセージ
意味的例外はユーザーの言語で話します:
#[Message([
'en' => 'Name cannot be empty.',
'ja' => '名前は空にできません。',
'es' => 'El nombre no puede estar vacío.'
])]
final readonly class EmptyNameException extends DomainException {}
#[Message([
'en' => 'Age must be between {min} and {max} years.',
'ja' => '年齢は{min}歳から{max}歳の間でなければなりません。'
])]
final readonly class AgeOutOfRangeException extends DomainException
{
public function __construct(
public int $age,
public int $min = 0,
public int $max = 150
) {}
}
自動エラー収集
フレームワークは投げる前にすべての検証失敗を収集します:
final readonly class UserValidation
{
public function __construct(
#[Input] string $name, // EmptyNameExceptionを投げる可能性
#[Input] string $email, // InvalidEmailFormatExceptionを投げる可能性
#[Input] int $age // NegativeAgeExceptionを投げる可能性
) {
// いずれかの検証が失敗すると、すべてのエラーが収集される
// 単一のSemanticVariableExceptionがすべてを含む
}
}
「即座に失敗」ではなく—完全な理解と共に完全に失敗。
エラー回復パターン
エラーは独自の権利における有効な存在になります:
#[Be([ValidUser::class, InvalidUser::class])]
final readonly class UserValidation
{
public ValidUser|InvalidUser $being;
public function __construct(
#[Input] string $name,
#[Input] string $email,
#[Input] int $age
) {
try {
$this->being = new ValidUser($name, $email, $age);
} catch (ValidationException $e) {
$this->being = new InvalidUser($e->getErrors());
}
}
}
意味ログ統合
検証失敗は文脈と共に自動的にログされます:
{
"event": "metamorphosis_failed",
"source_class": "UserInput",
"destination_class": "UserProfile",
"errors": [
{
"exception": "EmptyNameException",
"message": "名前は空にできません",
"field": "name",
"value": ""
}
]
}
開発 vs プロダクション
// 開発: 詳細なエラー詳細
if (app()->environment('local')) {
$errors->getDetailedMessages();
}
// プロダクション: ユーザーフレンドリーなメッセージ
$errors->getMessages('ja');
// ["名前は空にできません。", "メール形式が無効です。"]
エラー条件のテスト
public function testCollectsAllValidationErrors(): void
{
try {
$becoming(new UserInput('', 'invalid-email', -5));
$this->fail('SemanticVariableExceptionが予期されました');
} catch (SemanticVariableException $e) {
$errors = $e->getErrors();
$this->assertCount(3, $errors->exceptions);
}
}
革命
意味的例外はエラーハンドリングを問題報告から意味保持に変換します。
存在が失敗したとき、理由は明確で、実行可能で、多言語になります。
エラーは障害ではありません—それらは成功した変容へとユーザーを導く有効な存在です。
すべての変容はストーリー、意味的ログとして記録されます ➡️