Skip to content

Commit 6663d3d

Browse files
authored
Merge pull request #3 from BluePsyduck/feature/map-array
feature/map-array
2 parents 25208f9 + 8a249b7 commit 6663d3d

File tree

8 files changed

+138
-147
lines changed

8 files changed

+138
-147
lines changed

.github/problem-matchers/coverage-check.json

Lines changed: 0 additions & 13 deletions
This file was deleted.

.github/workflows/ci.yaml

Lines changed: 9 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -6,129 +6,12 @@ on:
66
- cron: "0 0 * * 0"
77

88
jobs:
9-
10-
unit-tests:
11-
name: Unit Tests
12-
strategy:
13-
fail-fast: false
14-
matrix:
15-
php-version:
16-
- "7.4"
17-
- "8.0"
18-
runs-on: ubuntu-latest
19-
steps:
20-
- name: Checkout
21-
uses: actions/checkout@v2
22-
23-
- name: Setup PHP
24-
uses: shivammathur/setup-php@v2
25-
with:
26-
php-version: ${{ matrix.php-version }}
27-
coverage: xdebug
28-
29-
- name: Get composer cache directory
30-
id: composer-cache
31-
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
32-
33-
- name: Cache composer dependencies
34-
uses: actions/cache@v2
35-
with:
36-
path: ${{ steps.composer-cache.outputs.dir }}
37-
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
38-
restore-keys: ${{ runner.os }}-composer-
39-
40-
- name: Setup problem matchers
41-
run: |
42-
echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
43-
echo "::add-matcher::.github/problem-matchers/coverage-check.json"
44-
45-
- name: Update dependencies
46-
run: composer update --no-progress --no-interaction --prefer-dist --optimize-autoloader
47-
48-
- name: Run phpunit
49-
run: vendor/bin/phpunit --testsuite unit-test --coverage-clover=coverage.xml
50-
51-
- name: Check coverage.xml existence
52-
id: check-coverage-file
53-
uses: andstor/file-existence-action@v1
54-
with:
55-
files: coverage.xml
56-
57-
- name: Run coverage-check
58-
if: ${{ always() && steps.check-coverage-file.outputs.files_exists == 'true' }}
59-
run: vendor/bin/coverage-check coverage.xml 100
60-
61-
- name: Upload coverage as artifacts
62-
if: ${{ always() && steps.check-coverage-file.outputs.files_exists == 'true' }}
63-
uses: actions/upload-artifact@v2
64-
with:
65-
name: coverage-${{ matrix.php-version }}
66-
path: coverage.xml
67-
68-
- name: Upload coverage to Codecov
69-
if: ${{ always() && steps.check-coverage-file.outputs.files_exists == 'true' }}
70-
uses: codecov/codecov-action@v1
71-
with:
72-
name: coverage-${{ matrix.php-version }}
73-
file: coverage.xml
74-
75-
coding-guidelines:
76-
name: Coding Guidelines
77-
runs-on: ubuntu-latest
78-
steps:
79-
- name: checkout
80-
uses: actions/checkout@v2
81-
82-
- name: Setup PHP
83-
uses: shivammathur/setup-php@v2
84-
with:
85-
php-version: "8.0"
86-
tools: cs2pr
87-
coverage: none
88-
89-
- name: Get composer cache directory
90-
id: composer-cache
91-
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
92-
93-
- name: Cache composer dependencies
94-
uses: actions/cache@v2
95-
with:
96-
path: ${{ steps.composer-cache.outputs.dir }}
97-
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
98-
restore-keys: ${{ runner.os }}-composer-
99-
100-
- name: Update dependencies
101-
run: composer update --no-progress --no-interaction --prefer-dist --optimize-autoloader
102-
103-
- name: Run phpcs
104-
run: vendor/bin/phpcs -q --report=checkstyle | cs2pr
105-
106-
type-checker:
107-
name: Type Checker
108-
runs-on: ubuntu-latest
109-
steps:
110-
- name: Checkout
111-
uses: actions/checkout@v2
112-
113-
- name: Setup PHP
114-
uses: shivammathur/setup-php@v2
115-
with:
116-
php-version: "8.0"
117-
coverage: none
118-
119-
- name: Get composer cache directory
120-
id: composer-cache
121-
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
122-
123-
- name: Cache composer dependencies
124-
uses: actions/cache@v2
125-
with:
126-
path: ${{ steps.composer-cache.outputs.dir }}
127-
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
128-
restore-keys: ${{ runner.os }}-composer-
129-
130-
- name: Update dependencies
131-
run: composer update --no-progress --no-interaction --prefer-dist --optimize-autoloader
132-
133-
- name: Run phpstan
134-
run: vendor/bin/phpstan analyse --no-interaction
9+
call-workflow-ci-php-general:
10+
name: General
11+
uses: BluePsyduck/github-workflows/.github/workflows/ci-php-general.yaml@v1
12+
13+
call-workflow-ci-php-tests:
14+
name: Tests
15+
uses: BluePsyduck/github-workflows/.github/workflows/ci-php-tests.yaml@v1
16+
with:
17+
min-php-version: "8.0"

