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