component_tree sequence keys sorted lexicographically instead of numerically, breaking component order at 10+ instances per level
>>> [!note] Migrated issue <!-- Drupal.org comment --> <!-- Migrated from issue #3582200. --> Reported by: [hanmant.sapkal](https://www.drupal.org/user/956996) Related to !818 >>> <h3 id="overview">Overview</h3> <p><code>ContentTemplate::set('component_tree', ...)</code> and similar Canvas config entities call <code>ComponentTreeItemListInstantiatorTrait::generateComponentTreeKeys()</code> to normalize component tree order. That method ends by calling <code>ksort($keyed_tree)</code>, which sorts keys lexicographically as strings instead of as hierarchical paths.</p> <p>This breaks layout order once there are more than 10 top-level components because keys like <code>10</code>, <code>11</code>, and <code>12</code> are sorted before <code>5</code>, <code>6</code>, <code>7</code>, <code>8</code>, and <code>9</code>.</p> <p>Child keys such as <code>4:slider_items:0</code> also need to be treated as hierarchical path segments, not plain strings.</p> <h3 id="problem">Problem</h3> <p>Canvas generates component tree keys like:</p> <ul> <li><code>0</code></li> <li><code>1</code></li> <li><code>4</code></li> <li><code>4:slider_items:0</code></li> <li><code>5</code></li> <li><code>10</code></li> </ul> <p>But <code>ksort()</code> compares them as raw strings, so the stored order becomes:</p> <ul> <li><code>0</code></li> <li><code>1</code></li> <li><code>10</code></li> <li><code>11</code></li> <li><code>12</code></li> <li><code>2</code></li> <li><code>3</code></li> <li><code>4</code></li> <li><code>4:slider_items:0</code></li> <li><code>5</code></li> </ul> <p>That reordered storage then propagates to the Canvas API and preview rendering.</p> <h3 id="expected-behavior">Expected behavior</h3> <ul> <li>Top-level numeric keys should sort numerically: <code>0, 1, 2, ... 9, 10, 11</code></li> <li>Child keys should sort hierarchically under their parent: for example <code>4</code> before <code>4:slider_items:0</code>, and <code>4:slider_items:0</code> before <code>5</code></li> </ul> <h3 id="actual-behavior">Actual behavior</h3> <ul> <li>Top-level keys are sorted lexicographically</li> <li>Layout order changes after config import or normalization whenever the tree has 10+ top-level items</li> </ul> <h3 id="reproduction-steps">Reproduction steps</h3> <ul> <li>Create or import a Canvas content template with more than 10 top-level components.</li> <li>Ensure one component after index 4 or later has children in a slot so the tree contains a key like <code>4:slider_items:0</code>.</li> <li>Save/import the template so Canvas normalizes the <code>component_tree</code>.</li> <li>Inspect the stored <code>component_tree</code>:</li> </ul> <pre>drush cget canvas.content_template.node.node_type.full component_tree --format=yaml</pre><ul> <li>Inspect the Canvas layout API response for the same template:</li> </ul> <pre>/canvas/api/v0/layout-content-template/node.node_type.full/{entity_id}</pre><h3 id="actual-result">Actual result</h3> <p>The stored/rendered top-level order becomes lexicographic, for example:</p> <ul> <li><code>0</code></li> <li><code>1</code></li> <li><code>10</code></li> <li><code>11</code></li> <li><code>12</code></li> <li><code>2</code></li> <li><code>3</code></li> <li><code>4</code></li> <li><code>5</code></li> </ul> <h3 id="expected-result">Expected result</h3> <p>The stored/rendered order should remain:</p> <ul> <li><code>0</code></li> <li><code>1</code></li> <li><code>2</code></li> <li><code>3</code></li> <li><code>4</code></li> <li><code>4:slider_items:0</code></li> <li><code>5</code></li> <li><code>6</code></li> <li><code>7</code></li> <li><code>8</code></li> <li><code>9</code></li> <li><code>10</code></li> </ul> <h3 id="root-cause">Root cause</h3> <p>In:</p> <p><code>src/Plugin/Field/FieldType/ComponentTreeItemListInstantiatorTrait.php</code></p> <p><code>generateComponentTreeKeys()</code> constructs hierarchical keys correctly, but then applies:</p> <pre>\ksort($keyed_tree);</pre><p>That loses the intended numeric/path ordering.</p> <h3 id="proposed-resolution">Proposed resolution</h3> <p>Replace the raw <code>ksort()</code> with a path-aware comparator that:</p> <ul> <li>splits keys on <code>:</code></li> <li>compares numeric segments numerically</li> <li>compares text segments as strings</li> <li>preserves parent-before-child ordering for shorter matching paths</li> </ul> <h3 id="proposed-test-coverage">Proposed test coverage</h3> <p>Add a regression test in <code>tests/src/Kernel/Plugin/Field/FieldType/ComponentTreeItemListTest.php</code> asserting that <code>generateComponentTreeKeys()</code> preserves this order:</p> <pre><pre>[<br>&nbsp; '0',<br>&nbsp; '1',<br>&nbsp; '2',<br>&nbsp; '3',<br>&nbsp; '4',<br>&nbsp; '4:slider_items:0',<br>&nbsp; '5',<br>&nbsp; '6',<br>&nbsp; '7',<br>&nbsp; '8',<br>&nbsp; '9',<br>&nbsp; '10',<br>]</pre></pre><h3 id="ui-changes">User interface changes</h3> <p>None.</p>
issue