Passed
Pull Request — master (#170)
by
unknown
03:11
created

MakeFactory::interact()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3.1825

Importance

Changes 0
Metric Value
eloc 8
c 0
b 0
f 0
dl 0
loc 15
ccs 8
cts 11
cp 0.7272
rs 10
cc 3
nc 3
nop 3
crap 3.1825
1
<?php
2
3
namespace Zenstruck\Foundry\Bundle\Maker;
4
5
use Doctrine\Persistence\ManagerRegistry;
6
use Symfony\Bundle\MakerBundle\ConsoleStyle;
7
use Symfony\Bundle\MakerBundle\DependencyBuilder;
8
use Symfony\Bundle\MakerBundle\Exception\RuntimeCommandException;
9
use Symfony\Bundle\MakerBundle\Generator;
10
use Symfony\Bundle\MakerBundle\InputConfiguration;
11
use Symfony\Bundle\MakerBundle\Maker\AbstractMaker;
12
use Symfony\Component\Console\Command\Command;
13
use Symfony\Component\Console\Input\InputArgument;
14
use Symfony\Component\Console\Input\InputInterface;
15
use Symfony\Component\Console\Input\InputOption;
16
use Zenstruck\Foundry\ModelFactory;
17
18
/**
19
 * @author Kevin Bond <[email protected]>
20
 */
21
final class MakeFactory extends AbstractMaker
22
{
23
    /** @var ManagerRegistry */
24
    private $managerRegistry;
25 20
26
    /** @var string[] */
27 20
    private $entitiesWithFactories;
28 20
29
    public function __construct(ManagerRegistry $managerRegistry, \Traversable $factories)
30 4
    {
31
        $this->managerRegistry = $managerRegistry;
32 4
        $this->entitiesWithFactories = \array_map(
33
            static function(ModelFactory $factory) {
34
                return $factory::getEntityClass();
35 20
            },
36
            \iterator_to_array($factories)
37
        );
38 20
    }
39 20
40 20
    public static function getCommandName(): string
41
    {
42
        return 'make:factory';
43 20
    }
44 20
45
    public static function getCommandDescription(): string
46 20
    {
47
        return 'Creates a Foundry model factory for a Doctrine entity class';
48 20
    }
49 12
50
    public function configureCommand(Command $command, InputConfiguration $inputConfig): void
51
    {
52 8
        $command
53 4
            ->setDescription(self::getCommandDescription())
54 4
            ->addArgument('entity', InputArgument::OPTIONAL, 'Entity class to create a factory for')
55
            ->addOption('namespace', null, InputOption::VALUE_REQUIRED, 'Customize the namespace for generated factories', 'Factory')
56
            ->addOption('test', null, InputOption::VALUE_NONE, 'Create in <fg=yellow>tests/</> instead of <fg=yellow>src/</>')
57 8
        ;
58 8
59
        $inputConfig->setArgumentAsNonInteractive('entity');
60 8
    }
61 8
62
    public function interact(InputInterface $input, ConsoleStyle $io, Command $command): void
63 20
    {
64
        if ($input->getArgument('entity')) {
65 20
            return;
66
        }
67 20
68 4
        if (!$input->getOption('test')) {
69
            $io->text('// Note: pass <fg=yellow>--test</> if you want to generate factories in your <fg=yellow>tests/</> directory');
70
            $io->newLine();
71 20
        }
72 4
73
        $argument = $command->getDefinition()->getArgument('entity');
74
        $entity = $io->choice($argument->getDescription(), $this->entityChoices());
75 16
76 16
        $input->setArgument('entity', $entity);
77 16
    }
78 16
79 16
    private function getNamespacePrefix(string $namespace, bool $isTest) : string
80
    {
81
        $appPos = strpos($namespace, 'App\\');
82 16
        if ($appPos !== false && $appPos <= 1) {
83
            $namespace = substr($namespace,$appPos+4);
84 16
        }
85
        if ($isTest && strpos($namespace, 'Tests\\') === false) {
86 16
            $namespace = 'Tests\\'.$namespace;
87
        }
88
        return $namespace;
89 16
    }
90 16
91 16
    public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void
92
    {
93 16
        $class = $input->getArgument('entity');
94 16
95
        if (!\class_exists($class)) {
0 ignored issues
show
Bug introduced by
It seems like $class can also be of type null and string[]; however, parameter $class of class_exists() 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

95
        if (!\class_exists(/** @scrutinizer ignore-type */ $class)) {
Loading history...
96
            $class = $generator->createClassNameDetails($class, 'Entity\\')->getFullName();
0 ignored issues
show
Bug introduced by
It seems like $class can also be of type null and string[]; however, parameter $name of Symfony\Bundle\MakerBund...reateClassNameDetails() 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

96
            $class = $generator->createClassNameDetails(/** @scrutinizer ignore-type */ $class, 'Entity\\')->getFullName();
Loading history...
97
        }
98 16
99
        if (!\class_exists($class)) {
100 16
            throw new RuntimeCommandException(\sprintf('Entity "%s" not found.', $input->getArgument('entity')));
0 ignored issues
show
Bug introduced by
It seems like $input->getArgument('entity') can also be of type string[]; however, parameter $values of sprintf() does only seem to accept double|integer|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

100
            throw new RuntimeCommandException(\sprintf('Entity "%s" not found.', /** @scrutinizer ignore-type */ $input->getArgument('entity')));
Loading history...
101
        }
102 16
103 16
        $entity = new \ReflectionClass($class);
0 ignored issues
show
Bug introduced by
It seems like $class can also be of type string[]; however, parameter $objectOrClass of ReflectionClass::__construct() does only seem to accept object|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

103
        $entity = new \ReflectionClass(/** @scrutinizer ignore-type */ $class);
Loading history...
104
        $factory = $generator->createClassNameDetails(
105
            $entity->getShortName(),
106 16
            $this->getNamespacePrefix($input->getOption('namespace'),$input->getOption('test')),
107
            'Factory'
108 20
        );
109
110
        $repository = new \ReflectionClass($this->managerRegistry->getRepository($entity->getName()));
111 20
112
        if (0 !== \mb_strpos($repository->getName(), $generator->getRootNamespace())) {
113 8
            // not using a custom repository
114
            $repository = null;
115 8
        }
116
117 8
        $generator->generateClass(
118 8
            $factory->getFullName(),
119 8
            __DIR__.'/../Resources/skeleton/Factory.tpl.php',
120
            [
121
                'entity' => $entity,
122
                'repository' => $repository,
123 8
            ]
124
        );
125 8
126
        $generator->writeChanges();
127
128
        $this->writeSuccessMessage($io);
129
130
        $io->text([
131
            'Next: Open your new factory and set default values/states.',
132
            'Find the documentation at https://github.com/zenstruck/foundry#model-factories',
133
        ]);
134
    }
135
136
    public function configureDependencies(DependencyBuilder $dependencies): void
137
    {
138
        // noop
139
    }
140
141
    private function entityChoices(): array
142
    {
143
        $choices = [];
144
145
        foreach ($this->managerRegistry->getManagers() as $manager) {
146
            foreach ($manager->getMetadataFactory()->getAllMetadata() as $metadata) {
147
                if (!\in_array($metadata->getName(), $this->entitiesWithFactories, true)) {
148
                    $choices[] = $metadata->getName();
149
                }
150
            }
151
        }
152
153
        \sort($choices);
154
155
        if (empty($choices)) {
156
            throw new RuntimeCommandException('No entities found.');
157
        }
158
159
        return $choices;
160
    }
161
}
162