diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index e8f3ae7d..69e7d139 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/stale@v7 + - uses: actions/stale@v8 with: repo-token: ${{ secrets.GITHUB_TOKEN }} stale-issue-message: 'This issue is stale because it has been open for 120 days with no activity. Remove the `stale` label or comment or this will be closed in 15 days' diff --git a/docs/en/development.rst b/docs/en/development.rst index 735c4183..2efb76b2 100644 --- a/docs/en/development.rst +++ b/docs/en/development.rst @@ -226,7 +226,7 @@ dependencies in your templates you can include template overrides in your application templates. These overrides work similar to overriding other plugin templates. -#. Create a new directory **/templates/plugin/Bake/**. +#. Create a new directory **/templates/plugin/Bake/bake/**. #. Copy any templates you want to override from **vendor/cakephp/bake/templates/bake/** to matching files in your application. diff --git a/phpcs.xml b/phpcs.xml index c68ff65c..d1748477 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -1,6 +1,10 @@ + + + src/ + tests/ diff --git a/src/Command/BakeCommand.php b/src/Command/BakeCommand.php index a03596eb..ac0ce16f 100644 --- a/src/Command/BakeCommand.php +++ b/src/Command/BakeCommand.php @@ -28,6 +28,7 @@ use Cake\Event\Event; use Cake\Event\EventManager; use InvalidArgumentException; +use function Cake\Core\pluginSplit; /** * Base class for commands that bake can use. diff --git a/src/Command/ModelCommand.php b/src/Command/ModelCommand.php index 12f94599..2b006f50 100644 --- a/src/Command/ModelCommand.php +++ b/src/Command/ModelCommand.php @@ -31,6 +31,7 @@ use Cake\Datasource\ConnectionManager; use Cake\ORM\Table; use Cake\Utility\Inflector; +use function Cake\Core\pluginSplit; /** * Command for generating model files. @@ -233,7 +234,7 @@ public function getAssociations(Table $table, Arguments $args, ConsoleIo $io): a ]; $primary = $table->getPrimaryKey(); - $associations = $this->findBelongsTo($table, $associations); + $associations = $this->findBelongsTo($table, $associations, $args); if (is_array($primary) && count($primary) > 1) { $io->warning( @@ -329,9 +330,10 @@ public function getAssociationInfo(Table $table): array * * @param \Cake\ORM\Table $model Database\Table instance of table being generated. * @param array $associations Array of in progress associations + * @param \Cake\Console\Arguments|null $args CLI arguments * @return array Associations with belongsTo added in. */ - public function findBelongsTo(Table $model, array $associations): array + public function findBelongsTo(Table $model, array $associations, ?Arguments $args = null): array { $schema = $model->getSchema(); foreach ($schema->columns() as $fieldName) { @@ -362,11 +364,13 @@ public function findBelongsTo(Table $model, array $associations): array get_class($associationTable) === Table::class && !in_array(Inflector::tableize($tmpModelName), $tables, true) ) { + $allowAliasRelations = $args && $args->getOption('skip-relation-check'); $found = $this->findTableReferencedBy($schema, $fieldName); - if (!$found) { + if ($found) { + $tmpModelName = Inflector::camelize($found); + } elseif (!$allowAliasRelations) { continue; } - $tmpModelName = Inflector::camelize($found); } $assoc = [ 'alias' => $tmpModelName, @@ -1319,6 +1323,10 @@ public function buildOptionParser(ConsoleOptionParser $parser): ConsoleOptionPar ])->addOption('no-fixture', [ 'boolean' => true, 'help' => 'Do not generate a test fixture skeleton.', + ])->addOption('skip-relation-check', [ + 'boolean' => true, + 'help' => 'Generate relations for all "example_id" fields' + . ' without checking the database if a table "examples" exists.', ])->setEpilog( 'Omitting all arguments and options will list the table names you can generate models for.' ); diff --git a/src/Command/PluginCommand.php b/src/Command/PluginCommand.php index 5855ef3a..3cf8f130 100644 --- a/src/Command/PluginCommand.php +++ b/src/Command/PluginCommand.php @@ -29,6 +29,7 @@ use Cake\Utility\Filesystem; use Cake\Utility\Inflector; use RuntimeException; +use function Cake\Core\env; /** * The Plugin Command handles creating an empty plugin, ready to be used @@ -384,7 +385,7 @@ public function buildOptionParser(ConsoleOptionParser $parser): ConsoleOptionPar ])->addOption('theme', [ 'short' => 't', 'help' => 'The theme to use when baking code.', - 'default' => Configure::read('Bake.theme') ?? '', + 'default' => Configure::read('Bake.theme') ?: null, 'choices' => $this->_getBakeThemes(), ]); diff --git a/src/Command/TemplateCommand.php b/src/Command/TemplateCommand.php index f7de54db..d72379ef 100644 --- a/src/Command/TemplateCommand.php +++ b/src/Command/TemplateCommand.php @@ -30,6 +30,7 @@ use Cake\View\Exception\MissingTemplateException; use Exception; use RuntimeException; +use function Cake\Core\namespaceSplit; /** * Task class for creating view template files. diff --git a/src/Command/TestCommand.php b/src/Command/TestCommand.php index 8bffadfa..8adc7b07 100644 --- a/src/Command/TestCommand.php +++ b/src/Command/TestCommand.php @@ -29,6 +29,8 @@ use Cake\Utility\Inflector; use ReflectionClass; use UnexpectedValueException; +use function Cake\Core\namespaceSplit; +use function Cake\Core\pluginSplit; /** * Command class for generating test files. diff --git a/src/Utility/Model/AssociationFilter.php b/src/Utility/Model/AssociationFilter.php index 7c0f401b..c6f6a47c 100644 --- a/src/Utility/Model/AssociationFilter.php +++ b/src/Utility/Model/AssociationFilter.php @@ -19,6 +19,7 @@ use Cake\ORM\Table; use Cake\Utility\Inflector; use Exception; +use function Cake\Core\namespaceSplit; /** * Utility class to filter Model Table associations diff --git a/src/View/BakeView.php b/src/View/BakeView.php index b9638c22..31f0a95a 100644 --- a/src/View/BakeView.php +++ b/src/View/BakeView.php @@ -21,6 +21,7 @@ use Cake\Event\EventDispatcherTrait; use Cake\Event\EventInterface; use Cake\TwigView\View\TwigView; +use function Cake\Core\pluginSplit; class BakeView extends TwigView { diff --git a/src/View/Helper/BakeHelper.php b/src/View/Helper/BakeHelper.php index 3e7b492e..70749111 100644 --- a/src/View/Helper/BakeHelper.php +++ b/src/View/Helper/BakeHelper.php @@ -13,7 +13,8 @@ use Cake\ORM\Table; use Cake\Utility\Inflector; use Cake\View\Helper; -use function pluginSplit; +use function Cake\Collection\collection; +use function Cake\Core\pluginSplit; /** * Bake helper @@ -280,7 +281,7 @@ public function getViewFieldsData(array $fields, SchemaInterface $schema, array $immediateAssociations = $associations['BelongsTo']; $associationFields = collection($fields) ->map(function ($field) use ($immediateAssociations) { - foreach ($immediateAssociations as $alias => $details) { + foreach ($immediateAssociations as $details) { if ($field === $details['foreignKey']) { return [$field => $details]; } diff --git a/tests/TestCase/CodeGen/FileBuilderTest.php b/tests/TestCase/CodeGen/FileBuilderTest.php index 2bf6c277..cae82eae 100644 --- a/tests/TestCase/CodeGen/FileBuilderTest.php +++ b/tests/TestCase/CodeGen/FileBuilderTest.php @@ -56,7 +56,7 @@ class TestTable{} ); $this->expectException(ParseException::class); - $builder = new FileBuilder($this->io, 'MyOtherApp\Model', $file); + new FileBuilder($this->io, 'MyOtherApp\Model', $file); } public function testUses(): void diff --git a/tests/TestCase/Command/MailerCommandTest.php b/tests/TestCase/Command/MailerCommandTest.php index e365bddb..58e0b6f7 100644 --- a/tests/TestCase/Command/MailerCommandTest.php +++ b/tests/TestCase/Command/MailerCommandTest.php @@ -64,7 +64,6 @@ public function testMainPlugin() { $this->_loadTestPlugin('TestBake'); $path = Plugin::path('TestBake'); - $templatePath = Plugin::templatePath('TestBake'); $this->generatedFiles = [ $path . 'src/Mailer/ExampleMailer.php', diff --git a/tests/TestCase/Command/ModelCommandTest.php b/tests/TestCase/Command/ModelCommandTest.php index f953b067..0e3b3930 100644 --- a/tests/TestCase/Command/ModelCommandTest.php +++ b/tests/TestCase/Command/ModelCommandTest.php @@ -497,6 +497,59 @@ public function testGetAssociationsAddAssociationIfTableExist() $this->assertEquals($expected, $result); } + /** + * Test that association generation adds `Anythings` association for `anything_id` field + * when using `--skip-relation-check` option, even if no db table exists + * + * @return void + */ + public function testGetAssociationsAddAssociationIfNoTableExistButAliasIsAllowed() + { + $items = $this->getTableLocator()->get('TodoItems'); + + $items->setSchema($items->getSchema()->addColumn('anything_id', ['type' => 'integer'])); + $command = new ModelCommand(); + $command->connection = 'test'; + + $args = new Arguments([], ['skip-relation-check' => true], []); + $io = $this->createMock(ConsoleIo::class); + $result = $command->getAssociations($items, $args, $io); + $expected = [ + 'belongsTo' => [ + [ + 'alias' => 'Users', + 'foreignKey' => 'user_id', + 'joinType' => 'INNER', + ], + [ + 'alias' => 'Anythings', + 'foreignKey' => 'anything_id', + ], + ], + 'hasMany' => [ + [ + 'alias' => 'TodoTasks', + 'foreignKey' => 'todo_item_id', + ], + ], + 'belongsToMany' => [ + [ + 'alias' => 'TodoLabels', + 'foreignKey' => 'todo_item_id', + 'joinTable' => 'todo_items_todo_labels', + 'targetForeignKey' => 'todo_label_id', + ], + ], + 'hasOne' => [ + [ + 'alias' => 'TodoReminders', + 'foreignKey' => 'todo_item_id', + ], + ], + ]; + $this->assertEquals($expected, $result); + } + /** * Test that association generation ignores `_id` fields * diff --git a/tests/TestCase/Command/PluginCommandTest.php b/tests/TestCase/Command/PluginCommandTest.php index efd0b004..7273ba09 100644 --- a/tests/TestCase/Command/PluginCommandTest.php +++ b/tests/TestCase/Command/PluginCommandTest.php @@ -34,6 +34,8 @@ class PluginCommandTest extends TestCase { protected $testAppFile = APP . 'Application.php'; + protected $pluginsPath = TMP . 'plugin_task' . DS; + /** * setUp method * @@ -46,12 +48,11 @@ public function setUp(): void $this->setAppNamespace('Bake\Test\App'); // Output into a safe place. - $path = TMP . 'plugin_task' . DS; - Configure::write('App.paths.plugins', [$path]); + Configure::write('App.paths.plugins', [$this->pluginsPath]); // Create the test output path - if (!file_exists($path)) { - mkdir($path, 0777, true); + if (!file_exists($this->pluginsPath)) { + mkdir($this->pluginsPath, 0777, true); } if (file_exists(APP . 'Application.php.bak')) { @@ -69,7 +70,7 @@ public function setUp(): void public function tearDown(): void { $fs = new Filesystem(); - $fs->deleteDir(TMP . 'plugin_task'); + $fs->deleteDir($this->pluginsPath); if (file_exists(APP . 'Application.php.bak')) { rename(APP . 'Application.php.bak', APP . 'Application.php'); @@ -90,6 +91,16 @@ public function testMainBakePluginContents() $this->assertPluginContents('SimpleExample'); } + public function testBakingWithNonExistentPluginsDir() + { + $fs = new Filesystem(); + $fs->deleteDir($this->pluginsPath); + + $this->exec('bake plugin SimpleExample', ['y', 'n']); + $this->assertExitCode(CommandInterface::CODE_SUCCESS); + $this->assertPluginContents('SimpleExample'); + } + /** * test creating a plugin with a custom app namespace. * @@ -212,7 +223,7 @@ public function testFindPathNonExistent() $result = $command->findPath($paths, $io); $this->assertNull($result, 'no return'); - $this->assertSame(TMP . 'plugin_task' . DS, $command->path); + $this->assertSame($this->pluginsPath, $command->path); } /** diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 2b40072f..a031b40e 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -16,6 +16,7 @@ // phpcs:ignoreFile +use function Cake\Core\env; use Bake\BakePlugin; use Cake\Cache\Cache; use Cake\Core\Configure; @@ -38,7 +39,6 @@ unset($findRoot); chdir($root); -require_once 'vendor/cakephp/cakephp/src/basics.php'; require_once 'vendor/autoload.php'; define('ROOT', $root . DS . 'tests' . DS . 'test_app' . DS); diff --git a/tests/comparisons/Template/testBakeView.php b/tests/comparisons/Template/testBakeView.php index 56ddb191..382ff664 100644 --- a/tests/comparisons/Template/testBakeView.php +++ b/tests/comparisons/Template/testBakeView.php @@ -28,7 +28,7 @@ - has('profile') ? $this->Html->link($author->profile->id, ['controller' => 'Profiles', 'action' => 'view', $author->profile->id]) : '' ?> + has('profile') ? $this->Html->link($author->profile->nick, ['controller' => 'Profiles', 'action' => 'view', $author->profile->id]) : '' ?>