Problem/Motivation

There are currently two different booelean field types.The core implementation "boolean", which has no formatters and widgets but is used in base fields a lot and "list_boolean" field type by options (ex-list) module, which has widgets and formatters and is tied to the allowed_values functionality of options.module.

Proposed resolution

Merge list_boolean into boolean. Simplify the additional features into an on_label and off_label, instead of tricks to use the allowed_values setting for that.

A core checkbox widget is provided which replaces OnOffWidget, with the same features (a single checkbox that can display the field label or the "on" label). The options_buttons widget is also available through AllowedValuesInterface, which is displayed as radio buttons which display On label and Off label.

A simple formatter is provided that displays the on or off label. The existing options.module formatter still uses the allowed_values setting/option_allowed_values() function instead of the interface and can't be used anymore for the moment.

Remaining tasks

User interface changes

The field settings of the boolean field type works a bit differently but the change is minor.

API changes

- list_boolean field type is removed, replaced with the existing 'boolean' field type, provided by Core
- options_onoff widget is renamed to boolean_checkbox.
- a dedicated 'boolean' formatter is added.

Original report by @andypost

Follow-up from #2169983: Move type-specific logic from ListItemBase to the appropriate subclass.

Merge Drupal\options\Plugin\Field\FieldType\ListBooleanItem
into Drupal\Core\Field\Plugin\Field\FieldType\BooleanItem

Actually no reason to postpone on #2171397: Move options_allowed_values() to a method somewhere ?

ListBooleanItem is sufficiently trivial regarding allowed values (always two allowed values, 0 and 1), and sufficiently disconnected from the other List* field types (as this patch here made clear) that I'm not sure that it's really held back by those issue.

So better to decouple that one from options module, probably by making a new settings form.

ToDo: Decide about widgets and formatters

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

andypost’s picture

could be attacked at DDD Szeged

andypost’s picture

Title: Merge Drupal\options\Plugin\Field\FieldType\ListBooleanItem into Drupal\Core\Field\Plugin\Field\FieldType\BooleanItemmerge Drupal\options\Plugin\Field\FieldType\ListBooleanItem into Drupal\Core\Field\Plugin\Field\FieldType\BooleanItem » Merge <code>Drupal\options\Plugin\Field\FieldType\ListBooleanItem</code> into <code>Drupal\Core\Field\Plugin\Field\FieldType\BooleanItem</code>
Issue summary: View changes

what's wrong with title

andypost’s picture

Title: Merge <code>Drupal\options\Plugin\Field\FieldType\ListBooleanItem</code> into <code>Drupal\Core\Field\Plugin\Field\FieldType\BooleanItem</code> » Merge Drupal\options\Plugin\Field\FieldType\ListBooleanItem into Drupal\Core\Field\Plugin\Field\FieldType\BooleanItem
Berdir’s picture

Title: Merge Drupal\options\Plugin\Field\FieldType\ListBooleanItem into Drupal\Core\Field\Plugin\Field\FieldType\BooleanItem » Merge ListBooleanItem from options module into BooleanItem

Making the title shorter, I think that's enough and easier to read.

plopesc’s picture

Status: Active » Needs review
FileSize
24.7 KB

First round...
Let's see what testbot says :)

Berdir’s picture

#2226493: Apply formatters and widgets to Node base fields also adds a very basic widget for the boolean field. Cross-referencing.

andypost’s picture

plopesc’s picture

FileSize
25.04 KB
1.19 KB

Re-rolling moving annotations to static defaultSettings() method.

The last submitted patch, 5: merge_boolean-2226063-5.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 8: merge_boolean-2226063-8.patch, failed testing.

plopesc’s picture

Status: Needs work » Needs review
FileSize
29.66 KB
5.48 KB

Fixing tests

Status: Needs review » Needs work

The last submitted patch, 11: merge_boolean-2226063-11.patch, failed testing.

andypost’s picture

Overall looks great! Suppose better to move OnOffWidget to core first as more useful for core entities.
Also it would be great to reuse this for one of fields like [2226493]

