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
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

🧹 Nitpick comments (4)
tests/Html/FormBuilderTest.php (4)

344-384: Icon/image option tests are clear and match the intended data- semantics*

These two tests cleanly verify the new ['Label', 'icon-or-image'] shape for simple, non-grouped options and ensure regular options are unaffected. Behavior and expectations look correct.

If you want slightly stronger coverage, consider adding a case where one of these icon/image options is the selected value, to lock in that selected="selected" and data-icon/data-image coexist as expected.


386-459: Optgroup tests (with and without icons/images) nicely exercise the mixed list shapes

The plain optgroup test and the mixed top-level/optgroup + icon/image test together give good confidence that:

  • assoc arrays become <optgroup>s,
  • nested ['Label', 'icon-or-image'] entries pick up data-icon / data-image,
  • top-level options and optgroup options interleave correctly.

The assertions look accurate and focused on the key behaviors.

You might add a follow‑up test where a value inside an optgroup that uses the array format (e.g. group1-opt2) is selected, just to explicitly guard the interaction of selection logic with the new array‑based display format.


524-548: Clarify intent around the “broken” optgroup backward‑compatibility example

The test nicely documents the narrow edge case where a 0‑based, two‑item int‑keyed array is now interpreted as ['label', 'iconOrImage']. The expectation for <option value="Group1" data-icon="Option 2">Option 1</option> correctly captures that behavior.

To make the test’s purpose even clearer, you could either:

  • assert that no option with value="0" or value="1" exists / is selected, or
  • drop the selected: 1 argument from the call if selection is not part of what you want to demonstrate.

That would reduce potential confusion about whether selection behavior is also under test here.


550-572: HTML escaping test is valuable; consider slightly loosening the double‑encoding expectation

This test is important to ensure both option values and labels are escaped and that no raw <script> / <b> tags leak through. The current assertion on value="&amp;lt;script&amp;gt;" tightly couples the test to the exact double‑encoding behavior.

If you want the test to be more resilient to future encoding implementation tweaks, you might instead assert that:

  • the value contains &lt;script&gt; (or at least script) but not <script>,
  • and the label contains &lt;b&gt;Bold Label&lt;/b&gt; but not <b>Bold Label</b>,

while dropping the strict &amp;lt;script&amp;gt; check. That still guarantees safety but allows the encoding pipeline to evolve.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3cb9129 and 76a0b31.

📒 Files selected for processing (2)
  • src/Html/FormBuilder.php (4 hunks)
  • tests/Html/FormBuilderTest.php (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/Html/FormBuilder.php
🧰 Additional context used
🧬 Code graph analysis (1)
tests/Html/FormBuilderTest.php (1)
src/Html/FormBuilder.php (1)
  • select (420-455)
⏰ 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.2
  • GitHub Check: windows-latest / PHP 8.1
🔇 Additional comments (2)
tests/Html/FormBuilderTest.php (2)

461-484: Simple backward‑compatibility test for string options is solid

This test is a good guardrail that:

  • plain key => "Label" options still behave as before (including selection),
  • no data-icon / data-image attributes leak into the legacy shape.

Expectations and assertions look correct.


486-522: Optgroup BC test with integer item keys is well targeted

This accurately simulates grouped options coming from a model where item keys are integers, and verifies:

  • groups still render as <optgroup> labels,
  • integer keys become option values with proper selection,
  • no icon/image attributes are introduced.

The scenario and assertions match the documented BC requirement.

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)
tests/Html/FormBuilderTest.php (1)

343-571: Consider adding edge case tests (optional).

The test coverage is solid for the main features. To further strengthen reliability, consider adding tests for edge cases:

  1. Malformed array formats:

    • Single-element array: ['Label'] (missing icon/image)
    • Three-element array: ['Label', 'icon', 'extra'] (unexpected format)
  2. Empty/null values:

    • Empty icon/image string: ['Label', '']
    • Null values: ['Label', null] or [null, 'icon']
  3. Empty list:

    • Behavior when $list is an empty array []

