Skip to content

Commit e22f0d8

Browse files
committed
feat: update transformer processors and traits with enhanced functionality
- Updated `AbstractTransformerProcessor` with validation logic: - Added `isValid` and `getErrorKey` methods for validation handling. - Included `guardAgainstInvalidType` method to enforce input type checks. - Enhanced array processors: - `ArrayFlattenTransformer`: added configurable `depth` and `separator` for flattening nested arrays. - `ArrayGroupTransformer`: added grouping logic with `groupBy` and `preserveKeys` options. - `ArrayKeyTransformer`: supports transforming array keys to different cases (`snake`, `camel`, `pascal`, `kebab`) with optional recursion. - `ArrayMapTransformer`: introduced `mapping`, `removeUnmapped`, and `recursive` options for flexible array mapping. - Extended `ArrayTransformerTrait` with case transformation methods: - Added `toCamelCase`, `toPascalCase`, `toSnakeCase`, and `toKebabCase` for consistent key formatting. - Updated `StringTransformerTrait` with additional string manipulation methods: - Added transformations for `toLowerCase`, `toUpperCase`, `toTitleCase`, `toSentenceCase`, and various case conversions.
1 parent c5b18ff commit e22f0d8

7 files changed

+399
-0
lines changed
Lines changed: 47 additions & 0 deletions
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+
}
Lines changed: 54 additions & 0 deletions
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+
}
Lines changed: 62 additions & 0 deletions
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+
}
Lines changed: 80 additions & 0 deletions
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+
}
Lines changed: 62 additions & 0 deletions
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+
}

src/Trait/ArrayTransformerTrait.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace KaririCode\Transformer\Trait;
6+
7+
trait ArrayTransformerTrait
8+
{
9+
protected function toCamelCase(string $input): string
10+
{
11+
$input = str_replace(['-', '_'], ' ', $input);
12+
$input = ucwords($input);
13+
$input = str_replace(' ', '', $input);
14+
15+
return lcfirst($input);
16+
}
17+
18+
protected function toPascalCase(string $input): string
19+
{
20+
return ucfirst($this->toCamelCase($input));
21+
}
22+
23+
protected function toSnakeCase(string $input): string
24+
{
25+
$pattern = '/([a-z0-9])([A-Z])/';
26+
$input = preg_replace($pattern, '$1_$2', $input);
27+
$input = str_replace(['-', ' '], '_', $input);
28+
29+
return strtolower($input);
30+
}
31+
32+
protected function toKebabCase(string $input): string
33+
{
34+
return str_replace('_', '-', $this->toSnakeCase($input));
35+
}
36+
}

0 commit comments

Comments
 (0)