Test Failed
Push — master ( c2bd3c...89cc0d )
by Curtis
02:04
created

GenerateDataFactoryCommand::buildBody()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 21
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 12
dl 0
loc 21
rs 9.8666
c 0
b 0
f 0
cc 2
nc 2
nop 1
1
<?php
2
3
namespace DoctrineRepoHelper\Command;
4
5
6
use Doctrine\ORM\Mapping\ClassMetadata;
7
use Doctrine\ORM\EntityManagerInterface;
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 = [];
184
185
        foreach ($metaData->associationMappings as $associationMapping) {
186
            switch ($associationMapping['type']) {
187
                case 1:
188
                case 2:
189
                case 3:
190
                    $data[] = sprintf(
191
                        "        '%s' => 'entity|' . \\%s::class,\n",
192
                        $associationMapping['fieldName'],
193
                        $associationMapping['targetEntity']
194
                    );
195
                    break;
196
                case 4:
197
                    // TODO one to many
198
                    break;
199
                case 8:
200
                    // TODO many to many
201
                    break;
202
                default:
203
                    break;
204
            }
205
        }
206
        return $data;
207
    }
208
209
    /**
210
     * @param ClassMetadata $metaData
211
     * @return array
212
     */
213
    private function buildFieldMappings(ClassMetadata $metaData): array
214
    {
215
        $data = [];
216
217
        foreach ($metaData->fieldMappings as $fieldMapping) {
218
            switch ($fieldMapping['type']) {
219
                case 'smallint':
220
                case 'integer':
221
                case 'bigint':
222
                    $data[] = sprintf(
223
                        "        '%s' => random_int(0, 65000),\n",
224
                        $fieldMapping['fieldName']
225
                    );
226
                    break;
227
                case 'decimal':
228
                case 'float':
229
                    $data[] = sprintf(
230
                        "        '%s' => Faker::randomFloat(2),\n",
231
                        $fieldMapping['fieldName']
232
                    );
233
                    break;
234
                case 'string':
235
                    $data[] = sprintf(
236
                        "        '%s' => Faker::sentence(),\n",
237
                        $fieldMapping['fieldName']
238
                    );
239
                    break;
240
                case 'text':
241
                    $data[] = sprintf(
242
                        "        '%s' => Faker::paragraph(),\n",
243
                        $fieldMapping['fieldName']
244
                    );
245
                    break;
246
                case 'guid':
247
                    $data[] = sprintf(
248
                        "        '%s' => uniqid('', true)",
249
                        $fieldMapping['fieldName']
250
                    );
251
                    break;
252
                case 'binary':
253
                case 'blob':
254
                    break;
255
                case 'boolean':
256
                    $data[] = sprintf(
257
                        "        '%s' => (bool)random_int(0, 1),\n",
258
                        $fieldMapping['fieldName']
259
                    );
260
                    break;
261
                case 'date':
262
                case 'date_immutable':
263
                case 'datetime':
264
                case 'datetime_immutable':
265
                case 'datetimetz':
266
                case 'datetimetz_immutable':
267
                case 'time':
268
                case 'time_immutable':
269
                    $data[] = sprintf(
270
                        "        '%s' => Faker::dateTime(),\n",
271
                        $fieldMapping['fieldName']
272
                    );
273
                    break;
274
                case 'dateinterval':
275
                case 'array':
276
                case 'simple_array':
277
                case 'json':
278
                case 'json_array':
279
                case 'object':
280
                    break;
281
                default:
282
                    $data[] = sprintf(
283
                        "        '%s' => Faker::word(),\n",
284
                        $fieldMapping['fieldName']
285
                    );
286
                    break;
287
            }
288
        }
289
290
        return $data;
291
    }
292
}