Skip to content

Commit ec19f66

Browse files
Merge remote-tracking branch 'origin/main' into #235_v3.1
# Conflicts: # src/CakephpFixtureFactoriesPlugin.php # src/Factory/AssociationBuilder.php # src/Factory/BaseFactory.php
2 parents 446ec21 + 5f02b9a commit ec19f66

30 files changed

+292
-102
lines changed

.github/workflows/phpstan.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ on:
88
branches:
99
- '*'
1010
schedule:
11-
- cron: '0 0 * * 0'
11+
- cron: '0 0 * * 0'
1212

1313
jobs:
1414
build:

.github/workflows/tests_composer2.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
strategy:
1818
fail-fast: false
1919
matrix:
20-
php-version: ['8.1', '8.2']
20+
php-version: ['8.1', '8.2', '8.3']
2121
db-type: [sqlite, mysql, pgsql]
2222
composer-type: [lowest, stable, dev]
2323

composer.json

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "vierge-noire/cakephp-fixture-factories",
33
"description": "CakePHP Dynamic Fixtures",
44
"type": "cakephp-plugin",
5-
"keywords": ["cakephp", "tests", "dynamic", "fixtures"],
5+
"keywords": ["cakephp", "tests", "dynamic", "fixtures", "dev", "cli"],
66
"authors": [
77
{
88
"name": "Nicolas Masson",
@@ -17,30 +17,31 @@
1717
"require": {
1818
"php": ">=8.1",
1919
"cakephp/orm": "^5.0",
20-
"fakerphp/faker": "^1.15",
20+
"fakerphp/faker": "^1.23",
2121
"vierge-noire/cakephp-test-suite-light": "^3.0"
2222
},
2323
"require-dev": {
2424
"cakephp/bake": "^3.0.0",
25+
"cakephp/twig-view": "^2.0.2",
2526
"cakephp/cakephp-codesniffer": "^5.1",
2627
"cakephp/migrations": "^4.0.0",
2728
"josegonzalez/dotenv": "^4.0.0",
2829
"phpstan/phpstan": "^1.0",
29-
"phpunit/phpunit": "^10.1",
30-
"vimeo/psalm": "^5.0"
30+
"phpunit/phpunit": "^10.5",
31+
"vimeo/psalm": "^6.0"
3132
},
3233
"autoload": {
3334
"psr-4": {
34-
"CakephpFixtureFactories\\": "src"
35+
"CakephpFixtureFactories\\": "src/"
3536
}
3637
},
3738
"autoload-dev": {
3839
"psr-4": {
39-
"CakephpFixtureFactories\\Test\\": "tests",
40-
"TestApp\\": "tests/TestApp/src",
41-
"TestApp\\Test\\": "tests/TestApp/tests",
42-
"TestPlugin\\": "tests/TestApp/plugins/TestPlugin/src",
43-
"TestPlugin\\Test\\": "tests/TestApp/plugins/TestPlugin/tests"
40+
"CakephpFixtureFactories\\Test\\": "tests/",
41+
"TestApp\\": "tests/TestApp/src/",
42+
"TestApp\\Test\\": "tests/TestApp/tests/",
43+
"TestPlugin\\": "tests/TestApp/plugins/TestPlugin/src/",
44+
"TestPlugin\\Test\\": "tests/TestApp/plugins/TestPlugin/tests/"
4445
}
4546
},
4647
"scripts": {
@@ -59,6 +60,6 @@
5960
"dealerdirect/phpcodesniffer-composer-installer": true
6061
}
6162
},
62-
"minimum-stability": "dev",
63+
"minimum-stability": "stable",
6364
"prefer-stable": true
6465
}

docs/setup.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ per default located in the root folder of your application:
4141
<listeners>
4242
<listener class="CakephpTestSuiteLight\FixtureInjector">
4343
<arguments>
44-
<object class="CakephpTestSuiteLight\FixtureManager" />
44+
<object class="CakephpTestSuiteLight\FixtureManager"/>
4545
</arguments>
4646
</listener>
4747
</listeners>
@@ -71,7 +71,7 @@ use TruncateDirtyTables
7171

7272
No modification to your PHPUnit configuration is required when using the trait.
7373

74-
**We recommend using migrations for managing the schema of your test DB with the [CakePHP Migrator tool.](https://book.cakephp.org/migrations/2/en/index.html#using-migrations-for-tests)**
74+
**We recommend using migrations for managing the schema of your test DB with the [CakePHP Migrator tool.](https://book.cakephp.org/migrations/3/en/index.html#using-migrations-for-tests)**
7575

7676

7777

psalm.xml

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,9 @@
1313
autoloader="tests/bootstrap.php"
1414
>
1515
<projectFiles>
16-
<directory name="src"/>
16+
<directory name="src/"/>
17+
<ignoreFiles>
18+
<file name="vendor/cakephp/cakephp/src/ORM/Locator/TableLocator.php" />
19+
</ignoreFiles>
1720
</projectFiles>
18-
<issueHandlers>
19-
<InvalidScalarArgument errorLevel="suppress" />
20-
<NoInterfaceProperties errorLevel="suppress" />
21-
</issueHandlers>
2221
</psalm>

src/CakephpFixtureFactoriesPlugin.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
use Cake\Core\BasePlugin;
1717

1818
/**
19-
* Plugin class for creating test fixtures
19+
* Plugin class for CakephpFixtureFactories
2020
*/
2121
class CakephpFixtureFactoriesPlugin extends BasePlugin
2222
{

src/Command/BakeFixtureFactoryCommand.php

Lines changed: 163 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@
1919
use Cake\Console\ConsoleIo;
2020
use Cake\Console\ConsoleOptionParser;
2121
use Cake\Core\Configure;
22+
use Cake\ORM\AssociationCollection;
2223
use Cake\ORM\Table;
2324
use Cake\ORM\TableRegistry;
2425
use Cake\Utility\Hash;
2526
use Cake\Utility\Inflector;
2627
use CakephpFixtureFactories\Factory\FactoryAwareTrait;
2728
use Exception;
29+
use Override;
2830
use ReflectionClass;
2931
use 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

Comments
 (0)