RestDto::getGetterMethod()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 6
nc 1
nop 2
dl 0
loc 11
ccs 8
cts 8
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
declare(strict_types = 1);
3
/**
4
 * /src/DTO/RestDto.php
5
 *
6
 * @author TLe, Tarmo Leppänen <[email protected]>
7
 */
8
9
namespace App\DTO;
10
11
use App\Entity\Interfaces\EntityInterface;
12
use BadMethodCallException;
13
use LogicException;
14
use function array_filter;
15
use function array_key_exists;
16
use function count;
17
use function current;
18
use function method_exists;
19
use function sprintf;
20
use function ucfirst;
21
22
/**
23
 * Class RestDto
24
 *
25
 * @package App\DTO
26
 * @author TLe, Tarmo Leppänen <[email protected]>
27
 */
28
abstract class RestDto implements RestDtoInterface
29
{
30
    /**
31
     * DTO property mappings to setter method.
32
     *
33
     * Example:
34
     *  static protected $mappings = [
35
     *      'someProperty' => 'methodInYourDtoClass',
36
     *  ]
37
     *
38
     * This will call below method in your DTO class:
39
     *  protected function methodInYourDtoClass($entity, $value)
40
     *
41
     * And in that method make all necessary that you need to set that specified value.
42
     *
43
     * @var array<string, string>
44
     */
45
    protected static array $mappings = [];
46
47
    protected ?string $id = null;
48
49
    /**
50
     * An array of 'visited' setter properties of current dto.
51
     *
52
     * @var array<int, string>
53
     */
54
    private array $visited = [];
55
56 51
    public function setId(string $id): self
57
    {
58 51
        $this->setVisited('id');
59
60 51
        $this->id = $id;
61
62 51
        return $this;
63
    }
64
65 16
    public function getId(): ?string
66
    {
67 16
        return $this->id;
68
    }
69
70 50
    public function getVisited(): array
71
    {
72 50
        return array_filter($this->visited, static fn (string $property): bool => $property !== 'id');
73
    }
74
75 81
    public function setVisited(string $property): self
76
    {
77 81
        $this->visited[] = $property;
78
79 81
        return $this;
80
    }
81
82 24
    public function update(EntityInterface $entity): EntityInterface
83
    {
84 24
        foreach ($this->getVisited() as $property) {
85 13
            if (array_key_exists($property, static::$mappings)) {
86 9
                $this->{static::$mappings[$property]}($entity, $this->{$property});
87
88 9
                continue;
89
            }
90
91
            // Determine setter method
92 7
            $setter = 'set' . ucfirst($property);
93
94
            // Update current dto property value
95 7
            $entity->{$setter}($this->{$property});
96
        }
97
98 24
        return $entity;
99
    }
100
101 30
    public function patch(RestDtoInterface $dto): self
102
    {
103 30
        foreach ($dto->getVisited() as $property) {
104
            // Determine getter method
105 7
            $getter = $this->determineGetterMethod($dto, $property);
106
107
            // Determine setter method
108 5
            $setter = 'set' . ucfirst($property);
109
110
            // Update current dto property value
111 5
            $this->{$setter}($dto->{$getter}());
112
        }
113
114 28
        return $this;
115
    }
116
117
    /**
118
     * Method to determine used getter method for DTO property.
119
     */
120 9
    private function determineGetterMethod(RestDtoInterface $dto, string $property): string
121
    {
122 9
        $getter = $this->getGetterMethod($dto, $property);
123
124
        // Oh noes - required getter method does not exist
125 8
        if ($getter === null) {
126 1
            $message = sprintf(
127 1
                'DTO class \'%s\' does not have getter method property \'%s\' - cannot patch dto',
128 1
                $dto::class,
129 1
                $property
130 1
            );
131
132 1
            throw new BadMethodCallException($message);
133
        }
134
135 7
        return $getter;
136
    }
137
138 9
    private function getGetterMethod(RestDtoInterface $dto, string $property): ?string
139
    {
140 9
        $getters = [
141 9
            'get' . ucfirst($property),
142 9
            'is' . ucfirst($property),
143 9
            'has' . ucfirst($property),
144 9
        ];
145
146 9
        $getterMethods = array_filter($getters, static fn (string $method): bool => method_exists($dto, $method));
147
148 9
        return $this->validateGetterMethod($property, $getterMethods);
149
    }
150
151
    /**
152
     * @param array<int, string> $getterMethods
153
     */
154 9
    private function validateGetterMethod(string $property, array $getterMethods): ?string
155
    {
156 9
        if (count($getterMethods) > 1) {
157 1
            $message = sprintf(
158 1
                'Property \'%s\' has multiple getter methods - this is insane!',
159 1
                $property
160 1
            );
161
162 1
            throw new LogicException($message);
163
        }
164
165 8
        $method = current($getterMethods);
166
167 8
        return $method === false ? null : $method;
168
    }
169
}
170