Problem/Motivation
Views integration works for config fields created in the UI, but not for base fields added to the entity types in code.
As an example, consider Date module. That diligently implements datetime_range_field_views_data() to declare its custom Views filters and arguments. But if I add a date field to my entity's base fields, the Views integration is completely missing.
Proposed resolution
Add the views handler to the field annotation as with the entity annotation. Provide the default base class for standard views data. Allow custom fields to have specific classes for views data.
Remaining tasks
Write a patch for the above proposed resolution.
User interface changes
None
API changes
API completion
Data model changes
None
Release notes snippet
TBC
| Comment | File | Size | Author |
|---|
Issue fork drupal-2337515
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #1
damiankloip commentedNice issue Daniel!
Comment #2
dawehner@damiankloip
Yeah love it :)
Comment #3
damiankloip commentedSomething like this could be useful.
Comment #4
dawehnerI would have expected the more generic alter to come after the field type specific one ... any reason why you have chosen that order?
Should we quickly explain what is in $context ?
Comment #5
tim.plunkettSee #2489476: [PP-1] Base fields miss out on Views data from hook_field_views_data()
Comment #6
jhedstromThis adds tests, and takes into account the feedback from #4.
Comment #7
Jaesin commentedWhy not allow fields to supply their default handlers in the field definition instead of making an assumption in the first place?
Something like:
Of course you would have to add getViewsDefaults to FieldStorageDefinitionInterface and BaseFieldDefinition.
Comment #8
dawehnerWell that is a good question. Should entries in
lib/Drupal/Core/Fieldcare about views? I don't know to be honest.Comment #9
Jaesin commentedI think so but added to FieldDefinitionInterface instead FieldStorageDefinitionInterface. Storage is only part of a field definition. Retrieval is the other part.
When I created a custom field, I looked for a viewsHandlers() callback and was surprised that I had to use HOOK_field_views_data.
Comment #10
tstoecklerWell we could add a generic
ViewsDataProviderInterfacein Views module and then inEntityViewsDatawe could check if the field item class implements that. Then at least for core (#2489476: [PP-1] Base fields miss out on Views data from hook_field_views_data()) and contributed (!) modules it would be quite simple and straightforward to provide their views data and would be a completely soft dependency. For the field types provided byDrupal\Corewe might not want to introduce the knowledge about Views module, soEntityViewsDatawould have to care for those itself, just like it does now.Thoughts?
Comment #11
dawehnerI like that!
Comment #12
jhedstromSomething like this? We'll need tests.
Comment #13
dawehnerIt would be great to check whether theoretically all needs of \entity_reference_field_views_data are covered.
Comment #14
Jaesin commentedWe already have field_views_data_alter so I think the point is to have the field plugin be able to supply the views data directly with something like $field_definition->getItemDefinition()->viewsSchema()
The $field_definition doesn't get you the field plugin. None of the fields I've seen so far override the BaseFieldDefinition It might be better to allow the item definition implement the ViewsDataProviderInterface.
Comment #15
jibranAdded related issues and it's at least a major issue. I think we can also delete all these methods (maybe in followup)
from EntityViewsData and move them to corresponding fields. Moreover
EntityViewsData::processViewsDataForEntityReference()handles only forward relationships and doesn't provide reverse relationships and we have a lot of ugly code for these inEntityViewsDatasub classes likeUserViewsData::getViewsData()addsWe can also handle this in follow up.
Can we handle forward and backward relationships if we implement #10? Does this work for both base fields and storage fields?
I think we can improve current approach. Just like for entity classes we add views_data class
* "views_data" = "Drupal\user\UserViewsData",. We can addviews_datakey to FieldType plugins. Core can provideFieldViewsDataand fields can subclass it just likeUserViewsDatadoes that for user. This will help also makeEntityViewsDataskinny. This mean moveEntityViewsData::mapSingleFieldViewsData()toFieldViewsData. Thoughts?Comment #16
Jaesin commented@JIbran, I do like where you are going with this and as long as "views_data" isn't required for field type definitions, we might be able to get away with it. This would bring field definitions a little close to what entity definitions look like.
Comment #17
Jaesin commentedComment #18
Jaesin commentedThis is just a start but it adds handlers to field types (which could be useful for contrib).
The concept here is you could use
$field_type->getHandler('views_data')->getViewsData().Comment #19
dawehnerIMHO its a mess that entity handlers aren't plugins, you know why, because now with field handlers we deal with the same plugins, should there be handlers,
or should be simply introduce another plugin thingy like we have for formatters and widgets ... No idea at all to be honest.
Comment #20
Jaesin commentedLike @dawehner was saying another approach would be to add a `@FieldHandler` or a `@Handler` plugin type. So you might have something like
\Drupal\text\Plugin\Field\Handler\TextViewsFieldHandlerThat has the all of the callbacks and specifies which fields it applies to.What do others think about this approach?
If handlers are generic, they will have to have a handler_type and/or context definition (which could dictate which interface they use). I think we have to have a default ViewsFieldHandler which brings up the issue, if handlers are generic how do they deal with defaults if a handler is not created for a field type. Maybe the handler consumer (views in this case) deals with this.
Also, this approach seems like would have a bit more overhead as it has to have another plugin manager for handlers.
The up side to this approach is that could be very useful in contrib to have a generic handler which you can specify context for in the handler definition.
@jibran @jhedstrom @damiankloip What do you guys think?
Comment #21
jibranI understand @dawehner's concern but let's not reinvent the wheel here. This is a major bug and we can't create new plugin system and get away with that at this point in time in release window. I suggested the handler apporoch because we already have that in core.
Comment #22
bojanz commentedI agree with jibran. We're staring RC in the face.
Comment #23
dawehnerI think this general statement is wrong, IMHO.
@damiankloip, @plach, @dawehner talked about this particular issue. Given that both entity selection thingies are plugins, but also as you see in the annotation above formatters, and widgets are plugins, so introducing handlers as an additional concept is more confusing and disruptive as adding a plugin type. Let's discuss more, but yeah we don't see an advantage of using the handlers approach to be honest, but for sure let's keep talking about it.
Comment #24
Jaesin commentedIt's debatable which would be more work because we don't need to create a plugin "system", per say. Creating a new plugin manager is relatively easy.
In core, the only concept of handlers that is similar/relevant are entity handlers (Dare: grep core for "handler"). Since the code for entity handlers isn't reusable, there is isn't much to leverage.
Comment #25
dawehnerExactly, entity handlers are a non long term solution IMHO, so let's do it right when we do it right.
Comment #26
christianadamski commentedAny progress here? We do use custom entities, we have complex fields attached to them, and any magic those fields provide to Views is lost.
Is there any workaround besides copying each fields type hook_fields_views_data() content into the entities getViewsData() ?
Comment #27
jibranWhatever we'll do here it will not be added to 8.0.x.
Comment #29
dawehner@alexpott and myself discussed a bit and we kinda think a plugin/field type handler would totally makes sense.
This would then result into something like this.
Comment #30
jibranCan you please elaborate on this? I know patch is just WIP perhaps update the issue summary with agreed apporoch.
Comment #31
dawehnerWell, its a 1to1 relationship between field type and views data. A hook for example is a relationship betweent he provider of a field type and the module of it. A hook also is some kind of event we are firing, this here is a clear functionality for itself.
Comment #32
dawehnerThis implements it for booleans and entity_references. I fear we need much more parameters though.
Comment #33
dawehnerComment #34
berdirShould the arguments/process be similar to the existing hook_field_views_data() pseudo hook/callback?
I suppose we could check if there is a hook, then call that and if not, use the new plugin (which has views_field_default_views_data() as the default implementation if nothing is specified exactly).
I don't think that supporting both is needed, so we'd deprecate the old hook in favor of this system?
Comment #35
dawehnerOh yeah the new system should be the prefered one.
Ah good idea. For now its kinda the other way round, as the default implementation is still the old way.
Comment #36
dawehnerIn case someone is interested in that ask during the devdays I'm happy to help out to get people started.
Comment #37
stborchertIf there are no objections or nobody else is working on this, I'll take a look ...
Comment #38
dawehnerNice! Please ping me when you need help / want to discuss something!
Comment #39
stborchertHere is a (very incomplete and partially working) draft of what I think could be a way to do this.
I guess there are some thing that could be done much easier and in a more elegant way.
Comment #40
stborchertNext try solving some issues.
Obviously the field definition (i.e. for body fields) are wrong so this does not work yet.
Comment #42
stborchertI'm off for two weeks, so setting to unassigned ...
Comment #43
dawehnerThanks a lot @stBorchert Enjoy your holidays!
Comment #45
borisson_I started looking at this and while reading the patch I figured I'd fix docs issues as I went trough it. I'll try fixing the failures now.
Comment #46
borisson_As discussed with @dawehner, I added string / integer fields.
Comment #47
borisson_Class / file name for string didn't match, so fixed that. Also fixed at least one testfailure, the bulkform test. Hopefully this also fixes more / other things.
Comment #50
borisson_@bojanz told the formats were not correct for integer / string, so I removed those.
I also changed one test so it doesn't fatal but fails as expected.
Comment #51
jibranNice progress.
So we removed the else part nicely done. We should add some docs in the if part.
This is not documented anywhere.
Field specific handler should be moved to respective fields and base field handler should be moved to Core fields plugin folders.
We need a hasHandler check before getHandler.
We have string id fields as well in core so we need a check here like _comment_entity_uses_integer_id().
Instead of calling gerTargetEntityTypeId again and again let's use a variable here.
Do we really need this?
Comment #53
borisson_#51:
.1 fixed.
.2 I'd like to do that when we figure out all the failures.
.3 same here, let's get the patch green before doing that.
.4 Makes sense, done.
.5 Yup, done.
.6 done.
.7 I have no idea really, I left it as-is now and I'll try to steal some @dawehner's time here at drupalaton.
Comment #55
borisson_Fixes #51.5, that was actually not fixed in #53.
Comment #56
jibranThanks for fixing the feedback.
These are the default values no need to set these again.
Comment #59
borisson_Fixes #56. I'll try to do more work on this during my flight from here back to Belgium but I can't promise that I'll figure out all the remaining fails and honestly I don't see too much time I can spend on this during the weeks after this.
Comment #60
borisson_Reduces unit test fails for EntityViewsDataTest
Comment #63
borisson_As promised, I did some work in the airport / on the plane, I have to say that I didn't really get any further, not even after trying to follow @dawehner's sugggestions. I did make a small change to improve readability and changed some documentation.
Leaving that here.
Comment #65
borisson_I'm not supposed to be working on drupal core issues now, but I was just explaining to someone why assigning something in an if-statement is a bad idea and I remembered that this patch also does that. So I cleaned that up.
Still the same amount of failures and still no concrete idea how to resolve them.
Comment #69
joachim commentedThis really should be a bug, as it's a big problem for a contrib module that provides a custom field type: Views integration works great for config fields created in the UI, but not for base fields added to the entity types in code.
As an example, consider State Machine module. That diligently implements hook_field_views_data() to declare is custom Views filter plugins. But if I add a state field to my entity's base fields, the Views integration is completely missing.
Comment #71
claudiu.cristeaStraight reroll of #65. Assigning.
Comment #73
amateescu commentedCleaning up tags.
Comment #75
kim.pepperComment #76
jibranGoing to have a look at DrupalSouth 2018.
Comment #77
pameeela commentedComment #78
jibranThanks, Pam, for creating the issue summary.
@Sam152, and I discussed this issue at DrupalSouth. We come up the views handler approach for FieldTypes same as Entities.
The idea is to deprecate the hook_views_field_data and provide modules the complete felxible API to provide views data for both base and configureable fields.
Comment #79
g089h515r806 commentedI suggest make it configurable in baseFieldDefinitions() of entity class.
allow config views settings using method:
It will be easy for developer to use this feature. Then I could add this feature to content entity builder module to make it configurable for base field by UI.
Comment #80
joachim commentedNeeds to be a sentence.
Most plugin base classes in core have a 'Base' suffix. They also usually go in the plugin folder, alongside the actual plugins.
Doesn't the PluginBase class do all this for us?
Missing @params.
This is being checked twice.
What does this mean? How would they write their own handlers, and how would those be used? What type of handlers does this mean anyway?
Why not fold all this logic into the plugin?
Comment #82
joachim commentedHere's a straight reroll of #71 for 8.8.x.
Comment #84
gregglesHere's a reroll of #82 for 8.8.x since that failed to apply.
Comment #85
duneblD 8.8.2
Unfortunately #84 is the origin of a new bug:
When I applied the patch, I got a "broken/missing handler" for a simple Filter criteria on an Entity Reference field (Taxonomy).
Here is how my broken filter is defined: my_taxo_field
is one ofa_value=>Same problem if I recreate this filter from scratch
If I unpatch #84, my filter is working again
Comment #86
geek-merlin@DuneBL: I guess this can only help if you add a backtrace.
Comment #88
japerryThe langauge plugin contained a reference to entity.manager. Lets see if removing that gets tests to pass.
Comment #93
golddragon007 commentedI've created this patch to apply to 9.2.x drupal, which I used as base the #88 patch, but it does not seem to solve my original problem.
Also, I've dropped the /core/modules/views/tests/src/Unit/EntityViewsDataTest.php file, as it does not exist in this drupal version.
Comment #94
joachim commentedBetween #19 and #35 there was discussion and agreement on changing the approach. But the issue summary now needs updating.
Comment #95
joachim commented> Also, I've dropped the /core/modules/views/tests/src/Unit/EntityViewsDataTest.php file, as it does not exist in this drupal version.
The unit test class EntityViewsDataTest.php was converted to a Kernel test, so #93 has lost work that needs to be added to the kernel test.Nope, the changes to that file were just a monstrous amount of mocking, which only goes to show that converting that unit test was the right thing to do :)
Comment #97
joachim commentedMoving this to MRs, as it'll be easier to rebase in future.
Comment #98
joachim commentedI'm having a more in-depth look at the current line of work, at a deeper level than my eyeball review in #80, and I'm not sure about this approach.
This is creating a 1-1 relationship between the field type ID and the field data ID.
I'm not sure this is very efficient. What if multiple field types have the same requirements?
It also means we have several plugins in the MR which are just empty classses, e.g.
Additionally, the plugin type's class names and namespaces aren't following the pattern for the rest of the Views plugins:
- FieldTypeViewsData for the plugin namespace -- other views plugins namespaces are in snake case
- FieldTypeViewsData for the base class -- plugin base classes are typically located in the plugin folder and called FooPluginBase
-
Comment #99
joachim commentedI've made a lot of the changes I wanted to make.
Still to do:
- I'm confused why these plugins don't define a 'field' section for Views data, just 'argument' and 'filter'. How is that getting defined?
- I want to consolidate the plugins some more
Also, two tests are failing with this and I have NO IDEA:
I can't find ANYWHERE in core where the string 'Translation language' is coming from!
Comment #100
berdir> - I'm confused why these plugins don't define a 'field' section for Views data, just 'argument' and 'filter'. How is that getting defined?
Because field rendering uses formatters just like view displays, so there is no views specific integration needed.
I haven't thought it through and I haven't look at the MR, but one option could be to use the same mechanism as field widgets and formatters, which have an annotation property that defines with which field types they are compatible with. That can also be altered through a plugin definition alter hook to add your own field type to an existing plugin. Although that also opens the door for multiple plugins per field type and I'm not sure we really want to go there. Similar to entity types <=> entity classes, we could explicitly check for multiple matches and throw an exception then
Comment #101
joachim commented> I haven't thought it through and I haven't look at the MR, but one option could be to use the same mechanism as field widgets and formatters, which have an annotation property that defines with which field types they are compatible with
Already done that in the MR :)
> we could explicitly check for multiple matches and throw an exception then
And that :)
Comment #102
berdirPosted a bunch of comments on the MR, I recommend reading through all of them before replying, sometimes I saw stuff further down that affected a previous comment.
Comment #103
joachim commentedFor anyone wanting to apply this as a patch to a 9.3 codebase, I've pushed a branch 2337515-93x-views-data-from-fieldtype which is at time of writing up to date with the work on the main development branch, 2337515-views-data-from-fieldtype.
Comment #107
joachim commentedRebased branches, and made a new MR for 9.5.x.
Not had time to consider the architectural questions raised by Berdir :(
I wonder if plugin decoration would be a way to do this: #2958184: Allow decoration of plugins.
Or we just specify a handler class name?
Comment #109
geek-merlinAmazing! Hiding patches iFo the MR.
Comment #111
joachim commentedI've just realised this is closely linked to hook_field_views_data_views_data_alter()
The docs for that say:
> The Views module's implementation of hook_views_data_alter() invokes this for each field storage, in the module that defines the field type. It is not invoked in other modules.
So it's basically: the module defining the field type gets to say something about Views data. That hook is still needed, because it allows field type providers to do things like reverse entity references.
But as a follow-up we could move that hook to the new handler classes that this issue will introduce.
Comment #112
joachim commentedI'm removing commit e7790601 because it's making lots of unrelated changes, which is making the branch harder to rebase.
Comment #113
joachim commentedRebased to 11.x and made a new branch.
Comment #115
nicxvan commentedCan we clean up the unused MRs?
Also does this have to be annotations still?
Comment #116
godotislateIf the approach with a new plugin type is used, then this definitely needs an attribute, and probably should replace the annotation, since there's no need for the annotation BC layer for a new plugin type.
Though I think @berdir suggested in #102/MR review that tagged services might be a better way to go than plugins?
Comment #117
berdirThis is going to be a pretty painful rebase now that views.views.inc is gone, but yes, since it is now in a service, I think it would make a lot of sense to make that essentially a tagged service and then we can just inject the handlers into that. I have this open and plan to see if I can make that work.
Comment #118
joachim commentedHow would a tagged service work?
> Which is where things start to get interesting. This currently only has access to its own definition. but that callback is called on the whole definition and is capable of implementing back-references (see file_field_views_data_views_data_alter() for example and core_field_views_data() for term entity reference fields.
This precise point has come up at #2706431: provide Views reverse relationships automatically for entity base fields -- handler classes for field type views data need to have one method for the field table, and another, optional one, to allow adding to the whole of the Views data.