CHANGELOG.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,24 @@
11
# Changelog
22

3+
## 1.3.0 - 2022-02-22
4+
5+
### Added
6+
7+
- Method `mapList` to the `MapperManager` and the `MapperManagerInterface`, which allows to map a list of sources at
8+
- once.
9+
10+
### Removed
11+
12+
- Support for PHP 7.4. The minimal required version is now PHP 8.0.
13+
314
## 1.2.0 - 2020-01-15
415

516
### Added
617

718
- Support for PHP 8.
819
- Generic doc-blocks to help with static analysers.
9-
- Return value to `MapperManagerInterface->map()`, which now returns the passed in `$destination` back. This allows for shorter code like `return $mapperManager->map($data, new Response());`.
20+
- Return value to `MapperManagerInterface->map()`, which now returns the passed in `$destination` back. This allows for
21+
shorter code like `return $mapperManager->map($data, new Response());`.
1022

1123
### Removed
1224

composer.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@
1313
],
1414
"minimum-stability": "stable",
1515
"require": {
16-
"php": "^7.4 | ^8.0",
16+
"php": "^8.0",
1717
"psr/container": "^1.0"
1818
},
1919
"require-dev": {
2020
"bluepsyduck/test-helper": "^2.0",
21-
"phpstan/phpstan": "^0.12",
22-
"phpstan/phpstan-phpunit": "^0.12",
23-
"phpstan/phpstan-strict-rules": "^0.12",
21+
"phpstan/phpstan": "^1.0",
22+
"phpstan/phpstan-phpunit": "^1.0",
23+
"phpstan/phpstan-strict-rules": "^1.0",
2424
"phpunit/phpunit": "^9.0",
2525
"rregeer/phpunit-coverage-check": "^0.1",
2626
"squizlabs/php_codesniffer": "^3.3"

src/MapperManager.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace BluePsyduck\MapperManager;
66

7+
use BluePsyduck\MapperManager\Exception\MapperException;
78
use BluePsyduck\MapperManager\Exception\MissingAdapterException;
89
use BluePsyduck\MapperManager\Exception\MissingMapperException;
910
use BluePsyduck\MapperManager\Adapter\MapperAdapterInterface;
@@ -30,6 +31,9 @@ public function addAdapter(MapperAdapterInterface $adapter): void
3031
$this->adapters[$adapter->getHandledMapperInterface()] = $adapter;
3132
}
3233

34+
/**
35+
* @throws MapperException
36+
*/
3337
public function addMapper(MapperInterface $mapper): void
3438
{
3539
$this->injectMapperManager($mapper);
@@ -43,6 +47,9 @@ public function addMapper(MapperInterface $mapper): void
4347
throw new MissingAdapterException(get_class($mapper));
4448
}
4549

