Skip to content

Commit d8410b2

Browse files
authored
Merge pull request #3 from KaririCode-Framework/develop
feat(transformer): implement data transformation system and test cove…
2 parents a87b2ba + a82feeb commit d8410b2

30 files changed

+2626
-169
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,5 +63,5 @@ tests/lista_de_arquivos.php
6363
tests/lista_de_arquivos_test.php
6464
lista_de_arquivos.txt
6565
lista_de_arquivos_tests.txt
66-
add_static_to_providers.php
66+
test_files_generate.php
6767
/composer.lock
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace KaririCode\Transformer\Exception;
6+
7+
use KaririCode\Exception\AbstractException;
8+
9+
final class DateTransformerException extends AbstractException
10+
{
11+
private const CODE_INVALID_TIMEZONE = 5101;
12+
private const CODE_INVALID_FORMAT = 5102;
13+
private const CODE_INVALID_DATE = 5103;
14+
15+
public static function invalidTimezone(string $timezone): self
16+
{
17+
return self::createException(
18+
self::CODE_INVALID_TIMEZONE,
19+
'INVALID_TIMEZONE',
20+
"Invalid timezone: {$timezone}"
21+
);
22+
}
23+
24+
public static function invalidFormat(string $format, string $value): self
25+
{
26+
return self::createException(
27+
self::CODE_INVALID_FORMAT,
28+
'INVALID_FORMAT',
29+
"Invalid date format. Expected {$format}, got '{$value}'"
30+
);
31+
}
32+
33+
public static function invalidDate(string $date): self
34+
{
35+
return self::createException(
36+
self::CODE_INVALID_DATE,
37+
'INVALID_DATE',
38+
"Invalid date: {$date}"
39+
);
40+
}
41+
}

src/Processor/Array/ArrayFlattenTransformer.php

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,9 @@
66

77
use KaririCode\Contract\Processor\ConfigurableProcessor;
88
use KaririCode\Transformer\Processor\AbstractTransformerProcessor;
9-
use KaririCode\Transformer\Trait\ArrayTransformerTrait;
109

