Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/Command/ControllerCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,12 @@ public function bake(string $controllerName, Arguments $args, ConsoleIo $io): vo
$singularHumanName = $this->_singularHumanName($controllerName);
$pluralHumanName = $this->_variableName($controllerName);

// Handle cases where singular and plural are identical (e.g., "news", "sheep")
// to avoid variable collisions in generated controller code
if ($singularName === $pluralName) {
$singularName .= 'Entity';
}

$defaultModel = sprintf('%s\Model\Table\%sTable', $namespace, $controllerName);
if (!class_exists($defaultModel)) {
$defaultModel = null;
Expand Down
6 changes: 6 additions & 0 deletions src/Command/TemplateCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,12 @@ protected function _loadController(ConsoleIo $io): array
$pluralVar = Inflector::variable($this->controllerName);
$pluralHumanName = $this->_pluralHumanName($this->controllerName);

// Handle cases where singular and plural are identical (e.g., "news", "sheep")
// to avoid generating invalid code like `foreach ($news as $news)`
if ($singularVar === $pluralVar) {
$singularVar .= 'Entity';
}

return compact(
'modelObject',
'modelClass',
Expand Down
3 changes: 3 additions & 0 deletions templates/bake/Template/view.twig
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@
{% set relations = associations.BelongsToMany|merge(associations.HasMany) %}
{% for alias, details in relations %}
{%~ set otherSingularVar = alias|singularize|variable %}
{%~ if otherSingularVar == singularVar %}
{%~ set otherSingularVar = otherSingularVar ~ 'Related' %}
{%~ endif %}
{%~ set otherPluralHumanName = details.controller|underscore|humanize %}
<div class="related">
<h4><?= __('Related {{ otherPluralHumanName }}') ?></h4>
Expand Down
3 changes: 3 additions & 0 deletions templates/bake/element/Controller/add.twig
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@
{%- for assoc in associations %}
{%~ set otherName = Bake.getAssociatedTableAlias(modelObj, assoc) %}
{%~ set otherPlural = otherName|variable %}
{%~ if otherPlural == singularName %}
{%~ set otherPlural = otherPlural ~ 'List' %}
{%~ endif %}
${{ otherPlural }} = $this->{{ currentModelName }}->{{ otherName }}->find('list', limit: 200)->all();
{%~ set compact = compact|merge(["'#{otherPlural}'"]) %}
{% endfor %}
Expand Down
3 changes: 3 additions & 0 deletions templates/bake/element/Controller/edit.twig
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@
{% for assoc in belongsTo|merge(belongsToMany) %}
{%~ set otherName = Bake.getAssociatedTableAlias(modelObj, assoc) %}
{%~ set otherPlural = otherName|variable %}
{%~ if otherPlural == singularName %}
{%~ set otherPlural = otherPlural ~ 'List' %}
{%~ endif %}
${{ otherPlural }} = $this->{{ currentModelName }}->{{ otherName }}->find('list', limit: 200)->all();
{%~ set compact = compact|merge(["'#{otherPlural}'"]) %}
{% endfor %}
Expand Down
28 changes: 28 additions & 0 deletions tests/Fixture/NewsFixture.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @license https://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace Bake\Test\Fixture;

use Cake\TestSuite\Fixture\TestFixture;

/**
* NewsFixture - tests singular/plural variable collision
* "news" is both singular and plural
*/
class NewsFixture extends TestFixture
{
public array $records = [
['title' => 'First News', 'body' => 'First News Body'],
['title' => 'Second News', 'body' => 'Second News Body'],
];
}
31 changes: 31 additions & 0 deletions tests/TestCase/Command/ControllerCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class ControllerCommandTest extends TestCase
'plugin.Bake.BakeArticlesBakeTags',
'plugin.Bake.BakeComments',
'plugin.Bake.BakeTags',
'plugin.Bake.News',
'plugin.Bake.Users',
];

Expand Down Expand Up @@ -435,4 +436,34 @@ public function testMainWithPluginOption()
$this->assertFileContains('use Company\Pastry\Controller\AppController;', $this->generatedFile);
$this->assertFileContains('BakeArticlesController extends AppController', $this->generatedFile);
}

/**
* Test baking controller for models where singular and plural are identical.
*
* This tests the fix for generating variable collisions like `$news` for both
* the entity and the paginated collection when the model name is both singular
* and plural (e.g., "news", "sheep", "fish").
*
* @return void
*/
public function testBakeControllerWithSingularPluralCollision(): void
{
$this->generatedFile = APP . 'Controller/NewsController.php';
if (file_exists($this->generatedFile)) {
unlink($this->generatedFile);
}
$this->exec('bake controller --connection test --no-test News');

$this->assertExitCode(CommandInterface::CODE_SUCCESS);
$this->assertFileExists($this->generatedFile);

$result = file_get_contents($this->generatedFile);

// In view/edit/add/delete actions, the entity should use 'newsEntity' to avoid collision
$this->assertStringContainsString('$newsEntity = $this->News->get(', $result);

// In index action, the paginated collection should still use 'news'
$this->assertStringContainsString('$news = $this->paginate(', $result);
$this->assertStringContainsString("compact('news')", $result);
}
}
26 changes: 26 additions & 0 deletions tests/TestCase/Command/TemplateCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class TemplateCommandTest extends TestCase
'plugin.Bake.BakeTemplateProfiles',
'plugin.Bake.CategoryThreads',
'plugin.Bake.HiddenFields',
'plugin.Bake.News',
];

/**
Expand Down Expand Up @@ -891,4 +892,29 @@ public function testMainWithMissingTable()

$this->assertExitCode(CommandInterface::CODE_ERROR);
}

/**
* Test baking templates for models where singular and plural are identical.
*
* This tests the fix for generating invalid code like `foreach ($news as $news)`
* when the model name is both singular and plural (e.g., "news", "sheep", "fish").
*
* @return void
*/
public function testBakeIndexWithSingularPluralCollision()
{
$this->generatedFile = ROOT . 'templates/News/index.php';
$this->exec('bake template News index');

$this->assertExitCode(CommandInterface::CODE_SUCCESS);
$this->assertFileExists($this->generatedFile);

$result = file_get_contents($this->generatedFile);

// Should NOT have `foreach ($news as $news)` which would overwrite the collection
$this->assertStringNotContainsString('foreach ($news as $news)', $result);

// Should have `foreach ($news as $newsEntity)` instead
$this->assertStringContainsString('foreach ($news as $newsEntity)', $result);
}
}
12 changes: 12 additions & 0 deletions tests/schema.php
Original file line number Diff line number Diff line change
Expand Up @@ -587,4 +587,16 @@
'unique_self_referencing_parent' => ['type' => 'unique', 'columns' => ['parent_id']],
],
],
// "news" is both singular and plural - tests variable collision fix
[
'table' => 'news',
'columns' => [
'id' => ['type' => 'integer'],
'title' => ['type' => 'string', 'length' => 255, 'null' => false],
'body' => ['type' => 'text'],
'created' => ['type' => 'datetime'],
'modified' => ['type' => 'datetime'],
],
'constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]],
],
];
Loading