ResourceGenerator::generateSerializer()   B
last analyzed

Complexity

Conditions 8
Paths 4

Size

Total Lines 51

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 72

Importance

Changes 0
Metric Value
dl 0
loc 51
ccs 0
cts 49
cp 0
rs 7.8246
c 0
b 0
f 0
cc 8
nc 4
nop 3
crap 72

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * generator code for resources
4
 */
5
6
namespace Graviton\GeneratorBundle\Generator;
7
8
use Graviton\GeneratorBundle\Definition\JsonDefinition;
9
use Graviton\GeneratorBundle\Definition\Schema\Solr;
10
use Graviton\GeneratorBundle\Generator\ResourceGenerator\FieldMapper;
11
use Graviton\GeneratorBundle\Generator\ResourceGenerator\ParameterBuilder;
12
use Symfony\Component\Filesystem\Filesystem;
13
use Symfony\Component\Yaml\Yaml;
14
15
/**
16
 * bundle containing various code generators
17
 *
18
 * This code is more or less loosley based on SensioBundleGenerator. It could
19
 * use some refactoring to duplicate less for that, but this is how i finally
20
 * got a working version.
21
 *
22
 * @author   List of contributors <https://github.com/libgraviton/graviton/graphs/contributors>
23
 * @license  https://opensource.org/licenses/MIT MIT License
24
 * @link     http://swisscom.ch
25
 */
