From e8c7cb42cf0de4fd53bcc18b0a9dd7cf4562f870 Mon Sep 17 00:00:00 2001 From: Alejandro Ibarra Date: Fri, 15 Jul 2022 09:43:40 +0200 Subject: [PATCH 1/4] Avoid creating association if database table or table object don't exist #839 --- src/Command/ModelCommand.php | 12 ++- tests/TestCase/Command/ModelCommandTest.php | 92 +++++++++++++++++++++ 2 files changed, 101 insertions(+), 3 deletions(-) diff --git a/src/Command/ModelCommand.php b/src/Command/ModelCommand.php index 37dc629a..c5b1a6a5 100644 --- a/src/Command/ModelCommand.php +++ b/src/Command/ModelCommand.php @@ -30,6 +30,7 @@ use Cake\Database\Schema\TableSchemaInterface; use Cake\Datasource\ConnectionManager; use Cake\ORM\Table; +use Cake\ORM\TableRegistry; use Cake\Utility\Inflector; /** @@ -346,11 +347,16 @@ public function findBelongsTo(Table $model, array $associations): array ]; } else { $tmpModelName = $this->_modelNameFromKey($fieldName); - if (!in_array(Inflector::tableize($tmpModelName), $this->_tables, true)) { + $this->getTableLocator()->get($tmpModelName); + $genericInstances = $this->getTableLocator()->genericInstances(); + $tables = $this->listAll(); + /** Check if association model could not be instantiated as a subclass but a generic Table instance instead. */ + if (isset($genericInstances[$tmpModelName]) && !in_array(Inflector::tableize($tmpModelName), $tables, true)) { $found = $this->findTableReferencedBy($schema, $fieldName); - if ($found) { - $tmpModelName = Inflector::camelize($found); + if (!$found) { + continue; } + $tmpModelName = Inflector::camelize($found); } $assoc = [ 'alias' => $tmpModelName, diff --git a/tests/TestCase/Command/ModelCommandTest.php b/tests/TestCase/Command/ModelCommandTest.php index e96fcfc8..fde9866e 100644 --- a/tests/TestCase/Command/ModelCommandTest.php +++ b/tests/TestCase/Command/ModelCommandTest.php @@ -375,6 +375,96 @@ public function testGetAssociationsPlugin() $this->assertEquals($expected, $result); } + /** + * Test that association generation ignores `anything_id` fields if + * AnythingsTable object nor `anythings` database table exist + * + * @return void + */ + public function testGetAssociationsIgnoreUnderscoreIdIfNoDbTable() + { + $items = $this->getTableLocator()->get('TodoItems'); + + $items->setSchema($items->getSchema()->addColumn('anything_id', ['type' => 'integer'])); + $command = new ModelCommand(); + $command->connection = 'test'; + + $args = new Arguments([], [], []); + $io = $this->createMock(ConsoleIo::class); + $result = $command->getAssociations($items, $args, $io); + $expected = [ + 'belongsTo' => [ + [ + 'alias' => 'Users', + 'foreignKey' => 'user_id', + 'joinType' => 'INNER', + ], + ], + 'hasMany' => [ + [ + 'alias' => 'TodoTasks', + 'foreignKey' => 'todo_item_id', + ], + ], + 'belongsToMany' => [ + [ + 'alias' => 'TodoLabels', + 'foreignKey' => 'todo_item_id', + 'joinTable' => 'todo_items_todo_labels', + 'targetForeignKey' => 'todo_label_id', + ], + ], + ]; + $this->assertEquals($expected, $result); + } + + /** + * Test that association generation adds association when `anything_id` fields and + * AnythingsTable object exist even if no db table + * + * @return void + */ + public function testGetAssociationsAddAssociationIfTableExist() + { + $items = $this->getTableLocator()->get('TodoItems'); + + $items->setSchema($items->getSchema()->addColumn('template_task_comment_id', ['type' => 'integer'])); + $command = new ModelCommand(); + $command->connection = 'test'; + + $args = new Arguments([], [], []); + $io = $this->createMock(ConsoleIo::class); + $result = $command->getAssociations($items, $args, $io); + $expected = [ + 'belongsTo' => [ + [ + 'alias' => 'Users', + 'foreignKey' => 'user_id', + 'joinType' => 'INNER', + ], + [ + 'alias' => 'TemplateTaskComments', + 'foreignKey' => 'template_task_comment_id' + ] + ], + 'hasMany' => [ + [ + 'alias' => 'TodoTasks', + 'foreignKey' => 'todo_item_id', + ], + ], + 'belongsToMany' => [ + [ + 'alias' => 'TodoLabels', + 'foreignKey' => 'todo_item_id', + 'joinTable' => 'todo_items_todo_labels', + 'targetForeignKey' => 'todo_label_id', + ], + ], + ]; + $this->assertEquals($expected, $result); + } + /** * Test that association generation ignores `_id` fields * @@ -491,6 +581,7 @@ public function testBelongsToGenerationConstraints() { $model = $this->getTableLocator()->get('Invitations'); $command = new ModelCommand(); + $command->connection = 'test'; $result = $command->findBelongsTo($model, []); $expected = [ 'belongsTo' => [ @@ -519,6 +610,7 @@ public function testBelongsToGenerationCompositeKey() { $model = $this->getTableLocator()->get('TodoItemsTodoLabels'); $command = new ModelCommand(); + $command->connection = 'test'; $result = $command->findBelongsTo($model, []); $expected = [ 'belongsTo' => [ From 218c3df68420e5b999c0d25d2751ab5c114d6d28 Mon Sep 17 00:00:00 2001 From: Alejandro Ibarra Date: Fri, 15 Jul 2022 09:45:44 +0200 Subject: [PATCH 2/4] Fix standards --- src/Command/ModelCommand.php | 6 ++++-- tests/TestCase/Command/ModelCommandTest.php | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Command/ModelCommand.php b/src/Command/ModelCommand.php index c5b1a6a5..71fdb66d 100644 --- a/src/Command/ModelCommand.php +++ b/src/Command/ModelCommand.php @@ -30,7 +30,6 @@ use Cake\Database\Schema\TableSchemaInterface; use Cake\Datasource\ConnectionManager; use Cake\ORM\Table; -use Cake\ORM\TableRegistry; use Cake\Utility\Inflector; /** @@ -351,7 +350,10 @@ public function findBelongsTo(Table $model, array $associations): array $genericInstances = $this->getTableLocator()->genericInstances(); $tables = $this->listAll(); /** Check if association model could not be instantiated as a subclass but a generic Table instance instead. */ - if (isset($genericInstances[$tmpModelName]) && !in_array(Inflector::tableize($tmpModelName), $tables, true)) { + if ( + isset($genericInstances[$tmpModelName]) && + !in_array(Inflector::tableize($tmpModelName), $tables, true) + ) { $found = $this->findTableReferencedBy($schema, $fieldName); if (!$found) { continue; diff --git a/tests/TestCase/Command/ModelCommandTest.php b/tests/TestCase/Command/ModelCommandTest.php index fde9866e..b989c09f 100644 --- a/tests/TestCase/Command/ModelCommandTest.php +++ b/tests/TestCase/Command/ModelCommandTest.php @@ -444,8 +444,8 @@ public function testGetAssociationsAddAssociationIfTableExist() ], [ 'alias' => 'TemplateTaskComments', - 'foreignKey' => 'template_task_comment_id' - ] + 'foreignKey' => 'template_task_comment_id', + ], ], 'hasMany' => [ [ From 9b923fecb5d9ecf7fbd6ccf83d38dd7b9ef755e9 Mon Sep 17 00:00:00 2001 From: Alejandro Ibarra Date: Fri, 15 Jul 2022 10:02:42 +0200 Subject: [PATCH 3/4] Fix review comments --- src/Command/ModelCommand.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Command/ModelCommand.php b/src/Command/ModelCommand.php index 71fdb66d..07bba472 100644 --- a/src/Command/ModelCommand.php +++ b/src/Command/ModelCommand.php @@ -346,12 +346,11 @@ public function findBelongsTo(Table $model, array $associations): array ]; } else { $tmpModelName = $this->_modelNameFromKey($fieldName); - $this->getTableLocator()->get($tmpModelName); - $genericInstances = $this->getTableLocator()->genericInstances(); + $associationTable = $this->getTableLocator()->get($tmpModelName); $tables = $this->listAll(); - /** Check if association model could not be instantiated as a subclass but a generic Table instance instead. */ + // Check if association model could not be instantiated as a subclass but a generic Table instance instead if ( - isset($genericInstances[$tmpModelName]) && + get_class($associationTable) === Table::class && !in_array(Inflector::tableize($tmpModelName), $tables, true) ) { $found = $this->findTableReferencedBy($schema, $fieldName); From dc59fbcb84384a014a38bce60593516900caa910 Mon Sep 17 00:00:00 2001 From: Alejandro Ibarra Date: Fri, 15 Jul 2022 10:15:25 +0200 Subject: [PATCH 4/4] Fix ModelCommandTest::testBakeEntityWithPlugin() --- tests/TestCase/Command/ModelCommandTest.php | 4 +- .../Model/testBakeEntityWithPlugin.php | 86 +++++++------------ 2 files changed, 32 insertions(+), 58 deletions(-) diff --git a/tests/TestCase/Command/ModelCommandTest.php b/tests/TestCase/Command/ModelCommandTest.php index b989c09f..9860ce39 100644 --- a/tests/TestCase/Command/ModelCommandTest.php +++ b/tests/TestCase/Command/ModelCommandTest.php @@ -1765,9 +1765,9 @@ public function testBakeEntityWithPlugin() $this->_loadTestPlugin('BakeTest'); $path = Plugin::path('BakeTest'); - $this->generatedFile = $path . 'src/Model/Table/UsersTable.php'; + $this->generatedFile = $path . 'src/Model/Entity/User.php'; - $this->exec('bake model --no-validation --no-test --no-fixture --no-entity -p BakeTest Users'); + $this->exec('bake model --no-validation --no-test --no-fixture --no-table BakeTest.Users'); $this->assertExitCode(CommandInterface::CODE_SUCCESS); $this->assertFileExists($this->generatedFile); diff --git a/tests/comparisons/Model/testBakeEntityWithPlugin.php b/tests/comparisons/Model/testBakeEntityWithPlugin.php index 15f39a41..c8b295a1 100644 --- a/tests/comparisons/Model/testBakeEntityWithPlugin.php +++ b/tests/comparisons/Model/testBakeEntityWithPlugin.php @@ -1,74 +1,48 @@ */ - public function initialize(array $config): void - { - parent::initialize($config); - - $this->setTable('users'); - $this->setDisplayField('id'); - $this->setPrimaryKey('id'); - - $this->addBehavior('Timestamp'); - - $this->hasMany('Comments', [ - 'foreignKey' => 'user_id', - 'className' => 'BakeTest.Comments', - ]); - $this->hasMany('TodoItems', [ - 'foreignKey' => 'user_id', - 'className' => 'BakeTest.TodoItems', - ]); - } + protected $_accessible = [ + 'username' => true, + 'password' => true, + 'created' => true, + 'updated' => true, + 'comments' => true, + 'todo_items' => true, + ]; /** - * Returns a rules checker object that will be used for validating - * application integrity. + * Fields that are excluded from JSON versions of the entity. * - * @param \Cake\ORM\RulesChecker $rules The rules object to be modified. - * @return \Cake\ORM\RulesChecker + * @var array */ - public function buildRules(RulesChecker $rules): RulesChecker - { - $rules->add($rules->isUnique(['username']), ['errorField' => 'username']); - - return $rules; - } + protected $_hidden = [ + 'password', + ]; }