Just nitpicks

  1. +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/BooleanItem.php
    @@ -26,6 +28,16 @@ class BooleanItem extends FieldItemBase {
    +  return array(
    +    'allowed_values' => array(0, 1),
    +    'allowed_values_function' => '',
    +  ) + parent::defaultSettings();
    +}
    

    indent--

  2. +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/BooleanItem.php
    @@ -48,4 +60,75 @@ public static function schema(FieldDefinitionInterface $field_definition) {
    +      '#value_callback' => array(get_class($this), 'optionsBooleanAllowedValues'),
    ...
    +  public static function optionsBooleanAllowedValues($element, $input, $form_state) {
    

    no reason in "options" prefix, maybe just getAllowedValues()

  3. +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/BooleanWidget.php
    @@ -0,0 +1,106 @@
    +      \Drupal::formBuilder()->setError($element, $form_state, t('!name field is required.', array('!name' => $element['#title'])));
    ...
    +    form_set_value($element, $items, $form_state);
    

    consistency \Drupal::formBuilder()->setValue()

plopesc’s picture

Status: Needs work » Needs review
FileSize
31.32 KB
5.24 KB

Hello,

Here is a new round of the patch... I addressed @andypost comments and moved the OnOffWidget to core. We shouls decide in which issue we are going to address the widget and formatter for Boolean field.

Tests will fail because I didn't change OptionsWidgetTest, so this is just for review.

I'm having also problems with FrontPageTest, trying to dig more on it in the meantime.

Regards

Status: Needs review » Needs work

The last submitted patch, 14: merge_boolean-2226063-14.patch, failed testing.

plopesc’s picture

Status: Needs work » Needs review
FileSize
31.17 KB
1.15 KB

New round fixing ForntPageTest issue, now it should fail only options widget tests.

Regards

Status: Needs review » Needs work

The last submitted patch, 16: merge_boolean-2226063-16.patch, failed testing.

yched’s picture

One tricky thing is that option.module's ListBooleanItem currently lets you use any two ints for the "on" and "off" values (schema is 'int'), while BooleanItem is strictly about 0 and 1 (and schema is 'tiny int').

In hindsight, that's a useless and absurd feature if you ask today's me, and I can't remember what led to this in CCK :-/. But there are probably existing D6 / D7 sites with data different from 0 / 1 in "boolean" fields... We could normalize the values back into 0 and 1 during migration, but then they might have views based on that...

Berdir’s picture

Status: Needs work » Needs review
FileSize
30.77 KB

Re-roll.

Status: Needs review » Needs work

The last submitted patch, 19: merge_boolean-2226063-19.patch, failed testing.

Berdir’s picture

Status: Needs work » Needs review
FileSize
41.42 KB
8.52 KB

Fixing tests. the schema problem was nasty.

Status: Needs review » Needs work

The last submitted patch, 21: merge_boolean-2226063-21.patch, failed testing.

Berdir’s picture

Status: Needs work » Needs review

Ok, so this almost passed.

Not exactly sure what the allowed values type should be, but I think it should be int, as that's what we're storing, no?

@yched: Ok, so you're suggesting to drop the ability, and then either take care of the migration processing here or do it in a separate issue? Seems like all the allowed values stuff would just go away then, sounds nice... I agree with not seeing why we'd want to support that.

And for the views problem, we have that elsewhere too, for example, we're now shortening vocabulary machine names and other identifiers like that, and if you use those in a view, it's going to be broken too.

yched’s picture

Ok, so you're suggesting to drop the ability, and then either take care of the migration processing here or do it in a separate issue?

Yup. Not too sure what the current core practices are regarding whether migration stuff needs to be done in the same patch or in a followup.

Seems like all the allowed values stuff would just go away then, sounds nice

Well, we still need the ability to store custom "on" and "off" labels, but the values themselves would always be 0 and 1, yes.

Berdir’s picture

Discussed with @benjy, he's OK with a follow-up for migrate, will create that.

Berdir’s picture

Ok, I'm pretty confused now, the way the allowed_values settings work seems very weird.. because I don't see how it allows to save a different value, it just seems to mis-use the same settings in a weird way but uses it very differently. What you set as on/off *value* is really the label, it always stores 0/1. If you enter strings or numbers, doesn't matter.

That seems very very confusing to me, As it's now split from the other option field types, it seems much easier to just have a on_label and off_label setting and explicitly use that instead of hardcoding the 0, 1 keys inside allowed_values? Also, the field settings allow for a allowed_values_function callback and the settings form there cares about it, but both widgets hardcode allowed_values and only use that?

So, suggestion:

Drop allowed_values stuff, explicitly use on_label and off_label as simple settings, make it explicit that this is the *label*, not the value.

BooleanWidget seems to be a weird name for the "Check boxes/radio buttons" widget, as is that name, what we really have is one widget that shows a single checkbox and one that shows radios as Yes/No. Naming suggestions for label and class name? It's now or never time...

Also, a multi-value boolean field is probably one of the weirder things I've seen so far, it does then display checkboxes instead of radios so you can have a boolean field set to both on and off and it stores both values as separate field items. options.module, you drunk? :)

andypost’s picture

Once boolean become inherit integer it makes sense to provide:
1) 3 kinds of widgets (checkbox, 2-radios and select)
2) formatter - display field label, display configured label value, display as row value

