Skip to content

Commit 2e460de

Browse files
authored
Merge pull request #28 from wiz-develop/endou-mame/issue27
2 parents 3e51ce6 + 7155f7d commit 2e460de

File tree

4 files changed

+277
-0
lines changed

4 files changed

+277
-0
lines changed

src/Collection/ArrayList.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use WizDevelop\PhpValueObject\Collection\Exception\MultipleCollectionsFoundException;
1919
use WizDevelop\PhpValueObject\Collection\List\IArrayList;
2020
use WizDevelop\PhpValueObject\Collection\List\IArrayListFactory;
21+
use WizDevelop\PhpValueObject\Error\IErrorValue;
2122
use WizDevelop\PhpValueObject\Error\ValueObjectError;
2223

2324
/**

src/Collection/Map.php

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
use WizDevelop\PhpValueObject\Collection\Exception\MultipleCollectionsFoundException;
2121
use WizDevelop\PhpValueObject\Collection\Map\IMap;
2222
use WizDevelop\PhpValueObject\Collection\Map\IMapFactory;
23+
use WizDevelop\PhpValueObject\Error\IErrorValue;
24+
use WizDevelop\PhpValueObject\Error\ValueObjectError;
2325
use WizDevelop\PhpValueObject\IValueObject;
2426

2527
/**
@@ -152,6 +154,54 @@ final public static function tryFrom(Pair ...$values): Result
152154
->andThen(static fn () => Result\ok(static::from(...$values)));
153155
}
154156

157+
/**
158+
* 信頼できるプリミティブ値からインスタンスを生成する
159+
*
160+
* @template TTryFromKey of TKey
161+
* @template TTryFromValue of TValue
162+
*
163+
* @param (Pair<Result<TTryFromKey,IErrorValue>,Result<TTryFromValue,IErrorValue>>|Pair) ...$values
164+
* @return Result<static<TTryFromKey,TTryFromValue>,ValueObjectError>
165+
*/
166+
/**
167+
* @phpstan-ignore-next-line
168+
*/
169+
#[Override]
170+
final public static function tryFromResults(Pair ...$values): Result
171+
{
172+
/**
173+
* @var array<int,Result<TTryFromKey,IErrorValue>>
174+
* @phpstan-ignore-next-line
175+
* */
176+
$keysResults = array_map(static fn ($pair) => $pair->key, $values);
177+
178+
/**
179+
* @var array<int,Result<TTryFromValue,IErrorValue>>
180+
* @phpstan-ignore-next-line
181+
*/
182+
$valuesResults = array_map(static fn ($pair) => $pair->value, $values);
183+
184+
$pairsResult = Result\combineWithErrorValue(
185+
...$keysResults,
186+
...$valuesResults
187+
);
188+
189+
if ($pairsResult->isErr()) {
190+
return Result\err(ValueObjectError::collection()->invalidElementValues(
191+
static::class,
192+
...$pairsResult->unwrapErr()
193+
));
194+
}
195+
196+
/** @var array<int,Pair<TTryFromKey,TTryFromValue>> */
197+
$values = array_map(static fn ($pair) => Pair::of($pair->key->unwrap(), $pair->value->unwrap()), $values); // @phpstan-ignore-line
198+
199+
// @phpstan-ignore return.type
200+
return static::isValid($values) // @phpstan-ignore argument.type
201+
->andThen(static fn () => static::isValidCount($values)) // @phpstan-ignore argument.type
202+
->andThen(static fn () => Result\ok(static::from(...$values))); // @phpstan-ignore-line
203+
}
204+
155205
#[Override]
156206
final public static function empty(): static
157207
{

src/Collection/Map/IMapFactory.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use WizDevelop\PhpMonad\Result;
88
use WizDevelop\PhpValueObject\Collection\Pair;
9+
use WizDevelop\PhpValueObject\Error\IErrorValue;
910
use WizDevelop\PhpValueObject\Error\ValueObjectError;
1011

1112
/**
@@ -39,6 +40,17 @@ public static function from(Pair ...$values): static;
3940
*/
4041
public static function tryFrom(Pair ...$values): Result;
4142

43+
/**
44+
* 信頼できるプリミティブ値からインスタンスを生成する
45+
*
46+
* @template TTryFromKey of TKey
47+
* @template TTryFromValue of TValue
48+
*
49+
* @param (Pair<Result<TTryFromKey,IErrorValue>,Result<TTryFromValue,IErrorValue>>|Pair) ...$values
50+
* @return Result<static<TTryFromKey,TTryFromValue>,ValueObjectError>
51+
*/
52+
public static function tryFromResults(Pair ...$values): Result; /** @phpstan-ignore-line */
53+
4254
/**
4355
* 空のコレクションを作成する
4456
* @return static<TKey,TValue>
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace WizDevelop\PhpValueObject\Tests\Unit\Collection;
6+
7+
use PHPUnit\Framework\Attributes\CoversClass;
8+
use PHPUnit\Framework\Attributes\Test;
9+
use PHPUnit\Framework\Attributes\TestDox;
10+
use PHPUnit\Framework\TestCase;
11+
use WizDevelop\PhpMonad\Result;
12+
use WizDevelop\PhpValueObject\Collection\Map;
13+
use WizDevelop\PhpValueObject\Collection\Pair;
14+
use WizDevelop\PhpValueObject\Error\ValueObjectError;
15+
use WizDevelop\PhpValueObject\Number\IntegerValue;
16+
use WizDevelop\PhpValueObject\String\StringValue;
17+
18+
#[TestDox('Map::tryFromResults メソッドのテスト')]
19+
#[CoversClass(Map::class)]
20+
final class MapFromResultsTest extends TestCase
21+
{
22+
#[Test]
23+
public function 成功したResultのPairから正常にMapが作成できる(): void
24+
{
25+
// 成功したResultのキーと値を持つPairを作成
26+
$pairs = [
27+
Pair::of(Result\ok('a'), Result\ok(1)),
28+
Pair::of(Result\ok('b'), Result\ok(2)),
29+
Pair::of(Result\ok('c'), Result\ok(3)),
30+
];
31+
32+
// tryFromResults メソッドを呼び出す
33+
$result = Map::tryFromResults(...$pairs);
34+
35+
// 結果が成功であることを確認
36+
$this->assertTrue($result->isOk());
37+
38+
// 正しく要素が取り出せることを確認
39+
$map = $result->unwrap();
40+
$this->assertInstanceOf(Map::class, $map);
41+
$this->assertEquals(['a' => 1, 'b' => 2, 'c' => 3], $map->toArray());
42+
}
43+
44+
#[Test]
45+
public function 成功したバリューオブジェクトのResultのPairから正常にMapが作成できる(): void
46+
{
47+
// 成功したバリューオブジェクトのResultのPairを作成
48+
$pairs = [
49+
Pair::of(Result\ok(StringValue::from('key1')), Result\ok(IntegerValue::from(10))),
50+
Pair::of(Result\ok(StringValue::from('key2')), Result\ok(IntegerValue::from(20))),
51+
Pair::of(Result\ok(StringValue::from('key3')), Result\ok(IntegerValue::from(30))),
52+
];
53+
54+
// tryFromResults メソッドを呼び出す
55+
$result = Map::tryFromResults(...$pairs);
56+
57+
// 結果が成功であることを確認
58+
$this->assertTrue($result->isOk());
59+
60+
// 正しく要素が取り出せることを確認
61+
$map = $result->unwrap();
62+
$this->assertInstanceOf(Map::class, $map);
63+
64+
// マップから各要素を取得して検証
65+
$this->assertCount(3, $map->toArray());
66+
67+
// キーと値を検証(特殊なキーの処理を考慮)
68+
$this->assertTrue($map->has(StringValue::from('key1')));
69+
$this->assertTrue($map->has(StringValue::from('key2')));
70+
$this->assertTrue($map->has(StringValue::from('key3')));
71+
72+
// StringValueのキーで値を取得
73+
$value1 = $map->get(StringValue::from('key1'))->unwrap();
74+
$value2 = $map->get(StringValue::from('key2'))->unwrap();
75+
$value3 = $map->get(StringValue::from('key3'))->unwrap();
76+
77+
$this->assertInstanceOf(IntegerValue::class, $value1);
78+
$this->assertInstanceOf(IntegerValue::class, $value2);
79+
$this->assertInstanceOf(IntegerValue::class, $value3);
80+
$this->assertEquals(10, $value1->value);
81+
$this->assertEquals(20, $value2->value);
82+
$this->assertEquals(30, $value3->value);
83+
}
84+
85+
#[Test]
86+
public function 異なる型のバリューオブジェクトのResultのPairから正常にMapが作成できる(): void
87+
{
88+
// 異なる型のバリューオブジェクトのResultのPairを作成
89+
$pairs = [
90+
Pair::of(Result\ok(IntegerValue::from(1)), Result\ok(StringValue::from('value1'))),
91+
Pair::of(Result\ok(StringValue::from('key2')), Result\ok(IntegerValue::from(2))),
92+
Pair::of(Result\ok(IntegerValue::from(3)), Result\ok(StringValue::from('value3'))),
93+
];
94+
95+
// tryFromResults メソッドを呼び出す
96+
$result = Map::tryFromResults(...$pairs);
97+
98+
// 結果が成功であることを確認
99+
$this->assertTrue($result->isOk());
100+
101+
// 正しく要素が取り出せることを確認
102+
$map = $result->unwrap();
103+
$this->assertInstanceOf(Map::class, $map);
104+
105+
// 各キーの存在を確認
106+
$this->assertTrue($map->has(IntegerValue::from(1)));
107+
$this->assertTrue($map->has(StringValue::from('key2')));
108+
$this->assertTrue($map->has(IntegerValue::from(3)));
109+
110+
// キーを使って値を取得して検証
111+
$value1 = $map->get(IntegerValue::from(1))->unwrap();
112+
$value2 = $map->get(StringValue::from('key2'))->unwrap();
113+
$value3 = $map->get(IntegerValue::from(3))->unwrap();
114+
115+
$this->assertInstanceOf(StringValue::class, $value1);
116+
$this->assertInstanceOf(IntegerValue::class, $value2);
117+
$this->assertInstanceOf(StringValue::class, $value3);
118+
$this->assertEquals('value1', $value1->value);
119+
$this->assertEquals(2, $value2->value);
120+
$this->assertEquals('value3', $value3->value);
121+
}
122+
123+
#[Test]
124+
public function キーに失敗したResultが含まれる場合はエラーが返される(): void
125+
{
126+
// キーに失敗したResultを含むPairの配列を作成
127+
$pairs = [
128+
Pair::of(Result\ok('a'), Result\ok(1)),
129+
Pair::of(Result\err(ValueObjectError::general()->invalid('キーエラー')), Result\ok(2)),
130+
Pair::of(Result\ok('c'), Result\ok(3)),
131+
];
132+
133+
// tryFromResults メソッドを呼び出す
134+
$result = Map::tryFromResults(...$pairs);
135+
136+
// 結果が失敗であることを確認
137+
$this->assertTrue($result->isErr());
138+
139+
// エラー情報を検証
140+
$error = $result->unwrapErr();
141+
$this->assertInstanceOf(ValueObjectError::class, $error);
142+
$this->assertStringContainsString('無効な要素が含まれています', $error->getMessage());
143+
$this->assertStringContainsString('キーエラー', $error->getMessage());
144+
}
145+
146+
#[Test]
147+
public function 値に失敗したResultが含まれる場合はエラーが返される(): void
148+
{
149+
// 値に失敗したResultを含むPairの配列を作成
150+
$pairs = [
151+
Pair::of(Result\ok('a'), Result\ok(1)),
152+
Pair::of(Result\ok('b'), Result\err(ValueObjectError::general()->invalid('値エラー'))),
153+
Pair::of(Result\ok('c'), Result\ok(3)),
154+
];
155+
156+
// tryFromResults メソッドを呼び出す
157+
$result = Map::tryFromResults(...$pairs);
158+
159+
// 結果が失敗であることを確認
160+
$this->assertTrue($result->isErr());
161+
162+
// エラー情報を検証
163+
$error = $result->unwrapErr();
164+
$this->assertInstanceOf(ValueObjectError::class, $error);
165+
$this->assertStringContainsString('無効な要素が含まれています', $error->getMessage());
166+
$this->assertStringContainsString('値エラー', $error->getMessage());
167+
}
168+
169+
#[Test]
170+
public function キーと値の両方に失敗したResultが含まれる場合は全てのエラーが集約される(): void
171+
{
172+
// キーと値の両方に失敗したResultを含むPairの配列を作成
173+
$pairs = [
174+
Pair::of(Result\ok('a'), Result\ok(1)),
175+
Pair::of(Result\err(ValueObjectError::general()->invalid('キーエラー1')), Result\ok(2)),
176+
Pair::of(Result\ok('c'), Result\err(ValueObjectError::general()->invalid('値エラー1'))),
177+
Pair::of(Result\err(ValueObjectError::general()->invalid('キーエラー2')), Result\err(ValueObjectError::general()->invalid('値エラー2'))),
178+
];
179+
180+
// tryFromResults メソッドを呼び出す
181+
$result = Map::tryFromResults(...$pairs);
182+
183+
// 結果が失敗であることを確認
184+
$this->assertTrue($result->isErr());
185+
186+
// エラー情報を検証
187+
$error = $result->unwrapErr();
188+
$this->assertInstanceOf(ValueObjectError::class, $error);
189+
$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());
194+
}
195+
196+
#[Test]
197+
public function 空のPair配列から空のMapが作成できる(): void
198+
{
199+
// 空の配列を作成
200+
$pairs = [];
201+
202+
// tryFromResults メソッドを呼び出す
203+
$result = Map::tryFromResults(...$pairs);
204+
205+
// 結果が成功であることを確認
206+
$this->assertTrue($result->isOk());
207+
208+
// 空のマップであることを確認
209+
$map = $result->unwrap();
210+
$this->assertInstanceOf(Map::class, $map);
211+
$this->assertEmpty($map->toArray());
212+
$this->assertEquals(0, $map->count());
213+
}
214+
}

0 commit comments

Comments
 (0)