Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
94 / 94
100.00% covered (success)
100.00%
6 / 6
CRAP
100.00% covered (success)
100.00%
1 / 1
CreateCustomLayoutRendererCommand
100.00% covered (success)
100.00%
94 / 94
100.00% covered (success)
100.00%
6 / 6
7
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 configure
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
1
 createBackendPath
100.00% covered (success)
100.00%
43 / 43
100.00% covered (success)
100.00%
1 / 1
1
 createFrontendPath
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
1
 syncFiles
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
2
 execute
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2namespace Apie\TwigTemplateLayoutRenderer\Command;
3
4use Apie\Common\ValueObjects\EntityNamespace;
5use Apie\Core\ApieLib;
6use Apie\Core\Identifiers\Identifier;
7use Apie\Core\Other\FileWriterInterface;
8use Apie\TwigTemplateLayoutRenderer\Skeleton\ClassCodeGenerator;
9use Symfony\Component\Console\Attribute\AsCommand;
10use Symfony\Component\Console\Command\Command;
11use Symfony\Component\Console\Input\InputArgument;
12use Symfony\Component\Console\Input\InputInterface;
13use Symfony\Component\Console\Input\InputOption;
14use Symfony\Component\Console\Output\OutputInterface;
15use Symfony\Component\Finder\Finder;
16
17#[AsCommand('apie:cms:create-custom-layout', 'create frontend and backend code for a custom layout')]
18class CreateCustomLayoutRendererCommand extends Command
19{
20    private const PADDING = 80;
21
22    public function __construct(
23        private readonly FileWriterInterface $filewriter,
24        private readonly ClassCodeGenerator $classCodeGenerator
25    ) {
26        parent::__construct();
27    }
28    protected function configure(): void
29    {
30        $this->addArgument('name', InputArgument::REQUIRED, 'name of custom layout');
31        $this->addArgument('backend-path', InputArgument::REQUIRED, 'path of backend (PHP) code');
32        $this->addArgument('backend-namespace', InputArgument::REQUIRED);
33        $this->addArgument('frontend-path', InputArgument::REQUIRED, 'path of frontend code');
34        $this->addOption(
35            'apie-version',
36            null,
37            InputOption::VALUE_REQUIRED,
38            'used apie version constraint',
39            ApieLib::VERSION
40        );
41    }
42
43    private function createBackendPath(
44        Identifier $name,
45        string $backendPath,
46        EntityNamespace $entityNamespace,
47        string $apieVersion,
48        OutputInterface $output
49    ): void {
50        $targetPath = $backendPath . 'src/' . $name->toPascalCaseSlug() . 'DesignSystemLayout.php';
51        $layoutClassCode = $this->classCodeGenerator->generateLayoutClass($name, $entityNamespace);
52        $output->write(str_pad($targetPath, self::PADDING, ' ', STR_PAD_RIGHT));
53        $output->writeln('(' . strlen($layoutClassCode) . ' bytes)');
54        $this->filewriter->writeFile(
55            $targetPath,
56            $layoutClassCode
57        );
58        $targetPath = $backendPath . 'tests/' . $name->toPascalCaseSlug() . 'DesignSystemLayoutTest.php';
59        $layoutClassCode = $this->classCodeGenerator->generateLayoutTestClass($name, $entityNamespace);
60        $output->write(str_pad($targetPath, self::PADDING, ' ', STR_PAD_RIGHT));
61        $output->writeln('(' . strlen($layoutClassCode) . ' bytes)');
62        $this->filewriter->writeFile(
63            $targetPath,
64            $layoutClassCode
65        );
66        $targetPath = $backendPath . 'composer.json';
67        $composerJsonContents = $this->classCodeGenerator->generateComposerJsonFile(
68            $name,
69            $entityNamespace,
70            $apieVersion
71        );
72        $output->write(str_pad($targetPath, self::PADDING, ' ', STR_PAD_RIGHT));
73        $output->writeln('(' . strlen($composerJsonContents) . ' bytes)');
74        $this->filewriter->writeFile(
75            $targetPath,
76            $composerJsonContents
77        );
78        $search = [
79            'skeleton',
80            'Skeleton',
81        ];
82        $replace = [
83            $name->toNative(),
84            $name->toPascalCaseSlug()->toNative(),
85        ];
86        $this->syncFiles(
87            __DIR__ . '/../../scaffolding/backend',
88            $backendPath,
89            $output,
90            $search,
91            $replace
92        );
93    }
94
95    private function createFrontendPath(
96        Identifier $name,
97        string $frontendPath,
98        OutputInterface $output
99    ): void {
100        $search = [
101            'skeleton',
102            'Skeleton',
103        ];
104        $replace = [
105            $name->toNative(),
106            $name->toPascalCaseSlug()->toNative(),
107        ];
108        $this->syncFiles(
109            __DIR__ . '/../../scaffolding/frontend',
110            $frontendPath,
111            $output,
112            $search,
113            $replace
114        );
115    }
116
117    private function syncFiles(
118        string $inputPath,
119        string $outputPath,
120        OutputInterface $output,
121        array $search,
122        array $replace
123    ): void {
124        $files = Finder::create()
125            ->files()
126            ->in($inputPath)
127            ->ignoreDotFiles(false)
128            ->ignoreVCS(false);
129        foreach ($files as $file) {
130            $contents = str_replace($search, $replace, $file->getContents());
131            $targetPath = str_replace($search, $replace, $outputPath . $file->getRelativePathname());
132            $output->write(str_pad($targetPath, self::PADDING, ' ', STR_PAD_RIGHT));
133            $output->writeln('(' . strlen($contents) . ' bytes)');
134            $this->filewriter->writeFile($targetPath, $contents);
135        }
136    }
137
138    public function execute(InputInterface $input, OutputInterface $output): int
139    {
140        $this->createFrontendPath(
141            new Identifier($input->getArgument('name')),
142            rtrim($input->getArgument('frontend-path'), '/') . '/',
143            $output
144        );
145
146        $this->createBackendPath(
147            new Identifier($input->getArgument('name')),
148            rtrim($input->getArgument('backend-path'), '/') . '/',
149            new EntityNamespace($input->getArgument('backend-namespace')),
150            $input->getOption('apie-version'),
151            $output
152        );
153
154        return Command::SUCCESS;
155    }
156}