Skip to content

Commit e43ba4c

Browse files
authored
Merge pull request #10 from KaririCode-Framework/develop
feat(processor-pipeline): implement attribute processing system
2 parents cb916d9 + fdd9dad commit e43ba4c

14 files changed

+1236
-64
lines changed

composer.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@
2323
"php": "^8.3",
2424
"kariricode/data-structure": "^1.1",
2525
"kariricode/contract": "^2.7",
26-
"kariricode/property-inspector": "^1.0",
27-
"kariricode/exception": "^1.2"
26+
"kariricode/exception": "^1.2",
27+
"kariricode/property-inspector": "^1.2"
2828
},
2929
"autoload": {
3030
"psr-4": {

composer.lock

Lines changed: 7 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace KaririCode\ProcessorPipeline\Contract;
6+
7+
use KaririCode\Contract\Processor\Attribute\ProcessableAttribute;
8+
9+
interface ProcessorConfigBuilder
10+
{
11+
/**
12+
* Constrói a configuração dos processadores a partir de um atributo processável.
13+
*
14+
* @param ProcessableAttribute $attribute o atributo que fornece os processadores
15+
*
16+
* @return array a configuração dos processadores
17+
*/
18+
public function build(ProcessableAttribute $attribute): array;
19+
}

src/Handler/AttributeHandler.php

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace KaririCode\ProcessorPipeline\Handler;
6+
7+
use KaririCode\Contract\Processor\Attribute\CustomizableMessageAttribute;
8+
use KaririCode\Contract\Processor\Attribute\ProcessableAttribute;
9+
use KaririCode\Contract\Processor\ProcessorBuilder;
10+
use KaririCode\Contract\Processor\ProcessorValidator as ProcessorProcessorContract;
11+
use KaririCode\ProcessorPipeline\Contract\ProcessorConfigBuilder as ProcessorConfigBuilderContract;
12+
use KaririCode\ProcessorPipeline\Processor\ProcessorConfigBuilder;
13+
use KaririCode\ProcessorPipeline\Processor\ProcessorValidator;
14+
use KaririCode\PropertyInspector\Contract\PropertyAttributeHandler;
15+
use KaririCode\PropertyInspector\Contract\PropertyChangeApplier;
16+
use KaririCode\PropertyInspector\Utility\PropertyAccessor;
17+
18+
class AttributeHandler implements PropertyAttributeHandler, PropertyChangeApplier
19+
{
20+
private array $processedPropertyValues = [];
21+
private array $processingResultErrors = [];
22+
private array $processingResultMessages = [];
23+
private array $processorCache = [];
24+
25+
public function __construct(
26+
private readonly string $processorType,
27+
private readonly ProcessorBuilder $builder,
28+
private readonly ProcessorProcessorContract $validator = new ProcessorValidator(),
29+
private readonly ProcessorConfigBuilderContract $configBuilder = new ProcessorConfigBuilder()
30+
) {
31+
}
32+
33+
public function handleAttribute(string $propertyName, object $attribute, mixed $value): mixed
34+
{
35+
if (!$attribute instanceof ProcessableAttribute) {
36+
return null;
37+
}
38+
39+
try {
40+
return $this->processAttribute($propertyName, $attribute, $value);
41+
} catch (\Exception $e) {
42+
$this->processingResultErrors[$propertyName][] = $e->getMessage();
43+
44+
return $value;
45+
}
46+
}
47+
48+
private function processAttribute(string $propertyName, ProcessableAttribute $attribute, mixed $value): mixed
49+
{
50+
$config = $this->configBuilder->build($attribute);
51+
$messages = [];
52+
53+
if ($attribute instanceof CustomizableMessageAttribute) {
54+
foreach ($config as $processorName => &$processorConfig) {
55+
if ($message = $attribute->getMessage($processorName)) {
56+
$processorConfig['customMessage'] = $message;
57+
$messages[$processorName] = $message;
58+
}
59+
}
60+
}
61+
62+
$processedValue = $this->processValue($value, $config);
63+
64+
if ($errors = $this->validateProcessors($config, $messages)) {
65+
$this->processingResultErrors[$propertyName] = $errors;
66+
}
67+
68+
$this->processedPropertyValues[$propertyName] = [
69+
'value' => $processedValue,
70+
'messages' => $messages,
71+
];
72+
73+
$this->processingResultMessages[$propertyName] = $messages;
74+
75+
return $processedValue;
76+
}
77+
78+
private function validateProcessors(array $processorsConfig, array $messages): array
79+
{
80+
$errors = [];
81+
foreach ($processorsConfig as $processorName => $config) {
82+
// Simplify cache key to processor name
83+
if (!isset($this->processorCache[$processorName])) {
84+
$this->processorCache[$processorName] = $this->builder->build(
85+
$this->processorType,
86+
$processorName,
87+
$config
88+
);
89+
}
90+
91+
$processor = $this->processorCache[$processorName];
92+
93+
if ($error = $this->validator->validate($processor, $processorName, $messages)) {
94+
$errors[$processorName] = $error;
95+
}
96+
}
97+
98+
return $errors;
99+
}
100+
101+
private function processValue(mixed $value, array $config): mixed
102+
{
103+
return $this->builder
104+
->buildPipeline($this->processorType, $config)
105+
->process($value);
106+
}
107+
108+
public function applyChanges(object $entity): void
109+
{
110+
foreach ($this->processedPropertyValues as $propertyName => $data) {
111+
(new PropertyAccessor($entity, $propertyName))->setValue($data['value']);
112+
}
113+
}
114+
115+
public function getProcessedPropertyValues(): array
116+
{
117+
return $this->processedPropertyValues;
118+
}
119+
120+
public function getProcessingResultErrors(): array
121+
{
122+
return $this->processingResultErrors;
123+
}
124+
125+
public function getProcessingResultMessages(): array
126+
{
127+
return $this->processingResultMessages;
128+
}
129+
}

src/Handler/ProcessorAttributeHandler.php

Lines changed: 27 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -5,96 +5,68 @@
55
namespace KaririCode\ProcessorPipeline\Handler;
66

77
use KaririCode\Contract\Processor\ProcessorBuilder;
8-
use KaririCode\Contract\Processor\ValidatableProcessor;
9-
use KaririCode\ProcessorPipeline\Result\ProcessedData;
10-
use KaririCode\ProcessorPipeline\Result\ProcessingError;
8+
use KaririCode\ProcessorPipeline\AttributeHandler;
9+
use KaririCode\ProcessorPipeline\Exception\ProcessorRuntimeException;
1110
use KaririCode\ProcessorPipeline\Result\ProcessingResultCollection;
12-
use KaririCode\PropertyInspector\AttributeHandler;
1311

14-
class ProcessorAttributeHandler extends AttributeHandler
12+
final class ProcessorAttributeHandler extends AttributeHandler
1513
{
16-
protected ProcessingResultCollection $results;
14+
private ProcessingResultCollection $results;
1715

1816
public function __construct(
19-
private readonly string $identifier,
20-
private readonly ProcessorBuilder $builder
17+
string $identifier,
18+
ProcessorBuilder $builder
2119
) {
2220
parent::__construct($identifier, $builder);
2321
$this->results = new ProcessingResultCollection();
2422
}
2523

2624
public function processPropertyValue(string $property, mixed $value): mixed
2725
{
28-
$pipeline = $this->builder->buildPipeline(
29-
$this->identifier,
30-
$this->getPropertyProcessors($property)
31-
);
26+
$processorSpecs = $this->getPropertyProcessors($property);
27+
28+
if (empty($processorSpecs)) {
29+
return $value;
30+
}
3231

3332
try {
34-
$processedValue = $pipeline->process($value);
35-
$this->storeProcessedValue($property, $processedValue);
33+
$pipeline = $this->builder->buildPipeline(
34+
$this->identifier,
35+
$processorSpecs
36+
);
3637

37-
// Verifica se há erros de validação
38-
$this->checkValidationErrors($property, $pipeline);
38+
$processedValue = $pipeline->process($value);
39+
$this->results->setProcessedData($property, $processedValue);
3940

4041
return $processedValue;
4142
} catch (\Exception $e) {
42-
$this->storeProcessingError($property, $e);
43-
44-
return $value;
45-
}
46-
}
47-
48-
protected function checkValidationErrors(string $property, $pipeline): void
49-
{
50-
foreach ($pipeline->getProcessors() as $processor) {
51-
if ($processor instanceof ValidatableProcessor && !$processor->isValid()) {
52-
$this->storeValidationError(
53-
$property,
54-
$processor->getErrorKey(),
55-
$processor->getErrorMessage()
56-
);
57-
}
43+
throw ProcessorRuntimeException::processingFailed($property, $e);
5844
}
5945
}
6046

61-
protected function storeProcessedValue(string $property, mixed $value): void
47+
public function getProcessedPropertyValues(): array
6248
{
63-
$processedData = new ProcessedData($property, $value);
64-
$this->results->addProcessedData($processedData);
49+
return [
50+
'values' => $this->results->getProcessedData(),
51+
'timestamp' => time(),
52+
];
6553
}
6654

67-
protected function storeProcessingError(string $property, \Exception $exception): void
55+
public function getProcessingResultErrors(): array
6856
{
69-
$error = new ProcessingError(
70-
$property,
71-
'processingError',
72-
$exception->getMessage()
73-
);
74-
$this->results->addError($error);
57+
return $this->results->getErrors();
7558
}
7659

77-
protected function storeValidationError(string $property, string $errorKey, string $message): void
60+
public function hasErrors(): bool
7861
{
79-
$error = new ProcessingError($property, $errorKey, $message);
80-
$this->results->addError($error);
62+
return $this->results->hasErrors();
8163
}
8264

8365
public function getProcessingResults(): ProcessingResultCollection
8466
{
8567
return $this->results;
8668
}
8769

88-
public function getProcessedPropertyValues(): array
89-
{
90-
return $this->results->getProcessedDataAsArray();
91-
}
92-
93-
public function getProcessingResultErrors(): array
94-
{
95-
return $this->results->getErrorsAsArray();
96-
}
97-
9870
public function reset(): void
9971
{
10072
$this->results = new ProcessingResultCollection();
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace KaririCode\ProcessorPipeline\Processor;
6+
7+
use KaririCode\Contract\Processor\Attribute\ProcessableAttribute;
8+
use KaririCode\ProcessorPipeline\Contract\ProcessorConfigBuilder as ProcessorConfigBuilderContract;
9+
10+
readonly class ProcessorConfigBuilder implements ProcessorConfigBuilderContract
11+
{
12+
public function build(ProcessableAttribute $attribute): array
13+
{
14+
$processors = $attribute->getProcessors();
15+
$processorsConfig = [];
16+
17+
foreach ($processors as $key => $processor) {
18+
if ($this->isSimpleProcessor($processor)) {
19+
$processorsConfig[$processor] = $this->getDefaultProcessorConfig();
20+
} elseif ($this->isConfigurableProcessor($processor)) {
21+
$processorName = $this->determineProcessorName($key, $processor);
22+
$processorsConfig[$processorName] = $this->getProcessorConfig($processor);
23+
}
24+
}
25+
26+
return $processorsConfig;
27+
}
28+
29+
private function isSimpleProcessor(mixed $processor): bool
30+
{
31+
return is_string($processor);
32+
}
33+
34+
private function isConfigurableProcessor(mixed $processor): bool
35+
{
36+
return is_array($processor);
37+
}
38+
39+
private function getDefaultProcessorConfig(): array
40+
{
41+
return [];
42+
}
43+
44+
private function determineProcessorName(string|int $key, array $processor): string
45+
{
46+
$nameNormalizer = new ProcessorNameNormalizer();
47+
48+
return $nameNormalizer->normalize($key, $processor);
49+
}
50+
51+
private function getProcessorConfig(array $processor): array
52+
{
53+
return $processor;
54+
}
55+
}

0 commit comments

Comments
 (0)