1110
class ArrayFlattenTransformer extends AbstractTransformerProcessor implements ConfigurableProcessor
1211
{
13-
use ArrayTransformerTrait;
14-
1512
private int $depth = -1;
1613
private string $separator = '.';
1714

@@ -40,10 +37,7 @@ private function flattenArray(array $array, string $prefix = '', int $depth = -1
4037
$newKey = $prefix ? $prefix . $this->separator . $key : $key;
4138

4239
if (is_array($value) && ($depth > 0 || -1 === $depth)) {
43-
$result = array_merge(
44-
$result,
45-
$this->flattenArray($value, $newKey, $depth > 0 ? $depth - 1 : -1)
46-
);
40+
$result += $this->flattenArray($value, $newKey, $depth > 0 ? $depth - 1 : -1);
4741
} else {
4842
$result[$newKey] = $value;
4943
}

src/Processor/Array/ArrayGroupTransformer.php

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

77
use KaririCode\Contract\Processor\ConfigurableProcessor;
88
use KaririCode\Transformer\Processor\AbstractTransformerProcessor;
9-
use KaririCode\Transformer\Trait\ArrayTransformerTrait;
109

1110
class ArrayGroupTransformer extends AbstractTransformerProcessor implements ConfigurableProcessor
1211
{
13-
use ArrayTransformerTrait;
14-
1512
private string $groupBy = '';
1613
private bool $preserveKeys = false;
1714

src/Processor/Array/ArrayKeyTransformer.php

Lines changed: 9 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,7 @@ class ArrayKeyTransformer extends AbstractTransformerProcessor implements Config
1212
{
1313
use ArrayTransformerTrait;
1414

15-
private const CASE_SNAKE = 'snake';
16-
private const CASE_CAMEL = 'camel';
17-
private const CASE_PASCAL = 'pascal';
18-
private const CASE_KEBAB = 'kebab';
19-
20-
private string $case = self::CASE_SNAKE;
15+
private string $case = 'snake'; // Valores possíveis: snake, camel, pascal, kebab
2116
private bool $recursive = true;
2217

2318
public function configure(array $options): void
@@ -37,44 +32,26 @@ public function process(mixed $input): array
3732
return [];
3833
}
3934

40-
return $this->transformArrayKeys($input);
35+
// Transforma as chaves apenas no nível principal se recursive for false
36+
return $this->recursive
37+
? $this->transformArrayKeys($input, $this->case)
38+
: $this->transformKeysNonRecursive($input, $this->case);
4139
}
4240

43-
private function transformArrayKeys(array $array): array
41+
private function transformKeysNonRecursive(array $array, string $case): array
4442
{
4543
$result = [];
4644

4745
foreach ($array as $key => $value) {
48-
$transformedKey = $this->transformKey((string) $key);
49-
50-
if (is_array($value) && $this->recursive) {
51-
$result[$transformedKey] = $this->transformArrayKeys($value);
52-
} else {
53-
$result[$transformedKey] = $value;
54-
}
46+
$transformedKey = $this->transformKeyByCase((string) $key, $case);
47+
$result[$transformedKey] = $value; // Mantém o valor original, sem recursão
5548
}
5649

5750
return $result;
5851
}
5952

60-
private function transformKey(string $key): string
61-
{
62-
return match ($this->case) {
63-
self::CASE_SNAKE => $this->toSnakeCase($key),
64-
self::CASE_CAMEL => $this->toCamelCase($key),
65-
self::CASE_PASCAL => $this->toPascalCase($key),
66-
self::CASE_KEBAB => $this->toKebabCase($key),
67-
default => $key,
68-
};
69-
}
70-
7153
private function getAllowedCases(): array
7254
{
73-
return [
74-
self::CASE_SNAKE,
75-
self::CASE_CAMEL,
76-
self::CASE_PASCAL,
77-
self::CASE_KEBAB,
78-
];
55+
return ['snake', 'camel', 'pascal', 'kebab'];
7956
}
8057
}

src/Processor/Array/ArrayMapTransformer.php

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ class ArrayMapTransformer extends AbstractTransformerProcessor implements Config
1515
private array $mapping = [];
1616
private bool $removeUnmapped = false;
1717
private bool $recursive = true;
18+
private ?string $case = null;
1819

1920
public function configure(array $options): void
2021
{
@@ -25,6 +26,7 @@ public function configure(array $options): void
2526
$this->mapping = $options['mapping'];
2627
$this->removeUnmapped = $options['removeUnmapped'] ?? $this->removeUnmapped;
2728
$this->recursive = $options['recursive'] ?? $this->recursive;
29+
$this->case = $options['case'] ?? null; // Opcional
2830
}
2931

3032
public function process(mixed $input): array
@@ -35,26 +37,23 @@ public function process(mixed $input): array
3537
return [];
3638
}
3739

38-
return $this->mapArray($input);
40+
$mappedArray = $this->mapArray($input);
41+
42+
return $this->case ? $this->transformArrayKeys($mappedArray, $this->case) : $mappedArray;
3943
}
4044

