Test Failed
Branch master (e1cf91)
by Curtis
02:30 queued 23s
created

GenerateDataFactoryCommand   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 235
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 18
eloc 125
dl 0
loc 235
rs 10
c 0
b 0
f 0

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A configure() 0 26 1
B execute() 0 68 6
A buildBody() 0 21 2
A buildFactoryData() 0 8 1
A buildAssociationMappings() 0 18 3
A buildFieldMappings() 0 49 4
1
<?php
2
3
namespace DoctrineRepoHelper\Command;
4
5
6
use Doctrine\ORM\EntityManagerInterface;
7
use Doctrine\ORM\Mapping\ClassMetadata;
8
use Symfony\Component\Console\Command\Command;
9
use Symfony\Component\Console\Input\InputInterface;
10
use Symfony\Component\Console\Input\InputOption;
11
use Symfony\Component\Console\Output\OutputInterface;
12
use Zend\Code\Generator\DocBlockGenerator;
13
use Zend\Code\Generator\FileGenerator;
14
15
/**
16
 * Class GenerateDataFactoryCommand
17
 * @package DoctrineRepoHelper\Command
18
 */
19
class GenerateDataFactoryCommand extends Command
20
{
21
    /** @var EntityManagerInterface */
22
    protected $entityManager;
23
24
    /**
25
     * GenerateTraitCommand constructor.
26
     * @param EntityManagerInterface $entityManager
27
     */
28
    public function __construct(EntityManagerInterface $entityManager)
29
    {
30
        $this->entityManager = $entityManager;
31
        parent::__construct();
32
    }
33
34
    protected function configure()
35
    {
36
        parent::configure();
37
38
        $this
39
            ->setName('orm:generate-data-factories')
40
            ->setDescription('Generate data factories for use with Codeception')
41
            ->setHelp('Generates data factories for use with the Codeception DataFactory module')
42
            ->addOption(
43
                'output',
44
                'o',
45
                InputOption::VALUE_OPTIONAL,
46
                'Output path',
47
                getcwd() . '/data/factories'
48
            )
49
            ->addOption(
50
                'force',
51
                'f',
52
                InputOption::VALUE_OPTIONAL,
53
                'Overwrite existing data factories'
54
            )
55
            ->addOption(
56
                'filter',
57
                null,
58
                InputOption::VALUE_OPTIONAL,
59
                'filter the list of entities data factories are created for'
60
            );
61
    }
62
63
    public function execute(InputInterface $input, OutputInterface $output)
64
    {
65
        $destination = $input->getOption('output');
66
        $metaDataEntries = $this->entityManager->getMetadataFactory()->getAllMetadata();
67
68
        if (!is_dir($destination)) {
0 ignored issues
show
Bug introduced by
It seems like $destination can also be of type string[]; however, parameter $filename of is_dir() 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

68
        if (!is_dir(/** @scrutinizer ignore-type */ $destination)) {
Loading history...
69
            mkdir($destination, 0777, true);
0 ignored issues
show
Bug introduced by
It seems like $destination can also be of type string[]; however, parameter $pathname of mkdir() 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

69
            mkdir(/** @scrutinizer ignore-type */ $destination, 0777, true);
Loading history...
70
        }
71
72
        /** @var ClassMetadata $metaData */
73
        foreach ($metaDataEntries as $metaData) {
74
            if ($filter = $input->getOption('filter')) {
75
                if (strpos($metaData->getName(), $filter) === false) {
76
                    $output->writeln(
77
                        sprintf(
78
                            'Filtering out %s...',
79
                            $metaData->getName()
80
                        ),
81
                        OutputInterface::VERBOSITY_VERY_VERBOSE
82
                    );
83
                    continue;
84
                }
85
            }
86
87
            $fileName = $destination . '/' . $metaData->reflClass->getShortName() . 'DataFactory.php';
0 ignored issues
show
Bug introduced by
Accessing reflClass on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
88
            // TODO handle duplicate names
89
90
            $file = FileGenerator::fromArray(
91
                [
92
                    'docblock' => DocBlockGenerator::fromArray(
93
                        [
94
                            'shortDescription' => '',
95
                            'longDescription' => '',
96
                            'tags' => [
97
                                [
98
                                    'name' => 'var',
99
                                    'description' => '\\Codeception\\Module\\DataFactory $factory'
100
                                ],
101
                                [
102
                                    'name' => 'var',
103
                                    'description' => '\\Doctrine\\ORM\\EntityManager $em'
104
                                ]
105
                            ]
106
                        ]
107
                    ),
108
                    'body' => $this->buildBody($metaData),
109
                ]
110
            );
111
112
            if (file_exists($fileName)) {
113
                $output->writeln(
114
                    sprintf(
115
                        '%s already exists. Skipping...',
116
                        $fileName
117
                    ),
118
                    OutputInterface::VERBOSITY_VERBOSE
119
                );
120
121
                continue;
122
            }
123
124
            file_put_contents($fileName, $file->generate());
125
        }
126
127
        $output->writeln(
128
            sprintf(
129
                'Data factories written to "%s"',
130
                $destination
0 ignored issues
show
Bug introduced by
It seems like $destination 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

130
                /** @scrutinizer ignore-type */ $destination
Loading history...
131
            )
132
        );
133
    }
134
135
    /**
136
     * @param ClassMetadata $metaData
137
     * @return string[]
138
     */
139
    private function buildFactoryData(ClassMetadata $metaData): array
140
    {
141
        $fieldMappings = $this->buildFieldMappings($metaData);
142
        $associationMappings = $this->buildAssociationMappings($metaData);
143
144
        return array_merge(
145
            $fieldMappings,
146
            $associationMappings
147
        );
148
    }
149
150
    /**
151
     * @param ClassMetadata $metaData
152
     * @return string
153
     */
154
    private function buildBody(ClassMetadata $metaData): string
155
    {
156
        $fields = $this->buildFactoryData($metaData);
157
158
        $body = sprintf(
159
            "use League\\FactoryMuffin\\Faker\\Facade as Faker;
160
161
\$factory->_define(
162
    %s::class,
163
    [\n",
164
            $metaData->getName()
165
        );
166
167
        foreach ($fields as $field) {
168
            $body .= $field;
169
        }
170
171
        $body .= '
172
    ]
173
);';
174
        return $body;
175
    }
176
177
    /**
178
     * @param ClassMetadata $metaData
179
     * @return array
180
     */
181
    private function buildAssociationMappings(ClassMetadata $metaData): array
182
    {
183
        $data = array_map(
184
            function (array $associationMapping) {
185
                if ($associationMapping['type'] > 0 && $associationMapping['type'] < 4) {
186
                    return sprintf(
187
                        "        '%s' => 'entity|' . \\%s::class,\n",
188
                        $associationMapping['fieldName'],
189
                        $associationMapping['targetEntity']
190
                    );
191
                }
192
193
                return null;
194
            },
195
            $metaData->associationMappings
196
        );
197
198
        return array_filter($data);
199
    }
200
201
    /**
202
     * @param ClassMetadata $metaData
203
     * @return array
204
     */
205
    private function buildFieldMappings(ClassMetadata $metaData): array
206
    {
207
        $data = [];
208
209
        $lookupTable = [
210
            'smallint' => 'random_int(0, 65000)',
211
            'integer' => 'random_int(0, 65000)',
212
            'bigint' => 'random_int(0, 65000)',
213
            'decimal' => 'Faker::randomFloat(2)',
214
            'float' => 'Faker::randomFloat(2)',
215
            'string' => 'Faker::sentence()',
216
            'text' => 'Faker::paragraph()',
217
            'guid' => "uniqid('', true)",
218
            'binary' => null,
219
            'blob' => null,
220
            'boolean' => '(bool)random_int(0, 1)',
221
            'date' => 'Faker::dateTime()',
222
            'date_immutable' => 'Faker::dateTime()',
223
            'datetime' => 'Faker::dateTime()',
224
            'datetime_immutable' => 'Faker::dateTime()',
225
            'datetimetz' => 'Faker::dateTime()',
226
            'datetimetz_immutable' => 'Faker::dateTime()',
227
            'time' => 'Faker::dateTime()',
228
            'time_immutable' => 'Faker::dateTime()',
229
            'dateinterval' => null,
230
            'array' => null,
231
            'simple_array' => null,
232
            'json' => null,
233
            'json_array' => null,
234
            'object' => null,
235
        ];
236
237
        foreach ($metaData->fieldMappings as $fieldMapping) {
238
            if (!array_key_exists($fieldMapping['type'], $lookupTable)) {
239
                continue;
240
            }
241
242
            $value = $lookupTable[$fieldMapping['type']];
243
244
            if ($value) {
245
                $data[] = sprintf(
246
                    "        '%s' => %s,\n",
247
                    $fieldMapping['fieldName'],
248
                    $value
249
                );
250
            }
251
        }
252
253
        return $data;
254
    }
255
}