50+
/**
51+
* @throws MapperException
52+
*/
4653
public function map(object $source, object $destination): object
4754
{
4855
foreach ($this->adapters as $adapter) {
@@ -55,6 +62,22 @@ public function map(object $source, object $destination): object
5562
throw new MissingMapperException(get_class($source), get_class($destination));
5663
}
5764

65+
/**
66+
* @throws MapperException
67+
*/
68+
public function mapList(iterable $sources, callable|string $destinationCreator): array
69+
{
70+
if (is_string($destinationCreator)) {
71+
$destinationCreator = fn() => new $destinationCreator();
72+
}
73+
74+
$result = [];
75+
foreach ($sources as $key => $source) {
76+
$result[$key] = $this->map($source, $destinationCreator());
77+
}
78+
return $result;
79+
}
80+
5881
private function injectMapperManager(object $object): void
5982
{
6083
if ($object instanceof MapperManagerAwareInterface) {

src/MapperManagerFactory.php

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use BluePsyduck\MapperManager\Constant\ConfigKey;
99
use BluePsyduck\MapperManager\Exception\MapperException;
1010
use BluePsyduck\MapperManager\Mapper\MapperInterface;
11+
use Psr\Container\ContainerExceptionInterface;
1112
use Psr\Container\ContainerInterface;
1213

1314
/**
@@ -25,12 +26,14 @@ class MapperManagerFactory
2526
* @param array<mixed>|null $options
2627
* @return MapperManagerInterface
2728
* @throws MapperException
29+
* @throws ContainerExceptionInterface
2830
*/
2931
public function __invoke(
3032
ContainerInterface $container,
3133
string $requestedName,
3234
?array $options = null
3335
): MapperManagerInterface {
36+
/** @var array<mixed> $config */
3437
$config = $container->get('config');
3538

3639
$manager = new MapperManager();
@@ -44,13 +47,16 @@ public function __invoke(
4447
* @param MapperManager $manager
4548
* @param ContainerInterface $container
4649
* @param array<mixed> $config
50+
* @throws ContainerExceptionInterface
4751
*/
4852
protected function addAdaptersToManager(
4953
MapperManager $manager,
5054
ContainerInterface $container,
5155
array $config
5256
): void {
53-
foreach (array_unique($config[ConfigKey::MAIN][ConfigKey::ADAPTERS] ?? []) as $alias) {
57+
/** @var array<class-string> $aliases */
58+
$aliases = array_unique($config[ConfigKey::MAIN][ConfigKey::ADAPTERS] ?? []); // @phpstan-ignore-line
59+
foreach ($aliases as $alias) {
5460
/** @var MapperAdapterInterface<MapperInterface<object, object>> $adapter */
5561
$adapter = $container->get($alias);
5662
$manager->addAdapter($adapter);
@@ -63,14 +69,19 @@ protected function addAdaptersToManager(
6369
* @param ContainerInterface $container
6470
* @param array<mixed> $config
6571
* @throws MapperException
72+
* @throws ContainerExceptionInterface
6673
*/
6774
protected function addMappersToManager(
6875
MapperManager $manager,
6976
ContainerInterface $container,
7077
array $config
7178
): void {
72-
foreach (array_unique($config[ConfigKey::MAIN][ConfigKey::MAPPERS] ?? []) as $alias) {
73-
$manager->addMapper($container->get($alias));
79+
/** @var array<class-string> $aliases */
80+
$aliases = array_unique($config[ConfigKey::MAIN][ConfigKey::MAPPERS] ?? []); // @phpstan-ignore-line
81+
foreach ($aliases as $alias) {
82+
/** @var MapperInterface<object, object> $mapper */
83+
$mapper = $container->get($alias);
84+
$manager->addMapper($mapper);
7485
}
7586
}
7687
}

src/MapperManagerInterface.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,14 @@ public function addMapper(MapperInterface $mapper): void;
3131
* @return TDest
3232
*/
3333
public function map(object $source, object $destination): object;
34+
35+
/**
36+
* Maps a list of source objects to destination ones.
37+
* @template TSrc of object
38+
* @template TDest of object
39+
* @param iterable<TSrc> $sources
40+
* @param class-string<TDest>|callable(): TDest $destinationCreator
41+
* @return array<TDest>
42+
*/
43+
public function mapList(iterable $sources, string|callable $destinationCreator): array;
3444
}

test/src/MapperManagerTest.php

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,71 @@ public function testMapWithoutMatchingMapper(): void
164164
$manager->map($source, $destination);
165165
}
166166

167+
/**
168+
* Tests the mapList method with a class name as creator.
169+
* @throws MapperException
170+
* @throws ReflectionException
171+
* @covers ::mapList
172+
*/
173+
public function testMapListWithClassName(): void
174+
{
175+
$source1 = new stdClass();
176+
$source2 = new stdClass();
177+
178+
$adapter = $this->createMock(MapperAdapterInterface::class);
179+
$adapter->expects($this->exactly(2))
180+
->method('map')
181+
->withConsecutive(
182+
[$this->identicalTo($source1), $this->isInstanceOf(stdClass::class)],
183+
[$this->identicalTo($source2), $this->isInstanceOf(stdClass::class)],
184+
)
185+
->willReturn(true);
186+
187+
$manager = new MapperManager();
188+
$this->injectProperty($manager, 'adapters', [$adapter]);
189+
190+
$result = $manager->mapList([$source1, $source2], stdClass::class);
191+
192+
$this->assertEquals([new stdClass(), new stdClass()], $result);
193+
}
194+
195+
/**
196+
* Tests the mapList method with a callback as creator.
197+
* @throws MapperException
198+
* @throws ReflectionException
199+
* @covers ::mapList
200+
*/
201+
public function testMapListWithCallback(): void
202+
{
203+
$source1 = new stdClass();
204+
$source2 = new stdClass();
205+
$destination1 = new stdClass();
206+
$destination1->foo = 'bar';
207+
$destination2 = new stdClass();
208+
$destination2->foo = 'baz';
209+
210+
$destinations = [$destination1, $destination2];
211+
$creator = function () use (&$destinations): stdClass {
212+
return array_shift($destinations);
213+
};
214+
215+
$adapter = $this->createMock(MapperAdapterInterface::class);
216+
$adapter->expects($this->exactly(2))
217+
->method('map')
218+
->withConsecutive(
219+
[$this->identicalTo($source1), $this->identicalTo($destination1)],
220+
[$this->identicalTo($source2), $this->identicalTo($destination2)],
221+
)
222+
->willReturn(true);
223+
224+
$manager = new MapperManager();
225+
$this->injectProperty($manager, 'adapters', [$adapter]);
226+
227+
$result = $manager->mapList([$source1, $source2], $creator);
228+
229+
$this->assertEquals([$destination1, $destination2], $result);
230+
}
231+
167232
/**
168233
* Tests the injectMapperManager method with implementing the interface.
169234
* @throws ReflectionException

0 commit comments

Comments
 (0)