Skip to content

Commit 2747ecb

Browse files
authored
Merge pull request #1 from KaririCode-Framework/develop
[Release] KaririCode Transformer v1.0.0 🚀
2 parents a3e2bdb + f708f8f commit 2747ecb

24 files changed

+2917
-92
lines changed

README.md

+868-44
Large diffs are not rendered by default.

README.pt-br.md

+545-48
Large diffs are not rendered by default.

src/Attribute/Transform.php

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace KaririCode\Transformer\Attribute;
6+
7+
use KaririCode\Contract\Processor\Attribute\BaseProcessorAttribute;
8+
9+
#[\Attribute(\Attribute::TARGET_PROPERTY)]
10+
final class Transform extends BaseProcessorAttribute
11+
{
12+
}

src/Contract/TransformationResult.php

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace KaririCode\Transformer\Contract;
6+
7+
interface TransformationResult
8+
{
9+
public function isValid(): bool;
10+
11+
public function getErrors(): array;
12+
13+
public function getTransformedData(): array;
14+
15+
public function toArray(): array;
16+
}
+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace KaririCode\Transformer\Exception;
6+
7+
use KaririCode\Exception\AbstractException;
8+
9+
final class TransformerException extends AbstractException
10+
{
11+
private const CODE_INVALID_INPUT = 5001;
12+
private const CODE_INVALID_FORMAT = 5002;
13+
private const CODE_INVALID_TYPE = 5003;
14+
15+
public static function invalidInput(string $expectedType, string $actualType): self
16+
{
17+
$message = sprintf(
18+
'Invalid input type. Expected %s, got %s.',
19+
$expectedType,
20+
$actualType
21+
);
22+
23+
return self::createException(
24+
self::CODE_INVALID_INPUT,
25+
'INVALID_INPUT_TYPE',
26+
$message
27+
);
28+
}
29+
30+
public static function invalidFormat(string $format, string $value): self
31+
{
32+
$message = sprintf(
33+
'Invalid format. Expected format %s, got %s.',
34+
$format,
35+
$value
36+
);
37+
38+
return self::createException(
39+
self::CODE_INVALID_FORMAT,
40+
'INVALID_FORMAT',
41+
$message
42+
);
43+
}
44+
45+
public static function invalidType(string $expectedType): self
46+
{
47+
$message = sprintf(
48+
'Invalid type. Expected %s.',
49+
$expectedType
50+
);
51+
52+
return self::createException(
53+
self::CODE_INVALID_TYPE,
54+
'INVALID_TYPE',
55+
$message
56+
);
57+
}
58+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace KaririCode\Transformer\Processor;
6+
7+
use KaririCode\Contract\Processor\Processor;
8+
use KaririCode\Contract\Processor\ValidatableProcessor;
9+
use KaririCode\Transformer\Exception\TransformerException;
10+
11+
abstract class AbstractTransformerProcessor implements Processor, ValidatableProcessor
12+
{
13+
protected bool $isValid = true;
14+
protected string $errorKey = '';
15+
16+
public function reset(): void
17+
{
18+
$this->isValid = true;
19+
$this->errorKey = '';
20+
}
21+
22+
protected function setInvalid(string $errorKey): void
23+
{
24+
$this->isValid = false;
25+
$this->errorKey = $errorKey;
26+
}
27+
28+
public function isValid(): bool
29+
{
30+
return $this->isValid;
31+
}
32+
33+
public function getErrorKey(): string
34+
{
35+
return $this->errorKey;
36+
}
37+
38+
protected function guardAgainstInvalidType(mixed $input, string $type): void
39+
{
40+
$actualType = get_debug_type($input);
41+
if ($actualType !== $type) {
42+
throw TransformerException::invalidType($type);
43+
}
44+
}
45+
46+
abstract public function process(mixed $input): mixed;
47+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace KaririCode\Transformer\Processor\Array;
6+
7+
use KaririCode\Contract\Processor\ConfigurableProcessor;
8+
use KaririCode\Transformer\Processor\AbstractTransformerProcessor;
9+
use KaririCode\Transformer\Trait\ArrayTransformerTrait;
10+
11+
class ArrayFlattenTransformer extends AbstractTransformerProcessor implements ConfigurableProcessor
12+
{
13+
use ArrayTransformerTrait;
14+
15+
private int $depth = -1;
16+
private string $separator = '.';
17+
18+
public function configure(array $options): void
19+
{
20+
$this->depth = $options['depth'] ?? $this->depth;
21+
$this->separator = $options['separator'] ?? $this->separator;
22+
}
23+
24+
public function process(mixed $input): array
25+
{
26+
if (!is_array($input)) {
27+
$this->setInvalid('notArray');
28+
29+
return [];
30+
}
31+
32+
return $this->flattenArray($input, '', $this->depth);
33+
}
34+
35+
private function flattenArray(array $array, string $prefix = '', int $depth = -1): array
36+
{
37+
$result = [];
38+
39+
foreach ($array as $key => $value) {
40+
$newKey = $prefix ? $prefix . $this->separator . $key : $key;
41+
42+
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+
);
47+
} else {
48+
$result[$newKey] = $value;
49+
}
50+
}
51+
52+
return $result;
53+
}
54+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace KaririCode\Transformer\Processor\Array;
6+
7+
use KaririCode\Contract\Processor\ConfigurableProcessor;
8+
use KaririCode\Transformer\Processor\AbstractTransformerProcessor;
9+
use KaririCode\Transformer\Trait\ArrayTransformerTrait;
10+
11+
class ArrayGroupTransformer extends AbstractTransformerProcessor implements ConfigurableProcessor
12+
{
13+
use ArrayTransformerTrait;
14+
15+
private string $groupBy = '';
16+
private bool $preserveKeys = false;
17+
18+
public function configure(array $options): void
19+
{
20+
if (!isset($options['groupBy'])) {
21+
throw new \InvalidArgumentException('The groupBy option is required');
22+
}
23+
24+
$this->groupBy = $options['groupBy'];
25+
$this->preserveKeys = $options['preserveKeys'] ?? $this->preserveKeys;
26+
}
27+
28+
public function process(mixed $input): array
29+
{
30+
if (!is_array($input)) {
31+
$this->setInvalid('notArray');
32+
33+
return [];
34+
}
35+
36+
return $this->groupArray($input);
37+
}
38+
39+
private function groupArray(array $array): array
40+
{
41+
$result = [];
42+
43+
foreach ($array as $key => $item) {
44+
if (!is_array($item)) {
45+
continue;
46+
}
47+
48+
$groupValue = $item[$this->groupBy] ?? null;
49+
if (null === $groupValue) {
50+
continue;
51+
}
52+
53+
if ($this->preserveKeys) {
54+
$result[$groupValue][$key] = $item;
55+
} else {
56+
$result[$groupValue][] = $item;
57+
}
58+
}
59+
60+
return $result;
61+
}
62+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace KaririCode\Transformer\Processor\Array;
6+
7+
use KaririCode\Contract\Processor\ConfigurableProcessor;
8+
use KaririCode\Transformer\Processor\AbstractTransformerProcessor;
9+
use KaririCode\Transformer\Trait\ArrayTransformerTrait;
10+
11+
class ArrayKeyTransformer extends AbstractTransformerProcessor implements ConfigurableProcessor
12+
{
13+
use ArrayTransformerTrait;
14+
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;
21+
private bool $recursive = true;
22+
23+
public function configure(array $options): void
24+
{
25+
if (isset($options['case']) && in_array($options['case'], $this->getAllowedCases(), true)) {
26+
$this->case = $options['case'];
27+
}
28+
29+
$this->recursive = $options['recursive'] ?? $this->recursive;
30+
}
31+
32+
public function process(mixed $input): array
33+
{
34+
if (!is_array($input)) {
35+
$this->setInvalid('notArray');
36+
37+
return [];
38+
}
39+
40+
return $this->transformArrayKeys($input);
41+
}
42+
43+
private function transformArrayKeys(array $array): array
44+
{
45+
$result = [];
46+
47+
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+
}
55+
}
56+
57+
return $result;
58+
}
59+
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+
71+
private function getAllowedCases(): array
72+
{
73+
return [
74+
self::CASE_SNAKE,
75+
self::CASE_CAMEL,
76+
self::CASE_PASCAL,
77+
self::CASE_KEBAB,
78+
];
79+
}
80+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace KaririCode\Transformer\Processor\Array;
6+
7+
use KaririCode\Contract\Processor\ConfigurableProcessor;
8+
use KaririCode\Transformer\Processor\AbstractTransformerProcessor;
9+
use KaririCode\Transformer\Trait\ArrayTransformerTrait;
10+
11+
class ArrayMapTransformer extends AbstractTransformerProcessor implements ConfigurableProcessor
12+
{
13+
use ArrayTransformerTrait;
14+
15+
private array $mapping = [];
16+
private bool $removeUnmapped = false;
17+
private bool $recursive = true;
18+
19+
public function configure(array $options): void
20+
{
21+
if (!isset($options['mapping']) || !is_array($options['mapping'])) {
22+
throw new \InvalidArgumentException('The mapping option is required and must be an array');
23+
}
24+
25+
$this->mapping = $options['mapping'];
26+
$this->removeUnmapped = $options['removeUnmapped'] ?? $this->removeUnmapped;
27+
$this->recursive = $options['recursive'] ?? $this->recursive;
28+
}
29+
30+
public function process(mixed $input): array
31+
{
32+
if (!is_array($input)) {
33+
$this->setInvalid('notArray');
34+
35+
return [];
36+
}
37+
38+
return $this->mapArray($input);
39+
}
40+
41+
private function mapArray(array $array): array
42+
{
43+
$result = [];
44+
45+
foreach ($array as $key => $value) {
46+
if (is_array($value) && $this->recursive) {
47+
$result[$key] = $this->mapArray($value);
48+
continue;
49+
}
50+
51+
$mappedKey = $this->mapping[$key] ?? $key;
52+
53+
if ($this->removeUnmapped && !isset($this->mapping[$key])) {
54+
continue;
55+
}
56+
57+
$result[$mappedKey] = $value;
58+
}
59+
60+
return $result;
61+
}
62+
}

0 commit comments

Comments
 (0)