-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from KaririCode-Framework/develop
[Release] KaririCode Transformer v1.0.0 🚀
- Loading branch information
Showing
24 changed files
with
2,917 additions
and
92 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace KaririCode\Transformer\Attribute; | ||
|
||
use KaririCode\Contract\Processor\Attribute\BaseProcessorAttribute; | ||
|
||
#[\Attribute(\Attribute::TARGET_PROPERTY)] | ||
final class Transform extends BaseProcessorAttribute | ||
{ | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace KaririCode\Transformer\Contract; | ||
|
||
interface TransformationResult | ||
{ | ||
public function isValid(): bool; | ||
|
||
public function getErrors(): array; | ||
|
||
public function getTransformedData(): array; | ||
|
||
public function toArray(): array; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace KaririCode\Transformer\Exception; | ||
|
||
use KaririCode\Exception\AbstractException; | ||
|
||
final class TransformerException extends AbstractException | ||
{ | ||
private const CODE_INVALID_INPUT = 5001; | ||
private const CODE_INVALID_FORMAT = 5002; | ||
private const CODE_INVALID_TYPE = 5003; | ||
|
||
public static function invalidInput(string $expectedType, string $actualType): self | ||
{ | ||
$message = sprintf( | ||
'Invalid input type. Expected %s, got %s.', | ||
$expectedType, | ||
$actualType | ||
); | ||
|
||
return self::createException( | ||
self::CODE_INVALID_INPUT, | ||
'INVALID_INPUT_TYPE', | ||
$message | ||
); | ||
} | ||
|
||
public static function invalidFormat(string $format, string $value): self | ||
{ | ||
$message = sprintf( | ||
'Invalid format. Expected format %s, got %s.', | ||
$format, | ||
$value | ||
); | ||
|
||
return self::createException( | ||
self::CODE_INVALID_FORMAT, | ||
'INVALID_FORMAT', | ||
$message | ||
); | ||
} | ||
|
||
public static function invalidType(string $expectedType): self | ||
{ | ||
$message = sprintf( | ||
'Invalid type. Expected %s.', | ||
$expectedType | ||
); | ||
|
||
return self::createException( | ||
self::CODE_INVALID_TYPE, | ||
'INVALID_TYPE', | ||
$message | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace KaririCode\Transformer\Processor; | ||
|
||
use KaririCode\Contract\Processor\Processor; | ||
use KaririCode\Contract\Processor\ValidatableProcessor; | ||
use KaririCode\Transformer\Exception\TransformerException; | ||
|
||
abstract class AbstractTransformerProcessor implements Processor, ValidatableProcessor | ||
{ | ||
protected bool $isValid = true; | ||
protected string $errorKey = ''; | ||
|
||
public function reset(): void | ||
{ | ||
$this->isValid = true; | ||
$this->errorKey = ''; | ||
} | ||
|
||
protected function setInvalid(string $errorKey): void | ||
{ | ||
$this->isValid = false; | ||
$this->errorKey = $errorKey; | ||
} | ||
|
||
public function isValid(): bool | ||
{ | ||
return $this->isValid; | ||
} | ||
|
||
public function getErrorKey(): string | ||
{ | ||
return $this->errorKey; | ||
} | ||
|
||
protected function guardAgainstInvalidType(mixed $input, string $type): void | ||
{ | ||
$actualType = get_debug_type($input); | ||
if ($actualType !== $type) { | ||
throw TransformerException::invalidType($type); | ||
} | ||
} | ||
|
||
abstract public function process(mixed $input): mixed; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace KaririCode\Transformer\Processor\Array; | ||
|
||
use KaririCode\Contract\Processor\ConfigurableProcessor; | ||
use KaririCode\Transformer\Processor\AbstractTransformerProcessor; | ||
use KaririCode\Transformer\Trait\ArrayTransformerTrait; | ||
|
||
class ArrayFlattenTransformer extends AbstractTransformerProcessor implements ConfigurableProcessor | ||
{ | ||
use ArrayTransformerTrait; | ||
|
||
private int $depth = -1; | ||
private string $separator = '.'; | ||
|
||
public function configure(array $options): void | ||
{ | ||
$this->depth = $options['depth'] ?? $this->depth; | ||
$this->separator = $options['separator'] ?? $this->separator; | ||
} | ||
|
||
public function process(mixed $input): array | ||
{ | ||
if (!is_array($input)) { | ||
$this->setInvalid('notArray'); | ||
|
||
return []; | ||
} | ||
|
||
return $this->flattenArray($input, '', $this->depth); | ||
} | ||
|
||
private function flattenArray(array $array, string $prefix = '', int $depth = -1): array | ||
{ | ||
$result = []; | ||
|
||
foreach ($array as $key => $value) { | ||
$newKey = $prefix ? $prefix . $this->separator . $key : $key; | ||
|
||
if (is_array($value) && ($depth > 0 || -1 === $depth)) { | ||
$result = array_merge( | ||
$result, | ||
$this->flattenArray($value, $newKey, $depth > 0 ? $depth - 1 : -1) | ||
); | ||
} else { | ||
$result[$newKey] = $value; | ||
} | ||
} | ||
|
||
return $result; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace KaririCode\Transformer\Processor\Array; | ||
|
||
use KaririCode\Contract\Processor\ConfigurableProcessor; | ||
use KaririCode\Transformer\Processor\AbstractTransformerProcessor; | ||
use KaririCode\Transformer\Trait\ArrayTransformerTrait; | ||
|
||
class ArrayGroupTransformer extends AbstractTransformerProcessor implements ConfigurableProcessor | ||
{ | ||
use ArrayTransformerTrait; | ||
|
||
private string $groupBy = ''; | ||
private bool $preserveKeys = false; | ||
|
||
public function configure(array $options): void | ||
{ | ||
if (!isset($options['groupBy'])) { | ||
throw new \InvalidArgumentException('The groupBy option is required'); | ||
} | ||
|
||
$this->groupBy = $options['groupBy']; | ||
$this->preserveKeys = $options['preserveKeys'] ?? $this->preserveKeys; | ||
} | ||
|
||
public function process(mixed $input): array | ||
{ | ||
if (!is_array($input)) { | ||
$this->setInvalid('notArray'); | ||
|
||
return []; | ||
} | ||
|
||
return $this->groupArray($input); | ||
} | ||
|
||
private function groupArray(array $array): array | ||
{ | ||
$result = []; | ||
|
||
foreach ($array as $key => $item) { | ||
if (!is_array($item)) { | ||
continue; | ||
} | ||
|
||
$groupValue = $item[$this->groupBy] ?? null; | ||
if (null === $groupValue) { | ||
continue; | ||
} | ||
|
||
if ($this->preserveKeys) { | ||
$result[$groupValue][$key] = $item; | ||
} else { | ||
$result[$groupValue][] = $item; | ||
} | ||
} | ||
|
||
return $result; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace KaririCode\Transformer\Processor\Array; | ||
|
||
use KaririCode\Contract\Processor\ConfigurableProcessor; | ||
use KaririCode\Transformer\Processor\AbstractTransformerProcessor; | ||
use KaririCode\Transformer\Trait\ArrayTransformerTrait; | ||
|
||
class ArrayKeyTransformer extends AbstractTransformerProcessor implements ConfigurableProcessor | ||
{ | ||
use ArrayTransformerTrait; | ||
|
||
private const CASE_SNAKE = 'snake'; | ||
private const CASE_CAMEL = 'camel'; | ||
private const CASE_PASCAL = 'pascal'; | ||
private const CASE_KEBAB = 'kebab'; | ||
|
||
private string $case = self::CASE_SNAKE; | ||
private bool $recursive = true; | ||
|
||
public function configure(array $options): void | ||
{ | ||
if (isset($options['case']) && in_array($options['case'], $this->getAllowedCases(), true)) { | ||
$this->case = $options['case']; | ||
} | ||
|
||
$this->recursive = $options['recursive'] ?? $this->recursive; | ||
} | ||
|
||
public function process(mixed $input): array | ||
{ | ||
if (!is_array($input)) { | ||
$this->setInvalid('notArray'); | ||
|
||
return []; | ||
} | ||
|
||
return $this->transformArrayKeys($input); | ||
} | ||
|
||
private function transformArrayKeys(array $array): array | ||
{ | ||
$result = []; | ||
|
||
foreach ($array as $key => $value) { | ||
$transformedKey = $this->transformKey((string) $key); | ||
|
||
if (is_array($value) && $this->recursive) { | ||
$result[$transformedKey] = $this->transformArrayKeys($value); | ||
} else { | ||
$result[$transformedKey] = $value; | ||
} | ||
} | ||
|
||
return $result; | ||
} | ||
|
||
private function transformKey(string $key): string | ||
{ | ||
return match ($this->case) { | ||
self::CASE_SNAKE => $this->toSnakeCase($key), | ||
self::CASE_CAMEL => $this->toCamelCase($key), | ||
self::CASE_PASCAL => $this->toPascalCase($key), | ||
self::CASE_KEBAB => $this->toKebabCase($key), | ||
default => $key, | ||
}; | ||
} | ||
|
||
private function getAllowedCases(): array | ||
{ | ||
return [ | ||
self::CASE_SNAKE, | ||
self::CASE_CAMEL, | ||
self::CASE_PASCAL, | ||
self::CASE_KEBAB, | ||
]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace KaririCode\Transformer\Processor\Array; | ||
|
||
use KaririCode\Contract\Processor\ConfigurableProcessor; | ||
use KaririCode\Transformer\Processor\AbstractTransformerProcessor; | ||
use KaririCode\Transformer\Trait\ArrayTransformerTrait; | ||
|
||
class ArrayMapTransformer extends AbstractTransformerProcessor implements ConfigurableProcessor | ||
{ | ||
use ArrayTransformerTrait; | ||
|
||
private array $mapping = []; | ||
private bool $removeUnmapped = false; | ||
private bool $recursive = true; | ||
|
||
public function configure(array $options): void | ||
{ | ||
if (!isset($options['mapping']) || !is_array($options['mapping'])) { | ||
throw new \InvalidArgumentException('The mapping option is required and must be an array'); | ||
} | ||
|
||
$this->mapping = $options['mapping']; | ||
$this->removeUnmapped = $options['removeUnmapped'] ?? $this->removeUnmapped; | ||
$this->recursive = $options['recursive'] ?? $this->recursive; | ||
} | ||
|
||
public function process(mixed $input): array | ||
{ | ||
if (!is_array($input)) { | ||
$this->setInvalid('notArray'); | ||
|
||
return []; | ||
} | ||
|
||
return $this->mapArray($input); | ||
} | ||
|
||
private function mapArray(array $array): array | ||
{ | ||
$result = []; | ||
|
||
foreach ($array as $key => $value) { | ||
if (is_array($value) && $this->recursive) { | ||
$result[$key] = $this->mapArray($value); | ||
continue; | ||
} | ||
|
||
$mappedKey = $this->mapping[$key] ?? $key; | ||
|
||
if ($this->removeUnmapped && !isset($this->mapping[$key])) { | ||
continue; | ||
} | ||
|
||
$result[$mappedKey] = $value; | ||
} | ||
|
||
return $result; | ||
} | ||
} |
Oops, something went wrong.