Skip to content

Conversation

@mjauvin
Copy link
Member

@mjauvin mjauvin commented Dec 6, 2025

Enhanced usage:

$list = [
    "option1" => "regular option",
    "option2" => ["Option With Image", "myImage.jpeg"],
    "Group1"  => [
        "group1-opt1" => "OptGroup Option1 regular option",
        "group1-opt2" => ["OptGroup Option2 with icon", "icon-refresh"],
        "group1-opt3" => ["OptGroup Option3 with image", "otherImage.png"],
     ],
    "Group2"  => [
        "group2-opt1" => "OptGroup2 Option1",
        "group2-opt2" => "OptGroup2 Option2",
        "group2-opt3" => "OptGroup2 Option3",
     ],
];
Form::select($name, $list, $selected, $options);

Backward compatibility is maintained, and once I update the _field_dropdown partial for the backend form widget to use the FormBuilder, we'll have support for optGroup in our dropdowns.

Example result with mixed formats:
image

Summary by CodeRabbit

  • New Features

    • Select options can include per-item icons or images exposed via data attributes; optgroups support mixed item types.
  • Bug Fixes / Behavior Changes

    • Improved detection to distinguish optgroups from single options and handle array-form displays for option entries.
  • Compatibility

    • Simple string options and existing selection behavior remain supported.
  • Tests

    • Added tests for icons, images, optgroups, HTML escaping, and backward-compatibility scenarios.
  • Documentation

    • Updated docs describing accepted select formats and icon vs. image detection.

✏️ Tip: You can customize this high-level summary in your review settings.

@mjauvin mjauvin requested a review from LukeTowers December 6, 2025 13:09
@mjauvin mjauvin self-assigned this Dec 6, 2025
@mjauvin mjauvin added the enhancement PRs that implement a new feature or substantial change label Dec 6, 2025
@wintercms wintercms deleted a comment from coderabbitai bot Dec 6, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/Html/FormBuilder.php (2)

485-496: Empty optgroups not rendered in non-legacy mode

When using the new items format in non-legacy mode, optgroups with empty item arrays are skipped entirely. The condition elseif ($items = array_get($display, 'items')) fails for empty arrays since they're falsy, causing the code to fall through and render an <option> instead of an <optgroup>.

For example, 'GroupLabel' => ['items' => []] produces an option rather than an empty optgroup.

Use array_key_exists('items', $display) to detect the presence of the items key regardless of its value:

-        if (is_array($display)) {
-            if ($legacyOptGroup) {
-                return $this->optionGroup($display, $value, $selected);
-            } elseif ($items = array_get($display, 'items')) {
-                return $this->optionGroup($items, $value, $selected);
-            }
-        }
+        if (is_array($display)) {
+            if ($legacyOptGroup) {
+                return $this->optionGroup($display, $value, $selected);
+            }
+
+            if (array_key_exists('items', $display)) {
+                $items = array_get($display, 'items', []);
+                return $this->optionGroup($items, $value, $selected);
+            }
+        }

515-534: Defensive null checks needed for the array branch in option()

The current implementation can trigger a Deprecation warning in PHP 8.1+ when the array structure is incomplete:

if (is_array($display)) {
    $data = array_get($display, 1);
    // ... strpos($data, '.') when $data is null
}

If $display only has index 0 (e.g., ['Label']), array_get() returns null, and strpos() will emit a Deprecation warning. In PHP 8.0, this casts to an empty string; in PHP 8.1+, it generates a warning; in PHP 9, it becomes a TypeError.

Add type and emptiness checks before calling strpos():

if (is_array($display)) {
    $label = array_get($display, 0);
    $data = array_get($display, 1);
    
    $display = $label ?? '';
    
    if (is_string($data) && $data !== '') {
        if (strpos($data, '.') !== false) {
            $options['data-image'] = $data;
        } else {
            $options['data-icon'] = $data;
        }
    }
}

This preserves the [label, data-attr] API while safely handling missing or unexpected array structures.

🧹 Nitpick comments (1)
src/Html/FormBuilder.php (1)

411-435: New legacyOptGroup parameter: good for optgroup control but introduces BC considerations and could be documented

Adding bool $legacyOptGroup = true to the public select() signature and forwarding it to getSelectOption() is a reasonable way to gate the new behaviour without changing existing call sites. However:

  • Any subclasses that override FormBuilder::select() or FormBuilder::getSelectOption() will now have incompatible signatures in PHP 8+, which can trigger fatal errors at load time.
  • The new parameter is not mentioned in the docblock for select(), so its purpose and default behaviour are only discoverable from the signature / implementation.

Consider:

  • Confirming whether FormBuilder is commonly subclassed in this ecosystem; if so, call this out in the changelog / upgrade notes.
  • Optionally updating the select() docblock to describe $legacyOptGroup (and that true preserves legacy behaviour, false enables treating non‑items arrays as regular options).
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between deb4304 and 89d9f54.

