Problem/Motivation
There is a valid use case to support creating configurable fields, whether through the UI or in code, that have custom storage.
With base fields this is simple, we can just call setCustomStorage(TRUE) and we're done. No field tables will get created for that field. However, if you want to implement this using base fields you can, but you would need to take care of your field configuration for instances, and settings etc...
However, this is not possible right now if you are using a configurable field, and the FieldStorageConfig class. hasCustomStorage is hard coded to FALSE. To achieve this currently, you need to alter the field definition in hook_entity_field_storage_info_alter, and wrap the FieldStorageConfig instance with a decorator class to override the custom storage. Like https://github.com/damiankloip/og/blob/audience-field-custom-storage/src...
Proposed resolution
Add a setCustomStorage() method to FieldStorageConfig so it can easily be overridden via hook_entity_field_storage_info_alter. A step further would be adding custom_storage to the actual configuration but that would require some config changes. So just adding the runtime setter is a pure addition.
Add $custom_storage as a property and exportable configuration property for FieldStorageConfig objects. We can still default this to FALSE, but allow configurability. In the future we can then apply this to any configurable field easily to suit any use cases.
Remaining tasks
User interface changes
API changes
Data model changes
Comment | File | Size | Author |
---|---|---|---|
#80 | interdiff.txt | 1.08 KB | yched |
#80 | 2571533-custom_storage_on_config_fields-80.patch | 13.31 KB | yched |
#79 | interdiff-2571533-79.txt | 1.04 KB | damiankloip |
#79 | 2571533-79.patch | 13.03 KB | damiankloip |
#74 | interdiff-2571533-74.txt | 1005 bytes | damiankloip |
Comments
Comment #2
damiankloip CreditAttribution: damiankloip commentedComment #3
damiankloip CreditAttribution: damiankloip commentedComment #4
swentel CreditAttribution: swentel commentedWorks for me. I guess you're working on the tests ? :)
One thing that I've been wondering is how field purging will behave when a such a field is deleted, we'll definitely have to check that.
Comment #5
damiankloip CreditAttribution: damiankloip commentedI think there is no data, so no problems? Just the configuration will get removed. Or do you see other issues? Yes, I will be working on some tests. Just wanted to get the basic patch up first :)
Comment #6
swentel CreditAttribution: swentel commentedYeah, I'm assuming that as well, also, you can't uninstall a module until the fields it owns are gone, so your alter runs, so yeah, we're probably fine :)
Comment #7
plachI'm personally fine with this approach, however I vaguely remember discussing it with @yched, so it would better to have his signoff. I know also @Berdir had ideas about this, so it would be better to gather some more feedback.
80 chars
Comment #8
damiankloip CreditAttribution: damiankloip commentedHere is some more work with some added tests, the test fails but this is what I think it should do :) Using this works for me testing with my D8 install. I get no table in the DB - here it seems we do. Answers on a postcard.
Comment #10
swentel CreditAttribution: swentel commentedinstance ? instance ? That would fit on a post card yes for D7 :)
Comment #12
damiankloip CreditAttribution: damiankloip commentedWhat can I say, I'm old school.
Comment #13
damiankloip CreditAttribution: damiankloip commentedSpoke to berdir, having a new annotation property would be better. Having to keep parity between the alter and/or config for custom setting for the field type. This way all fields would inherit the behaviour they needed. Seems safer and much less likely to break for people.
Comment #14
BerdirThis can be removed now.
as @swentel said, just field.
If I don't then someone is going to tell you to use FieldConfig::create() and FieldStorageConfig::create(), should also make the @var unnecessary.
So you need to return columns?
Comment #15
damiankloip CreditAttribution: damiankloip commented1. Removed.
2. Fine! :)
3. Yes, much better, that was an artefact of c&p really.
4. Yes, but added a fix in FieldStorageConfig instead in the latest patch. BaseFieldDefinition merges all keys ( 'columns', 'unique keys', 'indexes', 'foreign keys'), where are FieldStorageConfig does not contain 'columns'.
Comment #18
damiankloip CreditAttribution: damiankloip commentedComment #19
yched CreditAttribution: yched commentedYeah, I guess the reasoning was "if you are custom_storage, it means there is associated code somewhere that takes care of your storage, which means your code relies on your field, which is what base/code-defined fields are for". Things like : if the field is configurable, it can be removed. What happens then with the associated code ?
I'm fine with allowing the flexibility and letting people figure out the subtelties though :-)
As discussed with @Berdir and @damiankloip in BCN, I don't think this should involve the *field type* saying "I have custom storage", since that's not how it works for base fields : any base field can say "I'm of this field field type, and btw, I have custom storage", and the field type has no say in the process. FieldStorageDefinitionInterface::setCustomStorage() means any field of any type can have custom storage.
So we agreed that if configurable fields can have custom storage, it should be a regular property in the FieldStorageConfig yaml (most probably without a UI ?)
Then the case of OG ("for any field of type 'og_audience' that the admins create in Field UI, make sure it has custom_storage = TRUE") can be adressed by hook_entity_create() on 'field_storage_config' entities, since by the time the FSC entity is created, it already has a field type you can reason on.
Comment #20
damiankloip CreditAttribution: damiankloip commentedThanks yched, that sums it up perfectly. Anything implementing this functionality can take responsibility for setting this when the field is created. E.g. In OG we will implement this, as mentioned in hook_entity_create, to force this setting to TRUE for the og_membership_reference field type.
Comment #23
yched CreditAttribution: yched commentedNo longer relevant :-)
Nitpick, we could inline the definition arrays
Not sure why we need this. The schema() and properties() should simply be the regular ones for the field type, we just want to ensure no table was actually created ?
Which brings : not sure we actually need a specific 'test_field_custom_storage' to test this, using any field type shoud work ?
Comment #24
damiankloip CreditAttribution: damiankloip commentedComment #27
damiankloip CreditAttribution: damiankloip commentedHmm, ok. I think I just need to update default config, just for tests. In reality this should not be an issue/break any existing config.
Comment #28
yched CreditAttribution: yched commentedThanks @damiankloip - RTBC for me when green :-)
Comment #29
damiankloip CreditAttribution: damiankloip commentedThanks yched!
Comment #30
yched CreditAttribution: yched commentedThanks !
Comment #31
jibranCan we have a change notice and proper functional tests for a field with custom storage set to true?
Comment #32
damiankloip CreditAttribution: damiankloip commentedWhy do we need additional tests past this? Other stuff doesn't have it and I'm not sure I see any benefit. This is just a property on a config entity, nothing more.
Comment #33
dawehnerI don't see the point in the change record. If people want to use it, they will look at the interface anyway. People don't have to update existing code.
Too many change records cause a big issue.
Comment #34
jibranIMHO we should have a valid use case in the form of tests in core so that we can make sure it actually works and in that process if we have to fix/update somethings in the base class then we'll do it here and we don't have to wait for followup issues to fix that stuff.
If one has to look at the interface to understand it then IMHO the documentation is not covering everything.
I don't think so. Big issue for whom?
This is just my opinion if you don't agree with it please go ahead :) I didn't block the issue on any of those items. It was merely a suggestion.
Comment #35
yched CreditAttribution: yched commentedThe test added there checks that no table gets created when the field storage is saved. I guess we could additionally check that you can create an entity, put a value in the field, save it without any error, and that the field comes up empty on next load.
I don't think there's much more to test here ?
Side note : is the clearCachedFieldDefinitions() call really needed ?
Comment #36
damiankloip CreditAttribution: damiankloip commentedYes. I guess there is no harm in adding that for saving an entity. I'll add something soon (just about to fly).
Comment #37
damiankloip CreditAttribution: damiankloip commentedHere is some additional test coverage for what we discussed above. Thanks yched, jibran.
So do we want a short change notice then? I am not sure we need it, if others do, I am fine with that too. It will be short and sweet I think.
Comment #38
damiankloip CreditAttribution: damiankloip commentedSorry, actual interdiff.
Comment #39
damiankloip CreditAttribution: damiankloip commentedComment #40
damiankloip CreditAttribution: damiankloip commentedTentatively bumping too.
Comment #41
yched CreditAttribution: yched commentedThanks @damiankloip, looks good !
Nitpick : can we add a comment above the "tableExists" assert like : "Check that no table has been created for the field", and group the second part a bit more ? (better visual overview of the two things we're testing here)
Not a blocker obviously, so, RTBC.
Change record : well, yeah, let's create a tiny one to avoid controversy :-)
Comment #44
damiankloip CreditAttribution: damiankloip commentedSure, why not. Added a quick comment. Just setting back to RTBC now to save another round :)
Comment #45
damiankloip CreditAttribution: damiankloip commentedDraft CR to appease all: https://www.drupal.org/node/2575579
Comment #46
damiankloip CreditAttribution: damiankloip commentedComment #47
jibranThanks @damiankloip looks perfect now.
Comment #48
catchSo the patch looks good, but don't we need an upgrade path to set the property to FALSE for existing fields?
Comment #49
yched CreditAttribution: yched commentedre: update - yeah, that's probably right (discussed with @alexpott and he agrees). The new property has a default value so things would work if we didn't write an update hook, but then the yaml would change on next load/save, that would be slightly confusing ("But I didn't change anything ?")
- either we write a hook_update_N(), and directly read and write the raw config records (FieldStorageConfig::load() & ::save() are not allowed there)
- or we write a hook_post_update_NAME() that simply loads and saves the FieldStorageConfig entities (and the default value will automatically get added and saved) - like
foreach (FSC::loadMultiple() as $storage) {$storage->save();}
Both work, the latter is probably simpler to write :-)
Comment #50
damiankloip CreditAttribution: damiankloip commentedFair point, this will avoid any confusion/panic I guess. Thanks yched, simple sounds good! :)
Comment #51
catchFor me the latter is exactly what we added that hook for :)
Comment #52
damiankloip CreditAttribution: damiankloip commentedHow about this?
Not sure about the t() usage, I just followed block.post_update.php as an example.
Comment #54
yched CreditAttribution: yched commentedHm - then UpdatePostUpdateTest fails because it hardcodes the list of expected hook_post_update_NAME() implementations that exist, and explicitly adds the current one implementation we have in core, block_post_update_disable_blocks_with_missing_contexts().
It seems weird to have to update the expected list in UpdatePostUpdateTest each time we add a hook_post_update_NAME() ?
Comment #55
yched CreditAttribution: yched commentedLeft a note in #2538108: Add hook_post_update_NAME() for data value updates to reliably run after data format updates, but for now @catch agreed on IRC that we should just update UpdatePostUpdateTest here to make it pass...
Comment #56
damiankloip CreditAttribution: damiankloip commentedYes, that seems weird, and kind of crappy to have to do that :)
Added to that test now, thanks again yched! Tested and locally passing ok.
Comment #57
yched CreditAttribution: yched commentedAirport RTBC-if-green :-)
Comment #61
damiankloip CreditAttribution: damiankloip commentedNot sure if that fail is legit or not. Let's see.
Comment #62
damiankloip CreditAttribution: damiankloip commentedAs expected... moving back.
Comment #64
yched CreditAttribution: yched commentedHm, actually I just learned that post_update hooks were supposed to go in *.post_update.php rather than in *.install.inc
See #2578249-24: Some e_r fields get the wrong Selection handler and interdiff over there (one of those two patches will need a reroll after the other one gets in, obviously)
Comment #65
damiankloip CreditAttribution: damiankloip commentedThis does add a post_update.php file. Or is this just a general observation? :)
Comment #68
damiankloip CreditAttribution: damiankloip commentedOh. Will reroll this.
Comment #69
yched CreditAttribution: yched commentedHAH ! Indeed it does, I didn't even notice. Apparently I was the only one to not know about that. Don't mind me, sorry about the noise ;-)
Comment #71
damiankloip CreditAttribution: damiankloip commentedComment #74
damiankloip CreditAttribution: damiankloip commentedUpdatePostUpdateTest is *ridiculous*
Comment #75
damiankloip CreditAttribution: damiankloip commentedComment #78
yched CreditAttribution: yched commentedWrong permutation ;-). Yeah, UpdatePostUpdateTest is insane.
Comment #79
damiankloip CreditAttribution: damiankloip commentedNearly going to rage quit Drupal... :)
Didn't test this one either... uh oh
Comment #80
yched CreditAttribution: yched commentedMoving to a syntax that better reflects the expected order :-)
I'll be bold and move back to RTBC, this and #79 do pass locally.
Comment #81
damiankloip CreditAttribution: damiankloip commentedYeah. I was think about doing something like that but just spent 30 seconds rerolling. Total +1 from me. Thanks yched!
Comment #82
alexpottCommitting this since it is a soft blocker for a contrib module (og) and is the lightest possible approach and has test coverage and an update function. Committed daad7b1 and pushed to 8.0.x. Thanks!
Comment #84
amitaibuAwesome, thanks folks!
Comment #85
damiankloip CreditAttribution: damiankloip commentedTHANK YOU! This is most useful.
Comment #86
damiankloip CreditAttribution: damiankloip at Tag1 Consulting commentedComment #88
quietone CreditAttribution: quietone at PreviousNext commentedPublished the change record.