Completed
Push — feature/update-deps ( d0adde...e3b3ac )
by Narcotic
07:43
created

ResourceGenerator   C

Complexity

Total Complexity 60

Size/Duplication

Total Lines 933
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Test Coverage

Coverage 44.49%

Importance

Changes 0
Metric Value
wmc 60
lcom 1
cbo 7
dl 0
loc 933
ccs 210
cts 472
cp 0.4449
rs 5
c 0
b 0
f 0

24 Methods

Rating   Name   Duplication   Size   Complexity  
A parameterNodeExists() 0 7 1
B addNodeIfMissing() 0 26 4
A addAttributeToNode() 0 8 2
A addCallsToService() 0 6 2
A addCallToService() 0 22 1
A addArgumentsToService() 0 6 2
A addArgumentToService() 0 20 2
A __construct() 0 14 1
A setJson() 0 4 1
A setGenerateController() 0 4 1
B generate() 0 59 5
A persistServicesXML() 0 6 1
B generateDocument() 0 40 1
B generateServices() 0 110 1
B generateParameters() 0 19 5
A addXmlParameter() 0 16 3
A loadServices() 0 11 2
A addParam() 0 14 2
A addCollectionParam() 0 19 4
C addService() 0 64 8
C generateSerializer() 0 51 8
A generateModel() 0 72 1
B generateController() 0 39 1
A generateFixtures() 0 11 1

How to fix   Complexity   

Complex Class

Complex classes like ResourceGenerator often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ResourceGenerator, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * generator code for resources
4
 */
5
6
namespace Graviton\GeneratorBundle\Generator;
7
8
use Sensio\Bundle\GeneratorBundle\Model\EntityGeneratorResult;
9
use Doctrine\Common\Collections\ArrayCollection;
10
use Graviton\GeneratorBundle\Definition\JsonDefinition;
11
use Graviton\GeneratorBundle\Generator\ResourceGenerator\FieldMapper;
12
use Graviton\GeneratorBundle\Generator\ResourceGenerator\ParameterBuilder;
13
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
14
use Symfony\Component\Filesystem\Filesystem;
15
use Symfony\Component\HttpKernel\HttpKernelInterface;
16
use Doctrine\Bundle\DoctrineBundle\Registry as DoctrineRegistry;
17
18
/**
19
 * bundle containing various code generators
20
 *
21
 * This code is more or less loosley based on SensioBundleGenerator. It could
22
 * use some refactoring to duplicate less for that, but this is how i finally
23
 * got a working version.
24
 *
25
 * @author   List of contributors <https://github.com/libgraviton/graviton/graphs/contributors>
26
 * @license  http://opensource.org/licenses/gpl-license.php GNU Public License
27
 * @link     http://swisscom.ch
28
 */