4145
private function mapArray(array $array): array
4246
{
4347
$result = [];
4448

4549
foreach ($array as $key => $value) {
46-
if (is_array($value) && $this->recursive) {
47-
$result[$key] = $this->mapArray($value);
48-
continue;
49-
}
50-
5150
$mappedKey = $this->mapping[$key] ?? $key;
5251

5352
if ($this->removeUnmapped && !isset($this->mapping[$key])) {
5453
continue;
5554
}
5655

57-
$result[$mappedKey] = $value;
56+
$result[$mappedKey] = is_array($value) && $this->recursive ? $this->mapArray($value) : $value;
5857
}
5958

6059
return $result;

src/Processor/Data/DateTransformer.php

Lines changed: 66 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,52 +5,90 @@
55
namespace KaririCode\Transformer\Processor\Data;
66

77
use KaririCode\Contract\Processor\ConfigurableProcessor;
8+
use KaririCode\Transformer\Exception\DateTransformerException;
89
use KaririCode\Transformer\Processor\AbstractTransformerProcessor;
910

10-
class DateTransformer extends AbstractTransformerProcessor implements ConfigurableProcessor
11+
final class DateTransformer extends AbstractTransformerProcessor implements ConfigurableProcessor
1112
{
12-
private string $inputFormat = 'Y-m-d';
13-
private string $outputFormat = 'Y-m-d';
14-
private ?string $inputTimezone = null;
15-
private ?string $outputTimezone = null;
13+
private const DEFAULT_FORMAT = 'Y-m-d';
14+
private const ERROR_INVALID_STRING = 'notString';
15+
private const ERROR_INVALID_DATE = 'invalidDate';
16+
17+
private string $inputFormat = self::DEFAULT_FORMAT;
18+
private string $outputFormat = self::DEFAULT_FORMAT;
19+
private ?\DateTimeZone $inputTimezone = null;
20+
private ?\DateTimeZone $outputTimezone = null;
1621

1722
public function configure(array $options): void
1823
{
19-
$this->inputFormat = $options['inputFormat'] ?? $this->inputFormat;
20-
$this->outputFormat = $options['outputFormat'] ?? $this->outputFormat;
21-
$this->inputTimezone = $options['inputTimezone'] ?? $this->inputTimezone;
22-
$this->outputTimezone = $options['outputTimezone'] ?? $this->outputTimezone;
24+
$this->configureFormats($options);
25+
$this->configureTimezones($options);
2326
}
2427

2528
public function process(mixed $input): string
2629
{
27-
if (!is_string($input)) {
28-
$this->setInvalid('notString');
29-
30+
if (!$this->isValidInput($input)) {
3031
return '';
3132
}
3233

3334
try {
34-
$date = $this->createDateTime($input);
35-
36-
return $this->formatDate($date);
37-
} catch (\Exception $e) {
38-
$this->setInvalid('invalidDate');
35+
return $this->transformDate($input);
36+
} catch (DateTransformerException) {
37+
$this->setInvalid(self::ERROR_INVALID_DATE);
3938

4039
return '';
4140
}
4241
}
4342

44-
private function createDateTime(string $input): \DateTime
43+
private function configureFormats(array $options): void
44+
{
45+
$this->inputFormat = $options['inputFormat'] ?? self::DEFAULT_FORMAT;
46+
$this->outputFormat = $options['outputFormat'] ?? self::DEFAULT_FORMAT;
47+
}
48+
49+
private function configureTimezones(array $options): void
4550
{
46-
$date = \DateTime::createFromFormat($this->inputFormat, $input);
51+
$this->inputTimezone = $this->createTimezone($options['inputTimezone'] ?? null);
52+
$this->outputTimezone = $this->createTimezone($options['outputTimezone'] ?? null);
53+
}
54+
55+
private function createTimezone(?string $timezone): ?\DateTimeZone
56+
{
57+
if (!$timezone) {
58+
return null;
59+
}
60+
61+
try {
62+
return new \DateTimeZone($timezone);
63+
} catch (\Exception) {
64+
throw DateTransformerException::invalidTimezone($timezone);
65+
}
66+
}
4767

48-
if (false === $date) {
49-
throw new \RuntimeException('Invalid date format');
68+
private function isValidInput(mixed $input): bool
69+
{
70+
if (is_string($input)) {
71+
return true;
5072
}
5173

52-
if ($this->inputTimezone) {
53-
$date->setTimezone(new \DateTimeZone($this->inputTimezone));
74+
$this->setInvalid(self::ERROR_INVALID_STRING);
75+
76+
return false;
77+
}
78+
79+
private function transformDate(string $input): string
80+
{
81+
$date = $this->createDateTime($input);
82+
83+
return $this->formatDate($date);
84+
}
85+
86+
private function createDateTime(string $input): \DateTime
87+
{
88+
$date = \DateTime::createFromFormat($this->inputFormat, $input, $this->inputTimezone);
89+
90+
if (!$date) {
91+
throw DateTransformerException::invalidFormat($this->inputFormat, $input);
5492
}
5593

5694
return $date;
@@ -59,7 +97,11 @@ private function createDateTime(string $input): \DateTime
5997
private function formatDate(\DateTime $date): string
6098
{
6199
if ($this->outputTimezone) {
62-
$date->setTimezone(new \DateTimeZone($this->outputTimezone));
100+
try {
101+
$date->setTimezone($this->outputTimezone);
102+
} catch (\Exception) {
103+
throw DateTransformerException::invalidDate($date->format('Y-m-d H:i:s'));
104+
}
63105
}
64106

65107
return $date->format($this->outputFormat);

0 commit comments

Comments
 (0)