1919use Cake \Console \ConsoleIo ;
2020use Cake \Console \ConsoleOptionParser ;
2121use Cake \Core \Configure ;
22+ use Cake \ORM \AssociationCollection ;
2223use Cake \ORM \Table ;
2324use Cake \ORM \TableRegistry ;
2425use Cake \Utility \Hash ;
2526use Cake \Utility \Inflector ;
2627use CakephpFixtureFactories \Factory \FactoryAwareTrait ;
2728use Exception ;
29+ use Override ;
2830use ReflectionClass ;
2931use ReflectionException ;
3032
@@ -47,6 +49,35 @@ class BakeFixtureFactoryCommand extends BakeCommand
4749 */
4850 private Table $ table ;
4951
52+ /**
53+ * @var array
54+ */
55+ protected array $ map = [
56+ 'string ' => [
57+ 'name ' => 'name ' ,
58+ 'first_name ' => 'firstName ' ,
59+ 'last_name ' => 'lastName ' ,
60+ 'username ' => 'userName ' ,
61+ 'slug ' => 'slug ' ,
62+ 'email ' => 'email ' ,
63+ 'description ' => 'words ' ,
64+ 'postal_code ' => 'postcode ' ,
65+ 'city ' => 'city ' ,
66+ 'address ' => 'address ' ,
67+ 'url ' => 'url ' ,
68+ 'ip_address ' => 'ipv4 ' ,
69+ 'currency ' => 'currencyCode ' ,
70+ 'phone_number ' => 'phoneNumber ' ,
71+ 'timezone ' => 'timezone ' ,
72+ ],
73+ 'float ' => [
74+ 'latitude ' => 'latitude ' ,
75+ 'longitude ' => 'longitude ' ,
76+ ],
77+ 'integer ' => [
78+ ],
79+ ];
80+
5081 /**
5182 * @return string Name of the command
5283 */
@@ -66,6 +97,7 @@ public function template(): string
6697 /**
6798 * @inheritDoc
6899 */
100+ #[Override]
69101 public static function defaultName (): string
70102 {
71103 return 'bake fixture_factory ' ;
@@ -104,6 +136,7 @@ public function setTable(string $tableName, ConsoleIo $io)
104136 * @param \Cake\Console\Arguments $args Arguments
105137 * @return string
106138 */
139+ #[Override]
107140 public function getPath (Arguments $ args ): string
108141 {
109142 $ outputDir = Configure::read ('FixtureFactories.testFixtureOutputDir ' , 'Factory/ ' );
@@ -214,6 +247,7 @@ private function bakeAllModels(Arguments $args, ConsoleIo $io): string
214247 * @param \Cake\Console\ConsoleIo $io The console io
215248 * @return int|null The exit code or null for success
216249 */
250+ #[Override]
217251 public function execute (Arguments $ args , ConsoleIo $ io ): ?int
218252 {
219253 $ this ->extractCommonProperties ($ args );
@@ -271,6 +305,7 @@ public function bakeFixtureFactory(string $modelName, Arguments $args, ConsoleIo
271305
272306 $ renderer = new TemplateRenderer ('CakephpFixtureFactories ' );
273307 $ renderer ->set ($ this ->templateData ($ args ));
308+ $ renderer ->viewBuilder ()->addHelper ('CakephpFixtureFactories.FactoryBake ' );
274309
275310 $ contents = $ renderer ->generate ($ this ->template ());
276311
@@ -294,6 +329,7 @@ public function templateData(Arguments $arg): array
294329 'modelName ' => $ this ->modelName ,
295330 'factory ' => Inflector::singularize ($ this ->modelName ) . 'Factory ' ,
296331 'namespace ' => $ this ->getFactoryNamespace ($ this ->plugin ),
332+ 'defaultData ' => $ this ->defaultData (),
297333 ];
298334 $ useStatements = $ methods = [];
299335 if ($ arg ->getOption ('methods ' )) {
@@ -393,8 +429,8 @@ public function handleFactoryWithSameName(string $name, Arguments $args, Console
393429 $ io ->abort (
394430 sprintf (
395431 'A factory with the name `%s` already exists. ' ,
396- $ name
397- )
432+ $ name,
433+ ),
398434 );
399435 }
400436
@@ -415,6 +451,7 @@ public function handleFactoryWithSameName(string $name, Arguments $args, Console
415451 *
416452 * @return \Cake\Console\ConsoleOptionParser
417453 */
454+ #[Override]
418455 public function getOptionParser (): ConsoleOptionParser
419456 {
420457 $ name = ($ this ->plugin ? $ this ->plugin . '. ' : '' ) . $ this ->name ;
@@ -423,33 +460,19 @@ public function getOptionParser(): ConsoleOptionParser
423460 $ parser = $ this ->_setCommonOptions ($ parser );
424461
425462 $ parser ->setDescription (
426- 'Fixture factory generator. '
463+ 'Fixture factory generator. ' ,
427464 )
428465 ->addArgument ('model ' , [
429466 'help ' => 'Name of the model the factory will create entities from ' .
430467 '(plural, without the `Table` suffix). You can use the Foo.Bars notation ' .
431468 'to bake a factory for the model Bars located in the plugin Foo. \n
432469 Factories are located in the folder test\Factory of your app, resp. plugin. ' ,
433470 ])
434- ->addOption ('plugin ' , [
435- 'short ' => 'p ' ,
436- 'help ' => 'Plugin to bake into. ' ,
437- ])
438471 ->addOption ('all ' , [
439472 'short ' => 'a ' ,
440473 'boolean ' => true ,
441474 'help ' => 'Bake factories for all models. ' ,
442475 ])
443- ->addOption ('force ' , [
444- 'short ' => 'f ' ,
445- 'boolean ' => true ,
446- 'help ' => 'Force overwriting existing file if a factory already exists with the same name. ' ,
447- ])
448- ->addOption ('quiet ' , [
449- 'short ' => 'q ' ,
450- 'boolean ' => true ,
451- 'help ' => 'Enable quiet output. ' ,
452- ])
453476 ->addOption ('methods ' , [
454477 'short ' => 'm ' ,
455478 'boolean ' => true ,
@@ -458,4 +481,127 @@ public function getOptionParser(): ConsoleOptionParser
458481
459482 return $ parser ;
460483 }
484+
485+ /**
486+ * @return array<string, mixed>
487+ */
488+ protected function defaultData (): array
489+ {
490+ $ defaultData = [];
491+
492+ $ modelName = $ this ->getTable ()->getAlias ();
493+ $ schema = $ this ->getTable ()->getSchema ();
494+ $ columns = $ schema ->columns ();
495+ $ foreignKeys = $ this ->foreignKeys ($ this ->getTable ()->associations ());
496+ foreach ($ columns as $ column ) {
497+ $ keys = $ schema ->getPrimaryKey ();
498+ if (in_array ($ column , $ keys , true ) || in_array ($ column , $ foreignKeys , true )) {
499+ continue ;
500+ }
501+
502+ $ columnSchema = $ schema ->getColumn ($ column );
503+ if ($ columnSchema ['null ' ] || $ columnSchema ['default ' ] !== null ) {
504+ continue ;
505+ }
506+
507+ if (!in_array ($ columnSchema ['type ' ], ['integer ' , 'string ' , 'date ' , 'datetime ' , 'time ' , 'bool ' , 'float ' ])) {
508+ continue ;
509+ }
510+
511+ $ guessedDefault = $ this ->guessDefault ($ column , $ modelName , $ columnSchema );
512+ if ($ guessedDefault ) {
513+ $ defaultData [$ column ] = $ guessedDefault ;
514+ }
515+ }
516+
517+ return $ defaultData ;
518+ }
519+
520+ /**
521+ * @param string $column
522+ * @param string $modelName
523+ * @param array $columnSchema
524+ * @return mixed
525+ */
526+ protected function guessDefault (string $ column , string $ modelName , array $ columnSchema ): mixed
527+ {
528+ $ map = array_merge_recursive ($ this ->map , (array )Configure::read ('FixtureFactories.defaultDataMap ' ));
529+ $ map = $ map [$ columnSchema ['type ' ]] ?? [];
530+
531+ $ modelNameMap = [
532+ 'Countries ' => 'country ' ,
533+ 'Cities ' => 'city ' ,
534+ ];
535+
536+ if ($ columnSchema ['type ' ] === 'string ' ) {
537+ if ($ column === 'name ' && isset ($ modelNameMap [$ modelName ])) {
538+ return '$faker-> ' . $ modelNameMap [$ modelName ] . '() ' ;
539+ }
540+ if (isset ($ map [$ column ])) {
541+ return '$faker-> ' . $ map [$ column ] . '() ' ;
542+ }
543+
544+ if ($ columnSchema ['length ' ] && $ columnSchema ['length ' ] < 5 ) {
545+ return 'mb_substr($faker->text(5), 0, ' . $ columnSchema ['length ' ] . ') ' ;
546+ }
547+
548+ return '$faker->text( ' . $ columnSchema ['length ' ] . ') ' ;
549+ }
550+ if ($ columnSchema ['type ' ] === 'integer ' ) {
551+ if (isset ($ map [$ column ])) {
552+ return '$faker-> ' . $ map [$ column ] . '() ' ;
553+ }
554+
555+ return '$faker->randomNumber() ' ;
556+ }
557+ if ($ columnSchema ['type ' ] === 'boolean ' ) {
558+ if (isset ($ map [$ column ])) {
559+ return '$faker-> ' . $ map [$ column ] . '() ' ;
560+ }
561+
562+ return '$faker->boolean() ' ;
563+ }
564+ if ($ columnSchema ['type ' ] === 'date ' ) {
565+ if (isset ($ map [$ column ])) {
566+ return '$faker-> ' . $ map [$ column ] . '() ' ;
567+ }
568+
569+ return '$faker->date() ' ;
570+ }
571+ if ($ columnSchema ['type ' ] === 'datetime ' ) {
572+ if (isset ($ map [$ column ])) {
573+ return '$faker-> ' . $ map [$ column ] . '() ' ;
574+ }
575+
576+ return '$faker->datetime() ' ;
577+ }
578+ if ($ columnSchema ['type ' ] === 'time ' ) {
579+ if (isset ($ map [$ column ])) {
580+ return '$faker-> ' . $ map [$ column ] . '() ' ;
581+ }
582+
583+ return '$faker->time() ' ;
584+ }
585+
586+ return null ;
587+ }
588+
589+ /**
590+ * @param \Cake\ORM\AssociationCollection $associations
591+ * @return array<string>
592+ */
593+ protected function foreignKeys (AssociationCollection $ associations ): array
594+ {
595+ $ keys = [];
596+
597+ foreach ($ associations as $ association ) {
598+ $ key = $ association ->getForeignKey ();
599+ if ($ key === false ) {
600+ continue ;
601+ }
602+ $ keys = array_merge ($ keys , (array )$ key );
603+ }
604+
605+ return $ keys ;
606+ }
461607}
0 commit comments