These edge cases would help document and ensure graceful degradation for unexpected input.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 76a0b31 and afd40a0.

📒 Files selected for processing (1)
  • tests/Html/FormBuilderTest.php (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
tests/Html/FormBuilderTest.php (2)
src/Html/FormBuilder.php (1)
  • select (420-455)
tests/Assertions/AssertHtml.php (2)
  • assertElementIs (10-19)
  • assertElementAttributeEquals (37-50)
⏰ 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.3
  • GitHub Check: windows-latest / PHP 8.1
🔇 Additional comments (7)
tests/Html/FormBuilderTest.php (7)

347-363: LGTM! Good coverage for icon data attributes.

The test correctly verifies that icon options are rendered with the data-icon attribute while regular string options remain unchanged.


368-384: LGTM! Good coverage for image data attributes.

The test correctly verifies that image options are rendered with the data-image attribute.


389-416: LGTM! Solid optgroup test coverage.

The test correctly verifies optgroup rendering with multiple groups containing regular options.


421-459: LGTM! Excellent comprehensive test.

This test covers the most complex scenario, mixing top-level options (regular and with images) alongside optgroups containing regular options, icons, and images. Great integration test.


464-484: LGTM! Good backward compatibility verification.

The test confirms that existing simple string options continue to work without introducing unintended data attributes, and selection behavior is preserved.


489-522: LGTM! Important backward compatibility test for model-based options.

This test verifies that optgroups with integer keys (common when using model IDs) continue to work correctly, including selection behavior.


527-547: This is a known, intentional breaking change—not a bug.

The detection logic that treats [0 => 'Option 1', 1 => 'Option 2'] as an icon format rather than an optgroup is a documented design trade-off. The implementation prioritizes support for the icon/image syntax ['Label', 'icon-name'] at the cost of breaking backward compatibility with 0-indexed optgroups containing exactly two items.

The test correctly documents this limitation with the docstring "show case where backward compatibility is broken (expected)," and the code comments in FormBuilder.php explain the rationale. Real-world code using 0-indexed arrays for optgroups will break, but this is intentional, not accidental.

Recommendation: Ensure this breaking change is prominently documented in user-facing upgrade guides and changelog entries so developers are aware of the migration path.

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

♻️ Duplicate comments (1)
tests/Html/FormBuilderTest.php (1)

570-592: Escaping test codifies double‑encoding behavior that was previously flagged as problematic.

This test now asserts value="&amp;lt;script&amp;gt;" for the option whose raw value is <script>, explicitly relying on the value being escaped twice, while the label is only escaped once. A prior review already identified this double encoding path (FormBuilder::option + HtmlBuilder::attributeElement) as likely unintentional; hard‑coding the double‑escaped expectation here will make it harder to fix that bug in the future without changing the test. Consider instead documenting this as current behavior and planning to update both HtmlBuilder and this assertion to a single‑escaped value="&lt;script&gt;" if/when the underlying issue is corrected.

🧹 Nitpick comments (1)
tests/Html/FormBuilderTest.php (1)

545-568: Explicit “broken BC” case is useful but could be documented more broadly.

The test intentionally locks in the new behavior where a ['Group1' => [0 => 'Option 1', 1 => 'Option 2']] structure is treated as a single option-with-icon, not an optgroup. That’s fine if this edge case is accepted as a deliberate change, but consider also calling this out in user-facing upgrade notes/changelog so consumers relying on 0‑based optgroup keys aren’t surprised.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between afd40a0 and 1939c6d.

📒 Files selected for processing (1)
  • tests/Html/FormBuilderTest.php (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
tests/Html/FormBuilderTest.php (2)
src/Html/FormBuilder.php (1)
  • select (420-455)
tests/Assertions/AssertHtml.php (2)
  • assertElementIs (10-19)
  • assertElementAttributeEquals (37-50)
⏰ 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). (3)
  • GitHub Check: windows-latest / PHP 8.3
  • GitHub Check: windows-latest / PHP 8.2
  • GitHub Check: windows-latest / PHP 8.4
🔇 Additional comments (7)
tests/Html/FormBuilderTest.php (7)

343-363: Icon select test correctly captures extended option syntax.

The expectations for mixing a plain option with an [label, 'icon-…'] entry and asserting data-icon plus the untouched regular option look correct and exercise the new behavior without over-constraining the markup.


365-384: Image select test cleanly validates data-image behavior.

This mirrors the icon test for image paths and checks both the plain option and the image-bearing option; the assertions look accurate and sufficient.


386-405: Selected image scenario is covered appropriately.

Asserting the presence of selected="selected" alongside data-image on the chosen option (and the untouched regular option) is a good regression check for interaction between selection and the new attribute.


407-437: Baseline optgroup rendering test is solid.

This provides a clear control case for simple optgroups (no icons/images), asserting group labels, options, and closing tags, which should help catch regressions in optgroup detection.


439-480: Mixed options + optgroups + icons/images are well covered.

This test usefully combines top-level options (including an image) with optgroups containing both plain and icon/image entries, and the assertions line up with the intended rendering semantics.


482-505: Simple backward‑compatibility for string options is clearly specified.

Locking in that a flat list of string options still renders identically (including selection and absence of any data-icon / data-image attributes) is valuable and the checks are suitably narrow.


507-543: ID‑keyed optgroup backward‑compatibility is tested appropriately.

Using integer keys that don’t start at zero to simulate model IDs, and asserting proper <optgroup> markup with no data attributes, closely matches the BC goal and should protect existing usages.

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

♻️ Duplicate comments (1)
src/Html/FormBuilder.php (1)

534-547: Only set data-icon / data-image when a non‑empty second element is provided

The boolean strpos() usage is now fixed, but option() still sets data-icon or data-image even when the second element is missing or empty (e.g. ['Label']), resulting in attributes like data-icon="" and treating any array (even with just one element) as an icon/image pair.

To make this robust and match the intended “optional second element” semantics, only add the data attribute when the second element exists and is a non‑empty string, and avoid calling strpos() on non‑strings:

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

This keeps ['Label'] working as a simple option label, and only emits data-icon/data-image when a real icon/image identifier is passed.

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

524-530: Widened option() parameter type may break subclasses overriding the method

Changing option() to protected function option(string|array $display, ...) is correct for the new feature, but it can be a BC break for any subclasses that previously overrode option(string $display, ...) with a narrower parameter type (PHP will now see them as incompatible with the parent signature).

If you expect FormBuilder to be extended, consider either dropping the concrete parameter type in the method signature (and relying on the phpdoc) or at least documenting this as a potential BC break in the changelog/release notes.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

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

📒 Files selected for processing (2)
  • src/Html/FormBuilder.php (3 hunks)
  • tests/Html/FormBuilderTest.php (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/Html/FormBuilderTest.php
🧰 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.4
  • GitHub Check: windows-latest / PHP 8.1
  • GitHub Check: windows-latest / PHP 8.3
  • GitHub Check: windows-latest / PHP 8.2
🔇 Additional comments (2)
src/Html/FormBuilder.php (2)

410-419: select() phpdoc clearly documents the new option formats

The extended documentation for $list (simple options, icon/image pairs, optgroups, and mixed formats) is clear and matches the implemented behavior and PR description.


493-507: Current logic correctly routes optgroup and single-option arrays

The current implementation using array_keys($display) !== [0,1] is logically correct. When this condition is true (keys are not [0,1]), it calls optionGroup(). Arrays like ['Group' => ['Option 1', 'Option 2']] have outer keys of ['Group'], not [0,1], so the condition evaluates to true and correctly routes to optionGroup(). Two-element numeric-keyed arrays like ['Label', 'icon.png'] have keys of [0,1], so the condition is false and they correctly route to option().

The proposed refactoring, while more explicit in intent, is unnecessary as the current logic already handles all cases correctly.

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