Passed
Push — master ( ccf010...588656 )
by Curtis
04:54
created

GenerateTraitCommand::configure()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 49
Code Lines 40

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 40
dl 0
loc 49
c 0
b 0
f 0
rs 9.28
cc 1
nc 1
nop 0
1
<?php
2
/**
3
 * @noinspection PhpUndefinedFieldInspection
4
 */
5
6
namespace DoctrineRepoHelper\Command;
7
8
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
9
use Doctrine\ORM\EntityManager;
10
use Doctrine\ORM\EntityManagerInterface;
11
use Doctrine\ORM\EntityRepository;
12
use Symfony\Component\Console\Command\Command;
13
use Symfony\Component\Console\Input\InputInterface;
14
use Symfony\Component\Console\Input\InputOption;
15
use Symfony\Component\Console\Output\OutputInterface;
16
use Zend\Code\Generator\DocBlockGenerator;
17
use Zend\Code\Generator\Exception\InvalidArgumentException;
18
use Zend\Code\Generator\FileGenerator;
19
use Zend\Code\Generator\MethodGenerator;
20
use Zend\Code\Generator\TraitGenerator;
21
22
/**
23
 * Class GenerateTraitCommand
24
 * @package DoctrineRepoHelper\Command
25
 */
26
class GenerateTraitCommand extends Command
27
{
28
    /** @var EntityManagerInterface */
29
    protected $entityManager;
30
31
    /**
32
     * GenerateTraitCommand constructor.
33
     * @param EntityManagerInterface $entityManager
34
     */
35
    public function __construct(EntityManagerInterface $entityManager)
36
    {
37
        $this->entityManager = $entityManager;
38
        parent::__construct();
39
    }
40
41
    protected function configure()
42
    {
43
        parent::configure();
44
45
        $this
46
            ->setName('orm:generate-repository-trait')
47
            ->setDescription('Generate a repository helper trait')
48
            ->setHelp(
49
                'The generate repository trait command creates a trait that will allow your development 
50
                environment to autocomplete custom repository methods'
51
            )
52
            ->addOption(
53
                'namespace',
54
                null,
55
                InputOption::VALUE_OPTIONAL,
56
                'Declares the namespace'
57
            )
58
            ->addOption(
59
                'output',
60
                'o',
61
                InputOption::VALUE_OPTIONAL,
62
                'Output path',
63
                getcwd()
64
            )
65
            ->addOption(
66
                'className',
67
                'c',
68
                InputOption::VALUE_OPTIONAL,
69
                'Classname of the trait',
70
                'CustomRepositoryAwareTrait'
71
            )
72
            ->addOption(
73
                'em-getter',
74
                null,
75
                InputOption::VALUE_OPTIONAL,
76
                'Property or method name to access the EntityManager',
77
                'getObjectManager()'
78
            )
79
            ->addOption(
80
                'force',
81
                'f',
82
                InputOption::VALUE_NONE,
83
                'Overwrite existing trait'
84
            )
85
            ->addOption(
86
                'filter',
87
                null,
88
                InputOption::VALUE_OPTIONAL,
89
                'Filter the list of entities getters are created for'
90
            );
91
    }
92
93
    /**
94
     * @param InputInterface $input
95
     * @param OutputInterface $output
96
     * @return int|void|null
97
     * @throws \ReflectionException
98
     */
99
    public function execute(InputInterface $input, OutputInterface $output)
100
    {
101
        /** @var string $traitName */
102
        $traitName = (string)$input->getOption('className');
103
104
        /** @var string $destination */
105
        $destination = (string)$input->getOption('output');
106
107
        $outputFileName = sprintf(
108
            '%s/%s.php',
109
            $destination,
110
            $traitName
111
        );
112
113
        $metaDataEntries = $this->entityManager->getMetadataFactory()->getAllMetadata();
114
        $trait = $this->generateTrait($input);
115
116
        foreach ($metaDataEntries as $metaData) {
117
            if ($filter = $input->getOption('filter')) {
118
                if (strpos($metaData->getName(), $filter) === false) {
119
                    continue;
120
                }
121
            }
122
123
            $method = $this->generateMethod(
124
                $input,
125
                $metaData
126
            );
127
128
            try {
129
                $trait->addMethodFromGenerator($method);
130
            } catch (InvalidArgumentException $e) {
131
                $output->writeln(
132
                    sprintf(
133
                        'Method "%s" already exists in this class',
134
                        $method->getName()
135
                    )
136
                );
137
138
                $reflection = new \ReflectionClass($metaData->getName());
139
140
                $method->setName(
141
                    sprintf(
142
                        'get%sRepository%s',
143
                        $reflection->getShortName(),
144
                        str_replace('.', '', uniqid('', true))
145
                    )
146
                );
147
148
                $trait->addMethodFromGenerator($method);
149
150
                $output->writeln(
151
                    sprintf(
152
                        'Refactored the method to "%s". Please refactor to a usable name you will remember',
153
                        $method->getName()
154
                    )
155
                );
156
                $output->writeln('');
157
            }
158
        }
159
160
        $file = new FileGenerator();
161
        $file->setClass($trait);
162
163
        if (!is_dir($destination)) {
164
            mkdir($destination, 0777, true);
165
        }
166
167
        if (!$input->getOption('force')) {
168
            if (file_exists($outputFileName)) {
169
                $output->writeln('File already exists. Use "-f" to force overwriting the existing file');
170
                return;
171
            }
172
        }
173
174
        file_put_contents(
175
            $outputFileName,
176
            $file->generate()
177
        );
178
179
        $output->writeln('');
180
181
        $output->writeln(
182
            sprintf(
183
                'Trait created in "%s"',
184
                $outputFileName
185
            )
186
        );
187
188
        $output->writeln('');
189
    }
190
191
    /**
192
     * @param InputInterface $input
193
     * @return TraitGenerator
194
     */
195
    private function generateTrait(InputInterface $input): TraitGenerator
196
    {
197
        /** @var string $entityManagerGetter */
198
        $entityManagerGetter = (string)$input->getOption('em-getter');
199
        /** @var string $traitName */
200
        $traitName = (string)$input->getOption('className');
201
        /** @var string $traitNameSpace */
202
        $traitNameSpace = (string)$input->getOption('namespace');
203
204
        $trait = new TraitGenerator(
205
            $traitName,
206
            $traitNameSpace
207
        );
208
209
        $trait
210
            ->addUse(EntityManager::class)
211
            ->addUse(EntityRepository::class);
212
213
        $docBlock = DocBlockGenerator::fromArray(
214
            [
215
                'shortDescription' => $traitName,
216
                'longDescription' => 'Provides helper methods for accessing custom repositories. In addition, provides 
217
                    type hints to allow for custom method auto-completion within IDEs',
218
                'tags' => [
219
                    [
220
                        'name' => 'package',
221
                        'description' => $traitNameSpace
222
                    ],
223
                    [
224
                        'name' => 'method',
225
                        'description' => sprintf(
226
                            'EntityManager %s',
227
                            $entityManagerGetter
228
                        )
229
                    ]
230
                ]
231
            ]
232
        );
233
234
        $trait->setDocBlock($docBlock);
235
        return $trait;
236
    }
237
238
    /**
239
     * @param ClassMetadata $metaData
240
     * @param InputInterface $input
241
     * @return MethodGenerator
242
     * @throws \ReflectionException
243
     */
244
    private function generateMethod(InputInterface $input, ClassMetadata $metaData): MethodGenerator
245
    {
246
        $entityManagerGetter = $input->getOption('em-getter');
247
248
        $reflection = new \ReflectionClass($metaData->getName());
249
        $repoReflection = null;
250
251
        if ($metaData->customRepositoryClassName) {
0 ignored issues
show
Bug introduced by
Accessing customRepositoryClassName on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
252
            $repoReflection = new \ReflectionClass($metaData->customRepositoryClassName);
253
        }
254
255
        $method = new MethodGenerator(
256
            sprintf(
257
                'get%sRepository',
258
                $reflection->getShortName()
259
            ),
260
            [],
261
            MethodGenerator::FLAG_PUBLIC,
262
            sprintf(
263
                'return $this->%s->getRepository(\\%s::class);',
264
                $entityManagerGetter,
0 ignored issues
show
Bug introduced by
It seems like $entityManagerGetter can also be of type string[]; however, parameter $args of sprintf() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

264
                /** @scrutinizer ignore-type */ $entityManagerGetter,
Loading history...
265
                $reflection->getName()
266
            )
267
        );
268
269
270
        $docBlock = DocBlockGenerator::fromArray(
271
            [
272
                'tags' => [
273
                    [
274
                        'name' => 'return',
275
                        'description' => sprintf(
276
                            '%s%s',
277
                            'EntityRepository',
278
                            $repoReflection ? '|\\' . $repoReflection->getName() : ''
279
                        )
280
                    ]
281
                ]
282
            ]
283
        );
284
285
        $method->setDocBlock($docBlock);
286
        return $method;
287
    }
288
}
289