probably there should be just configurable widget and formatter then.

PS: storing configurable "on/off" was a nice feature

yched’s picture

Thanks so much for working on this @Berdir :-)

re #26

- Storing on_label and off_label : yes, totally

- widgets : yes, I see no reason to merge "checkbox" & "select" into one widget.
I guess the checkbox one could be Core/Field/Plugin/Field/FieldWidget/CheckboxWidget, plugin id 'checkbox' ?
Then, having a "select" widget would be nice, but I don't think that's the main use case. Maybe just keeping option.module's SelectWidget for this would be good enough ? It's only available if you enable options.module, but do we currently have "boolean" base fields that are output using a select ?

- Actually, the widgets provided by options.module are in fact supposed to be agnostic and work with any field type that implements AllowedValuesInterface.
So they would be better off in Core, now that Core can provide plugins (wasn't the case back when we converted options.module to plugins). No need to depend on options.module if you just want the widgets.
That's kind of what we had in D7 - options.module was just providing agnostic widgets, and list.module was providing the field types. Then the two were merged because for some reason "list" was problematic for a module name in D8 (reserved keyword or something). But now we have Core for that :-)
Probably for a separate issue though. Opened #2302021: Move options.module widgets in Core.

- multiple-valued booleans : yeah, those are a bit awkward :-/. IIRC, the current checkbox widget actually only lets you edit one value at delta 0.
Strictly speaking, no reason a "multiple value boolean field" wouldn't make sense, even though in practice that's really rare.
It's more a question of how to present the checkbox widget - a series of separate checkboxes, all with the same label, in a tabledrag ? (well, why not I guess :-/)

- I didn't remember that the "radios / checkboxes" widget (ButtonsWidget) was available for boolean fields, but apparently that's the case in D7 too :-/. Yeah, I guess its behavior is a bit weird, the "single = checkboxes / multiple = radios" logic in ButtonsWidget is totally not a good match here.
A "radio" widget would make sense for bools, but it probably needs to be a separate widget strictly for bools - probably for a followup ?

Putting it all together, we'd have :
- Core/SelectWidget : applies to whatever field types want to (and are able to) use them, and that includes bools
- Core/[the other widgets currently provided by options.module] : same as above, but not used for bools, not relevant
- Core/BooleanCheckboxWidget : the current OnOffWidget, only applies to bool items
- (possibly) Core/BooleanRadioWidget : a new widget, only applies to bool items

Berdir’s picture

Ok, did most of that I think.

Kept the buttons/radio widget available for the boolean field type, works fine apart from the weird multiple behavior and that was like that before.

Fixed the new tests (they were still PSR-0) and extend them, removed the boolean/checkbox tests from OptionsWidgetsTests.

Moving the widgets sounds like a good idea, I guess we could move the formatters too? For some reason that I don't understand, they still use options_allowed_values(), while they could just use the AllowedValuesInterface methods? That part I'm not sure about, because now we can't use those formatters anymore, as the allowed_values setting keys are gone.

Status: Needs review » Needs work

The last submitted patch, 29: merge_boolean-2226063-29.patch, failed testing.

Berdir’s picture

Status: Needs work » Needs review
FileSize
41.94 KB
2.62 KB

Fixed tests.

Biggest left-over is formatters, as mentioned above. We can't use the options.module formatter right now, but it would be possible if they'd use the interface. Being able to output 0/1 with the number formatter doesn't seem very useful, if we can't fix the options formatters here, maybe we should add a simple formatter that displays the on/off label instead?

yched’s picture

Yes, agreed that options.module formatter should use AllowedValuesInterface instead of options_allowed_values(), stumbled on it in #2293773-68: Field allowed values use dots in key names - not allowed in config too.

Berdir’s picture

Should we clean that up the other issue or should we fix it here instead of introducing a new formatter for the boolean field?

yched’s picture

I think at this point it would make sense to postpone #2293773: Field allowed values use dots in key names - not allowed in config on this one here.
Removing Bool as a List field type will significantly simplify whatever approach gets retained over there.

[edit: fixed issue link. Pasting issues URLs on a phone is hell]

Berdir’s picture

Added the formatter.

Wim Leers’s picture

Status: Needs review » Needs work
  1. +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/BooleanItem.php
    @@ -18,10 +20,21 @@
    +      'on_label' => '1',
    +      'off_label' => '0',
    

    These default labels seem kind of bizarre? "On" and "Off" or "Enabled" and "Disabled" seem more logical?

  2. +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/BooleanItem.php
    @@ -48,4 +61,56 @@ public static function schema(FieldStorageDefinitionInterface $field_definition)
    +      '#title' => t('On label'),
    ...
    +      '#title' => t('Off label'),
    

    What about '"On" label' and '"Off" label'? Kind of difficult to understand otherwise, no? :)

    Also: $this->t()? Perhaps you'll need to use StringTranslationTrait though.

  3. +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/BooleanItem.php
    @@ -48,4 +61,56 @@ public static function schema(FieldStorageDefinitionInterface $field_definition)
    +  /**
    +   * {@inheritdoc}
    +   */
    +  public function getPossibleValues(AccountInterface $account = NULL) {
    +    return array(0, 1);
    +  }
    +
    +  /**
    +   * {@inheritdoc}
    +   */
    +  public function getPossibleOptions(AccountInterface $account = NULL) {
    +    return array(
    +      0 => $this->getSetting('off_label'),
    +      1 => $this->getSetting('on_label'),
    +    );
    +  }
    +
    +  /**
    +   * {@inheritdoc}
    +   */
    +  public function getSettableValues(AccountInterface $account = NULL) {
    +    return array(0, 1);
    +  }
    

    Why zero and one and not… boolean values for this? It's called the BooleanItem after all?

    If it gets stored as a zero and a one, that's fine, but that implementation detail shouldn't matter here, right?