29
class ResourceGenerator extends AbstractGenerator
30
{
31
    /**
32
     * @private Filesystem
33
     */
34
    private $filesystem;
35
36
    /**
37
     * @private DoctrineRegistry
38
     */
39
    private $doctrine;
40
41
    /**
42
     * @private HttpKernelInterface
43
     */
44
    private $kernel;
45
46
    /**
47
     * our json file definition
48
     *
49
     * @var JsonDefinition|null
50
     */
51
    private $json = null;
52
53
    /**
54
     * @var ArrayCollection
55
     */
56
    protected $xmlParameters;
57
58
    /**
59
     * @var \DomDocument
60
     */
61
    private $serviceDOM;
62
63
    /**
64
     * @var FieldMapper
65
     */
66
    private $mapper;
67
68
    /**
69
     * @var boolean
70
     */
71
    private $generateController = false;
72
73
    /**
74
     * @var ParameterBuilder
75
     */
76
    private $parameterBuilder;
77
78
    /**
79
     * Instantiates generator object
80
     *
81
     * @param Filesystem          $filesystem       fs abstraction layer
82
     * @param DoctrineRegistry    $doctrine         odm registry
83
     * @param HttpKernelInterface $kernel           app kernel
84
     * @param FieldMapper         $mapper           field type mapper
85
     * @param ParameterBuilder    $parameterBuilder param builder
86
     */
87 4
    public function __construct(
88
        Filesystem $filesystem,
89
        DoctrineRegistry $doctrine,
90
        HttpKernelInterface $kernel,
91
        FieldMapper $mapper,
92
        ParameterBuilder $parameterBuilder
93
    ) {
94 4
        $this->filesystem = $filesystem;
95 4
        $this->doctrine = $doctrine;
96 4
        $this->kernel = $kernel;
97 4
        $this->mapper = $mapper;
98 4
        $this->parameterBuilder = $parameterBuilder;
99 4
        $this->xmlParameters = new ArrayCollection();
100 4
    }
101
102
    /**
103
     * @param JsonDefinition $json optional JsonDefinition object
104
     *
105
     * @return void
106
     */
107
    public function setJson(JsonDefinition $json)
108
    {
109
        $this->json = $json;
110
    }
111
112
    /**
113
     * @param boolean $generateController should the controller be generated or not
114
     *
115
     * @return void
116
     */
117
    public function setGenerateController($generateController)
118
    {
119
        $this->generateController = $generateController;
120
    }
121
122
    /**
123
     * generate the resource with all its bits and parts
124
     *
125
     * @param BundleInterface $bundle   bundle
126
     * @param string          $document document name
127
     * @param string          $format   format of config files (please use xml)
128
     * @param array           $fields   fields to add
129
     *
130
     * @return EntityGeneratorResult
131
     */
132
    public function generate(
133
        BundleInterface $bundle,
134
        $document,
135
        $format,
136
        array $fields
137
    ) {
138
        $dir = $bundle->getPath();
139
        $basename = $this->getBundleBaseName($document);
140
        $bundleNamespace = substr(get_class($bundle), 0, 0 - strlen($bundle->getName()));
141
142
        if (!is_null($this->json)) {
143
            $this->json->setNamespace($bundleNamespace);
144
        }
145
146
        // add more info to the fields array
147
        $mapper = $this->mapper;
148
        $fields = array_map(
149
            function ($field) use ($mapper) {
150
                return $mapper->map($field, $this->json);
151
            },
152
            $fields
153
        );
154
155
        $parameters = $this->parameterBuilder
156
            ->setParameter('document', $document)
157
            ->setParameter('base', $bundleNamespace)
158
            ->setParameter('bundle', $bundle->getName())
159
            ->setParameter('format', $format)
160
            ->setParameter('json', $this->json)
161
            ->setParameter('fields', $fields)
162
            ->setParameter('basename', $basename)
163
            ->setParameter('isrecordOriginFlagSet', $this->json->isRecordOriginFlagSet())
164
            ->setParameter('recordOriginModifiable', $this->json->isRecordOriginModifiable())
165
            ->setParameter('isVersioning', $this->json->isVersionedService())
166
            ->setParameter('collection', $this->json->getServiceCollection())
167
            ->setParameter('indexes', $this->json->getIndexes())
168
            ->setParameter('textIndexes', $this->json->getAllTextIndexes())
169
            ->getParameters();
170
171
        $this->generateDocument($parameters, $dir, $document);
172
        $this->generateSerializer($parameters, $dir, $document);
173
        $this->generateModel($parameters, $dir, $document);
174
175
        if ($this->json instanceof JsonDefinition && $this->json->hasFixtures() === true) {
176
            $this->generateFixtures($parameters, $dir, $document);
177
        }
178
179
        if ($this->generateController) {
180
            $this->generateController($parameters, $dir, $document);
181
        }
182
183
        $this->generateParameters($dir);
184
185
        return new EntityGeneratorResult(
186
            $dir . '/Document/' . $document . '.php',
187
            $dir . '/Repository/' . $document . 'Repository.php',
188
            $dir . '/Resources/config/doctrine/' . $document . '.mongodb.xml'
189
        );
190
    }
191
192
    /**
193
     * Writes the current services definition to a file.
194
     *
195
     * @param string $dir base bundle dir
196
     *
197
     * @return void
198
     */
199 8
    protected function persistServicesXML($dir)
200
    {
201 8
        $services = $this->loadServices($dir);
202
203 8
        file_put_contents($dir . '/Resources/config/services.xml', $services->saveXML());
204 8
    }
205
206
    /**
207
     * generate document part of a resource
208
     *
209
     * @param array  $parameters twig parameters
210
     * @param string $dir        base bundle dir
211
     * @param string $document   document name
212
     *
213
     * @return void
214
     */
215 6
    protected function generateDocument($parameters, $dir, $document)
216
    {
217
        // doctrine mapping normal class
218 6
        $this->renderFile(
219 6
            'document/Document.mongodb.xml.twig',
220 6
            $dir . '/Resources/config/doctrine/' . $document . '.mongodb.xml',
221
            $parameters
222 3
        );
223
224
        // doctrine mapping embedded
225 6
        $this->renderFile(
226 6
            'document/Document.mongodb.xml.twig',
227 6
            $dir . '/Resources/config/doctrine/' . $document . 'Embedded.mongodb.xml',
228 3
            array_merge(
229 3
                $parameters,
230
                [
231 6
                    'document' => $document.'Embedded',
232 3
                    'docType' => 'embedded-document'
233 3
                ]
234 3
            )
235 3
        );
236
237 6
        $this->renderFile(
238 6
            'document/Document.php.twig',
239 6
            $dir . '/Document/' . $document . '.php',
240
            $parameters
241 3
        );
242 6
        $this->renderFile(
243 6
            'document/DocumentEmbedded.php.twig',
244 6
            $dir . '/Document/' . $document . 'Embedded.php',
245
            $parameters
246 3
        );
247 6
        $this->renderFile(
248 6
            'document/DocumentBase.php.twig',
249 6
            $dir . '/Document/' . $document . 'Base.php',
250
            $parameters
251 3
        );
252
253 6
        $this->generateServices($parameters, $dir, $document);
254 6
    }
255
256
    /**
257
     * update xml services
258
     *
259
     * @param array  $parameters twig parameters
260
     * @param string $dir        base bundle dir
261
     * @param string $document   document name
262
     *
263
     * @return void
264
     */
265 6
    protected function generateServices($parameters, $dir, $document)
266
    {
267 6
        $services = $this->loadServices($dir);
268
269 6
        $bundleParts = explode('\\', $parameters['base']);
270 6
        $shortName = $bundleParts[0];
271 6
        $shortBundle = $this->getBundleBaseName($bundleParts[1]);
272
273 6
        $docName = implode(
274 6
            '.',
275
            array(
276 6
                strtolower($shortName),
277 6
                strtolower($shortBundle),
278 6
                'document',
279 6
                strtolower($parameters['document'])
280 3
            )
281 3
        );
282
283 6
        $this->addXMLParameter(
284 6
            $parameters['base'] . 'Document\\' . $parameters['document'],
285 3
            $docName . '.class'
286 3
        );
287
288 6
        $this->addXMLParameter(
289 6
            $parameters['json']->getRoles(),
290 6
            $docName . '.roles',
291 3
            'collection'
292 3
        );
293
294 6
        $services = $this->addService(
295 3
            $services,
296
            $docName
297 3
        );
298
299 6
        $repoName = implode(
300 6
            '.',
301
            array(
302 6
                strtolower($shortName),
303 6
                strtolower($shortBundle),
304 6
                'repository',
305 6
                strtolower($parameters['document'])
306 3
            )
307 3
        );
308
309
        // normal repo service
310 6
        $services = $this->addParam(
311 3
            $services,
312 6
            $repoName . '.class',
313 6
            $parameters['base'] . 'Repository\\' . $parameters['document']
314 3
        );
315
316 6
        $this->addService(
317 3
            $services,
318 3
            $repoName,
319 6
            null,
320 6
            null,
321 6
            array(),
322 6
            null,
323
            array(
324
                array(
325 6
                    'type' => 'string',
326 6
                    'value' => $parameters['bundle'] . ':' . $document
327 3
                )
328 3
            ),
329 6
            'doctrine_mongodb.odm.default_document_manager',
330 3
            'getRepository'
331 3
        );
332
333
        // embedded repo service
334 6
        $services = $this->addParam(
335 3
            $services,
336 6
            $repoName . 'embedded.class',
337 6
            $parameters['base'] . 'Repository\\' . $parameters['document'] . 'Embedded'
338 3
        );
339
340 6
        $this->addService(
341 3
            $services,
342 6
            $repoName . 'embedded',
343 6
            null,
344 6
            null,
345 6
            array(),
346 6
            null,
347
            array(
348
                array(
349 6
                    'type' => 'string',
350 6
                    'value' => $parameters['bundle'] . ':' . $document . 'Embedded'
351 3
                )
352 3
            ),
353 6
            'doctrine_mongodb.odm.default_document_manager',
354 3
            'getRepository'
355 3
        );
356
357 6
        $this->renderFile(
358 6
            'document/DocumentRepository.php.twig',
359 6
            $dir . '/Repository/' . $document . 'Repository.php',
360
            $parameters
361 3
        );
362 6
        $this->renderFile(
363 6
            'document/DocumentRepository.php.twig',
364 6
            $dir . '/Repository/' . $document . 'EmbeddedRepository.php',
365 3
            array_merge(
366 3
                $parameters,
367
                [
368 6
                    'document' => $document.'Embedded',
369
                ]
370 3
            )
371 3
        );
372
373 6
        $this->persistServicesXML($dir);
374 6
    }
375
376
    /**
377
     * Generates the parameters section of the services.xml file.
378
     *
379
     * @param string $dir base bundle dir
380
     *
381
     * @return void
382
     */
383 2
    protected function generateParameters($dir)
384
    {
385 2
        if ($this->xmlParameters->count() > 0) {
386 2
            $services = $this->loadServices($dir);
387
388 2
            foreach ($this->xmlParameters as $parameter) {
389 2
                switch ($parameter['type']) {
390 2
                    case 'collection':
391 2
                        $this->addCollectionParam($services, $parameter['key'], $parameter['content']);
392 2
                        break;
393 2
                    case 'string':
394 1
                    default:
395 2
                        $this->addParam($services, $parameter['key'], $parameter['content']);
396 1
                }
397 1
            }
398 1
        }
399
400 2
        $this->persistServicesXML($dir);
401 2
    }
402
403
    /**
404
     * Registers information to be generated to a parameter tag.
405
     *
406
     * @param mixed  $value Content of the tag
407
     * @param string $key   Content of the key attribute
408
     * @param string $type  Type of the tag
409
     *
410
     * @return void
411
     */
412 2
    protected function addXmlParameter($value, $key, $type = 'string')
413
    {
414
        $element = array(
415 2
            'content' => $value,
416 2
            'key' => $key,
417 2
            'type' => strtolower($type),
418 1
        );
419
420 2
        if (!isset($this->xmlParameters)) {
421
            $this->xmlParameters = new ArrayCollection();
422
        }
423
424 2
        if (!$this->xmlParameters->contains($element)) {
425 2
            $this->xmlParameters->add($element);
426 1
        }
427 2
    }
428
429
    /**
430
     * load services.xml
431
     *
432
     * @param string $dir base dir
433
     *
434
     * @return \DOMDocument
435
     */
436 4
    protected function loadServices($dir)
437
    {
438 4
        if (empty($this->serviceDOM)) {
439 4
            $this->serviceDOM = new \DOMDocument;
440 4
            $this->serviceDOM->formatOutput = true;
441 4
            $this->serviceDOM->preserveWhiteSpace = false;
442 4
            $this->serviceDOM->load($dir . '/Resources/config/services.xml');
443 2
        }
444
445 4
        return $this->serviceDOM;
446
    }
447
448
    /**
449
     * add param to services.xml
450
     *
451
     * @param \DOMDocument $dom   services.xml document
452
     * @param string       $key   parameter key
453
     * @param string       $value parameter value
454
     *
455
     * @return \DOMDocument
456
     */
457 12
    protected function addParam(\DOMDocument $dom, $key, $value)
458
    {
459 12
        $paramNode = $this->addNodeIfMissing($dom, 'parameters', '//services');
460
461 12
        if (!$this->parameterNodeExists($dom, $key)) {
462 12
            $attrNode = $dom->createElement('parameter', $value);
463
464 12
            $this->addAttributeToNode('key', $key, $dom, $attrNode);
465
466 12
            $paramNode->appendChild($attrNode);
467 6
        }
468
469 12
        return $dom;
470
    }
471
472
    /**
473
     * Adds a new parameter tag to parameters section reflecting the defined roles.
474
     *
475
     * @param \DOMDocument $dom    services.xml document
476
     * @param string       $key    parameter key
477
     * @param array        $values parameter value
478
     *
479
     * @return void
480
     *
481
     * @link http://symfony.com/doc/current/book/service_container.html#array-parameters
482
     */
483 4
    protected function addCollectionParam(\DomDocument $dom, $key, array $values)
484
    {
485 4
        $paramNode = $this->addNodeIfMissing($dom, 'parameters', '//services');
486
487 4
        if (!$this->parameterNodeExists($dom, $key)) {
488 4
            if (!empty($values)) {
489 4
                $rolesNode = $dom->createElement('parameter');
490 4
                $this->addAttributeToNode('key', $key, $dom, $rolesNode);
491 4
                $this->addAttributeToNode('type', 'collection', $dom, $rolesNode);
492
493 4
                foreach ($values as $item) {
494 4
                    $roleNode = $dom->createElement('parameter', $item);
495 4
                    $rolesNode->appendChild($roleNode);
496 2
                }
497
498 4
                $paramNode->appendChild($rolesNode);
499 2
            }
500 2
        }
501 4
    }
502
503
    /**
504
     * Determines, if the provided key attribute was already claimed by a parameter node.
505
     *
506
     * @param \DomDocument $dom Current document
507
     * @param string       $key Key to be found in document
508
     *
509
     * @return bool
510
     */
511 14
    private function parameterNodeExists(\DomDocument $dom, $key)
0 ignored issues
show
Coding Style introduced by
function parameterNodeExists() does not seem to conform to the naming convention (^(?:is|has|should|may|supports)).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
512
    {
513 14
        $xpath = new \DomXpath($dom);
514 14
        $nodes = $xpath->query('//parameters/parameter[@key="' . $key . '"]');
515
516 14
        return $nodes->length > 0;
517
    }
518
519
    /**
520
     * add node if missing
521
     *
522
     * @param \DOMDocument $dom          document
523
     * @param string       $element      name for new node element
524
     * @param string       $insertBefore xPath query of the new node shall be added before
525
     * @param string       $container    name of container tag
526
     *
527
     * @return \DOMNode new element node
528
     */
529 14
    private function addNodeIfMissing(&$dom, $element, $insertBefore = '', $container = 'container')
530
    {
531 14
        $container = $dom->getElementsByTagName($container)
532 14
            ->item(0);
533 14
        $nodes = $dom->getElementsByTagName($element);
534 14
        if ($nodes->length < 1) {
535 14
            $newNode = $dom->createElement($element);
536
537 14
            if (!empty($insertBefore)) {
538 14
                $xpath = new \DomXpath($dom);
539 14
                $found = $xpath->query($insertBefore);
540
541 14
                if ($found->length > 0) {
542
                    $container->insertBefore($newNode, $found->item(0));
543
                } else {
544 14
                    $container->appendChild($newNode);
545
                }
546 7
            } else {
547 7
                $container->appendChild($newNode);
548
            }
549 7
        } else {
550 4
            $newNode = $nodes->item(0);
551
        }
552
553 14
        return $newNode;
554
    }
555
556
    /**
557
     * add attribute to node if needed
558
     *
559
     * @param string       $name  attribute name
560
     * @param string       $value attribute value
561
     * @param \DOMDocument $dom   document
562
     * @param \DOMElement  $node  parent node
563
     *
564
     * @return void
565
     */
566 14
    private function addAttributeToNode($name, $value, $dom, $node)
567
    {
568 14
        if ($value) {
569 14
            $attr = $dom->createAttribute($name);
570 14
            $attr->value = $value;
571 14
            $node->appendChild($attr);
572 7
        }
573 14
    }
574
575
    /**
576
     * add service to services.xml
577
     *
578
     * @param \DOMDocument $dom            services.xml dom
579
     * @param string       $id             id of new service
580
     * @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...
581
     * @param string       $scope          scope of service
0 ignored issues
show
Documentation introduced by
Should the type for parameter $scope 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...
582
     * @param array        $calls          methodCalls to add
583
     * @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...
584
     * @param array        $arguments      service arguments
585
     * @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...
586
     * @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...
587
     *
588
     * @return \DOMDocument
589
     */
590
    protected function addService(
591
        $dom,
592
        $id,
593
        $parent = null,
594
        $scope = null,
595
        array $calls = array(),
596
        $tag = null,
597
        array $arguments = array(),
598
        $factoryService = null,
599
        $factoryMethod = null
600
    ) {
601
        $servicesNode = $this->addNodeIfMissing($dom, 'services');
602
603
        $xpath = new \DomXpath($dom);
604
605
        // add controller to services
606
        $nodes = $xpath->query('//services/service[@id="' . $id . '"]');
607
        if ($nodes->length < 1) {
608
            $attrNode = $dom->createElement('service');
609
610
            $this->addAttributeToNode('id', $id, $dom, $attrNode);
611
            $this->addAttributeToNode('class', '%' . $id . '.class%', $dom, $attrNode);
612
            $this->addAttributeToNode('parent', $parent, $dom, $attrNode);
613
            $this->addAttributeToNode('scope', $scope, $dom, $attrNode);
614
            if ($factoryService && $factoryMethod) {
615
                $factoryNode = $dom->createElement('factory');
616
                $this->addAttributeToNode('service', $factoryService, $dom, $factoryNode);
617
                $this->addAttributeToNode('method', $factoryMethod, $dom, $factoryNode);
618
                $attrNode->appendChild($factoryNode);
619
            }
620
            $this->addCallsToService($calls, $dom, $attrNode);
621
622
            if ($tag) {
623
                $tagNode = $dom->createElement('tag');
624
625
                $this->addAttributeToNode('name', $tag, $dom, $tagNode);
626
627
                // get stuff from json definition
628
                if ($this->json instanceof JsonDefinition) {
629
                    // id is also name of collection in mongodb
630
                    $this->addAttributeToNode('collection', $this->json->getId(), $dom, $tagNode);
631
632
                    // is this read only?
633
                    if ($this->json->isReadOnlyService()) {
634
                        $this->addAttributeToNode('read-only', 'true', $dom, $tagNode);
635
                    }
636
637
                    // router base defined?
638
                    $routerBase = $this->json->getRouterBase();
639
                    if ($routerBase !== false) {
640
                        $this->addAttributeToNode('router-base', $routerBase, $dom, $tagNode);
641
                    }
642
                }
643
644
                $attrNode->appendChild($tagNode);
645
            }
646
647
            $this->addArgumentsToService($arguments, $dom, $attrNode);
648
649
            $servicesNode->appendChild($attrNode);
650
        }
651
652
        return $dom;
653
    }
654
655
    /**
656
     * add calls to service
657
     *
658
     * @param array        $calls info on calls to create
659
     * @param \DOMDocument $dom   current domdocument
660
     * @param \DOMElement  $node  node to add call to
661
     *
662
     * @return void
663
     */
664
    private function addCallsToService($calls, $dom, $node)
665
    {
666
        foreach ($calls as $call) {
667
            $this->addCallToService($call, $dom, $node);
668
        }
669
    }
670
671
    /**
672
     * add call to service
673
     *
674
     * @param array        $call info on call node to create
675
     * @param \DOMDocument $dom  current domdocument
676
     * @param \DOMElement  $node node to add call to
677
     *
678
     * @return void
679
     */
680
    private function addCallToService($call, $dom, $node)
681
    {
682
        $callNode = $dom->createElement('call');
683
684
        $attr = $dom->createAttribute('method');
685
        $attr->value = $call['method'];
686
        $callNode->appendChild($attr);
687
688
        $argNode = $dom->createElement('argument');
689
690
        $attr = $dom->createAttribute('type');
691
        $attr->value = 'service';
692
        $argNode->appendChild($attr);
693
694
        $attr = $dom->createAttribute('id');
695
        $attr->value = $call['service'];
696
        $argNode->appendChild($attr);
697
698
        $callNode->appendChild($argNode);
699
700
        $node->appendChild($callNode);
701
    }
702
703
    /**
704
     * add arguments to servie
705
     *
706
     * @param array        $arguments arguments to create
707
     * @param \DOMDocument $dom       dom document to add to
708
     * @param \DOMElement  $node      node to use as parent
709
     *
710
     * @return void
711
     */
712
    private function addArgumentsToService($arguments, $dom, $node)
713
    {
714
        foreach ($arguments as $argument) {
715
            $this->addArgumentToService($argument, $dom, $node);
716
        }
717
    }
718
719
    /**
720
     * add argument to service
721
     *
722
     * @param array        $argument info on argument to create
723
     * @param \DOMDocument $dom      dom document to add to
724
     * @param \DOMElement  $node     node to use as parent
725
     *
726
     * @return void
727
     */
728
    private function addArgumentToService($argument, $dom, $node)
729
    {
730
        $isService = $argument['type'] == 'service';
731
732
        if ($isService) {
733
            $argNode = $dom->createElement('argument');
734
735
            $idArg = $dom->createAttribute('id');
736
            $idArg->value = $argument['id'];
737
            $argNode->appendChild($idArg);
738
        } else {
739
            $argNode = $dom->createElement('argument', $argument['value']);
740
        }
741
742
        $argType = $dom->createAttribute('type');
743
        $argType->value = $argument['type'];
744
        $argNode->appendChild($argType);
745
746
        $node->appendChild($argNode);
747
    }
748
749
    /**
750
     * generate serializer part of a resource
751
     *
752
     * @param array  $parameters twig parameters
753
     * @param string $dir        base bundle dir
754
     * @param string $document   document name
755
     *
756
     * @return void
757
     */
758
    protected function generateSerializer(array $parameters, $dir, $document)
759
    {
760
        // @TODO in Embedded and document just render the differences..
761
        $this->renderFile(
762
            'serializer/Document.xml.twig',
763
            $dir . '/Resources/config/serializer/Document.' . $document . 'Embedded.xml',
764
            array_merge(
765
                $parameters,
766
                [
767
                    'document' => $document.'Embedded',
768
                    'noIdField' => true,
769
                    'realIdField' => true
770
                ]
771
            )
772
        );
773
774
        foreach ($parameters['fields'] as $key => $field) {
775
            if (substr($field['serializerType'], 0, 14) == 'array<Graviton' &&
776
                strpos($field['serializerType'], '\\Entity') === false &&
777
                $field['relType'] == 'embed'
778
            ) {
779
                $parameters['fields'][$key]['serializerType'] = substr($field['serializerType'], 0, -1).'Embedded>';
780
            } elseif (substr($field['serializerType'], 0, 8) == 'Graviton' &&
781
                strpos($field['serializerType'], '\\Entity') === false &&
782
                $field['relType'] == 'embed'
783
            ) {
784
                $parameters['fields'][$key]['serializerType'] = $field['serializerType'].'Embedded';
785
            }
786
        }
787
        $this->renderFile(
788
            'serializer/Document.xml.twig',
789
            $dir . '/Resources/config/serializer/Document.' . $document . '.xml',
790
            array_merge(
791
                $parameters,
792
                [
793
                    'realIdField' => false
794
                ]
795
            )
796
        );
797
        $this->renderFile(
798
            'serializer/Document.xml.twig',
799
            $dir . '/Resources/config/serializer/Document.' . $document . 'Base.xml',
800
            array_merge(
801
                $parameters,
802
                [
803
                    'document' => $document.'Base',
804
                    'realIdField' => false
805
                ]
806
            )
807
        );
808
    }
809
810
    /**
811
     * generate model part of a resource
812
     *
813
     * @param array  $parameters twig parameters
814
     * @param string $dir        base bundle dir
815
     * @param string $document   document name
816
     *
817
     * @return void
818
     */
819
    protected function generateModel(array $parameters, $dir, $document)
820
    {
821
        $this->renderFile(
822
            'model/Model.php.twig',
823
            $dir . '/Model/' . $document . '.php',
824
            $parameters
825
        );
826
        $this->renderFile(
827
            'model/schema.json.twig',
828
            $dir . '/Resources/config/schema/' . $document . '.json',
829
            $parameters
830
        );
831
832
        // embedded versions
833
        $this->renderFile(
834
            'model/Model.php.twig',
835
            $dir . '/Model/' . $document . 'Embedded.php',
836
            array_merge($parameters, ['document' => $document.'Embedded'])
837
        );
838
        $this->renderFile(
839
            'model/schema.json.twig',
840
            $dir . '/Resources/config/schema/' . $document . 'Embedded.json',
841
            array_merge($parameters, ['document' => $document.'Embedded'])
842
        );
843
844
        $services = $this->loadServices($dir);
845
846
        $bundleParts = explode('\\', $parameters['base']);
847
        $shortName = strtolower($bundleParts[0]);
848
        $shortBundle = strtolower(substr($bundleParts[1], 0, -6));
849
        $paramName = implode('.', array($shortName, $shortBundle, 'model', strtolower($parameters['document'])));
850
        $repoName = implode('.', array($shortName, $shortBundle, 'repository', strtolower($parameters['document'])));
851
852
        $this->addXmlParameter($parameters['base'] . 'Model\\' . $parameters['document'], $paramName . '.class');
853
854
        // normal service
855
        $this->addService(
856
            $services,
857
            $paramName,
858
            'graviton.rest.model',
859
            null,
860
            array(
861
                [
862
                    'method' => 'setRepository',
863
                    'service' => $repoName
864
                ],
865
            ),
866
            null
867
        );
868
869
        // embedded service
870
        $this->addXmlParameter(
871
            $parameters['base'] . 'Model\\' . $parameters['document'] . 'Embedded',
872
            $paramName . 'embedded.class'
873
        );
874
875
        $this->addService(
876
            $services,
877
            $paramName . 'embedded',
878
            'graviton.rest.model',
879
            null,
880
            array(
881
                [
882
                    'method' => 'setRepository',
883
                    'service' => $repoName . 'embedded'
884
                ],
885
            ),
886
            null
887
        );
888
889
        $this->persistServicesXML($dir);
890
    }
891
892
    /**
893
     * generate RESTful controllers ans service configs
894
     *
895
     * @param array  $parameters twig parameters
896
     * @param string $dir        base bundle dir
897
     * @param string $document   document name
898
     *
899
     * @return void
900
     */
901
    protected function generateController(array $parameters, $dir, $document)
902
    {
903
        $this->renderFile(
904
            'controller/DocumentController.php.twig',
905
            $dir . '/Controller/' . $document . 'Controller.php',
906
            $parameters
907
        );
908
909
        $services = $this->loadServices($dir);
910
911
        $bundleParts = explode('\\', $parameters['base']);
912
        $shortName = strtolower($bundleParts[0]);
913
        $shortBundle = strtolower(substr($bundleParts[1], 0, -6));
914
        $paramName = implode('.', array($shortName, $shortBundle, 'controller', strtolower($parameters['document'])));
915
916
        $this->addXmlParameter(
917
            $parameters['base'] . 'Controller\\' . $parameters['document'] . 'Controller',
918
            $paramName . '.class'
919
        );
920
921
        $this->addService(
922
            $services,
923
            $paramName,
924
            $parameters['parent'],
925
            'request',
926
            array(
927
                array(
928
                    'method' => 'setModel',
929
                    'service' => implode(
930
                        '.',
931
                        array($shortName, $shortBundle, 'model', strtolower($parameters['document']))
932
                    )
933
                )
934
            ),
935
            'graviton.rest'
936
        );
937
938
        $this->persistServicesXML($dir);
939
    }
940
941
    /**
942
     * generates fixtures
943
     *
944
     * @param array  $parameters twig parameters
945
     * @param string $dir        base bundle dir
946
     * @param string $document   document name
947
     *
948
     * @return void
949
     */
950
    protected function generateFixtures(array $parameters, $dir, $document)
951
    {
952
        $parameters['fixtures_json'] = addcslashes(json_encode($this->json->getFixtures()), "'");
953
        $parameters['fixtureOrder'] = $this->json->getFixtureOrder();
954
955
        $this->renderFile(
956
            'fixtures/LoadFixtures.php.twig',
957
            $dir . '/DataFixtures/MongoDB/Load' . $document . 'Data.php',
958
            $parameters
959
        );
960
    }
961
}
962