Skip to content

Commit 2e1382f

Browse files
Phillip Lookpl-github
andauthored
feat: Provide class to build request data easily (#7)
Co-authored-by: Phillip Look <[email protected]>
1 parent d33aa2b commit 2e1382f

File tree

2 files changed

+211
-0
lines changed

2 files changed

+211
-0
lines changed

src/Request/DataBuilder.php

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Brainbits\FunctionalTestHelpers\Request;
6+
7+
use function array_pop;
8+
use function count;
9+
10+
final class DataBuilder
11+
{
12+
/** @var mixed[] */
13+
private array $data;
14+
15+
/**
16+
* @param mixed[] $data
17+
*/
18+
private function __construct(array $data)
19+
{
20+
$this->data = $data;
21+
}
22+
23+
/**
24+
* @param mixed[] $data
25+
*/
26+
public static function from(array $data): self
27+
{
28+
return new self($data);
29+
}
30+
31+
/**
32+
* @return mixed[]
33+
*/
34+
public function __invoke(): array
35+
{
36+
return $this->data;
37+
}
38+
39+
/**
40+
* @param string|int ...$path
41+
*/
42+
public function without(...$path): self
43+
{
44+
if (count($path) === 0) {
45+
return $this;
46+
}
47+
48+
$indexToUnset = array_pop($path);
49+
50+
$dataToModify = &$this->data;
51+
foreach ($path as $index) {
52+
$dataToModify = &$dataToModify[$index];
53+
}
54+
55+
unset($dataToModify[$indexToUnset]);
56+
57+
return $this;
58+
}
59+
60+
/**
61+
* @param mixed $newValue
62+
* @param string|int ...$path
63+
*/
64+
public function with($newValue, ...$path): self
65+
{
66+
if (count($path) === 0) {
67+
return $this;
68+
}
69+
70+
$indexToModify = array_pop($path);
71+
72+
$dataToModify = &$this->data;
73+
foreach ($path as $index) {
74+
$dataToModify = &$dataToModify[$index];
75+
}
76+
77+
$dataToModify[$indexToModify] = $newValue;
78+
79+
return $this;
80+
}
81+
82+
/**
83+
* @param mixed $newValue
84+
* @param string|int ...$path
85+
*/
86+
public function add($newValue, ...$path): self
87+
{
88+
$dataToModify = &$this->data;
89+
foreach ($path as $index) {
90+
$dataToModify = &$dataToModify[$index];
91+
}
92+
93+
$dataToModify[] = $newValue;
94+
95+
return $this;
96+
}
97+
}

tests/Request/DataBuilderTest.php

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Brainbits\FunctionalTestHelpers\Tests\Request;
6+
7+
use Brainbits\FunctionalTestHelpers\Request\DataBuilder;
8+
use PHPUnit\Framework\TestCase;
9+
10+
/**
11+
* @covers \Brainbits\FunctionalTestHelpers\Request
12+
*/
13+
final class DataBuilderTest extends TestCase
14+
{
15+
public function testItEncapsulatesDataAsArray(): void
16+
{
17+
$dataProvider = DataBuilder::from(['data']);
18+
19+
self::assertSame(['data'], $dataProvider());
20+
}
21+
22+
public function testRemovesNumberIndex(): void
23+
{
24+
$dataProvider = DataBuilder::from(['zero', 'one'])
25+
->without(1);
26+
27+
self::assertEquals(['zero'], $dataProvider());
28+
}
29+
30+
public function testRemovesNamedIndex(): void
31+
{
32+
$dataProvider = DataBuilder::from(['one' => 'one', 'two' => 'two'])
33+
->without('two');
34+
35+
self::assertEquals(['one' => 'one'], $dataProvider());
36+
}
37+
38+
public function testRemovesNestedIndex(): void
39+
{
40+
$dataProvider = DataBuilder::from(['root' => ['one' => 'one', 'two' => 'two']])
41+
->without('root', 'two');
42+
43+
self::assertEquals(['root' => ['one' => 'one']], $dataProvider());
44+
}
45+
46+
public function testRemovesDeeplyNestedIndex(): void
47+
{
48+
$dataProvider = DataBuilder::from(['one' => ['two' => ['three' => ['four' => ['five' => 'xxx']]]]])
49+
->without('one', 'two', 'three', 'four', 'five');
50+
51+
self::assertEquals(['one' => ['two' => ['three' => ['four' => []]]]], $dataProvider());
52+
}
53+
54+
public function testSetsNumberIndex(): void
55+
{
56+
$dataProvider = DataBuilder::from(['root' => ['old value']])
57+
->with('new value', 'root', 1);
58+
59+
self::assertEquals(['root' => ['old value', 'new value']], $dataProvider());
60+
}
61+
62+
public function testSetsNamedIndex(): void
63+
{
64+
$dataProvider = DataBuilder::from(['root' => ['old index' => 'old value']]);
65+
66+
self::assertEquals(
67+
['root' => ['old index' => 'old value', 'new index' => 'new value']],
68+
$dataProvider->with('new value', 'root', 'new index')()
69+
);
70+
}
71+
72+
public function testCreatesMissingHierarchiesOnSettingValues(): void
73+
{
74+
$dataProvider = DataBuilder::from([])
75+
->with('value', 'level1', 'level2', 'level3');
76+
77+
self::assertEquals(['level1' => ['level2' => ['level3' => 'value']]], $dataProvider());
78+
}
79+
80+
public function testAddsValues(): void
81+
{
82+
$dataProvider = DataBuilder::from([])
83+
->add('value1');
84+
85+
self::assertEquals(['value1'], $dataProvider());
86+
}
87+
88+
public function testAddsNestedValues(): void
89+
{
90+
$dataProvider = DataBuilder::from(['level1' => []])
91+
->add('value1', 'level1');
92+
93+
self::assertEquals(['level1' => ['value1']], $dataProvider());
94+
}
95+
96+
public function testCreatesMissingHierarchiesOnAddingValues(): void
97+
{
98+
$dataProvider = DataBuilder::from([])
99+
->add('value1', 'level1', 'level2', 'level3')
100+
->add('value2', 'level1', 'level2', 'level3');
101+
102+
self::assertEquals(['level1' => ['level2' => ['level3' => ['value1', 'value2']]]], $dataProvider());
103+
}
104+
105+
public function testModifiersAreChainable(): void
106+
{
107+
$dataProvider = DataBuilder::from([])
108+
->with('value1', 'key1')
109+
->with('value2', 'key2')
110+
->without('key1');
111+
112+
self::assertEquals(['key2' => 'value2'], $dataProvider());
113+
}
114+
}

0 commit comments

Comments
 (0)