Completed
Pull Request — 5.6 (#2830)
by Jeroen
14:14
created

GeneratorBundle/Generator/KunstmaanGenerator.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace Kunstmaan\GeneratorBundle\Generator;
4
5
use Doctrine\Common\Inflector\Inflector;
6
use Doctrine\ORM\Mapping\ClassMetadataInfo;
7
use Doctrine\ORM\Mapping\UnderscoreNamingStrategy;
8
use Doctrine\ORM\Tools\EntityGenerator;
9
use Doctrine\ORM\Tools\EntityRepositoryGenerator;
10
use Kunstmaan\GeneratorBundle\Helper\CommandAssistant;
11
use Kunstmaan\GeneratorBundle\Helper\GeneratorUtils;
12
use Sensio\Bundle\GeneratorBundle\Generator\Generator;
13
use Symfony\Bridge\Doctrine\RegistryInterface;
14
use Symfony\Component\DependencyInjection\ContainerInterface;
15
use Symfony\Component\Filesystem\Filesystem;
16
use Symfony\Component\Finder\Finder;
17
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
18
use Symfony\Component\HttpKernel\Kernel;
19
use Twig\Environment;
20
use Twig\Lexer;
21
use Twig\Loader\FilesystemLoader;
22
23
/**
24
 * Class that contains all common generator logic.
25
 */
26
class KunstmaanGenerator extends Generator
27
{
28
    /**
29
     * @var Filesystem
30
     */
31
    protected $filesystem;
32
33
    /**
34
     * @var RegistryInterface
35
     */
36
    protected $registry;
37
38
    /**
39
     * @var string
40
     */
41
    protected $skeletonDir;
42
43
    /**
44
     * @var CommandAssistant
45
     */
46
    protected $assistant;
47
48
    /**
49
     * @var ContainerInterface
50
     */
51
    protected $container;
52
53
    /**
54
     * @param Filesystem         $filesystem  The filesystem
55
     * @param RegistryInterface  $registry    The registry
56
     * @param string             $skeletonDir The directory of the skeleton
57
     * @param CommandAssistant   $assistant   The command assistant
58
     * @param ContainerInterface $container   The container
59
     */
60 1
    public function __construct(
61
        Filesystem $filesystem,
62
        RegistryInterface $registry,
63
        $skeletonDir,
64
        CommandAssistant $assistant,
65
        ContainerInterface $container = null
66
    ) {
67 1
        $this->filesystem = $filesystem;
68 1
        $this->registry = $registry;
69 1
        $this->skeletonDir = GeneratorUtils::getFullSkeletonPath($skeletonDir);
70 1
        $this->assistant = $assistant;
71 1
        $this->container = $container;
72
73 1
        $this->setSkeletonDirs([$this->skeletonDir, GeneratorUtils::getFullSkeletonPath('/common')]);
74 1
    }
75
76
    /**
77
     * Check that the keyword is a reserved word for the database system.
78
     *
79
     * @param string $keyword
80
     *
81
     * @return bool
82
     */
83
    public function isReservedKeyword($keyword)
84
    {
85
        return $this->registry->getConnection()->getDatabasePlatform()->getReservedKeywordsList()->isKeyword($keyword);
86
    }
87
88
    /**
89
     * Generate the entity PHP code.
90
     *
91
     * @param string      $name
92
     * @param array       $fields
93
     * @param string      $namePrefix
94
     * @param string      $dbPrefix
95
     * @param string|null $extendClass
96
     * @param bool        $withRepository
97
     *
98
     * @return array
99
     *
100
     * @throws \RuntimeException
101
     */
102
    protected function generateEntity(
103
        BundleInterface $bundle,
104
        $name,
105
        $fields,
106
        $namePrefix,
107
        $dbPrefix,
108
        $extendClass = null,
109
        $withRepository = false
110
    ) {
111
        // configure the bundle (needed if the bundle does not contain any Entities yet)
112
        $config = $this->registry->getManager(null)->getConfiguration();
113
        $config->setEntityNamespaces(
114
            array_merge(
115
                [$bundle->getName() => $bundle->getNamespace() . '\\Entity' . ($namePrefix ? '\\' . $namePrefix : '')],
116
                $config->getEntityNamespaces()
117
            )
118
        );
119
120
        $entityClass = $this->registry->getAliasNamespace($bundle->getName()) . ($namePrefix ? '\\' . $namePrefix : '') . '\\' . $name;
121
        $entityPath = $bundle->getPath() . '/Entity/' . ($namePrefix ? $namePrefix . '/' : '') . str_replace('\\', '/', $name) . '.php';
122
        if (file_exists($entityPath)) {
123
            throw new \RuntimeException(sprintf('Entity "%s" already exists.', $entityClass));
124
        }
125
126
        $class = new ClassMetadataInfo($entityClass, new UnderscoreNamingStrategy());
127
        if ($withRepository) {
128
            if ($this->isSymfony4()) {
129
                $repositoryClass = preg_replace('/\\\\Entity\\\\/', '\\Repository\\', $entityClass, 1) . 'Repository';
130
                $class->customRepositoryClassName = $repositoryClass;
131
                $this->getSymfony4RepositoryGenerator()->writeEntityRepositoryClass($entityClass, $repositoryClass, $bundle->getPath());
132
            } else {
133
                $entityClass = preg_replace('/\\\\Entity\\\\/', '\\Repository\\', $entityClass, 1);
134
                $class->customRepositoryClassName = $entityClass . 'Repository';
135
                $path = $bundle->getPath() . str_repeat('/..', substr_count(get_class($bundle), '\\'));
136
                $this->getRepositoryGenerator()->writeEntityRepositoryClass($class->customRepositoryClassName, $path);
137
            }
138
        }
139
140
        foreach ($fields as $fieldSet) {
141
            foreach ($fieldSet as $fieldArray) {
142
                foreach ($fieldArray as $field) {
143
                    if (array_key_exists('joinColumn', $field)) {
144
                        $class->mapManyToOne($field);
145
                    } elseif (array_key_exists('joinTable', $field)) {
146
                        $class->mapManyToMany($field);
147
                    } else {
148
                        $class->mapField($field);
149
                    }
150
                }
151
            }
152
        }
153
        $class->setPrimaryTable(
154
            [
155
                'name' => strtolower($dbPrefix) . Inflector::tableize(Inflector::pluralize($name)),
156
            ]
157
        );
158
        $entityCode = $this->getEntityGenerator($extendClass)->generateEntityClass($class);
159
160
        return [$entityCode, $entityPath];
161
    }
162
163
    /**
164
     * Get a Doctrine EntityGenerator instance.
165
     *
166
     * @param string|null $classToExtend
167
     *
168
     * @return EntityGenerator
169
     */
170
    protected function getEntityGenerator($classToExtend = null)
171
    {
172
        $entityGenerator = new EntityGenerator();
173
        if (!is_null($classToExtend)) {
174
            $entityGenerator->setClassToExtend($classToExtend);
175
        }
176
        $entityGenerator->setGenerateAnnotations(true);
177
        $entityGenerator->setGenerateStubMethods(true);
178
        $entityGenerator->setRegenerateEntityIfExists(false);
179
        $entityGenerator->setUpdateEntityIfExists(true);
180
        $entityGenerator->setNumSpaces(4);
181
        $entityGenerator->setAnnotationPrefix('ORM\\');
182
183
        return $entityGenerator;
184
    }
185
186
    /**
187
     * Generate the entity admin type.
188
     *
189
     * @param        $bundle
190
     * @param        $entityName
191
     * @param        $entityPrefix
192
     * @param string $extendClass
193
     */
194
    protected function generateEntityAdminType(
195
        $bundle,
196
        $entityName,
197
        $entityPrefix,
198
        array $fields,
199
        $extendClass = '\Symfony\Component\Form\AbstractType'
200
    ) {
201
        $className = $entityName . 'AdminType';
202
        $savePath = $bundle->getPath() . '/Form/' . $entityPrefix . '/' . $className . '.php';
203
        $name = str_replace(
204
                '\\',
205
                '_',
206
                strtolower($bundle->getNamespace())
207
            ) . '_' . strtolower($entityName) . 'type';
208
209
        $params = [
210
            'className' => $className,
211
            'name' => $name,
212
            'namespace' => $bundle->getNamespace(),
213
            'entity' => '\\' . $bundle->getNamespace() . '\Entity\\' . $entityPrefix . '\\' . $entityName,
214
            'fields' => $fields,
215
            'entity_prefix' => $entityPrefix,
216
            'extend_class' => $extendClass,
217
        ];
218
        $this->renderFile('/Form/EntityAdminType.php', $savePath, $params);
219
    }
220
221
    /**
222
     * Install the default page templates.
223
     *
224
     * @param BundleInterface $bundle
225
     */
226
    protected function installDefaultPageTemplates($bundle)
227
    {
228
        // Configuration templates
229 View Code Duplication
        if ($this->isSymfony4()) {
230
            $dirPath = $this->container->getParameter('kernel.project_dir') . '/config/kunstmaancms/pagetemplates/';
231
        } else {
232
            $dirPath = sprintf('%s/Resources/config/pagetemplates/', $bundle->getPath());
233
        }
234
235
        $skeletonDir = sprintf('%s/Resources/config/pagetemplates/', GeneratorUtils::getFullSkeletonPath('/common'));
236
237
        // Only copy templates over when the folder does not exist yet...
238
        if (!$this->filesystem->exists($dirPath)) {
239
            $files = [
240
                'default-one-column.yml',
241
                'default-two-column-left.yml',
242
                'default-two-column-right.yml',
243
                'default-three-column.yml',
244
            ];
245
            foreach ($files as $file) {
246
                $this->filesystem->copy($skeletonDir . $file, $dirPath . $file, false);
247
                GeneratorUtils::replace('~~~BUNDLE~~~', $bundle->getName(), $dirPath . $file);
248
            }
249
        }
250
251
        // Twig templates
252
        $dirPath = $this->getTemplateDir($bundle) . '/Pages/Common/';
253
254
        $skeletonDir = sprintf('%s/Resources/views/Pages/Common/', GeneratorUtils::getFullSkeletonPath('/common'));
255
256
        if (!$this->filesystem->exists($dirPath)) {
257
            $files = [
258
                'one-column-pagetemplate.html.twig',
259
                'two-column-left-pagetemplate.html.twig',
260
                'two-column-right-pagetemplate.html.twig',
261
                'three-column-pagetemplate.html.twig',
262
            ];
263
            foreach ($files as $file) {
264
                $this->filesystem->copy($skeletonDir . $file, $dirPath . $file, false);
265
            }
266
            $this->filesystem->copy($skeletonDir . 'view.html.twig', $dirPath . 'view.html.twig', false);
267
        }
268
269
        $contents = file_get_contents($dirPath . 'view.html.twig');
270
271
        $twigFile = $this->isSymfony4() ?
272
            $twigFile = "{% extends 'Layout/layout.html.twig' %}\n" :
273
            $twigFile = "{% extends '" . $bundle->getName() . ":Layout:layout.html.twig' %}\n"
274
        ;
275
276
        if (strpos($contents, '{% extends ') === false) {
277
            GeneratorUtils::prepend(
278
                $twigFile,
279
                $dirPath . 'view.html.twig'
280
            );
281
        }
282
    }
283
284
    /**
285
     * Install the default pagepart configuration.
286
     *
287
     * @param BundleInterface $bundle
288
     */
289
    protected function installDefaultPagePartConfiguration($bundle)
290
    {
291
        // Pagepart configuration
292 View Code Duplication
        if ($this->isSymfony4()) {
293
            $dirPath = $this->container->getParameter('kernel.project_dir') . '/config/kunstmaancms/pageparts/';
294
        } else {
295
            $dirPath = sprintf('%s/Resources/config/pageparts/', $bundle->getPath());
296
        }
297
298
        $skeletonDir = sprintf('%s/Resources/config/pageparts/', GeneratorUtils::getFullSkeletonPath('/common'));
299
300
        // Only copy when folder does not exist yet
301
        if (!$this->filesystem->exists($dirPath)) {
302
            $files = ['footer.yml', 'main.yml', 'left-sidebar.yml', 'right-sidebar.yml'];
303
            foreach ($files as $file) {
304
                $this->filesystem->copy($skeletonDir . $file, $dirPath . $file, false);
305
            }
306
        }
307
    }
308
309
    /**
310
     * Render all files in the source directory and copy them to the target directory.
311
     *
312
     * @param string $sourceDir  The source directory where we need to look in
313
     * @param string $targetDir  The target directory where we need to copy the files too
314
     * @param array  $parameters The parameters that will be passed to the templates
315
     * @param bool   $override   Whether to override an existing file or not
316
     * @param bool   $recursive  Whether to render all files recursively or not
317
     */
318 1
    public function renderFiles($sourceDir, $targetDir, array $parameters, $override = false, $recursive = true)
319
    {
320
        // Make sure the source -and target dir contain a trailing slash
321 1
        $sourceDir = rtrim($sourceDir, '/') . '/';
322 1
        $targetDir = rtrim($targetDir, '/') . '/';
323
324 1
        $this->setSkeletonDirs([$sourceDir]);
325
326 1
        $finder = new Finder();
327 1
        $finder->files()->in($sourceDir);
328 1
        if (!$recursive) {
329
            $finder->depth('== 0');
330
        }
331
332
        // Get all files in the source directory
333 1
        foreach ($finder as $file) {
334 1
            $name = $file->getRelativePathname();
335
336
            // Check that we are allowed to overwrite the file if it already exists
337 1 View Code Duplication
            if (!is_file($targetDir . $name) || $override === true) {
338 1
                $fileParts = explode('.', $name);
339 1
                if (end($fileParts) === 'twig') {
340 1
                    $this->renderTwigFile($name, $targetDir . $name, $parameters, $sourceDir);
341
                } else {
342
                    $this->renderFile($name, $targetDir . $name, $parameters);
343
                }
344
            }
345
        }
346 1
    }
347
348
    /**
349
     * Render all files in the source directory and copy them to the target directory.
350
     *
351
     * @param string      $sourceDir      The source directory where we need to look in
352
     * @param string      $targetDir      The target directory where we need to copy the files too
353
     * @param string      $filename       The name of the file that needs to be rendered
354
     * @param array       $parameters     The parameters that will be passed to the templates
355
     * @param bool        $override       Whether to override an existing file or not
356
     * @param string|null $targetFilename The name of the target file (if null, then use $filename)
357
     */
358 1
    public function renderSingleFile($sourceDir, $targetDir, $filename, array $parameters, $override = false, $targetFilename = null)
359
    {
360
        // Make sure the source -and target dir contain a trailing slash
361 1
        $sourceDir = rtrim($sourceDir, '/') . '/';
362 1
        $targetDir = rtrim($targetDir, '/') . '/';
363 1
        if (is_null($targetFilename)) {
364 1
            $targetFilename = $filename;
365
        }
366
367 1
        $this->setSkeletonDirs([$sourceDir]);
368
369 1 View Code Duplication
        if (is_file($sourceDir . $filename)) {
370
            // Check that we are allowed the overwrite the file if it already exists
371 1
            if (!is_file($targetDir . $targetFilename) || $override === true) {
372 1
                $fileParts = explode('.', $filename);
373 1
                if (end($fileParts) === 'twig') {
374 1
                    $this->renderTwigFile($filename, $targetDir . $targetFilename, $parameters, $sourceDir);
375
                } else {
376 1
                    $this->renderFile($filename, $targetDir . $targetFilename, $parameters);
377
                }
378
            }
379
        }
380 1
    }
381
382
    /**
383
     * Render a file and make it executable.
384
     *
385
     * @param string $sourceDir  The source directory where we need to look in
386
     * @param string $targetDir  The target directory where we need to copy the files too
387
     * @param string $filename   The name of the file that needs to be rendered
388
     * @param array  $parameters The parameters that will be passed to the templates
389
     * @param bool   $override   Whether to override an existing file or not
390
     * @param int    $mode       The mode
391
     */
392
    public function renderExecutableFile($sourceDir, $targetDir, $filename, array $parameters, $override = false, $mode = 0774)
393
    {
394
        $this->renderSingleFile($sourceDir, $targetDir, $filename, $parameters, $override);
395
396
        $targetDir = rtrim($targetDir, '/') . '/';
397
        $targetFile = $targetDir . $filename;
398
        $this->filesystem->chmod($targetFile, $mode);
399
    }
400
401
    /**
402
     * Copy all files in the source directory to the target directory.
403
     *
404
     * @param string $sourceDir The source directory where we need to look in
405
     * @param string $targetDir The target directory where we need to copy the files too
406
     * @param bool   $override  Whether to override an existing file or not
407
     */
408
    public function copyFiles($sourceDir, $targetDir, $override = false)
409
    {
410
        // Make sure the source -and target dir contain a trailing slash
411
        $sourceDir = rtrim($sourceDir, '/') . '/';
412
        $targetDir = rtrim($targetDir, '/') . '/';
413
414
        $this->filesystem->mirror($sourceDir, $targetDir, null, ['override' => $override]);
415
    }
416
417
    /**
418
     * Remove a directory from the filesystem.
419
     *
420
     * @param string $targetDir
421
     */
422
    public function removeDirectory($targetDir)
423
    {
424
        // Make sure the target dir contain a trailing slash
425
        $targetDir = rtrim($targetDir, '/') . '/';
426
427
        $this->filesystem->remove($targetDir);
428
    }
429
430
    /**
431
     * Remove a file from the filesystem.
432
     *
433
     * @param string $file
434
     */
435
    public function removeFile($file)
436
    {
437
        $this->filesystem->remove($file);
438
    }
439
440
    /**
441
     * Render a twig file with custom twig tags.
442
     *
443
     * @param string $template
444
     * @param string $sourceDir
445
     *
446
     * @return string
447
     */
448 1
    public function renderTwig($template, array $parameters, $sourceDir)
449
    {
450 1
        $twig = new Environment(
451 1
            new FilesystemLoader([$sourceDir]), [
452 1
                'debug' => true,
453
                'cache' => false,
454
                'strict_variables' => true,
455
                'autoescape' => false,
456
            ]
457
        );
458
459
        // Ruby erb template syntax
460 1
        $lexer = new Lexer(
461 1
            $twig, [
462 1
                'tag_comment' => ['<%#', '%>'],
463
                'tag_block' => ['<%', '%>'],
464
                'tag_variable' => ['<%=', '%>'],
465
            ]
466
        );
467
468 1
        $twig->setLexer($lexer);
469
470 1
        return $twig->render($template, $parameters);
471
    }
472
473
    /**
474
     * Render a twig file, and save it to disk.
475
     *
476
     * @param string $template
477
     * @param string $target
478
     * @param string $sourceDir
479
     *
480
     * @return int
481
     */
482 1
    public function renderTwigFile($template, $target, array $parameters, $sourceDir)
483
    {
484 1
        if (!is_dir(dirname($target))) {
485 1
            mkdir(dirname($target), 0777, true);
486
        }
487
488 1
        return file_put_contents($target, $this->renderTwig($template, $parameters, $sourceDir));
489
    }
490
491
    /**
492
     * @return \Doctrine\ORM\Tools\EntityRepositoryGenerator
493
     */
494
    protected function getRepositoryGenerator()
495
    {
496
        return new EntityRepositoryGenerator();
0 ignored issues
show
Deprecated Code introduced by
The class Doctrine\ORM\Tools\EntityRepositoryGenerator has been deprecated with message: 2.7 This class is being removed from the ORM and won't have any replacement

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
497
    }
498
499
    /**
500
     * @return \Kunstmaan\GeneratorBundle\Generator\Symfony4EntityRepositoryGenerator
501
     */
502
    protected function getSymfony4RepositoryGenerator()
503
    {
504
        return new \Kunstmaan\GeneratorBundle\Generator\Symfony4EntityRepositoryGenerator();
505
    }
506
507
    /**
508
     * @internal
509
     */
510 1 View Code Duplication
    protected function getTemplateDir(BundleInterface $bundle)
511
    {
512 1
        if ($this->isSymfony4()) {
513 1
            return $this->container->getParameter('kernel.project_dir') . '/templates';
514
        }
515
516
        return $bundle->getPath() . '/Resources/views';
517
    }
518
519
    /**
520
     * @internal
521
     */
522 View Code Duplication
    protected function getAssetsDir(BundleInterface $bundle)
523
    {
524
        if ($this->isSymfony4()) {
525
            return $this->container->getParameter('kernel.project_dir') . '/assets';
526
        }
527
528
        return $bundle->getPath() . '/Resources';
529
    }
530
531
    /**
532
     * @internal
533
     */
534 1
    protected function isSymfony4()
535
    {
536 1
        return Kernel::VERSION_ID >= 40000;
537
    }
538
}
539