📒 Files selected for processing (1)
  • src/Html/FormBuilder.php (5 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/Html/FormBuilder.php (1)
src/Support/helpers-array.php (1)
  • array_get (187-190)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: windows-latest / PHP 8.3
  • GitHub Check: windows-latest / PHP 8.4
  • GitHub Check: windows-latest / PHP 8.1
  • GitHub Check: windows-latest / PHP 8.2

@wintercms wintercms deleted a comment from coderabbitai bot Dec 6, 2025
@coderabbitai
Copy link

coderabbitai bot commented Dec 6, 2025

Warning

Rate limit exceeded

@mjauvin has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 9 minutes and 12 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between d0ccfa6 and f3b8e4d.

📒 Files selected for processing (1)
  • src/Html/FormBuilder.php (3 hunks)

Walkthrough

Select rendering now accepts string or array displays; array displays can render single options with data-icon/data-image or associative arrays as <optgroup>s. option() signature was widened and tests for icons, images, optgroups, escaping, and backward compatibility were added.

Changes

Cohort / File(s) Summary
FormBuilder option rendering
src/Html/FormBuilder.php
- option() signature changed from protected function option(string $display, ...) to `protected function option(string
Tests for select rendering
tests/Html/FormBuilderTest.php
- Added tests: testSelectWithIcon, testSelectWithImage, testSelectWithSelectedImage, testSelectWithOptgroups, testSelectWithOptgroupsAndIconsImages, testSelectBackwardCompatibility, testSelectBackwardCompatibilityOptgroupIdItemsKeys, testSelectBackwardCompatibilityBrokenOptGroup, testSelectHtmlEscaping.
- Expanded coverage for data-attributes on options, optgroup rendering (including mixed icon/image entries), selected-state behavior, HTML escaping, and backward-compatibility scenarios.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Inspect associative vs numeric-key detection for edge cases (sparse/mixed keys).
  • Verify widened option() type compatibility with existing callers.
  • Confirm HTML escaping/encoding and exact attribute rendering (whitespace/ordering) to avoid fragile tests.
  • Review the dot-based heuristic for data-image vs data-icon and associated docblocks.

Poem

🐰 I nibble labels, sniff each little file,

A dot means picture, otherwise an icon smile.
Groups hop together, options wear their art,
Tests clap tiny paws — each attribute a part.
A munched carrot of code, and I bounce with heart.

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add image/icon support for FormBuilder::select' directly and clearly describes the main change: extending the select form builder to support image and icon options, which is the core enhancement documented in the PR.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between deb4304 and a49a2df.

📒 Files selected for processing (1)
  • src/Html/FormBuilder.php (5 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: windows-latest / PHP 8.4
  • GitHub Check: windows-latest / PHP 8.2
  • GitHub Check: windows-latest / PHP 8.1
  • GitHub Check: windows-latest / PHP 8.3
🔇 Additional comments (2)
src/Html/FormBuilder.php (2)

411-411: LGTM! Backward-compatible signature extension.

The new $legacyOptGroup parameter with a default value of true maintains backward compatibility while enabling the new feature.

Also applies to: 434-434


485-496: LGTM! Correct conditional logic for new optgroup structure.

The method correctly handles both legacy optgroup rendering (when $legacyOptGroup is true) and the new item-based grouping format (when false). When $legacyOptGroup is false and no 'items' key exists, it appropriately falls through to option() for image/icon support.

@LukeTowers
Copy link
Member

@mjauvin in addition to the rabbit's feedback, couldn't we check for the presence of the structure expected by an optiongroup item rather than having a legacyOptGroup parameter?

@LukeTowers
Copy link
Member

Also this would need tests.

@LukeTowers LukeTowers added the needs response Issues/PRs where a maintainer is awaiting a response from the submitter label Dec 6, 2025
@mjauvin
Copy link
Member Author

mjauvin commented Dec 6, 2025

Also this would need tests.

I added this so that the previous behavior is maintained, otherwise this would break code using Form::select() with optGroup as just arrays

@LukeTowers
Copy link
Member

@mjauvin I don't follow, can you provide code examples?

@mjauvin
Copy link
Member Author

mjauvin commented Dec 6, 2025

@mjauvin I don't follow, can you provide code examples?

I updated the PR description with usage in both modes.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/Html/FormBuilder.php (1)

488-491: Prefer is_string() for type checking.

Using gettype($keys[0]) === 'string' works correctly but is_string($keys[0]) is more idiomatic and readable.

Apply this diff:

         if (is_array($display)) {
             $keys = array_keys($display);
-            if (count($keys) && gettype($keys[0]) === 'string') {
+            if (count($keys) && is_string($keys[0])) {
                 return $this->optionGroup($display, $value, $selected);
             }
         }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a49a2df and dcebe5b.

📒 Files selected for processing (1)
  • src/Html/FormBuilder.php (3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: windows-latest / PHP 8.4
  • GitHub Check: windows-latest / PHP 8.3
  • GitHub Check: windows-latest / PHP 8.1
  • GitHub Check: windows-latest / PHP 8.2

@mjauvin mjauvin removed the needs response Issues/PRs where a maintainer is awaiting a response from the submitter label Dec 6, 2025
@mjauvin mjauvin added this to the 1.2.10 milestone Dec 6, 2025
coderabbitai[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

@LukeTowers LukeTowers merged commit 33e93d1 into develop Dec 10, 2025
11 checks passed
@LukeTowers LukeTowers deleted the select-plus branch December 10, 2025 20:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement PRs that implement a new feature or substantial change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants