curtiskelsey /
doctrine-repo-helper
| 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
Loading history...
|
|||||
| 69 | mkdir($destination, 0777, true); |
||||
|
0 ignored issues
–
show
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
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
|
|||||
| 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
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
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 | } |