Skip to content

Commit 36570cf

Browse files
author
awu
committed
feat: Add CSV support to DataMapper and update documentation
1 parent be7939e commit 36570cf

File tree

4 files changed

+81
-32
lines changed

4 files changed

+81
-32
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
A Symfony integration for [wundii/data-mapper](https://github.com/wundii/data-mapper).
2020
This library is an extremely fast and strictly typed object mapper built for modern PHP (8.2+).
21-
It seamlessly transforms data from formats like JSON, NEON, XML, YAML, arrays, and standard objects into well-structured PHP objects.
21+
It seamlessly transforms data from formats like CSV, JSON, NEON, XML, YAML, array, and standard objects into well-structured PHP objects.
2222

2323
Ideal for developers who need reliable and efficient data mapping without sacrificing code quality or modern best practices.
2424

@@ -49,6 +49,7 @@ Ideal for developers who need reliable and efficient data mapping without sacrif
4949
## Supported Formats
5050
optional formats are marked with an asterisk `*`
5151
- `array`
52+
- `csv`
5253
- `json`
5354
- `neon`*
5455
- `object`

composer.json

Lines changed: 12 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"name": "wundii/data-mapper-symfony-bundle",
33
"type": "symfony-bundle",
4-
"description": "A Symfony integration for wundii/data-mapper, modern and fast object mapper for PHP 8.2+. Strict types. Converts JSON, XML, YAML, NEON, arrays, and objects to PHP objects.",
5-
"keywords": ["symfony","symfony-bundle","bundle","datamapper","mapper","xml","json","array","object", "deserialization", "deserialize"],
4+
"description": "A Symfony integration for wundii/data-mapper, modern and fast object mapper for PHP 8.2+. Strict types. Converts CSV, JSON, XML, YAML, NEON, arrays, and objects to PHP objects.",
5+
"keywords": ["symfony","symfony-bundle","bundle","datamapper","mapper","xml","json","csv","array","object", "deserialization", "deserialize"],
66
"license": "MIT",
77
"authors": [
88
{
@@ -64,33 +64,15 @@
6464
"@ecs-dry",
6565
"@unittest"
6666
],
67-
"rector-dry": [
68-
"php vendor/bin/rector process --dry-run --ansi"
69-
],
70-
"rector-apply": [
71-
"php vendor/bin/rector process --ansi"
72-
],
73-
"ecs-dry": [
74-
"php vendor/bin/ecs check"
75-
],
76-
"ecs-apply": [
77-
"php vendor/bin/ecs check --fix"
78-
],
79-
"phpstan": [
80-
"php vendor/bin/phpstan analyse"
81-
],
82-
"unittest": [
83-
"php vendor/bin/phpunit --configuration phpunit.xml"
84-
],
85-
"cache-clear": [
86-
"rm -R ./Cache/* -f"
87-
],
88-
"phplint": [
89-
"php vendor/bin/phplint"
90-
],
91-
"coverage": [
92-
"php vendor/bin/phpunit --coverage-clover coverage.xml"
93-
]
67+
"cache-clear": "rm -R ./cache/* -f",
68+
"coverage": "php vendor/bin/phpunit --coverage-clover coverage.xml",
69+
"ecs-apply": "php vendor/bin/ecs check --fix",
70+
"ecs-dry": "php vendor/bin/ecs check",
71+
"phplint": "php vendor/bin/phplint",
72+
"phpstan": "php vendor/bin/phpstan analyse",
73+
"rector-apply": "php vendor/bin/rector process --ansi",
74+
"rector-dry": "php vendor/bin/rector process --dry-run --ansi",
75+
"unittest": "php vendor/bin/phpunit --configuration phpunit.xml"
9476
},
95-
"minimum-stability": "dev"
77+
"minimum-stability": "stable"
9678
}