Berdir’s picture

Thanks for the review.

1. They are, but that's what we had before (well, before it was empty with a description that it will default to 1/0).

2. Makes sense to me, wasn't sure what to use there.

3. Both the storage and the form API uses 0/1, the boolean field value was always 0/1, I'm just making it a bit more explict than it was before (which had array($off, $on), resulting in the same).

Berdir’s picture

Issue summary: View changes
Status: Needs work » Needs review
Issue tags: -Needs issue summary update
FileSize
43.16 KB
1.9 KB

Ok, switched to On and Off by default, using $this->t() (can't use it in defaultSettings(), that's static).

Updated the issue summary.

Wim Leers’s picture

Thanks for the clarification and reroll. I still think it's bizarre that we use 0/1 rather than false/true, but that's here for historical reasons and it's out of scope to change that.

Other than that, the patch looks good to me, besides the silliest of silly nitpicks:

+++ b/core/modules/field/config/schema/field.schema.yml
@@ -97,6 +97,34 @@ entity_form_display.field.hidden:
+  label: 'List (boolean) settings'
...
+  label: 'List (boolean)'

The "List" part is now obsolete?

Berdir’s picture

yched’s picture

Status: Needs review » Needs work

This is just lovely :-)

Minor remarks :

  1. +++ b/core/modules/node/src/Tests/NodeAccessLanguageAwareCombinationTest.php
    @@ -53,7 +53,7 @@ public function setUp() {
    -      'type' => 'list_boolean',
    +      'type' => 'boolean',
    ...
           'settings' => array(
    

    not visible in patch context, but the 'settings' is still 'allowed_values' - which shows that it's irrelevant now and could be ditched altogether.
    Same in NodeAccessLanguageAwareTest

  2. +++ b/core/modules/options/src/Tests/OptionsWidgetsTest.php
    @@ -97,7 +97,7 @@ function setUp() {
         $this->bool = entity_create('field_config', array(
           'name' => 'bool',
           'entity_type' => 'entity_test',
    -      'type' => 'list_boolean',
    +      'type' => 'boolean',
           'cardinality' => 1,
    

    $this->bool is actually never used in the test now, the whole definition could go away ?

  3. +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/CheckboxWidget.php
    @@ -62,22 +61,17 @@ public function settingsSummary() {
    +      '#default_value' => !empty($items[0]->value),
    

    ok, options.module fun ahead :-)

    The previous OnOffWidget was 'multiple_value' = TRUE.
    Meaning one single copy of its formElement() was handling the edition of multiple values. This formElement() actually returned one single checkbox, meaning, the widget actually ever only let you edit one single value. Thus, it was only using $items[0]->value, since that's the only value it ever affected.

    Now, the new CheckboxWidget doesn't have 'multiple_value' = TRUE, so, like most widgets, it gets repeaded several times in a tabledrag when used on a multiple-value field.
    Then, the code above should become !empty($items[$delta]->value)

    BUT : WidgetBase automatically adds one extraneous empty element. And a checkbox, the moment it's there, submits either 0 or 1 - you can not leave the widget "empty".
    Meaning, on an unlimited field, you save one new value each time you submit the form :-)

    As stated above, "multiple boolean" fields are an edge case in practice. I'd suggest leaving CheckboxWidget to 'multiple_value' = TRUE, and we can add a BooleanRadios widget later, that will allow multiple values. Started working on one, but would clutter this patch.

    In prevision for this, I'd also suggest renaming CheckboxWidget / checkbox to BooleanCheckboxWidget / boolean_checkbox. We'll have BooleanRadiosWidget / boolean_radios next to that.

  4. +++ b/core/modules/field/src/Tests/Boolean/BooleanFieldTest.php
    @@ -0,0 +1,177 @@
    +  public static $modules = array('entity_test', 'field_ui', 'options');
    

    'options' is not needed ?

Also, I'd hate to bikeshed, but I'd think "Yes / No" would be more suitable for default labels. I can't think of a case where I wouldn't want to change the default "On / Off".

Berdir’s picture

Status: Needs work » Needs review
FileSize
43.15 KB
44.47 KB
7.41 KB

You don't have to delete my patch just because it's not perfect :p (Yes, I know it's a bug ;))

1. Updated it to us on_label, off_label, the took the extra step before, doesn't hurt to keep that.
2. Nice catch, removed.
3. Ok, convinced ;) Back to multiple, renamed. This might break some tests due to the form element change, but I'm too lazy right now to look for that.
4. I need options module because I'm also testing the radio widget provided by options.module. We can remove that when we move those widgets :)

Messing with the types just reminded me that this will conflict with #597236: Add entity caching to core but one more re-roll isn't going to make a difference there.

Status: Needs review » Needs work

The last submitted patch, 42: merge_boolean-2226063-42.patch, failed testing.

Berdir’s picture

Status: Needs work » Needs review
FileSize
44.46 KB
1.24 KB

Just that test, then :)

yched’s picture

Oops - yeah, I do remember a crosspost when posting #41, didn't see it had such radical effects :-)
[heh, almost did that again just right now]

+++ b/core/modules/field/config/schema/field.schema.yml
@@ -291,3 +319,15 @@ entity_form_display.field.number:
+entity_form_display.field.checkbox:

Needs to be adjusted for the new plugin id ?

Berdir’s picture

yched’s picture

Status: Needs review » Reviewed & tested by the community

Yay !

Wim Leers’s picture

RTBC++ :) yched beat me to it :D

yched’s picture

Working on a draft notice

yched’s picture

Issue summary: View changes

Draft notice: https://www.drupal.org/node/2304127

+ Updated the "API changes" section a bit in the summary.

Gábor Hojtsy’s picture

Priority: Normal » Major

This is at a minimum major given that it would help us move forward with the beta blocker at #2293773: Field allowed values use dots in key names - not allowed in config.

Berdir’s picture

Yay!

Reminder: If this is committed, make sure that #597236: Add entity caching to core is set back to needs work, as they conflict (not sure if the patch conflicts, but that issue adds a new list_boolean field)

Oh, ignore that, I actually used an integer field type there because boolean had no widgets :)

catch’s picture

Status: Reviewed & tested by the community » Fixed

Looks fantastic. Committed/pushed to 8.x, thanks!

  • catch committed a780b52 on 8.x
    Issue #2226063 by Berdir, plopesc: Merge ListBooleanItem from options...
yched’s picture

Awesome ! Congrats !

Gábor Hojtsy’s picture

Yay, rerolled all the approaches in #2293773: Field allowed values use dots in key names - not allowed in config following this one.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.