Updated: Comment #28
Problem/Motivation
Once procedural code has been moved to ListItemBase
in #2015689: Convert field type to typed data plugin for options module . Specific code for each $field_type should be moved to each subclass, instead of switch on $field_type in messy methods in ListItemBase
Proposed resolution
Split methods in ListItemBase
and move type-specific logic to the appropriate subclass. Given that ListBooleanItem
logic is much simpler than the other types, it will be decoupled from ListItemBase
and will inherit from FieldItemBase
.
Remaining tasks
Review patch.
Commit.
User interface changes
None
API changes
None
Original report by @effulgentsia
Followup to #2015689: Convert field type to typed data plugin for options module . That issue moved procedural code from options.module to ListItemBase. Ideally though, the base class shouldn't need to switch on $field_type: all type-specific logic belongs in the type-specific subclass.
Comment | File | Size | Author |
---|---|---|---|
#41 | interdiff.txt | 780 bytes | plopesc |
#41 | options_logic-2169983-41.patch | 29.71 KB | plopesc |
Comments
Comment #1
yched CreditAttribution: yched commentedSide note: ListItemBase::getSchema() defining just indexes feels a bit weird IMO.
Maybe remove it and repeat the indexes in the actual child classes ?
Comment #2
plopescWorking on this as I talk with @yched on IRC.
Attaching patch which moves code from
ListItemBase
to each subclass. Maybe it could be split into some more methods to avoid some code duplication invalidateAllowedValues()
orextractAllowedValues()
, but I believe this is fine now.Let's see testbot.
Regards.
Comment #3
yched CreditAttribution: yched commentedneeds reroll, it seems
Comment #4
yched CreditAttribution: yched commentedJust a reroll, not reviewed.
Comment #5
yched CreditAttribution: yched commentedideally this would be moved to a static method on the class
validateAllowedValues() would also be best as a static method, set with
'#element_validate' => array(get_class($this), 'validateAllowedValues'))
(#field_name and #entity_type will need to be placed on the $element, so that the static method doesn't need to use $this)
- That method would also be a static
- It could just receive the $element['#value'] as a string, would make the "job" of the method clearer.
(would need to receive $element['#field_has_data'] as a $generate_keys bool param as well)
- Not sure whether that one can be smartly split betwwen a common implementation in the base class + smart & limited local overrides. Let's keep that for a later step.
It looks like most of this implementation could be kept in the parent ListItemBase class, just delegating to a protected method in each child class for the $description text.
Only ListBooleanItem::settingsForm() is different enough that it would override ListItemBase::settingsForm() completely.
Similarly, that method could be kept in the base class ?
Comment #7
plopescAttaching patch. I think it addresses all the points you suggested in #5.
Added some empty protected static methods in
ListItemBase
. Also proposed a split approach forextractAllowedValues()
method.This patch removes almost 100 lines of code compared to patch in #4 :)
6 files changed, 233 insertions(+), 328 deletions(-)
Any thoughts?
Comment #8
yched CreditAttribution: yched commentedNice !
Nitpick: let's move #access above #element_validate, instead of in the missdle of ad-hoc # properties that are there for validateAllowedValues (#field_has_data, #field_name, ...)
misses a PHPdoc - also, could be declared as abstract with no method body ?
While we're moving this around, we could get rid of the ->get()->getValue() syntax, which is especially confusing here.
return empty($this->value) && (string) $this->value !== 0;
should work.
Also, the method would probably be better off moved up before settingsForm() rather than in the middle of the helpers for "allowed values".
name of the method is not ideal. validateOption() ?
The comment about the "little trick" should be moved to the subclass that does the "trick" :-).
Hm - keep as an abstract ?
Just leave an empty function body then ?
"Checks whether a candidate allowed value is valid" ?
More generally :
Latest patch manages to mutualize the overall extractAllowedValues() logic at the base class level, delegating specific processing to three helper methods:
validateOptionValue()
processOptionKey()
invalidOption()
Great, but IMO there is still room for improvement on the method names:
- to clarify the exact role of each method as opposed to the other two, it's a bit confusing right now.
- to make it clear that those methods pertain to the processing of the "allowed values" setting (as opposed to handling actual runtime field values in the FieldItem)
With the current method names, it's a nice code achievement, but arguably a loss in understandability - it's a fine line :-)
The old $string name was better IMO - the fact that it comes from an $element['#value'] is irrelevant to the method.
Comment #9
plopescHi, thanks for reviewing @yched ;)
Addressing most of your points, commenting some of them here:
2.- It can't be abstract because currently
ListOptionBoolean
does not display a description. We could add a '#markup' item to settingsForm(), but I think it is not necessary.6.- This function can't be static because it is not necessary in
ListOptionBoolean
8.- Methods renamed:
While working on this patch I realized that those all new functions are related only for that field types which provide multiple options. All those methods are not necessary for the boolean field type. So I believe we could create a new level of abstraction, creating a new abstract class, where we place the settingsForm() and all its related methods.
ListFloatItem
,ListIntegerItem
andListTextItem
would inherit from that class, keepingListItemBase
cleaner and containing only the logic common to all the options field types. It would improve readability, however, it makes the architecture a bit complex.Attaching file
options_logic-2169983-9-abstract.patch
where I implement the proposed concept. I tried to make some of its methods abstract (abstract protected static function foo()), but my PHP throws some errors, maybe because of the version,Regards
Comment #12
plopescFixing bug in explicit type comparison...
Comment #13
yched CreditAttribution: yched commentedActually, the "bool" type is different enough that IMO it would be simpler to disconnect it completely from a List**Base class.
Even the reusing the getPossibleValues() / getSettableValues() from ListItemBase is not a real gain since the "flatten" part is irrelevant for the bool field type.
Keeping a simple class hierarchy IMO wins over duplicating 10 lines of simple code that only make partial sense to reuse.
+ I think in the end we want to merge ListBooleanItem up into the 'boolean' "base / non-configurable" field type (Drupal\Core\Entity\Plugin\Field\FieldType\BooleanItem). that will be easier if it doesn't depend on a much richer base class it doesn't really need.
Comment #14
plopescDecoupling
ListBooleanItem
fromListItemBase
.Comment #15
plopescRe-rolling
Comment #16
plopescRe-rolling after #2152825: rename FieldItemBase::getFieldSetting[s]() to getSetting[s]() #2200821: Rename Fieldinterface and FieldInstanceInterface and #2095303: Rename 'field_entity' to 'field_config' and 'field_instance' to 'field_instance_config'
Comment #18
plopescRe-rolling
Comment #19
yched CreditAttribution: yched commentedThanks @plopesc, sorry for letting that one slip by.
I think we can still improve / simplify the helper submethods.
allowedValueAlter() is only needed by ListFloatItem, and the task could probably be done in an override of extractAllowedValues() building on $values = parent::extractAllowedValues(); and then massaging the keys.
The else clause can be omitted - same in other implementations of the method.
Also, in practice, validateAllowedValue() & validateOptionValue() look like they are duplicate - they check the same condition, and are named confisingly similar :-). They could probably be merged in a single validateAllowedValue() method ?
Then we're left with only validateAllowedValue(), which in fact could probably have a single generic implementation in ListItemBase, that validates the value against the TypedData definition of the 'value' property defined in propertyDefinitions(), which already specifies all the constraints we need and is able to check them.
That last part is probably better left for a followup though :-)
Also, not directly touched by the patch, but according to PHPstorm the "$value = $key = FALSE;" line in extractAllowedValues() is useless.
Comment #20
plopescAddressing your comments:
This iteration looks good :)
4 files changed, 22 insertions(+), 63 deletions(-)
About the validation against typedData, I made some tests wit no luck. Some of them because we were calling it from a static context. I'm not familiar with it, and probably I'm missing something.
Regards
Comment #21
yched CreditAttribution: yched commentedNice - let's see what the testbot says.
Side note : Arguably, a more elegant way for ListFloatItem::extractAllowedValues() could be:
Comment #22
plopeschah, I had almost that code, but finally I decided to change it...
I thought that the use of array_keys() + array_values() + array_combine() could hit the performance (although this function is not critical at all), so I replaced it with just a foreach loop instantiating a new array.
I'm totally open to change it if you consider ti better. ;)
Comment #23
yched CreditAttribution: yched commentedYeah, perf is not too much of an issue here. Well, no biggie either way :-)
Comment #24
plopescUploading patch with more elegant implementation, saving more lines of code!
1 file changed, 6 insertions(+), 13 deletions(-)
Comment #25
andypostNeeds serious re-roll after #2192259: Remove ConfigField.. Item and List classes + interfaces
Comment #26
plopescRe-rolling.
It was easier than I expected :)
Let's see what testbot says...
Comment #27
andypostAny reason for index to add?
Comment #28
plopescComment #29
andypost@yched List fields are only ones that defines indexes - any reason?
Suppose better to merge this with core's
BooleanItem
as we do for other types in #2169983: Move type-specific logic from ListItemBase to the appropriate subclassThis part is messy, "field type settings form element"...
But actually used as description for allowed values!
Comment #30
BerdirThis does make sense to me. Checkboxes and Selects or something that you then often create views with filters, e.g. display all nodes which have selected option C. You don't do that for a textfield or a number (or much less frequently)
Comment #31
plopescPatch trying to address #29
Renamed
settingsFormDescription()
toallowedValuesDescription()
Removed list_boolean into boolean field type. Not sure if this is the best approach, or maybe could be better extend
Drupal\Core\Entity\Plugin\Field\FieldType\BooleanItem
in options module, like entity_reference does.This is just a proof of concept... Let's see what testbot says
Comment #33
andyposttypo: Make the boolean field configurable
Should be moved to annotation
not sure is needed
Comment #34
yched CreditAttribution: yched commented@andypost:
- indexes: what @Berdir said. Plus list types are not the only ones, text fields have an index on the 'format' property, and e_r fields have an index on the target_id
- merging bool field types: was mentioned earlier in this thread, totally +1, but I wanted to keep that for a followup, to not bikeshed this patch more than it already has been. It's kind of outside the scope here (internal code refactoring without any functional change)
If latest patch is fine, then good, otherwise i'd suggest working on "merge bool types" in a separate issue.
Comment #35
yched CreditAttribution: yched commentedAlzo, @andypost #29.2 : yes, what is the issue ? The allowed values are field-level settings, and thus the input ui is in the "field settings form".
Also, this patch just moves code around but doesnt change ui or help texts from current HEAD, so discussing this, if needed, is not for this issue.
Comment #36
andypostYes, we really should limit scope to #26
Moving to Core needs more changes... then schema...
@plopesc Please re-roll it with rename of the description.
When no 'options' module enabled this does not works
Comment #37
plopescRe-rolling...
Comment #38
plopescRemoving unnecessary "module" keys in @FieldType annotation.
Comment #39
andypostLooks rtbc!
Comment #40
alexpottWe've lost the comment as to why we're doing the (string) (float) casting
Comment #41
plopescAdding the removed comment line.
Putting it back to RTBC given that the interdiff only adds the comment.
Regards.
Comment #42
alexpottCommitted 5909b03 and pushed to 8.x. Thanks!
We need to check existing change notices.
Comment #44
yched CreditAttribution: yched commentedAnyone up for a "merge Drupal\options\Plugin\Field\FieldType\ListBooleanItem into Drupal\Core\Field\Plugin\Field\FieldType\BooleanItem" followup ? :-)
Comment #45
andypostIf we wanna move it, then at least first issue should be solved to remove dependency from
ListBooleanItem
1) #2171397: Move options_allowed_values() to a method somewhere ?
2) #2037217: Should we need dynamic options in options_allowed_values()?
Comment #46
yched CreditAttribution: yched commented@andypost: hm, is that for sure ? 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.
Comment #47
andypostSo filed #2226063: Merge ListBooleanItem from options module into BooleanItem
Just not sure about formatters and widgets
Comment #48
yched CreditAttribution: yched commentedThanks @andypost !
And thanks @plopesc for sticking with this issue here :-)
Comment #49
yched CreditAttribution: yched commentedAlso, opened #2226071: List field types: replace implementations of validateAllowedValue() with Typeddata validation for #19 :
In #20, @plopesc said he had troubles making this work, but maybe it might help with @fago & @berdir around in Szeged ?