26
class ResourceGenerator extends AbstractGenerator
27
{
28
    /**
29
     * @private Filesystem
30
     */
31
    private $filesystem;
32
33
    /**
34
     * our json file definition
35
     *
36
     * @var JsonDefinition|null
37
     */
38
    private $json = null;
39
40
    /**
41
     * @var array
42
     */
43
    protected $parameters = [];
44
45
    /**
46
     * @var string
47
     */
48
    protected $parametersFile;
49
50
    /**
51
     * @var array
52
     */
53
    protected $services = [];
54
55
    /**
56
     * @var string
57
     */
58
    protected $servicesFile;
59
60
    /**
61
     * @var FieldMapper
62
     */
63
    private $mapper;
64
65
    /**
66
     * @var boolean
67
     */
68
    private $generateController = false;
69
70
    /**
71
     * @var ParameterBuilder
72
     */
73
    private $parameterBuilder;
74
75
    /**
76
     * Instantiates generator object
77
     *
78
     * @param Filesystem       $filesystem       fs abstraction layer
79
     * @param FieldMapper      $mapper           field type mapper
80
     * @param ParameterBuilder $parameterBuilder parameter builder
81
     */
82
    public function __construct(
83
        Filesystem $filesystem,
84
        FieldMapper $mapper,
85
        ParameterBuilder $parameterBuilder
86
    ) {
87
        parent::__construct();
88
        $this->filesystem = $filesystem;
89
        $this->mapper = $mapper;
90
        $this->parameterBuilder = $parameterBuilder;
91
    }
92
93
    /**
94
     * @param JsonDefinition $json optional JsonDefinition object
95
     *
96
     * @return void
97
     */
98
    public function setJson(JsonDefinition $json)
99
    {
100
        $this->json = $json;
101
    }
102
103
    /**
104
     * @param boolean $generateController should the controller be generated or not
105
     *
106
     * @return void
107
     */
108
    public function setGenerateController($generateController)
109
    {
110
        $this->generateController = $generateController;
111
    }
112
113
    /**
114
     * generate the resource with all its bits and parts
115
     *
116
     * @param string $bundleDir       bundle dir
117
     * @param string $bundleNamespace bundle namespace
118
     * @param string $bundleName      bundle name
119
     * @param string $document        document name
120
     *
121
     * @return void
122
     */
123
    public function generate(
124
        $bundleDir,
125
        $bundleNamespace,
126
        $bundleName,
127
        $document
128
    ) {
129
        $this->readServicesAndParams($bundleDir);
130
131
        $basename = $this->getBundleBaseName($document);
132
133
        if (!is_null($this->json)) {
134
            $this->json->setNamespace($bundleNamespace);
135
        }
136
137
        // add more info to the fields array
138
        $mapper = $this->mapper;
139
        $fields = array_map(
140
            function ($field) use ($mapper) {
141
                return $mapper->map($field, $this->json);
142
            },
143
            $this->mapper->buildFields($this->json)
0 ignored issues
show
Bug introduced by
It seems like $this->json can be null; however, buildFields() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
144
        );
145
146
        $parameters = $this->parameterBuilder
147
            ->reset()
148
            ->setParameter('document', $document)
149
            ->setParameter('base', $bundleNamespace)
150
            ->setParameter('bundle', $bundleName)
151
            ->setParameter('json', $this->json)
152
            ->setParameter('fields', $fields)
153
            ->setParameter('basename', $basename)
154
            ->setParameter('isrecordOriginFlagSet', $this->json->isRecordOriginFlagSet())
155
            ->setParameter('recordOriginModifiable', $this->json->isRecordOriginModifiable())
156
            ->setParameter('isVersioning', $this->json->isVersionedService())
157
            ->setParameter('collection', $this->json->getServiceCollection())
158
            ->setParameter('indexes', $this->json->getIndexes())
159
            ->setParameter('textIndexes', $this->json->getAllTextIndexes())
160
            ->setParameter('solr', $this->json->getSolrFields())
161
            ->getParameters();
162
163
        $this->generateDocument($parameters, $bundleDir, $document);
164
        $this->generateSerializer($parameters, $bundleDir, $document);
165
        $this->generateModel($parameters, $bundleDir, $document);
166
167
        if ($this->json instanceof JsonDefinition && $this->json->hasFixtures() === true) {
168
            $this->generateFixtures($parameters, $bundleDir, $document);
169
        }
170
171
        if ($this->generateController) {
172
            $this->generateController($parameters, $bundleDir, $document);
173
        }
174
175
        $this->persistServicesAndParams();
176
    }
177
178
    /**
179
     * reads the services.yml file
180
     *
181
     * @param string $bundleDir bundle dir
182
     *
183
     * @return void
184
     */
185
    protected function readServicesAndParams($bundleDir)
186
    {
187
        $this->servicesFile = $bundleDir.'/Resources/config/services.yml';
188
        if ($this->fs->exists($this->servicesFile)) {
189
            $this->services = Yaml::parseFile($this->servicesFile);
190
        }
191
192
        if (!isset($this->services['services'])) {
193
            $this->services['services'] = [];
194
        }
195
196
        if (isset($this->services['parameters'])) {
197
            $this->parameters['parameters'] = $this->services['parameters'];
198
            unset($this->services['parameters']);
199
        } else {
200
            $this->parameters['parameters'] = [];
201
        }
202
    }
203
204
    /**
205
     * writes the services.yml file which includes the params
206
     *
207
     * @return void
208
     */
209
    protected function persistServicesAndParams()
210
    {
211
        $this->filesystem->dumpFile(
212
            $this->servicesFile,
213
            Yaml::dump(array_merge($this->parameters, $this->services))
214
        );
215
    }
216
217
    /**
218
     * generate document part of a resource
219
     *
220
     * @param array  $parameters twig parameters
221
     * @param string $dir        base bundle dir
222
     * @param string $document   document name
223
     *
224
     * @return void
225
     */
226
    protected function generateDocument($parameters, $dir, $document)
227
    {
228
        // doctrine mapping normal class
229
        $this->renderFile(
230
            'document/Document.mongodb.yml.twig',
231
            $dir . '/Resources/config/doctrine/' . $document . '.mongodb.yml',
232
            $parameters
233
        );
234
235
        // doctrine mapping embedded
236
        $this->renderFile(
237
            'document/Document.mongodb.yml.twig',
238
            $dir . '/Resources/config/doctrine/' . $document . 'Embedded.mongodb.yml',
239
            array_merge(
240
                $parameters,
241
                [
242
                    'document' => $document.'Embedded',
243
                    'docType' => 'embeddedDocument'
244
                ]
245
            )
246
        );
247
248
        $this->renderFile(
249
            'document/Document.php.twig',
250
            $dir . '/Document/' . $document . '.php',
251
            $parameters
252
        );
253
        $this->renderFile(
254
            'document/DocumentEmbedded.php.twig',
255
            $dir . '/Document/' . $document . 'Embedded.php',
256
            $parameters
257
        );
258
        $this->renderFile(
259
            'document/DocumentBase.php.twig',
260
            $dir . '/Document/' . $document . 'Base.php',
261
            $parameters
262
        );
263
264
        $this->generateServices($parameters, $dir, $document);
265
    }
266
267
    /**
268
     * update xml services
269
     *
270
     * @param array  $parameters twig parameters
271
     * @param string $dir        base bundle dir
272
     * @param string $document   document name
273
     *
274
     * @return void
275
     */
276
    protected function generateServices($parameters, $dir, $document)
277
    {
278
        $bundleParts = explode('\\', $parameters['base']);
279
        $shortName = $bundleParts[0];
280
        $shortBundle = $this->getBundleBaseName($bundleParts[1]);
281
282
        $docName = implode(
283
            '.',
284
            array(
285
                strtolower($shortName),
286
                strtolower($shortBundle),
287
                'document',
288
                strtolower($parameters['document'])
289
            )
290
        );
291
292
        $this->addParameter(
293
            $parameters['base'] . 'Document\\' . $parameters['document'],
294
            $docName . '.class'
295
        );
296
297
        $this->addService(
298
            $docName,
299
            null,
300
            [],
301
            null,
302
            [],
303
            null,
304
            null,
305
            '%'. $docName . '.class%'
306
        );
307
308
        $this->addParameter(
309
            (array) $parameters['json']->getRoles(),
310
            $docName . '.roles'
311
        );
312
313
        $repoName = implode(
314
            '.',
315
            array(
316
                strtolower($shortName),
317
                strtolower($shortBundle),
318
                'repository',
319
                strtolower($parameters['document'])
320
            )
321
        );
322
323
        $this->addService(
324
            $repoName,
325
            null,
326
            [],
327
            null,
328
            array(
329
                array(
330
                    'type' => 'string',
331
                    'value' => $parameters['bundle'] . ':' . $document
332
                )
333
            ),
334
            'doctrine_mongodb.odm.default_document_manager',
335
            'getRepository',
336
            'Doctrine\ODM\MongoDB\DocumentRepository'
337
        );
338
339
        $this->addService(
340
            $repoName . 'embedded',
341
            null,
342
            [],
343
            null,
344
            array(
345
                array(
346
                    'type' => 'string',
347
                    'value' => $parameters['bundle'] . ':' . $document . 'Embedded'
348
                )
349
            ),
350
            'doctrine_mongodb.odm.default_document_manager',
351
            'getRepository',
352
            'Doctrine\ODM\MongoDB\DocumentRepository'
353
        );
354
    }
355
356
    /**
357
     * Registers information to be generated to a parameter tag.
358
     *
359
     * @param mixed  $value Content of the tag
360
     * @param string $key   Content of the key attribute
361
     *
362
     * @return void
363
     */
364
    protected function addParameter($value, $key)
365
    {
366
        $this->parameters['parameters'][$key] = $value;
367
        return $this->parameters['parameters'];
368
    }
369
370
    /**
371
     * add service to services.yml
372
     *
373
     * @param string $id             id of new service
374
     * @param string $parent         parent for service
0 ignored issues
show
Documentation introduced by
Should the type for parameter $parent not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
375
     * @param array  $calls          methodCalls to add
376
     * @param string $tag            tag name or empty if no tag needed
0 ignored issues
show
Documentation introduced by
Should the type for parameter $tag not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
377
     * @param array  $arguments      service arguments
378
     * @param string $factoryService factory service id
0 ignored issues
show
Documentation introduced by
Should the type for parameter $factoryService not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
379
     * @param string $factoryMethod  factory method name
0 ignored issues
show
Documentation introduced by
Should the type for parameter $factoryMethod not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
380
     * @param string $className      class name to override
0 ignored issues
show
Documentation introduced by
Should the type for parameter $className not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
381
     *
382
     * @return void
383
     */
384
    protected function addService(
385
        $id,
386
        $parent = null,
387
        array $calls = [],
388
        $tag = null,
389
        array $arguments = [],
390
        $factoryService = null,
391
        $factoryMethod = null,
392
        $className = null
393
    ) {
394
        $service = [];
395
        $service['public'] = true;
396
397
        // classname
398
        if (is_null($className)) {
399
            $className = '%' . $id . '.class%';
400
        }
401
        $service['class'] = $className;
402
403
        // parent
404
        if (!is_null($parent)) {
405
            $service['parent'] = $parent;
406
        }
407
408
        // factory
409
        if ($factoryService && $factoryMethod) {
410
            $service['factory'] = [
411
                '@'.$factoryService,
412
                $factoryMethod
413
            ];
414
        }
415
416
        foreach ($arguments as $argument) {
417
            if ($argument['type'] == 'service') {
418
                $service['arguments'][] = '@'.$argument['id'];
419
            } else {
420
                $service['arguments'][] = $argument['value'];
421
            }
422
        }
423
424
        // calls
425
        foreach ($calls as $call) {
426
            $service['calls'][] = [
427
                $call['method'],
428
                ['@'.$call['service']]
429
            ];
430
        }
431
432
        // tags
433
        if ($tag) {
434
            $thisTag = [
435
                'name' => $tag
436
            ];
437
438
            if ($this->json instanceof JsonDefinition) {
439
                $thisTag['collection'] = $this->json->getId();
440
441
                // is this read only?
442
                if ($this->json->isReadOnlyService()) {
443
                    $thisTag['read-only'] = true;
444
                }
445
446
                // router base defined?
447
                $routerBase = $this->json->getRouterBase();
448
                if ($routerBase !== false) {
449
                    $thisTag['router-base'] = $routerBase;
450
                }
451
            }
452
453
            $service['tags'][] = $thisTag;
454
        }
455
456
        $this->services['services'][$id] = $service;
457
    }
458
459
    /**
460
     * generate serializer part of a resource
461
     *
462
     * @param array  $parameters twig parameters
463
     * @param string $dir        base bundle dir
464
     * @param string $document   document name
465
     *
466
     * @return void
467
     */
468
    protected function generateSerializer(array $parameters, $dir, $document)
469
    {
470
        // @TODO in Embedded and document just render the differences..
471
        $this->renderFile(
472
            'serializer/Document.xml.twig',
473
            $dir . '/Resources/config/serializer/Document.' . $document . 'Embedded.xml',
474
            array_merge(
475
                $parameters,
476
                [
477
                    'document' => $document.'Embedded',
478
                    'noIdField' => true,
479
                    'realIdField' => true
480
                ]
481
            )
482
        );
483
484
        foreach ($parameters['fields'] as $key => $field) {
485
            if (substr($field['serializerType'], 0, 14) == 'array<Graviton' &&
486
                strpos($field['serializerType'], '\\Entity') === false &&
487
                $field['relType'] == 'embed'
488
            ) {
489
                $parameters['fields'][$key]['serializerType'] = substr($field['serializerType'], 0, -1).'Embedded>';
490
            } elseif (substr($field['serializerType'], 0, 8) == 'Graviton' &&
491
                strpos($field['serializerType'], '\\Entity') === false &&
492
                $field['relType'] == 'embed'
493
            ) {
494
                $parameters['fields'][$key]['serializerType'] = $field['serializerType'].'Embedded';
495
            }
496
        }
497
        $this->renderFile(
498
            'serializer/Document.xml.twig',
499
            $dir . '/Resources/config/serializer/Document.' . $document . '.xml',
500
            array_merge(
501
                $parameters,
502
                [
503
                    'realIdField' => false
504
                ]
505
            )
506
        );
507
        $this->renderFile(
508
            'serializer/Document.xml.twig',
509
            $dir . '/Resources/config/serializer/Document.' . $document . 'Base.xml',
510
            array_merge(
511
                $parameters,
512
                [
513
                    'document' => $document.'Base',
514
                    'realIdField' => false
515
                ]
516
            )
517
        );
518
    }
519
520
    /**
521
     * generate model part of a resource
522
     *
523
     * @param array  $parameters twig parameters
524
     * @param string $dir        base bundle dir
525
     * @param string $document   document name
526
     *
527
     * @return void
528
     */
529
    protected function generateModel(array $parameters, $dir, $document)
530
    {
531
        $this->renderFile(
532
            'model/Model.php.twig',
533
            $dir . '/Model/' . $document . '.php',
534
            $parameters
535
        );
536
        $this->renderFile(
537
            'model/schema.json.twig',
538
            $dir . '/Resources/config/schema/' . $document . '.json',
539
            $parameters
540
        );
541
542
        // embedded versions
543
        $this->renderFile(
544
            'model/Model.php.twig',
545
            $dir . '/Model/' . $document . 'Embedded.php',
546
            array_merge($parameters, ['document' => $document.'Embedded'])
547
        );
548
        $this->renderFile(
549
            'model/schema.json.twig',
550
            $dir . '/Resources/config/schema/' . $document . 'Embedded.json',
551
            array_merge($parameters, ['document' => $document.'Embedded'])
552
        );
553
554
        $bundleParts = explode('\\', $parameters['base']);
555
        $shortName = strtolower($bundleParts[0]);
556
        $shortBundle = strtolower(substr($bundleParts[1], 0, -6));
557
        $paramName = implode('.', array($shortName, $shortBundle, 'model', strtolower($parameters['document'])));
558
        $repoName = implode('.', array($shortName, $shortBundle, 'repository', strtolower($parameters['document'])));
559
560
        $this->addParameter($parameters['base'] . 'Model\\' . $parameters['document'], $paramName . '.class');
561
562
        // normal service
563
        $this->addService(
564
            $paramName,
565
            'graviton.rest.model',
566
            array(
567
                [
568
                    'method' => 'setRepository',
569
                    'service' => $repoName
570
                ],
571
            ),
572
            null
573
        );
574
575
        // embedded service
576
        $this->addParameter(
577
            $parameters['base'] . 'Model\\' . $parameters['document'] . 'Embedded',
578
            $paramName . 'embedded.class'
579
        );
580
581
        $this->addService(
582
            $paramName . 'embedded',
583
            'graviton.rest.model',
584
            array(
585
                [
586
                    'method' => 'setRepository',
587
                    'service' => $repoName . 'embedded'
588
                ],
589
            ),
590
            null
591
        );
592
    }
593
594
    /**
595
     * generate RESTful controllers ans service configs
596
     *
597
     * @param array  $parameters twig parameters
598
     * @param string $dir        base bundle dir
599
     * @param string $document   document name
600
     *
601
     * @return void
602
     */
603
    protected function generateController(array $parameters, $dir, $document)
604
    {
605
        $this->renderFile(
606
            'controller/DocumentController.php.twig',
607
            $dir . '/Controller/' . $document . 'Controller.php',
608
            $parameters
609
        );
610
611
        $bundleParts = explode('\\', $parameters['base']);
612
        $shortName = strtolower($bundleParts[0]);
613
        $shortBundle = strtolower(substr($bundleParts[1], 0, -6));
614
        $paramName = implode('.', array($shortName, $shortBundle, 'controller', strtolower($parameters['document'])));
615
616
        $this->addParameter(
617
            $parameters['base'] . 'Controller\\' . $parameters['document'] . 'Controller',
618
            $paramName . '.class'
619
        );
620
621
        $this->addService(
622
            $paramName,
623
            $parameters['parent'],
624
            array(
625
                array(
626
                    'method' => 'setModel',
627
                    'service' => implode(
628
                        '.',
629
                        array($shortName, $shortBundle, 'model', strtolower($parameters['document']))
630
                    )
631
                )
632
            ),
633
            'graviton.rest'
634
        );
635
    }
636
637
    /**
638
     * generates fixtures
639
     *
640
     * @param array  $parameters twig parameters
641
     * @param string $dir        base bundle dir
642
     * @param string $document   document name
643
     *
644
     * @return void
645
     */
646
    protected function generateFixtures(array $parameters, $dir, $document)
647
    {
648
        $parameters['fixtures_json'] = addcslashes(json_encode($this->json->getFixtures()), "'");
649
        $parameters['fixtureOrder'] = $this->json->getFixtureOrder();
650
651
        $this->renderFile(
652
            'fixtures/LoadFixtures.php.twig',
653
            $dir . '/DataFixtures/MongoDB/Load' . $document . 'Data.php',
654
            $parameters
655
        );
656
    }
657
}
658