Completed
Push — master ( 5b8e04...dae852 )
by Frank
01:56
created

CodeDumper::fieldsFrom()   B

Complexity

Conditions 6
Paths 8

Size

Total Lines 20
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 6.0359

Importance

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