Skip to content

Commit 3e51ce6

Browse files
authored
Merge pull request #26 from wiz-develop:endou-mame/issue25
feat: ArrayListにtryFromResultsメソッドを追加し、Result型からのインスタンス生成をサポート
2 parents 929ad4c + c468396 commit 3e51ce6

File tree

5 files changed

+269
-13
lines changed

5 files changed

+269
-13
lines changed

composer.lock

Lines changed: 13 additions & 13 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Collection/ArrayList.php

Lines changed: 33 additions & 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\ValueObjectError;
2122

2223
/**
2324
* リストコレクション
@@ -102,6 +103,38 @@ final public static function tryFrom(iterable $elements): Result
102103
->andThen(static fn () => Result\ok(new static($elements)));
103104
}
104105

106+
/**
107+
* 信頼できないResult型のプリミティブ値からインスタンスを生成する
108+
*
109+
* @template TTryFromValue of TValue
110+
*
111+
* @param iterable<int,(Result<TTryFromValue,IErrorValue>|Result)> $results
112+
* @return Result<static<TTryFromValue>,ValueObjectError>
113+
*/
114+
/**
115+
* @phpstan-ignore-next-line
116+
*/
117+
#[Override]
118+
final public static function tryFromResults(iterable $results): Result
119+
{
120+
$elements = is_array($results) ? $results : iterator_to_array($results);
121+
122+
$elementsResult = Result\combineWithErrorValue(...$elements);
123+
if ($elementsResult->isErr()) {
124+
return Result\err(ValueObjectError::collection()->invalidElementValues(
125+
static::class,
126+
...$elementsResult->unwrapErr()
127+
));
128+
}
129+
130+
$elements = array_map(static fn ($result) => $result->unwrap(), $elements);
131+
132+
// @phpstan-ignore return.type
133+
return static::isValid($elements)
134+
->andThen(static fn () => static::isValidCount($elements))
135+
->andThen(static fn () => Result\ok(new static($elements)));
136+
}
137+
105138
#[Override]
106139
final public static function empty(): static
107140
{

src/Collection/List/IArrayListFactory.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace WizDevelop\PhpValueObject\Collection\List;
66

77
use WizDevelop\PhpMonad\Result;
8+
use WizDevelop\PhpValueObject\Error\IErrorValue;
89
use WizDevelop\PhpValueObject\Error\ValueObjectError;
910

1011
/**
@@ -35,6 +36,16 @@ public static function from(iterable $elements): static;
3536
*/
3637
public static function tryFrom(iterable $elements): Result;
3738

39+
/**
40+
* 信頼できないResult型のプリミティブ値からインスタンスを生成する
41+
*
42+
* @template TTryFromValue of TValue
43+
*
44+
* @param iterable<int,(Result<TTryFromValue,IErrorValue>|Result)> $results
45+
* @return Result<static<TTryFromValue>,ValueObjectError>
46+
*/
47+
public static function tryFromResults(iterable $results): Result; /** @phpstan-ignore-line */
48+
3849
/**
3950
* 空のコレクションを作成する
4051
* @return static<TValue>

src/Error/CollectionValueError.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,23 @@ public static function invalidRange(
6262
message: "{$displayName}{$min}個以上、{$max}個以下である必要があります。(要素数:{$count})",
6363
);
6464
}
65+
66+
/**
67+
* 要素が無効
68+
* @param class-string<CollectionBase<mixed,mixed>> $className
69+
*/
70+
public static function invalidElementValues(string $className, IErrorValue ...$errors): ValueObjectError
71+
{
72+
$displayName = ValueObjectError::getDisplayName($className);
73+
74+
$errorsStr = implode(
75+
', ',
76+
array_map(static fn (IErrorValue $error) => $error->getMessage(), $errors),
77+
);
78+
79+
return ValueObjectError::of(
80+
code: 'value_object.collection.invalid_element_values',
81+
message: "{$displayName}に無効な要素が含まれています。(無効な要素の詳細: {$errorsStr})",
82+
);
83+
}
6584
}
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
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\ArrayList;
13+
use WizDevelop\PhpValueObject\Error\ValueObjectError;
14+
use WizDevelop\PhpValueObject\Number\IntegerValue;
15+
use WizDevelop\PhpValueObject\String\StringValue;
16+
17+
#[TestDox('ArrayList::tryFromResults メソッドのテスト')]
18+
#[CoversClass(ArrayList::class)]
19+
final class ArrayListFromResultsTest extends TestCase
20+
{
21+
#[Test]
22+
public function 成功したResultの配列から正常にArrayListが作成できる(): void
23+
{
24+
// 成功したResultの配列を作成
25+
$results = [
26+
Result\ok(1),
27+
Result\ok(2),
28+
Result\ok(3),
29+
];
30+
31+
// tryFromResults メソッドを呼び出す
32+
$result = ArrayList::tryFromResults($results);
33+
34+
// 結果が成功であることを確認
35+
$this->assertTrue($result->isOk());
36+
37+
// 正しく要素が取り出せることを確認
38+
$list = $result->unwrap();
39+
$this->assertInstanceOf(ArrayList::class, $list);
40+
$this->assertEquals([1, 2, 3], $list->toArray());
41+
}
42+
43+
#[Test]
44+
public function 成功したバリューオブジェクトのResultの配列から正常にArrayListが作成できる(): void
45+
{
46+
// 成功したバリューオブジェクトのResultの配列を作成
47+
$results = [
48+
IntegerValue::tryFrom(10),
49+
IntegerValue::tryFrom(20),
50+
IntegerValue::tryFrom(30),
51+
];
52+
53+
// tryFromResults メソッドを呼び出す
54+
$result = ArrayList::tryFromResults($results);
55+
56+
// 結果が成功であることを確認
57+
$this->assertTrue($result->isOk());
58+
59+
// 正しく要素が取り出せることを確認
60+
$list = $result->unwrap();
61+
$this->assertInstanceOf(ArrayList::class, $list);
62+
63+
// バリューオブジェクトの値を検証
64+
$elements = $list->toArray();
65+
$this->assertCount(3, $elements);
66+
$this->assertInstanceOf(IntegerValue::class, $elements[0]);
67+
$this->assertInstanceOf(IntegerValue::class, $elements[1]);
68+
$this->assertInstanceOf(IntegerValue::class, $elements[2]);
69+
$this->assertEquals(10, $elements[0]->value);
70+
$this->assertEquals(20, $elements[1]->value);
71+
$this->assertEquals(30, $elements[2]->value);
72+
}
73+
74+
#[Test]
75+
public function 異なる型のバリューオブジェクトのResultの配列から正常にArrayListが作成できる(): void
76+
{
77+
// 異なる型のバリューオブジェクトのResultの配列を作成
78+
$results = [
79+
StringValue::tryFrom('hello'),
80+
IntegerValue::tryFrom(42),
81+
StringValue::tryFrom('world'),
82+
];
83+
84+
// tryFromResults メソッドを呼び出す
85+
$result = ArrayList::tryFromResults($results);
86+
87+
// 結果が成功であることを確認
88+
$this->assertTrue($result->isOk());
89+
90+
// 正しく要素が取り出せることを確認
91+
$list = $result->unwrap();
92+
$this->assertInstanceOf(ArrayList::class, $list);
93+
94+
// バリューオブジェクトの値を検証
95+
$elements = $list->toArray();
96+
$this->assertCount(3, $elements);
97+
$this->assertInstanceOf(StringValue::class, $elements[0]);
98+
$this->assertInstanceOf(IntegerValue::class, $elements[1]);
99+
$this->assertInstanceOf(StringValue::class, $elements[2]);
100+
$this->assertEquals('hello', $elements[0]->value);
101+
$this->assertEquals(42, $elements[1]->value);
102+
$this->assertEquals('world', $elements[2]->value);
103+
}
104+
105+
#[Test]
106+
public function 一つでも失敗したResultが含まれる場合はエラーが返される(): void
107+
{
108+
// 失敗したResultを1つ含む配列を作成
109+
$results = [
110+
Result\ok(1),
111+
Result\err(ValueObjectError::general()->invalid('テストエラー')),
112+
Result\ok(3),
113+
];
114+
115+
// tryFromResults メソッドを呼び出す
116+
$result = ArrayList::tryFromResults($results);
117+
118+
// 結果が失敗であることを確認
119+
$this->assertTrue($result->isErr());
120+
121+
// エラー情報を検証
122+
$error = $result->unwrapErr();
123+
$this->assertInstanceOf(ValueObjectError::class, $error);
124+
$this->assertStringContainsString('無効な要素が含まれています', $error->getMessage());
125+
$this->assertStringContainsString('テストエラー', $error->getMessage());
126+
}
127+
128+
#[Test]
129+
public function 複数の失敗したResultが含まれる場合は全てのエラーが集約される(): void
130+
{
131+
// 複数の失敗したResultを含む配列を作成
132+
$results = [
133+
Result\ok(1),
134+
Result\err(ValueObjectError::general()->invalid('エラー1')),
135+
Result\ok(3),
136+
Result\err(ValueObjectError::general()->invalid('エラー2')),
137+
];
138+
139+
// tryFromResults メソッドを呼び出す
140+
$result = ArrayList::tryFromResults($results);
141+
142+
// 結果が失敗であることを確認
143+
$this->assertTrue($result->isErr());
144+
145+
// エラー情報を検証
146+
$error = $result->unwrapErr();
147+
$this->assertInstanceOf(ValueObjectError::class, $error);
148+
$this->assertStringContainsString('無効な要素が含まれています', $error->getMessage());
149+
$this->assertStringContainsString('エラー1', $error->getMessage());
150+
$this->assertStringContainsString('エラー2', $error->getMessage());
151+
}
152+
153+
#[Test]
154+
public function 空の配列から空のArrayListが作成できる(): void
155+
{
156+
// 空の配列を作成
157+
$results = [];
158+
159+
// tryFromResults メソッドを呼び出す
160+
$result = ArrayList::tryFromResults($results);
161+
162+
// 結果が成功であることを確認
163+
$this->assertTrue($result->isOk());
164+
165+
// 空のリストであることを確認
166+
$list = $result->unwrap();
167+
$this->assertInstanceOf(ArrayList::class, $list);
168+
$this->assertEmpty($list->toArray());
169+
$this->assertEquals(0, $list->count());
170+
}
171+
172+
#[Test]
173+
public function イテラブルなオブジェクトからArrayListが作成できる(): void
174+
{
175+
// イテラブルなオブジェクトを作成(ジェネレータを使用)
176+
$generator = (static function () {
177+
yield Result\ok(1);
178+
yield Result\ok(2);
179+
yield Result\ok(3);
180+
})();
181+
182+
// tryFromResults メソッドを呼び出す
183+
$result = ArrayList::tryFromResults($generator);
184+
185+
// 結果が成功であることを確認
186+
$this->assertTrue($result->isOk());
187+
188+
// 正しく要素が取り出せることを確認
189+
$list = $result->unwrap();
190+
$this->assertInstanceOf(ArrayList::class, $list);
191+
$this->assertEquals([1, 2, 3], $list->toArray());
192+
}
193+
}

0 commit comments

Comments
 (0)