Problem/Motivation
Views needs to build a form with a list coming from a options field.
Sadly there is nothing like
options_regular_allowed_values()
anymore so you always need an entity instance, which you don't have in a views configuration UI
this is blocking #2012130: Regression: Views integration for "list" field types is broken
Beta phase evaluation
Issue category | Bug because it opens up to fix an existing problem in views |
---|---|
Issue priority | Critical because not being able to filter by list fields in views affects a LOT of people. |
Disruption | Disruptive for contrib modules is quite low as this is a really specific functionality.
Everyone who provides an "allowed_value_function" for fields, would have to make it possible to pass in no entity. |
Proposed resolution
Allow to optionally pass along the field items/entity to options_regular_allowed_values()
.
This allows views to use that function itself, so that the critical bug is fixed.
It was discussed whether this issue should be marked as a duplicate of #2329937: Allow definition objects to provide options.
- That other issue would allow to later write a total generic integration, which is great, but not a requirement to fix the critical.
- The later issue though needs some time, and having 2 criticals floating around blocks us from thinking about other stuff
- The other issue has potential more impact in various places
- Rules for itself though probably requires the more generic issue
Remaining tasks
Task | Novice task? | Contributor instructions | Complete? |
---|---|---|---|
Update the issue summary noting if allowed during the beta | Instructions | done in #57 | |
Add automated tests | Instructions | done in #58 | |
when this is fixed, unpostpone #2012130: Regression: Views integration for "list" field types is broken |
User interface changes
API changes
options_regular_allowed_values() now takes a FieldItemList instead of an entity. The FieldItemList is also optional on top of it.
Comment | File | Size | Author |
---|---|---|---|
#82 | interdiff-82.txt | 1.39 KB | xjm |
#82 | options-2238085-82.patch | 12.88 KB | xjm |
#81 | callback-interdiff.txt | 4.76 KB | xjm |
#81 | options-2238085-81.patch | 12.78 KB | xjm |
#76 | interdiff.txt | 687 bytes | yched |
Comments
Comment #1
dawehner.
Comment #2
dawehnerOh, given that this blocks #2012130: Regression: Views integration for "list" field types is broken it is indeed critical.
Comment #3
catchWhat's needed here that isn't in #2238085: [regression] options_allowed_values() signature doesn't allow for Views filter configuration?
Comment #4
dawehnerYou probably refer to #2012130: Regression: Views integration for "list" field types is broken ... that other issue does not introduce the helper function we need yet ... as it was not needed a some time in the past.
Comment #5
catchSorry I meant #2116363: Unified repository of field definitions (cache + API).
Comment #6
fagoI think we should improve allowed values workflow to work along what I've proposed for default values here: #2226267: Improve default value handling of fields to be consistent.
In short, the main way to get allowed values should be via the (field) definition. Field types should be able to provide a suiting default implementation.
Comment #7
fagoMarking as beta deadline as this will change how you define allowed values for field types and how you retrieve them.
Comment #8
catchBeta deadline means we'd bump it to 8.1.x or 9.0.x if it doesn't get in, which we can't if it's supposed to block the 8.x release.
This feels like it's more likely to be an API addition than a change, but moving to beta blocker since fago's comment suggests not.
Comment #9
xjm@fago, so #2226267: Improve default value handling of fields to be consistent would potentially solve this and (with a regression test and fix to the Views handler) this one could then be marked as a duplicate?
(The patch in #2226267: Improve default value handling of fields to be consistent so far only includes API additions but it also looks like it was mainly a proof-of-concept.)
Comment #10
damiankloip CreditAttribution: damiankloip commentedNot sure if that issue will solve this? This is about having to pass a field instance in to get option values.
Comment #11
breathingrock CreditAttribution: breathingrock commentedComment #12
breathingrock CreditAttribution: breathingrock commentedPlease include steps to reproduce this.
Comment #13
breathingrock CreditAttribution: breathingrock commentedComment #14
damiankloip CreditAttribution: damiankloip commentedThis is not really something to reproduce. This is just something that is not currently possible.
Comment #15
fagook, I started looking at this and came up with a possible solution:
Attached is a first implementation start, unfortunately I was not able to finish it.
Comment #16
damiankloip CreditAttribution: damiankloip commentedComment #17
xjmComment #18
yched CreditAttribution: yched commentedIt's not really a "default" (as in "basic implementation that works in most cases"), it's an implementation based on an 'allowed_values' field setting.
IMO it would make sense to split this into an abstract Base class containing the flattenOptions(), getPossibleValues(), getSettableValues(), getSettableOptions() methods, and leaving the getPossibleOptions() method for actual implementations.
Which points out that
- it's slightly inconsistent to have the interface be Allowed*Values*providerInterface, with the actual business case logic residing on getPossible*Options*()
- the 'allowed_values' setting is in fact about 'options', not 'values' :-)
:-)
Not a fan of this. 'settings' are the land of field types, we shouldn't have methods around this in the generic FieldDefinition. The split between 'generic logic of the Field API' and 'business logic specific to a (couple) field type(s)' is a very structuring one, I'd be wary of blurring that line.
It's a fact that the FieldDefinition API provides no generic, built-in functionality for "allowed values", because, unlike "default values", it's a notion that makes sense for only a couple field types. So it's not really a bug if there's no "generic way to get the list of possible values for a field", it never was the case in D7.
Comment #19
tim.plunkettIf *anyone* else wants to work on this, please just ping me and I'll hand it back :)
But, for now, I'm going to look at it.
Comment #20
damiankloip CreditAttribution: damiankloip commentedIn d7 you could get allowed values based on a field instance. Now you need to pass in an actual entity. That's the main issue here.
Comment #21
damiankloip CreditAttribution: damiankloip commentedHere is a fixed version of the patch. This now doesn't blow up at least :)
Let's see how it gets on anyway.
However, after getting it working and having a dig around. This still does not fix our immediate issue of what to do about options_allowed_values() and allowed_values_function and having to pass an entity as a parameter. We need to only support list fields that do not want to use an entity, or drop the parameter I guess...
Comment #22
damiankloip CreditAttribution: damiankloip commentedOr, we just do something like this :p
I do think having a provider is better though.
EDIT: yched, I haven't ignored your feedback, getting on to that.
Comment #24
catchI was pondering #22. I think that might be enough if we fix all the places expecting an actual entity?
Comment #25
yched CreditAttribution: yched commentedWell, IIRC, in D7 the ability to make the "allowed values callback" depend on the host entity got added as a side feature request, with the explicit warning that
- there would not always be a known entity (specifically for cross-bundle cases like Views),
- and that the callbacks needed to account for that fact : e.g the result with a NULL entity should be a superset of the responses with an actual $entity.
Comment #26
yched CreditAttribution: yched commentedThe problem with #22 is that options_allowed_values() calls methods on $entity to build the static cache key :
$cache_id = implode(':', array($entity->getEntityTypeId(), $entity->bundle(), $field_definition->getName()));
We need to replace that with $field_definition->getTargetEntityTypeId(), $field_definition->getBundle()
(the latter doesn't exist currently, it's being added in #2144263: Decouple entity field storage from configurable fields)
Comment #27
damiankloip CreditAttribution: damiankloip commentedYes, that would break for sure, was just the general idea :)
That is one easy way to tack it though....
Comment #28
mradcliffeWould it be possible to get the allowed values similar to how the AllowedValuesConstraintValidator works?
Comment #29
xjmRe. #26/27. From that issue:
None of that looks too specific to what's being done in the rest of the issue; maybe we could pull it out into its own separate patch? Edit: unless the bundle is not yet part of the definition and I just missed that elsewhere. :)
Comment #30
geerlingguy CreditAttribution: geerlingguy commentedRe: #26 — so should this be postponed/blocked on #2144263: Decouple entity field storage from configurable fields?
Comment #31
yched CreditAttribution: yched commented@geerlingguy : well, #2144263: Decouple entity field storage from configurable fields should be close now, but worst case, as @xjm said, the patch here can always stel the parts that add getBundle(), it's fairly straightforward code IIRC.
Comment #32
xjmYeah, I'd suggest just trying this patch with those bits added and maybe a -do-not-test.patch that excludes them; we can easily remove them once #2144263: Decouple entity field storage from configurable fields goes in. If it turns out to be more onerous than that we can postpone it.
Comment #33
vijaycs85Here is an update as per #32
Comment #35
damiankloip CreditAttribution: damiankloip commentedHere is a reroll of #21. I still think that if we want to make this better, D8 style, we need this. And not the new simplistic approach suggested in #22. We would still be left with the procedural function that we would need to call. which is meh.
Thoughts on that? If we want the simple approach, we can. It is just a quick fix though really. This could be a good chance to clean this area up a little. Do we also want to consider whether we are going to make the entity dependency optional for allowed values or remove it entirely. I think this is fine, views options will not work correctly if an entity is needed. So any field that did this would be useless there anyway. Or if we suck it up and make it optional, and accept this will not work with views. Basing options on the current entity is really not a major use case? Seems like very few people would want this.
Is there are way someone could get the entity if needed? So we could just have it available but not a part of the api to call this?
Comment #37
yched CreditAttribution: yched commentedLooking back at #1541792: Enable dynamic allowed list values function with additional context, that introduced this "allowed values by $entity" feature 2 years ago (thus quite late post 7.0 - was added to D8 with a backport to D7 - see commit at #1541792-45: Enable dynamic allowed list values function with additional context)
- Originally posted by @amitaibu, @dww and @chx picked up with lots of interest, notably for project.module (https://www.drupal.org/project/dereference_list). @tim.plunkett and @sun seemed to want this too
- Views and the impact on Views is not mentioned once in the issue :-)
The behavior this introduced is : the allowed values for the field "in general", and for the field "in a specific $entity" can be different.
The only way this can be consistent is : the $entity-specific allowed values have to be a subset of the "general" allowed values. Then how it translates in Views can make sense.
But this constraint is :
a) entirely up to the implementations of the custom "allowed values callbacks",
b) not explicitely mentioned anywhere in the docs
(As an interesting side note, it was that issue that added '#entity' => $entity in each $form[$field_name], just so that the options widget can grab it. Widgets have other ways to access the parent $entity now, so we can unburden the $form structure. Separate issue though, opened #2293723: Generate lighter $form[$field] structures)
I'm really not a fan of this feature either (and it seems I didn't participate in that issue at all back then, I guess i was afk). But the uses cases seem real :-/
Also, D7 + Views D7 have been working with it for the past 2 years (kind of - you can get weird behavior if your "allowed values" callbacks don't respect the implicit "subset" constraint). It's only the current shape of the D8 API that provides no way to get the allowed values without an $entity, and thus blocks Views, the D7 API allows that.
So I'd say it's more a question of fixing the D8 API than removing the feature, as kludgy / fragile as it is.
Or : if we removed that feature, which I'd be happy with :-), how can similar behavior be attained ? By form alters and adding custom constraints ?
Comment #38
tim.plunkettPart of me thinks we should just remove the regression by returning to the optional-ness of $entity.
Comment #39
effulgentsia CreditAttribution: effulgentsia commentedI don't see how #38 gets us any closer to solving the Views use case. If we want to make $entity optional, we can do so without passing in $entity_type_id, since that can be retrieved internally within options_allowed_values() via $field_definition->getFieldStorageDefinition()->getTargetEntityType().
However, the more important problem for the Views use case is that the first parameter is a FieldDefinitionInterface, and Views only has a FieldStorageDefinitionInterface to work with. Which makes figuring out a good signature for the
allowed_values_function
difficult.Comment #40
effulgentsia CreditAttribution: effulgentsia commentedI have an idea I want to try.
Comment #41
tim.plunkettYou can't always get the allowed values in D7 views either, but at least it doesn't fatal.
I was shooting for that level of fix. But I completely missed the FieldDefinitionInterface vs FieldStorageDefinitionInterface difference :(
Looking forward to your idea.
Comment #42
effulgentsia CreditAttribution: effulgentsia commentedHere's what I think is the minimum needed to solve this issue. I'll open some follow ups though in a bit.
Comment #44
effulgentsia CreditAttribution: effulgentsia commentedFiled all the followups. Will work on #42's test failures next.
Comment #45
effulgentsia CreditAttribution: effulgentsia commentedI don't understand why flattenOptions() strips labels, but apparently that's by design, so adjusted for that.
Comment #46
effulgentsia CreditAttribution: effulgentsia commentedNothing in #45 looks beta-bound to me, so removing that tag. Instead, I added a "beta deadline" tag to #2305397-1: Field type (item) classes implementing AllowedValuesInterface::getPossible(Values|Options)() is incompatible with Views and possibly other use cases. Also, retitling for the limited fix of #45.
Regarding the "needs tests" tag, do we need to add tests here, or can we do that in #2012130: Regression: Views integration for "list" field types is broken instead? I don't think additional unit tests would be helpful for this. Integration tests with Views would be, but need that issue.
Comment #47
pcambraHere's a plain re roll after #2287727: Rename FieldConfig to FieldStorageConfig
Comment #48
fagoComment #49
dawehnerIs anything in this issue being blocked? I would like to resolve this issue in order to fix the views critical.
Some random suggestion.
It would be great on the longrun to check that the function is also callable, but this could be for sure done in a follow up.
Shouldn't this be = NULL, in order to not fail if items is not passed? What about expanding the test coverage for that in general?
Comment #50
moshe weitzman CreditAttribution: moshe weitzman commented2) In #46, @effulgentsia suggested that we add tests as part of #2012130: Regression: Views integration for "list" field types is broken.
Sounds like nothing is blocking this one. I'll request a retest .
Comment #52
fagoWhile the proposed patch solves the problem for Views by bypassing our new API for options lists, it does not solve the underlying problem. However, as effulgentsia summarized nicely at #2305397: Field type (item) classes implementing AllowedValuesInterface::getPossible(Values|Options)() is incompatible with Views and possibly other use cases the current API could be used by creating dumb, empty entity objects. Still, that's sub-optimal and I'd love to see a decent solution that solves the problems outlined here and covers our other use cases on context definitions as well. Therefore, I opened #2329937: Allow definition objects to provide options for the proposed solution.
Comment #53
Fabianx CreditAttribution: Fabianx commentedCode needs work based on #52, we need to eat our own food or order something else, not sneak in the kitchen to pick some sweets
Comment #54
YesCT CreditAttribution: YesCT commentedupdated issue summary to note this is blocking #2012130: Regression: Views integration for "list" field types is broken (so when something happens here, like a fix, we remember to activate the other issue)
Comment #55
cilefen CreditAttribution: cilefen commentedReroll of #47
Comment #56
dawehnerWell, we could skip that API change and still pass along the entity optionally. This still serves at least the bug in views, any opinion from someone else?
Comment #57
dawehnerThe part in #56 is though not really a critical problem itself, its just something you could avoid in the future.
update the IS and added a beta evaluation.
Sadly we still need some tests here.
Comment #58
dawehnerI just want to ensure that we move forward here, given how easy the fix is.
Getting the proper fix in, is, as said, still possible.
Comment #59
YesCT CreditAttribution: YesCT commentedadding blocker tag, since this blocks #2012130: Regression: Views integration for "list" field types is broken, so that the d8 blockers is accurate. Remove the blocker tag when this issue is fixed, and unpostpone 2012130.
Comment #60
swentel CreditAttribution: swentel commentedCan't find anything that annoys me, but this could probably get a +1 from fago and/or yched for RTBC.
Comment #61
fagoI've been discussing #2329937: Allow definition objects to provide options with yched yesterday and it will require some more work, thus I'm fine with moving on with this stop-gap fix first. Patch generally looks good, but here a remark:
It's not allowed values interface any more. Howsoever, possible options should include all possible options which cannot vary by value as well, it should include all possible options. Thus, I'd say it's ok to just do $items->getFieldDefinition()->getOptionsProvider->getPossibleOptions() what
a) looks less weird
b) it's the main API one should use for fetching options, as it would cover any other options that might be set on the field definition
Also, strictly speaking the change of the function signature is an "API" change, but I don't think it's an issue because as said the right API to fetch options is the field definition getOptionsProvider() method.
Comment #62
fagoComment #63
yched CreditAttribution: yched commentedReceiving a FSDI rather than a FDI is fine (nitpick : I'd still go for accuracy and call the param $field_storage_definition)
But why pass a FieldItemListInterface instead of an Entity ?
I dug into the git history of this line, and I think we shouldn't bother with adding the bundle here. It was added by #1950632: Create a FieldDefinitionInterface and use it for formatters and widgets (d9f7b3a), which was the root of the issue here : it's the issue that changed options_allowed_values() from
"receive a $field + maybe an $instance and an $entity for context"
to
"always receive a FDI and an $entity".
Now that we revert to a saner state ("receive an FSDI + maybe an entity for context"), we should also revert to "cache values if they depend only on the FSDI (= entity_type + field_name), and let callbacks opt out of cacheability if the additionally depend on the $entity context, through the $cacheable parameter)
Comment #64
yched CreditAttribution: yched commentedoptions groups : right, OptionsProviderInterface provides no way to get "a *flat* list of values with labels".
The efficient way, for cases with huge lists of allowed values, would be to have a getLabels(array $values) method in OptionsProviderInterface, instead of fetching the full list of values.
Adding a new method to OptionsProviderInterface is a BC break for existing implementations. We're considering maybe breaking it in #2329937: Allow definition objects to provide options, but if we do we should try to break it only once.
So for now, we need to flatten the list ourselves here :-/. And yeah, it's sad that OptGroup::flattenOptions() doesn't retain labels. I opened #2392301: OptGroups::flattenOptions() should preserve labels.
Meanwhile, it looks like we need to copy the helper code from OptionWidgetBase::flattenOptions() :-(
Comment #65
dawehnerWell, sure, let's get rid of that. It is not worth to do now, see first bit of the response of @yched.
I hope its okay, to mark this method a public static one. In the context of options module, its fine to share code like that,
but it should not be really reused as a generic API.
Comment #66
yched CreditAttribution: yched commentedHmm, not too fond of this. A Widget base class is not a great place to put generic helpers :-)
I'd be fine with it being temporary until we have a better solution like #2392301: OptGroups::flattenOptions() should preserve labels, but if we commit this now, removing it later would be an API change.
Oops, I thought I posted that earlier, but apparently d.o ate it (or I never pushed "Submit").
Now that this works on $items, this has no reason to be in the loop anymore.
looks way simpler :-)
Comment #68
dawehnerWell, okay.
Alright, let's fix it and cache the allowed options differently in case someone passed an entity.
Comment #69
yched CreditAttribution: yched commentedSorry :-/
Er, we can further simplify that, can't we ? ;-)
Also, that longish comment is not needed IMO. Yes, allowed values are the same for all deltas, that's not a new thing, that's just how they work. OptionsWidget::getOptions() does the same already, and doesn't bother with this long explanation.
So in the end it's as simple as :
The "option group" part is irrelevant now
Comment #70
dawehnerLet's hate each other even more.
Comment #71
dawehnerHere is the interdiff.
Comment #72
yched CreditAttribution: yched commentedStreamlined the formatter code a bit more
+ as mentioned in #66, we need an if ($items->count()) so that we don't trigger a possibly costly computation of allowed values if there is nothing to display.
Comment #73
yched CreditAttribution: yched commentedOops, same as #72 without the trailing space :-/
Comment #74
fagominor: I'd even say it should be that way.
one space to much before "array keys".
still white space here.
Isn't that an API change for allowed values callbacks?
Comment #75
dawehnerI kinda doubt it is, given that you just have fields on fieldable entities.
Comment #76
yched CreditAttribution: yched commentedWell,
is indeed an API change - that this exactly the broken concept that is being fixed here, but yes, it is an API change.
Other than that, @fago pointed that the signature of options_test_allowed_values_callback() should have the $entity param default to NULL, as otherwise PHP will fatal when the function is called with a NULL $entity (the patch already changes the other test callback, options_test_dynamic_values_callback() that way).
Comment #78
dawehnerDocumented the API change as part of the beta evaluation.
@yched @fago @anyone
Can we RTBC it?
Comment #79
xjmI am going to work on a bit of... unclear documentation in the patch. :)
Comment #80
xjmSo one of the reasons the documentation for
options_allowed_values()
is confusing is that inside the parameter documentation, it's attempting to explain how to write a specific kind of callback that happens to get registered within that function. We have a standard way of documenting callbacks since #1250500: [policy adopted; patch done] Find a (standard) way to document callback signatures, so I'll try adding that to make things more clear for developers providing field option callbacks.Comment #81
xjmHow's this?
Comment #82
xjmNow with the missing predicate.
Comment #84
swentel CreditAttribution: swentel commentedWay much better!
Comment #86
alexpottNice to see this being sorted out - I think we also need to do something for
default_value_callback
since this requires an entity too - although this might not be so critical. I might have created an issue for this already but I can not find it.This issue addresses a critical bug and is allowed per https://www.drupal.org/core/beta-changes. Committed 4de09b6 and pushed to 8.0.x. Thanks!
Did the above (very minor) fixes on commit.
Comment #88
yched CreditAttribution: yched commentedPosted a followup patch to refine the callback docs : #2393061: Adjust phpdoc for callback_allowed_values_function()