Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 44 additions & 15 deletions src/AttributeHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -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()
) {
}

Expand All @@ -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
Expand Down
54 changes: 54 additions & 0 deletions src/Processor/ProcessorConfigBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

declare(strict_types=1);

namespace KaririCode\PropertyInspector\Processor;

use KaririCode\Contract\Processor\Attribute\ProcessableAttribute;

readonly class ProcessorConfigBuilder
{
public function build(ProcessableAttribute $attribute): array
{
$processors = $attribute->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;
}
}
25 changes: 25 additions & 0 deletions src/Processor/ProcessorNameNormalizer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

declare(strict_types=1);

namespace KaririCode\PropertyInspector\Processor;

final readonly class ProcessorNameNormalizer
{
public function normalize(string|int $key, array $processor): string
{
return $this->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 : '';
}
}
33 changes: 22 additions & 11 deletions tests/AttributeHandlerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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')
Expand All @@ -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);
}

Expand Down Expand Up @@ -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')
Expand All @@ -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
Expand Down
63 changes: 63 additions & 0 deletions tests/Processor/ProcessorConfigBuilderTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php

declare(strict_types=1);

namespace KaririCode\PropertyInspector\Tests\Processor;

use KaririCode\Contract\Processor\Attribute\ProcessableAttribute;
use KaririCode\PropertyInspector\Processor\ProcessorConfigBuilder;
use PHPUnit\Framework\TestCase;

class ProcessorConfigBuilderTest extends TestCase
{
private ProcessorConfigBuilder $configBuilder;

protected function setUp(): void
{
$this->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);
}
}
39 changes: 39 additions & 0 deletions tests/Processor/ProcessorNameNormalizerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

declare(strict_types=1);

namespace KaririCode\PropertyInspector\Tests\Processor;

use KaririCode\PropertyInspector\Processor\ProcessorNameNormalizer;
use PHPUnit\Framework\TestCase;

class ProcessorNameNormalizerTest extends TestCase
{
private ProcessorNameNormalizer $normalizer;

protected function setUp(): void
{
$this->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);
}
}
Loading