Skip to content

Commit 0c0b571

Browse files
authored
Merge pull request #36 from wiz-develop/endou-mame/issue35
2 parents b2b2f24 + a00fa80 commit 0c0b571

10 files changed

+319
-45
lines changed

src/Error/CollectionValueError.php

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,13 @@ public static function invalidRange(
5757
): ValueObjectError {
5858
$displayName = ValueObjectError::getDisplayName($className);
5959

60+
if ($min === $max) {
61+
return ValueObjectError::of(
62+
code: 'value_object.collection.invalid_count_exact',
63+
message: "{$displayName}{$min}個である必要があります。(要素数:{$count})",
64+
);
65+
}
66+
6067
return ValueObjectError::of(
6168
code: 'value_object.collection.invalid_range',
6269
message: "{$displayName}{$min}個以上、{$max}個以下である必要があります。(要素数:{$count})",
@@ -71,14 +78,10 @@ public static function invalidElementValues(string $className, IErrorValue ...$e
7178
{
7279
$displayName = ValueObjectError::getDisplayName($className);
7380

74-
$errorsStr = implode(
75-
', ',
76-
array_map(static fn (IErrorValue $error) => $error->getMessage(), $errors),
77-
);
78-
7981
return ValueObjectError::of(
8082
code: 'value_object.collection.invalid_element_values',
81-
message: "{$displayName}に無効な要素が含まれています。(無効な要素の詳細: {$errorsStr})",
83+
message: "{$displayName}に無効な要素が含まれています。",
84+
details: $errors,
8285
);
8386
}
8487
}

src/Error/DateTimeValueError.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,14 @@ public static function invalidRange(
2222
?string $maxValue = null,
2323
): ValueObjectError {
2424
$displayName = ValueObjectError::getDisplayName($className);
25+
26+
if ($minValue === $maxValue) {
27+
return ValueObjectError::of(
28+
code: 'value_object.datetime.invalid_range_exact',
29+
message: "{$displayName}{$minValue}である必要があります。(値:{$value})",
30+
);
31+
}
32+
2533
$message = "{$displayName}は有効な{$attributeName}の範囲内である必要があります。(値:{$value})";
2634

2735
if ($minValue !== null && $maxValue !== null) {

src/Error/ErrorValue.php

Lines changed: 68 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,43 +5,31 @@
55
namespace WizDevelop\PhpValueObject\Error;
66

77
use Override;
8-
use WizDevelop\PhpValueObject\IValueObject;
8+
use WizDevelop\PhpValueObject\ValueObjectDefault;
99

1010
/**
1111
* エラー値オブジェクト
1212
*/
1313
readonly class ErrorValue implements IErrorValue
1414
{
15+
use ValueObjectDefault;
16+
17+
/**
18+
* @param IErrorValue[] $details
19+
*/
1520
final private function __construct(
1621
private string $code,
1722
private string $message,
23+
private array $details,
1824
) {
1925
}
2026

21-
final public static function of(string $code, string $message): static
22-
{
23-
return new static($code, $message);
24-
}
25-
26-
#[Override]
27-
final public function equals(IValueObject $other): bool
28-
{
29-
return $this->code === $other->code;
30-
}
31-
32-
#[Override]
33-
final public function __toString(): string
34-
{
35-
return $this->serialize();
36-
}
37-
3827
/**
39-
* @return array<mixed>
28+
* @param IErrorValue[] $details
4029
*/
41-
#[Override]
42-
final public function jsonSerialize(): array
30+
final public static function of(string $code, string $message, array $details = []): static
4331
{
44-
return get_object_vars($this);
32+
return new static($code, $message, $details);
4533
}
4634

4735
#[Override]
@@ -56,19 +44,73 @@ final public function getMessage(): string
5644
return $this->message;
5745
}
5846

47+
#[Override]
48+
final public function getDetails(): array
49+
{
50+
return $this->details;
51+
}
52+
5953
#[Override]
6054
final public function serialize(): string
6155
{
62-
return $this->code . static::SEPARATOR . $this->message;
56+
$result = $this->code . static::SEPARATOR . $this->message;
57+
58+
if (count($this->details) > 0) {
59+
$result .= static::SEPARATOR . count($this->details);
60+
foreach ($this->details as $detail) {
61+
$result .= static::SEPARATOR . $detail->serialize();
62+
}
63+
}
64+
65+
return $result;
6366
}
6467

6568
#[Override]
6669
final public static function deserialize(string $serialized): static
6770
{
68-
$exploded = explode(static::SEPARATOR, $serialized);
71+
$parts = explode(static::SEPARATOR, $serialized);
72+
assert(count($parts) >= 2, 'Invalid serialized error value format.');
73+
74+
[$code, $message] = $parts;
75+
$details = [];
76+
77+
if (count($parts) > 2) {
78+
$detailCount = (int)$parts[2];
79+
assert($detailCount >= 0, 'Invalid detail count in serialized error value.');
80+
81+
$index = 3;
82+
for ($i = 0; $i < $detailCount; ++$i) {
83+
[$detail, $index] = self::parseDetail($parts, $index);
84+
$details[] = $detail;
85+
}
86+
}
87+
88+
return new static($code, $message, $details);
89+
}
90+
91+
/**
92+
* @param array<int, string> $parts
93+
* @return array{IErrorValue, int}
94+
*/
95+
private static function parseDetail(array $parts, int $index): array
96+
{
97+
assert($index + 1 < count($parts), 'Invalid index for detail parsing.');
98+
99+
$code = $parts[$index];
100+
$message = $parts[$index + 1];
101+
$index += 2;
102+
$details = [];
103+
104+
if ($index < count($parts) && is_numeric($parts[$index])) {
105+
$nestedCount = (int)$parts[$index];
106+
++$index;
69107

70-
assert(count($exploded) === 2);
108+
for ($i = 0; $i < $nestedCount; ++$i) {
109+
[$detail, $index] = self::parseDetail($parts, $index);
110+
$details[] = $detail;
111+
}
112+
}
71113

72-
return new static(...$exploded);
114+
return [new static($code, $message, $details), $index];
73115
}
74116
}

src/Error/IErrorValue.php

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,13 @@ public function getCode(): string;
2828
*/
2929
public function getMessage(): string;
3030

31+
/**
32+
* エラーの詳細を取得する
33+
*
34+
* @return IErrorValue[] エラーの詳細
35+
*/
36+
public function getDetails(): array;
37+
3138
/**
3239
* エラーをシリアライズする
3340
*/
@@ -36,8 +43,8 @@ public function serialize(): string;
3643
/**
3744
* シリアライズされたエラーをデシリアライズする
3845
*
39-
* @param string $serialized シリアライズされたエラー
40-
* @return static デシリアライズされたエラー
46+
* @param string $serialized シリアライズされたエラー
47+
* @return IErrorValue デシリアライズされたエラー
4148
*/
42-
public static function deserialize(string $serialized): static;
49+
public static function deserialize(string $serialized): self;
4350
}

src/Error/NumberValueError.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@ public static function invalidRange(
2424
bool $isMaxInclusive = true,
2525
): ValueObjectError {
2626
$displayName = ValueObjectError::getDisplayName($className);
27+
28+
if ($min === $max) {
29+
return ValueObjectError::of(
30+
code: 'value_object.number.invalid_range_exact',
31+
message: "{$displayName}{$min}である必要があります。(値:{$value})",
32+
);
33+
}
34+
2735
$minText = $isMinInclusive ? '以上' : 'より大きい';
2836
$maxText = $isMaxInclusive ? '以下' : '未満';
2937

src/Error/StringValueError.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@ public static function invalidLength(
2323
): ValueObjectError {
2424
$displayName = ValueObjectError::getDisplayName($className);
2525

26+
if ($min_length === $max_length) {
27+
return ValueObjectError::of(
28+
code: 'value_object.string.invalid_length_exact',
29+
message: "{$displayName}{$min_length}文字である必要があります。(値:{$value})",
30+
);
31+
}
32+
2633
return ValueObjectError::of(
2734
code: 'value_object.string.invalid_length',
2835
message: "{$displayName}{$min_length}文字以上{$max_length}文字以下である必要があります。(値:{$value})",

src/ValueObjectDefault.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ final public function equals(IValueObject $other): bool
2222
#[Override]
2323
final public function __toString(): string
2424
{
25-
return json_encode($this->jsonSerialize(), JSON_THROW_ON_ERROR);
25+
return json_encode($this->jsonSerialize(), JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
2626
}
2727

2828
/**

tests/Unit/Collection/ArrayListFromResultsTest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ public function 一つでも失敗したResultが含まれる場合はエラー
122122
$error = $result->unwrapErr();
123123
$this->assertInstanceOf(ValueObjectError::class, $error);
124124
$this->assertStringContainsString('無効な要素が含まれています', $error->getMessage());
125-
$this->assertStringContainsString('テストエラー', $error->getMessage());
125+
$this->assertStringContainsString('テストエラー', $error->serialize());
126126
}
127127

128128
#[Test]
@@ -146,8 +146,8 @@ public function 複数の失敗したResultが含まれる場合は全てのエ
146146
$error = $result->unwrapErr();
147147
$this->assertInstanceOf(ValueObjectError::class, $error);
148148
$this->assertStringContainsString('無効な要素が含まれています', $error->getMessage());
149-
$this->assertStringContainsString('エラー1', $error->getMessage());
150-
$this->assertStringContainsString('エラー2', $error->getMessage());
149+
$this->assertStringContainsString('エラー1', $error->serialize());
150+
$this->assertStringContainsString('エラー2', $error->serialize());
151151
}
152152

153153
#[Test]

tests/Unit/Collection/MapFromResultsTest.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ public function キーに失敗したResultが含まれる場合はエラーが
140140
$error = $result->unwrapErr();
141141
$this->assertInstanceOf(ValueObjectError::class, $error);
142142
$this->assertStringContainsString('無効な要素が含まれています', $error->getMessage());
143-
$this->assertStringContainsString('キーエラー', $error->getMessage());
143+
$this->assertStringContainsString('キーエラー', $error->serialize());
144144
}
145145

146146
#[Test]
@@ -163,7 +163,7 @@ public function 値に失敗したResultが含まれる場合はエラーが返
163163
$error = $result->unwrapErr();
164164
$this->assertInstanceOf(ValueObjectError::class, $error);
165165
$this->assertStringContainsString('無効な要素が含まれています', $error->getMessage());
166-
$this->assertStringContainsString('値エラー', $error->getMessage());
166+
$this->assertStringContainsString('値エラー', $error->serialize());
167167
}
168168

169169
#[Test]
@@ -187,10 +187,10 @@ public function キーと値の両方に失敗したResultが含まれる場合
187187
$error = $result->unwrapErr();
188188
$this->assertInstanceOf(ValueObjectError::class, $error);
189189
$this->assertStringContainsString('無効な要素が含まれています', $error->getMessage());
190-
$this->assertStringContainsString('キーエラー1', $error->getMessage());
191-
$this->assertStringContainsString('値エラー1', $error->getMessage());
192-
$this->assertStringContainsString('キーエラー2', $error->getMessage());
193-
$this->assertStringContainsString('値エラー2', $error->getMessage());
190+
$this->assertStringContainsString('キーエラー1', $error->serialize());
191+
$this->assertStringContainsString('値エラー1', $error->serialize());
192+
$this->assertStringContainsString('キーエラー2', $error->serialize());
193+
$this->assertStringContainsString('値エラー2', $error->serialize());
194194
}
195195

196196
#[Test]

0 commit comments

Comments
 (0)