Completed
Push — master ( 1f78f7...502d16 )
by Frank
10s
created

CodeDumper::dumpCommands()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 16
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 16
ccs 7
cts 7
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 8
nc 2
nop 1
crap 2
1
<?php
2
3
namespace EventSauce\EventSourcing\CodeGeneration;
4
5
use EventSauce\EventSourcing\Event;
6
use LogicException;
7
use const null;
8
use function array_filter;
9
use function join;
10
use function sprintf;
11
use function ucfirst;
12
use function var_export;
13
14
class CodeDumper
15
{
16
    /**
17
     * @var DefinitionGroup
18
     */
19
    private $definitionGroup;
20
21 5
    public function dump(DefinitionGroup $definitionGroup, bool $withHelpers = true): string
22
    {
23 5
        $this->definitionGroup = $definitionGroup;
24 5
        $eventsCode = $this->dumpEvents($definitionGroup->events(), $withHelpers);
25 5
        $commandCode = $this->dumpCommands($definitionGroup->commands());
26 4
        $namespace = $definitionGroup->namespace();
27 4
        $allCode = join(array_filter([$eventsCode, $commandCode]), "\n\n");
28
29
        return <<<EOF
30
<?php
31
32 4
namespace $namespace;
33
34
use EventSauce\\EventSourcing\\Event;
35
36 4
$allCode
37
38
EOF;
39
    }
40
41 5
    private function dumpEvents(array $events, bool $withHelpers): string
42
    {
43 5
        $code = [];
44
45 5
        if (empty($events)) {
46 2
            return '';
47
        }
48
49 4
        foreach ($events as $event) {
50 4
            $name = $event->name();
51 4
            $fields = $this->dumpFields($event);
52 4
            $constructor = $this->dumpConstructor($event);
53 4
            $methods = $this->dumpMethods($event);
54 4
            $deserializer = $this->dumpSerializationMethods($event);
55 4
            $testHelpers = $withHelpers ? $this->dumpTestHelpers($event) : '';
56
57 4
            $code[] = <<<EOF
58 4
final class $name implements Event
59
{
60 4
$fields$constructor$methods$deserializer
61
62 4
$testHelpers}
63
64
65
EOF;
66
        }
67
68 4
        return rtrim(join('', $code));
69
    }
70
71 5
    private function dumpFields(DefinitionWithFields $definition): string
72
    {
73 5
        $fields = $this->fieldsFromDefinition($definition);
74 4
        $code = [];
75 4
        $code[] = <<<EOF
76
77
EOF;
78
79
80 4
        foreach ($fields as $field) {
81 4
            $name = $field['name'];
82 4
            $type = $field['type'];
83
84 4
            $code[] = <<<EOF
85
    /**
86 4
     * @var $type
87
     */
88 4
    private \$$name;
89
90
91
EOF;
92
93
        }
94
95 4
        return join('', $code);
96
    }
97
98 4
    private function dumpConstructor(DefinitionWithFields $definition): string
99
    {
100 4
        $arguments = [];
101 4
        $assignments = [];
102 4
        $fields = $this->fieldsFromDefinition($definition);
103
104 4
        foreach ($fields as $field) {
105 4
            $arguments[] = sprintf('        %s $%s', $field['type'], $field['name']);
106 4
            $assignments[] = sprintf('        $this->%s = $%s;', $field['name'], $field['name']);
107
        }
108
109 4
        $arguments = join(",\n", $arguments);
110 4
        $assignments = join("\n", $assignments);
111
112
113
        return <<<EOF
114
    public function __construct(
115 4
$arguments
116
    ) {
117 4
$assignments
118
    }
119
120
121
EOF;
122
123
    }
124
125 4
    private function dumpMethods(DefinitionWithFields $command): string
126
    {
127 4
        $methods = [];
128
129 4
        foreach ($this->fieldsFromDefinition($command) as $field) {
130 4
            $methods[] = <<<EOF
131 4
    public function {$field['name']}(): {$field['type']}
132
    {
133 4
        return \$this->{$field['name']};
134
    }
135
136
137
EOF;
138
        }
139
140 4
        return empty($methods) ? '' : rtrim(join('', $methods)) . "\n";
141
    }
142
143 4
    private function dumpSerializationMethods(EventDefinition $event)
144
    {
145 4
        $name = $event->name();
146 4
        $arguments = [];
147 4
        $serializers = [];
148
149 4
        foreach ($this->fieldsFromDefinition($event) as $field) {
150 4
            $parameter = sprintf('$payload[\'%s\']', $field['name']);
151 4
            $template = $event->deserializerForField($field['name'])
152 4
                ?: $event->deserializerForType($field['type']);
153 4
            $arguments[] = trim(strtr($template, ['{type}' => $field['type'], '{param}' => $parameter]));
154
155 4
            $property = sprintf('$this->%s', $field['name']);
156 4
            $template = $event->serializerForField($field['name'])
157 4
                ?: $event->serializerForType($field['type']);
158 4
            $template = sprintf("'%s' => %s", $field['name'], $template);
159 4
            $serializers[] = trim(strtr($template, ['{type}' => $field['type'], '{param}' => $property]));
160
        }
161
162 4
        $arguments = preg_replace('/^.{2,}$/m', '            $0', join(",\n", $arguments));
163
164 4
        if ( ! empty($arguments)) {
165 4
            $arguments = "\n$arguments";
166
        }
167
168 4
        $serializers = preg_replace('/^.{2,}$/m', '            $0', join(",\n", $serializers));
169
170 4
        if ( ! empty($serializers)) {
171 4
            $serializers = "\n$serializers,\n        ";
172
        }
173
174
        return <<<EOF
175
    public static function fromPayload(array \$payload): Event
176
    {
177 4
        return new $name($arguments);
178
    }
179
180
    public function toPayload(): array
181
    {
182 4
        return [$serializers];
183
    }
184
EOF;
185
186
187
    }
188
189 2
    private function dumpTestHelpers(EventDefinition $event): string
190
    {
191 2
        $constructor = [];
192 2
        $constructorArguments = '';
193 2
        $constructorValues = [];
194 2
        $helpers = [];
195
196 2
        foreach ($this->fieldsFromDefinition($event) as $field) {
197 2
            if ($field['example'] === null) {
198 2
                $constructor[] = ucfirst($field['name']);
199
200 2
                if ($constructorArguments !== '') {
201 1
                    $constructorArguments .= ', ';
202
                }
203
204 2
                $constructorArguments .= sprintf('%s $%s', $field['type'], $field['name']);
205 2
                $constructorValues[] = sprintf('$%s', $field['name']);
206
            } else {
207 2
                $constructorValues[] = $this->dumpConstructorValue($field, $event);
208 2
                $method = sprintf('with%s', ucfirst($field['name']));
209 2
                $helpers[] = <<<EOF
210
    /**
211
     * @codeCoverageIgnore
212
     */
213 2
    public function $method({$field['type']} \${$field['name']}): {$event->name()}
214
    {
215 2
        \$this->{$field['name']} = \${$field['name']};
216
217
        return \$this;
218
    }
219
220
221
EOF;
222
            }
223
        }
224
225 2
        $constructor = sprintf('with%s', join('And', $constructor));
226 2
        $constructorValues = join(",\n            ", $constructorValues);
227 2
        $helpers[] = <<<EOF
228
    /**
229
     * @codeCoverageIgnore
230
     */
231 2
    public static function $constructor($constructorArguments): {$event->name()}
232
    {
233 2
        return new {$event->name()}(
234 2
            $constructorValues
235
        );
236
    }
237
238
239
EOF;
240
241
242 2
        return rtrim(join('', $helpers))."\n";
243
    }
244
245 2
    private function dumpConstructorValue(array $field, EventDefinition $event): string
246
    {
247 2
        $parameter = rtrim($field['example']);
248
249 2
        if (gettype($parameter) === $field['type']) {
250 1
            $parameter = var_export($parameter, true);
251
        }
252
253 2
        $template = $event->deserializerForField($field['name'])
254 2
            ?: $event->deserializerForType($field['type']);
255
256 2
        return rtrim(strtr($template, ['{type}' => $field['type'], '{param}' => $parameter]));
257
    }
258
259
    /**
260
     * @param CommandDefinition[] $commands
261
     * @return string
262
     */
263 5
    private function dumpCommands(array $commands): string
264
    {
265 5
        $code = [];
266
267 5
        foreach ($commands as $command) {
268 4
            $code[] = <<<EOF
269 5
final class {$command->name()}
270
{
271 5
{$this->dumpFields($command)}{$this->dumpConstructor($command)}{$this->dumpMethods($command)}}
272
273
274
EOF;
275
        }
276
277 4
        return rtrim(join('', $code));
278
    }
279
280
    /**
281
     * @param DefinitionWithFields $definition
282
     * @return array
283
     */
284 5
    private function fieldsFromDefinition(DefinitionWithFields $definition): array
285
    {
286 5
        $fields = $this->fieldsFrom($definition->fieldsFrom());
287
288 4
        foreach ($definition->fields() as $field) {
289 4
            array_push($fields, $field);
290
        }
291
292 4
        return $fields;
293
    }
294
295 5
    private function fieldsFrom(string $fieldsFrom): array
296
    {
297 5
        if (empty($fieldsFrom)) {
298 4
            return [];
299
        }
300
301 2
        foreach ($this->definitionGroup->events() as $event) {
302 1
            if ($event->name() === $fieldsFrom) {
303 1
                return $event->fields();
304
            }
305
        }
306
307 2
        foreach ($this->definitionGroup->commands() as $command) {
308 2
            if ($command->name() === $fieldsFrom) {
309 2
                return $command->fields();
310
            }
311
        }
312
313 1
        throw new LogicException("Could not inherit fields from {$fieldsFrom}.");
314
    }
315
}