diff --git a/src/AttributeHandler.php b/src/AttributeHandler.php index fd10d9e..313ae4c 100644 --- a/src/AttributeHandler.php +++ b/src/AttributeHandler.php @@ -7,19 +7,21 @@ use KaririCode\Contract\Processor\Attribute\CustomizableMessageAttribute; use KaririCode\Contract\Processor\Attribute\ProcessableAttribute; use KaririCode\Contract\Processor\ProcessorBuilder; -use KaririCode\ProcessorPipeline\Exception\ProcessingException; use KaririCode\PropertyInspector\Contract\PropertyAttributeHandler; use KaririCode\PropertyInspector\Contract\PropertyChangeApplier; +use KaririCode\PropertyInspector\Processor\ProcessorConfigBuilder; use KaririCode\PropertyInspector\Utility\PropertyAccessor; class AttributeHandler implements PropertyAttributeHandler, PropertyChangeApplier { private array $processedValues = []; private array $processingErrors = []; + private array $processingMessages = []; public function __construct( private readonly string $processorType, private readonly ProcessorBuilder $builder, + private readonly ProcessorConfigBuilder $configBuilder = new ProcessorConfigBuilder() ) { } @@ -29,38 +31,65 @@ public function handleAttribute(string $propertyName, object $attribute, mixed $ return null; } - $processors = $attribute->getProcessors(); + $processorsConfig = $this->configBuilder->build($attribute); + $messages = $this->extractCustomMessages($attribute, $processorsConfig); + + try { + $processedValue = $this->processValue($value, $processorsConfig); + $this->storeProcessedValue($propertyName, $processedValue, $messages); + + return $processedValue; // Return the processed value, not the original + } catch (\Exception $e) { + $this->storeProcessingError($propertyName, $e->getMessage()); + + return $value; + } + } + + private function extractCustomMessages(ProcessableAttribute $attribute, array &$processorsConfig): array + { + $messages = []; if ($attribute instanceof CustomizableMessageAttribute) { - foreach ($processors as $processorName => &$config) { + foreach ($processorsConfig as $processorName => &$config) { $customMessage = $attribute->getMessage($processorName); if (null !== $customMessage) { $config['customMessage'] = $customMessage; + $messages[$processorName] = $customMessage; } } } - $pipeline = $this->builder->buildPipeline($this->processorType, $processors); + return $messages; + } - try { - $processedValue = $pipeline->process($value); - $this->processedValues[$propertyName] = $processedValue; + private function processValue(mixed $value, array $processorsConfig): mixed + { + $pipeline = $this->builder->buildPipeline($this->processorType, $processorsConfig); - return $processedValue; - } catch (ProcessingException $e) { - $this->processingErrors[$propertyName][] = $e->getMessage(); + return $pipeline->process($value); + } - return $value; // Return original value in case of processing error - } + private function storeProcessedValue(string $propertyName, mixed $processedValue, array $messages): void + { + $this->processedValues[$propertyName] = [ + 'value' => $processedValue, + 'messages' => $messages, + ]; + $this->processingMessages[$propertyName] = $messages; + } + + private function storeProcessingError(string $propertyName, string $errorMessage): void + { + $this->processingErrors[$propertyName][] = $errorMessage; } public function applyChanges(object $entity): void { - foreach ($this->processedValues as $propertyName => $value) { + foreach ($this->processedValues as $propertyName => $data) { $accessor = new PropertyAccessor($entity, $propertyName); - $accessor->setValue($value); + $accessor->setValue($data['value']); } - $this->processedValues = []; // Clear the processed values after applying } public function getProcessedValues(): array diff --git a/src/Processor/ProcessorConfigBuilder.php b/src/Processor/ProcessorConfigBuilder.php new file mode 100644 index 0000000..dc67ed9 --- /dev/null +++ b/src/Processor/ProcessorConfigBuilder.php @@ -0,0 +1,54 @@ +getProcessors(); + $processorsConfig = []; + + foreach ($processors as $key => $processor) { + if ($this->isSimpleProcessor($processor)) { + $processorsConfig[$processor] = $this->getDefaultProcessorConfig(); + } elseif ($this->isConfigurableProcessor($processor)) { + $processorName = $this->determineProcessorName($key, $processor); + $processorsConfig[$processorName] = $this->getProcessorConfig($processor); + } + } + + return $processorsConfig; + } + + private function isSimpleProcessor(mixed $processor): bool + { + return is_string($processor); + } + + private function isConfigurableProcessor(mixed $processor): bool + { + return is_array($processor); + } + + private function getDefaultProcessorConfig(): array + { + return []; + } + + private function determineProcessorName(string|int $key, array $processor): string + { + $nameNormalizer = new ProcessorNameNormalizer(); + + return $nameNormalizer->normalize($key, $processor); + } + + private function getProcessorConfig(array $processor): array + { + return $processor; + } +} diff --git a/src/Processor/ProcessorNameNormalizer.php b/src/Processor/ProcessorNameNormalizer.php new file mode 100644 index 0000000..2b1f2b5 --- /dev/null +++ b/src/Processor/ProcessorNameNormalizer.php @@ -0,0 +1,25 @@ +isNamedProcessor($key) ? (string) $key : $this->extractProcessorName($processor); + } + + private function isNamedProcessor(string|int $key): bool + { + return is_string($key); + } + + private function extractProcessorName(array $processor): string + { + $firstKey = array_key_first($processor); + + return is_string($firstKey) ? $firstKey : ''; + } +} diff --git a/tests/AttributeHandlerTest.php b/tests/AttributeHandlerTest.php index 495a38f..849d5c7 100644 --- a/tests/AttributeHandlerTest.php +++ b/tests/AttributeHandlerTest.php @@ -10,6 +10,7 @@ use KaririCode\Contract\Processor\ProcessorBuilder; use KaririCode\ProcessorPipeline\Exception\ProcessingException; use KaririCode\PropertyInspector\AttributeHandler; +use KaririCode\PropertyInspector\Processor\ProcessorConfigBuilder; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -34,6 +35,11 @@ public function testHandleAttributeProcessesValue(): void { $mockAttribute = $this->createMock(ProcessableAttribute::class); $mockPipeline = $this->createMock(Pipeline::class); + $mockConfigBuilder = $this->createMock(ProcessorConfigBuilder::class); + + $mockConfigBuilder->expects($this->once()) + ->method('build') + ->willReturn(['processor1' => []]); $mockPipeline->expects($this->once()) ->method('process') @@ -42,14 +48,13 @@ public function testHandleAttributeProcessesValue(): void $this->processorBuilder->expects($this->once()) ->method('buildPipeline') - ->with('testProcessor', ['processor1']) + ->with($this->equalTo('testProcessor'), $this->equalTo(['processor1' => []])) ->willReturn($mockPipeline); - $mockAttribute->expects($this->once()) - ->method('getProcessors') - ->willReturn(['processor1']); + $attributeHandler = new AttributeHandler('testProcessor', $this->processorBuilder, $mockConfigBuilder); + + $result = $attributeHandler->handleAttribute('testProperty', $mockAttribute, 'initialValue'); - $result = $this->attributeHandler->handleAttribute('testProperty', $mockAttribute, 'initialValue'); $this->assertSame('processedValue', $result); } @@ -117,6 +122,7 @@ public function testGetProcessedValuesReturnsProcessedData(): void { $mockAttribute = $this->createMock(ProcessableAttribute::class); $mockPipeline = $this->createMock(Pipeline::class); + $mockConfigBuilder = $this->createMock(ProcessorConfigBuilder::class); $mockPipeline->expects($this->once()) ->method('process') @@ -127,15 +133,20 @@ public function testGetProcessedValuesReturnsProcessedData(): void ->method('buildPipeline') ->willReturn($mockPipeline); - $mockAttribute->expects($this->once()) - ->method('getProcessors') - ->willReturn(['processor1']); + $mockConfigBuilder->expects($this->once()) + ->method('build') + ->willReturn(['processor1' => []]); - $this->attributeHandler->handleAttribute('testProperty', $mockAttribute, 'initialValue'); - $processedValues = $this->attributeHandler->getProcessedValues(); + $attributeHandler = new AttributeHandler('testProcessor', $this->processorBuilder, $mockConfigBuilder); + $attributeHandler->handleAttribute('testProperty', $mockAttribute, 'initialValue'); + $processedValues = $attributeHandler->getProcessedValues(); $this->assertArrayHasKey('testProperty', $processedValues); - $this->assertSame('processedValue', $processedValues['testProperty']); + $this->assertIsArray($processedValues['testProperty']); + $this->assertArrayHasKey('value', $processedValues['testProperty']); + $this->assertArrayHasKey('messages', $processedValues['testProperty']); + $this->assertSame('processedValue', $processedValues['testProperty']['value']); + $this->assertIsArray($processedValues['testProperty']['messages']); } public function testHandleAttributeWithCustomizableMessageAttribute(): void diff --git a/tests/Processor/ProcessorConfigBuilderTest.php b/tests/Processor/ProcessorConfigBuilderTest.php new file mode 100644 index 0000000..81bcbce --- /dev/null +++ b/tests/Processor/ProcessorConfigBuilderTest.php @@ -0,0 +1,63 @@ +configBuilder = new ProcessorConfigBuilder(); + } + + public function testBuildWithSimpleProcessors(): void + { + $attribute = $this->createMock(ProcessableAttribute::class); + $attribute->method('getProcessors')->willReturn(['processor1', 'processor2']); + + $result = $this->configBuilder->build($attribute); + + $this->assertEquals(['processor1' => [], 'processor2' => []], $result); + } + + public function testBuildWithConfigurableProcessors(): void + { + $attribute = $this->createMock(ProcessableAttribute::class); + $attribute->method('getProcessors')->willReturn([ + 'processor1' => ['option' => 'value'], + 'processor2' => ['another_option' => 'another_value'], + ]); + + $result = $this->configBuilder->build($attribute); + + $this->assertEquals([ + 'processor1' => ['option' => 'value'], + 'processor2' => ['another_option' => 'another_value'], + ], $result); + } + + public function testBuildWithMixedProcessors(): void + { + $attribute = $this->createMock(ProcessableAttribute::class); + $attribute->method('getProcessors')->willReturn([ + 'processor1', + 'processor2' => ['option' => 'value'], + ['unnamed_processor' => []], + ]); + + $result = $this->configBuilder->build($attribute); + + $this->assertEquals([ + 'processor1' => [], + 'processor2' => ['option' => 'value'], + 'unnamed_processor' => ['unnamed_processor' => []], + ], $result); + } +} diff --git a/tests/Processor/ProcessorNameNormalizerTest.php b/tests/Processor/ProcessorNameNormalizerTest.php new file mode 100644 index 0000000..9937ab8 --- /dev/null +++ b/tests/Processor/ProcessorNameNormalizerTest.php @@ -0,0 +1,39 @@ +normalizer = new ProcessorNameNormalizer(); + } + + public function testNormalizeWithStringKey(): void + { + $result = $this->normalizer->normalize('processor_name', []); + + $this->assertEquals('processor_name', $result); + } + + public function testNormalizeWithIntegerKey(): void + { + $result = $this->normalizer->normalize(0, ['processor_name' => []]); + + $this->assertEquals('processor_name', $result); + } + + public function testNormalizeWithEmptyProcessor(): void + { + $result = $this->normalizer->normalize(0, []); + + $this->assertEquals('', $result); + } +}