src/DataMapper.php

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Exception;
88
use Symfony\Component\HttpFoundation\Request;
99
use Wundii\DataMapper\DataMapper as BaseDataMapper;
10+
use Wundii\DataMapper\Dto\CsvDto;
1011
use Wundii\DataMapper\Enum\SourceTypeEnum;
1112
use Wundii\DataMapper\Exception\DataMapperException;
1213
use Wundii\DataMapper\SymfonyBundle\Enum\MapStatusEnum;
@@ -35,16 +36,19 @@ public function getMapStatusEnum(): MapStatusEnum
3536
* @param class-string<T>|T $object
3637
* @param string[] $rootElementTree
3738
* @param bool $forceInstance // create a new instance, if no data can be found for the object
39+
* @param string[] $options
3840
* @return ($object is class-string ? T : T[])
3941
*/
4042
public function request(
4143
Request $request,
4244
string|object $object,
4345
array $rootElementTree = [],
4446
bool $forceInstance = false,
47+
array $options = [],
4548
): object|array {
4649
$content = $request->getContent();
4750
$sourceTypeEnum = match ($request->headers->get('Content-Type')) {
51+
'application/csv', 'text/csv' => SourceTypeEnum::CSV,
4852
'application/json' => SourceTypeEnum::JSON,
4953
'application/neon', 'text/neon' => SourceTypeEnum::NEON,
5054
'application/xml', 'text/xml' => SourceTypeEnum::XML,
@@ -56,20 +60,34 @@ public function request(
5660
throw DataMapperException::InvalidArgument('No content provided in request');
5761
}
5862

63+
if ($sourceTypeEnum === SourceTypeEnum::CSV) {
64+
$csvDto = new CsvDto(
65+
$content,
66+
$options['separator'] ?? CsvDto::DEFAULT_SEPARATOR,
67+
$options['enclosure'] ?? CsvDto::DEFAULT_ENCLOSURE,
68+
$options['escape'] ?? CsvDto::DEFAULT_ESCAPE,
69+
(int) ($options['headerLine'] ?? CsvDto::DEFAULT_HEADER_LINE),
70+
(int) ($options['firstLine'] ?? CsvDto::DEFAULT_FIRST_LINE),
71+
);
72+
$content = $csvDto;
73+
}
74+
5975
return $this->map($sourceTypeEnum, $content, $object, $rootElementTree, $forceInstance);
6076
}
6177

6278
/**
6379
* @param class-string<T>|T $object
6480
* @param string[] $rootElementTree
6581
* @param bool $forceInstance // create a new instance, if no data can be found for the object
82+
* @param string[] $options
6683
* @return ($object is class-string ? T : T[])
6784
*/
6885
public function tryRequest(
6986
Request $request,
7087
string|object $object,
7188
array $rootElementTree = [],
7289
bool $forceInstance = false,
90+
array $options = [],
7391
): object|array|null {
7492
/**
7593
* reset error message
@@ -78,7 +96,7 @@ public function tryRequest(
7896

7997
try {
8098
$this->mapStatusEnum = MapStatusEnum::SUCCESS;
81-
return $this->request($request, $object, $rootElementTree, $forceInstance);
99+
return $this->request($request, $object, $rootElementTree, $forceInstance, $options);
82100
} catch (Exception $exception) {
83101
$this->errorMessage = $exception->getMessage();
84102
$this->mapStatusEnum = MapStatusEnum::ERROR;

tests/DataMapperTest.php

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,4 +262,52 @@ public function testRequestWithEmptyContentTypeYamlAndForceInstance(): void
262262
$this->assertEquals(MapStatusEnum::ERROR, $this->dataMapper->getMapStatusEnum());
263263
$this->assertSame('Invalid Yaml decode return', $this->dataMapper->getErrorMessage());
264264
}
265+
266+
/**
267+
* @throws Exception
268+
*/
269+
public function testRequestWithValidContentTypeCsv(): void
270+
{
271+
$content = "string\ntest\n";
272+
$object = TypeString::class;
273+
274+
$request = $this->getRequest('application/csv', $content);
275+
$result = $this->dataMapper->tryRequest($request, $object);
276+
277+
$this->assertNull($this->dataMapper->getErrorMessage());
278+
$this->assertEquals(MapStatusEnum::SUCCESS, $this->dataMapper->getMapStatusEnum());
279+
$this->assertEquals([$this->expectedObject()], $result);
280+
}
281+
282+
/**
283+
* @throws Exception
284+
*/
285+
public function testRequestWithEmptyContentTypeCsv(): void
286+
{
287+
$content = '';
288+
$object = TypeString::class;
289+
290+
$request = $this->getRequest('application/csv', $content);
291+
$result = $this->dataMapper->tryRequest($request, $object);
292+
293+
$this->assertNull($result);
294+
$this->assertEquals(MapStatusEnum::ERROR, $this->dataMapper->getMapStatusEnum());
295+
$this->assertSame('No content provided in request', $this->dataMapper->getErrorMessage());
296+
}
297+
298+
/**
299+
* @throws Exception
300+
*/
301+
public function testRequestWithEmptyContentTypeCsvAndForceInstance(): void
302+
{
303+
$content = '';
304+
$object = TypeString::class;
305+
306+
$request = $this->getRequest('application/csv', $content);
307+
$result = $this->dataMapper->tryRequest($request, $object, [], true);
308+
309+
$this->assertNull($result);
310+
$this->assertEquals(MapStatusEnum::ERROR, $this->dataMapper->getMapStatusEnum());
311+
$this->assertStringContainsString('The file "" could not be written', $this->dataMapper->getErrorMessage());
312+
}
265313
}

0 commit comments

Comments
 (0)