Problem/Motivation
Problem/Motivation
\Drupal\Core\TypedData\Plugin\DataType\Map
allows a TypedData object to be created that emulates an associative array. Unlike all other TypedData objects, a value of the Map
type does not require its properties to have definitions. It's "freeform" so to speak.
It seems that the original intent was to have it serve primarily as a base class for other complex data, but not be the primary data itself. However, that usecase was not precluded.
Several implementations in Drupal core and contrib have defined values that use the raw Map
type.
The serialization system has default normalizers for objects that implement ComplexDataInterface
, which the Map
class does. It makes a call to getProperties()
, then normalizes each of those properties.
Every TypedData value instance is supposed to carry a definition. The default implementation of getProperties
uses this definition and the static method getPropertyDefinitions
on it to return definitions for each property of the map.
Unfortunately, raw uses of Map and MapItem don't need to have those definitions statically defined. So an empty list of definitions is returned and the serializer thinks: "great, there's nothing to do!"
The serializers must then be able to take this into account. A TypedData object which returns no properties should simply escape the TypedData system and be normalized as a PHP value.
Remaining tasks
1. Write a failing test
2. Auto cast Map data values into its properties
3. Decide if a REST test is necessary, and how to test it with Core only, see #60
Use Core's link options(Map dataType) serialization test instead
4. The newest patch has been tested on 8.4.x,So it can be directly used on 8.4.
Back port the patch to 8.4.x (Given the timing, this backport won't get committed, but it's still valuable when people search for a solution on 8.4.x for the next 6 months or so.)
User interface changes
None
API changes
None
Data model changes
None
Comment | File | Size | Author |
---|---|---|---|
#179 | 2895532-179.patch | 27.8 KB | Wim Leers |
#179 | interdiff.txt | 5.84 KB | Wim Leers |
#167 | 2895532-167.patch | 25.09 KB | Wim Leers |
#167 | interdiff.txt | 1.05 KB | Wim Leers |
#165 | 2895532-165.patch | 25.1 KB | Wim Leers |
Comments
Comment #2
Wim LeersIt's not clear to me what you are suggesting/asking. Can you clarify the two directions in the IS?
Comment #3
Wim LeersComment #4
skyredwangBelow is one example response from JSONAPI for retrieving a commerce_promotion entity:
From this example, we can see "offer" and "conditions" are empty, because they couldn't be normalized. This is because they are commerce custom implementation.
Let's take a look at the "offer" class,
In order to make "PromotionOfferBase" normalizable, I suggest to implement "ComplexDataInterface" on this class, so the class definition would become:
Then, implement the necessary methods the interface requires.
Comment #5
skyredwangComment #6
bojanz CreditAttribution: bojanz at Centarro commentedThis is not what I expected.
So it did work partially, the property names are there, and the plugin ID has been exported properly.
Only the configuration is empty. Probably because of missing functionality in the used core normalizer (inability to normalize Map properties)
Comment #7
skyredwangComment #8
lawxen CreditAttribution: lawxen commentedComment #9
lawxen CreditAttribution: lawxen commentedI find the issue not just happen on jsonapi,If we just serialize the commerce promotion,the error still happen(no matter whether to enable jsonapi,this exclude the jsonapi's own normalize's influence)
the result is:
Then I debug the flow of execution
https://www.processon.com/view/link/5992a90ee4b06df7265ae9a6
Then I try to find which interface to implement:PrimitiveInterface or ComplexDataInterface?
Comment #10
lawxen CreditAttribution: lawxen commentedComment #11
skyredwangYes. if you use another web services like Core REST module, this is still a problem.
Probably
ComplexDataInterface
Comment #12
lawxen CreditAttribution: lawxen commentedwe can see that the target_plugin_configuration is an instance of map,but the map has implemented the ComplexDataInterface,So we shouldn't do it again,Maybe just override some function of map.I will look further into this matter
Comment #13
skyredwangAh, so you are saying there is actually a bug. We need to investigate it.
Comment #14
lawxen CreditAttribution: lawxen commentedI'm not sure, So I need some more investigation about Map DataType and the whole serialize/normalize actuating logic.
Just implement ComplexDataInterface or other related interface seems to be useless.
Comment #15
lawxen CreditAttribution: lawxen commentedNow I find the offer's targert_plugin_configuration's PropertyDefinitions is null
So the ComplexDataNormalizer hasn't executed the normalize() function
So the solution concentrated on that Class Map can't get PropertyDefinitions like bojanz saied,
The most import thing is to find whether we should repair the issue on the commerce module,
Continuous exploration...
Comment #16
skyredwangI can't think of a reason we shouldn't.
Comment #17
lawxen CreditAttribution: lawxen commentedSorry for delay of this issue because of many other task has to be done
On the basis of my debug(break off on getting PropertyDefinitions),I have found the right solution to this issue.
https://www.drupal.org/docs/8/api/typed-data-api/data-type-plugins
https://www.drupal.org/docs/8/api/typed-data-api/data-definitions
we must define definition_class on promotion plugin's annotation definition
If the orientation is right, I think I can fix it this week
Comment #18
skyredwangThat sounds like a good plan to me. However, it might be only part of the story.
As we have:
@CommercePromotionOffer plugin:
1. OrderItemPercentageOff.php
2. OrderFixedAmountOff.php
3. OrderItemFixedAmountOff
4. OrderPercentageOff
So, by adding a
definition_class
to each of the plugin definition above, we would fix the serialization for @CommercePromotionOffer plugins.But, there is still a part "conditions" needed to be investigate and serialized
Comment #19
skyredwang"conditions" are @CommerceCondition plugin, its annotation is defined at /commerce/src/Annotation/CommerceCondition.php, then
/commerce/modules/order/src/Plugin/Commerce/Condition/ has a list of plugins:
1. OrderItemProduct.php
2. OrderBillingAddress.php
3. OrderType.php
4. OrderStore.php
5. OrderItemQuantity.php
6. OrderCustomerRole.php
7. OrderTotalPrice.php
Actually,I re-read the Data type plugins docs, the
definition_class
seems to be only available to @DataType plugin not custom plugin like @CommercePromotionOffer.@liuxin do you find a place (e.g. the parent class of @CommercePromotionOffer) where you can inject the
definition_class
?Comment #20
lawxen CreditAttribution: lawxen commented@skyredwang Just imitate the core/lib/TypedData
Then do everything like the typedata's documents
Comment #21
lawxen CreditAttribution: lawxen commented...
Comment #22
lawxen CreditAttribution: lawxen commentedI find this issue can be fixed in core
Comment #23
lawxen CreditAttribution: lawxen commentedif we add
to web/core/modules/serialization/src/Normalizer/ComplexDataNormalizer.php
Offer and conditions and anything else work ok
Comment #24
lawxen CreditAttribution: lawxen commentedThe issue concentrated on null propertyDefinitions
Comment #25
skyredwang@berdir on IRC gave us hints below:
Comment #26
lawxen CreditAttribution: lawxen commented@skyredwang Thanks,you help me hit the right point
Comment #27
lawxen CreditAttribution: lawxen commentedLast monday(8.14),I have used this method to solve the serialization problem,It define custom normalizer(the second method you provided on the origin page),You veto my method when I told you,You said:
"it is not right solution, Just implement ComplexdataInterface".
@skyredwang
Comment #28
lawxen CreditAttribution: lawxen commentedAll Plugins(like field) that be setted to MapDataDefinition can't be normaliozed,Include core and third-party modules,It seems to be more serious in Drupal
the modules list:
Comment #29
lawxen CreditAttribution: lawxen commentedAvaliable solutions:
Each of above is not the perfect solution
Comment #30
lawxen CreditAttribution: lawxen commentedweb/modules/contrib/commerce/src/Plugin/Field/FieldType/PluginItem.php
,
I add setPropertyDefinition, how to pass arguments to PropertyDefinition may be a resolution, Who has some idea?
Comment #31
lawxen CreditAttribution: lawxen commentedfixed another way
Comment #32
lawxen CreditAttribution: lawxen commentedComment #33
lawxen CreditAttribution: lawxen commentedThe above patch make the normalizer execute TypedDataNormalizer->normalize() finally when normalize 'target_plugin_configuration'
work nothing with:
But it dosn't hack drupal core
Comment #34
skyredwangComment #35
skyredwangComment #36
skyredwangComment #37
lawxen CreditAttribution: lawxen at Sparkpad commentedComment #38
BerdirAs I said in IRC, there is an existing issue (at least one) and I found it now: #2876686: [PP-1] Normalization of LinkItem's 'options' property. It is currently only about LinkItem but I already suggested there to make the fix more generic.
But as I also said, that issue has been open for a long time, and the chance of getting a custom fix into commerce might be higher than seeing that fixed in core, which might also only be added to 8.5.
Comment #39
lawxen CreditAttribution: lawxen at Sparkpad commentedDrupal 8.4.0-alpha1 has been released on August 4, 2017, which means new developments and disruptive changes should now be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.
Comment #40
lawxen CreditAttribution: lawxen at Sparkpad commentedtest result:
Comment #41
lawxen CreditAttribution: lawxen at Sparkpad commentedI rewrite the test through kerneltest because the UnitTestBase cannot simply define the relation between normarlizer and services
I write two test:
Test normalize map data that don't set setPropertyDefinition
Test normalize map data that set setPropertyDefinition
Results:
Comment #42
lawxen CreditAttribution: lawxen at Sparkpad commentedComment #43
lawxen CreditAttribution: lawxen at Sparkpad commentedIf we make this modification
we can make the test above all passed,But it will make field can't work
So we must find other place to inject the PropertyDefinition
Comment #44
lawxen CreditAttribution: lawxen at Sparkpad commentedTo make the patch take effect,All the existed data(like commerce_promotion) that use map must be re-saved;
below is the output of commerce_promotion in jsonapi
Comment #45
skyredwangThis change looks unnecessary
Need to document what this is doing
Comment #46
lawxen CreditAttribution: lawxen at Sparkpad commentedadd comments on DataDefinition::create('any'), and reduce some unneeded changed code by phpstorm
Comment #47
lawxen CreditAttribution: lawxen at Sparkpad commentedchange $PropertyDefinitions to $property_definitions because it don't conform to php code standards
Comment #48
lawxen CreditAttribution: lawxen at Sparkpad commentedIf the element of the map value is array , define it as map, else define it as any
Comment #49
lawxen CreditAttribution: lawxen at Sparkpad commentedChange directly getting PropertyDefinition to implementing setProperDefinition to speed up,
Because the code of PropertyDefinition just execute on the moment of data creating.
Comment #50
lawxen CreditAttribution: lawxen at Sparkpad commentedchange(patch):
1.code standards
2.Change directly getting PropertyDefinition to implementing setProperDefinition to speed up,
Because the code of PropertyDefinition just execute on the moment of data creating.
Comment #51
lawxen CreditAttribution: lawxen at Sparkpad commentedComment #52
lawxen CreditAttribution: lawxen at Sparkpad commentedThe faling auto test LinkViewsTokensTest still fail when not apply the patch,
Below is the image that executed by myself when not apply the patch,
Comment #53
skyredwang#50 patch makes sense to me. It tries its best to auto put values into properties.
Comment #54
lawxen CreditAttribution: lawxen at Sparkpad commentedthe #50 patch-8 use setProperDefinition has bug,
So the #46 patch-6 should be the right solution;
The patch-9 is same as patch6 with a little code standars changing
Comment #55
lawxen CreditAttribution: lawxen at Sparkpad commentedchange judge if $this->definition->getPropertyDefinitions is empty to judge $properties,
So the code will be more nice
Comment #56
skyredwangHide a few outdated patches on this issue.
Comment #59
lawxen CreditAttribution: lawxen at Sparkpad commentedAbove is the running result of LinkViewsTokensTest on https://simplytest.me,it's a refresh installing of 8.5.x with no other modules
So the failing test is not caused by the patch
Comment #60
dawehnerI am wondering whether this is the right level of the fix. If you think about it this change results in
getProperties
to be more dynamic.If you ask about the properties of this field on the schema level, you will still return no values, given the values depend on entity per entity level.
Then on the actual runtime you have different values and you end up with different properties.
Let's compare
\Drupal\Core\TypedData\Plugin\DataType\Map::toArray
and\Drupal\Core\Field\Plugin\Field\FieldType\MapItem::toArray
... as you seeMap
doesn't take into account its properties. On the other hand\Drupal\Core\Config\Schema\ArrayElement::getProperties
is actually doing basically exactly what you need.Given that I think the fix is on the right level!
Steps I think we need to do to get this in:
It would be nice to point to the MapItem field type ...
What about using instanceof and !is_subclass_of ?
Can values ever be NULL or something like this?
Comment #61
lawxen CreditAttribution: lawxen at Sparkpad commented@dawehner Thanks for your advice,
I have optimized the code about the 2 and 3 of your advices;
Please help me complete the comments,Thank you!
Comment #62
dawehnerI removed some level of ifs, and expanded the test coverage as well as expanded the documentation.
Sadly there is a failure on
buildExampleTypedDataWithProperties
. @liuxin Do you want to try to fix that failure? It seems at least not obvious for me.Comment #64
lawxen CreditAttribution: lawxen at Sparkpad commented@dawehner ok,I will fix the failure today
Comment #65
lawxen CreditAttribution: lawxen at Sparkpad commented@dawehner
The testMapWithPropertiesNormalize will still fail without the modification of Map.php
So the failure didn't caused by the patch,It may should not be fixed here(still fixed on the 2895532-65.patch).
Above is the flow chart of execute $serializer->normalize($typed_data, 'json');
When executing to the
core/lib/Drupal/Core/TypedData/TypedDataManager.php:173
We can see that if the array data of map has no key or has custom numerical key,
There will be only one property (the first numerical value's property) will return
The original design didn't take into consideration of when use setPropertyDefinition, We may pass an array that has numerical key with different type of value
So I change (is_numeric($property_name) ? 0 : $property_name) to
Comment #66
lawxen CreditAttribution: lawxen at Sparkpad commentedComment #67
lawxen CreditAttribution: lawxen at Sparkpad commentedComment #68
lawxen CreditAttribution: lawxen at Sparkpad commentedComment #69
skyredwangComment #70
skyredwangComment #71
lawxen CreditAttribution: lawxen at Sparkpad commentedComment #72
dawehnerIt feels we need someone with typed data knowledge who can judge whether having numeric keys is actually meant to be supported. I pinged @berdir about that.
Comment #73
lawxen CreditAttribution: lawxen at Sparkpad commentedAdd Core's link options(Map dataType) serialization test
Comment #75
lawxen CreditAttribution: lawxen at Sparkpad commentedComment #76
lawxen CreditAttribution: lawxen at Sparkpad commentedChange: code standard
Comment #78
lawxen CreditAttribution: lawxen at Sparkpad commentedComment #79
lawxen CreditAttribution: lawxen at Sparkpad commentedRemove whitespace
Comment #81
lawxen CreditAttribution: lawxen at Sparkpad commentedRemove whitespace
Comment #82
dravenkComment #84
dravenkdelete my comment because misunderstand
Comment #85
tedbowThis seems overly complex to fake DataDefinitions just to get normalization to work.
I know this is tricky problem and earlier in the issue changing ComplexDataNormalizer was tried but what if a new MapNormalizer was added but use
supportsNormalization()
to make sure this normalizer only is used whengetPropertyDefinitions()
returns and empty value?This patch tries that. The tests in the current patch still pass.
Comment #87
dravenkdelete because misunderstand
Comment #88
lawxen CreditAttribution: lawxen at Sparkpad commentedHi @tedbow Thanks for the advice
Two methods both work
But I don't think it's a good idea
MapNormalizer ignores the root cause: missing property definition
And the map was used or inherited on many place like field, So we shouldn't increase the system's complexity
If you think
I suggest use setPropertyDefinition instead like #50
The reason why I don't use #50, Because it has a little bug waiting to be fixed
(#50 bug:When the commerce_promotion's condition both choose curruency and order_total_price,It just set the first element's property definition ,The data modle is ["map-1" => ["map-2" => ["map-3a" => "value3a", "map-3b" => "value3b"]]];)
Comment #89
skyredwangIt looks like #85 is simpler. But, I need to test if the second approach is complete, meaning it can solve the same amount problems.
Comment #90
tedbowUPDATE: I had typo in \Drupal\serialization\Normalizer\MapNormalizer::supportsNormalization which caused a bunch errors, fixing
\Drupal\Core\Field\FieldItemBase
extends Map so because the priority of the new normalizer is higher it will be selected in\Symfony\Component\Serializer\Serializer::getNormalizer
but only if there are no data definitions.I don't think this actually a problem but by intention.
I think root cause is actually
getNormalizer()
is not selecting a normalizer that cannot handle a Map with no data definitions. We should make sure our normalizers handle our typed data types not change for the Serialization module.If we look at the class comment for Map
Maps are used when you can't know the keys beforehand and they will change with every instances. I would guess that if they are being used where you can keys beforehand and they are the same on every instance then you should actually have a data definition.
Also if we look at the class comment for
\Drupal\Core\TypedData\DataDefinition
It sounds like the previous patch was not sticking to this. It using the data definition to describe the values which are not typed data.
Comment #91
lawxen CreditAttribution: lawxen at Sparkpad commentedHi @tedbow, Thanks for your so detailed explanation
I have tested the 2895532-90.patch for every extreme case that I have known, Every case works well.
depends on your comment
data will change with every instances
I re-debug the previous patch, And confirm that MapNormalizer is a better choice.
Comment #92
tedbow@liuxin thanks, glad you agree.
One thing I was wondering, should we name it something different than
MapNormalizer
since it is really a normalizer that should will only take effect if there are no data definitions? MaybeDefinitionlessMapNormalizer
. Or maybe it is fineComment #93
Wim LeersAwesome work here!
@dawehner said this in #72:
We still need a Typed Data expert to sign off.
@dawehner said this in #60:
We still need this. See
\Drupal\Tests\datetime\Functional\EntityResource\EntityTest\EntityTestDatetimeTest
for an example where we're testing\Drupal\datetime\Plugin\Field\FieldType\DateTimeItem
.This will guarantee that the actual REST responses, the REST developer experience will never regress in the future.
Patch review:
We can't leave old code around in a comment.
This entire comment needs to be cleaned up.
We need a comment to explain why this priority was chosen. It would be even better if we'd be able to remove this priority.
Comment could indicate what happens for Map objects that *do* have property definitions.
This is interesting — this is normalizing at the
@DataType
level, not the@FieldType
level!Unlike many other normalizers.
This is actually preferable, because it means the JSON API module will be able to benefit from this too — see #2860350: Document why JSON API only supports @DataType-level normalizers!
Comment #94
tedbow@Wim Leers thanks and making sure it is not RTBC as committer would have definitely kicked it back, so saves us time.
tl;dr
supportsNormalization
and$supportedInterfaceOrClass
, I am not sure that belongs in anyone normalizer.Basically if for core if the class does not extend
FieldItemInterface
then it will be handled byComplexDataNormalizer
if it does will be handled by\Drupal\serialization\Normalizer\FieldItemNormalizer
unless it is format is hal or it is or extends another interface/class that another normalizer with a higher priority supports.Comment #96
tedbowSo #94
\Drupal\Tests\field\Kernel\FieldTypePluginManagerTest::testMainProperty
failed because it was looking the main propertyvalue
this is because\Drupal\Core\Field\FieldItemBase::mainPropertyName
returnsvalue
so instead of overriding
mainPropertyName()
in the test field item it makes sense to just change the column name tovalue
.Also I didn't notice
\Drupal\Core\Field\Plugin\Field\FieldType\MapItem
already existed but it throws an exception if I try to use it in the test.Comment #97
dawehnerThis needs some documentation ...
Personally I still think the old way was the right way to fix it, see#60, given it actually not only fixes bugs in normalization, but in all the code relying on the properties being there. I am wondering, do we really not care?
Comment #98
skyredwangI have tested both #96 and #81 patches against Drupal Commerce 2.0 stable release.
The Adjustment on Order Entity still serialize into this below:
Adjustment doesn't use MapData, but the original issue description actually points a direction for the fix. So, let's leave this issue for core, but re-open/create an issue for Drupal Commerce.
Comment #99
lawxen CreditAttribution: lawxen at Sparkpad commented@skyredwang
Dear mentor , below is my survey result:
The commerce order's field: adjustments defines an 'any' property on
modules/contrib/commerce/modules/order/src/Plugin/Field/FieldType/AdjustmentItem.php:29
And pass an object Drupal\commerce_order\Adjustment to $this->value on setValue() function
modules/contrib/commerce/modules/order/src/Plugin/Field/FieldType/AdjustmentItem.php:46
Serialization: first normalize data then encode data
But the normalize result is object, object can not be encode, so it can not be serialized
Proposed resolution:
Core type data’s normalize level(best):
like 'any' type data's doc says:
so i think the right type data's design should be:
Pros:
Cons:
①.core’s type data(core)(ps: just an example for design's idea, the code below may not right)
②. Make Adjustment object could be normalized,but it should be easy(modules)
Custom module(commerce) level: don’t pass object in setValue() (not good)
pros:
Cons:
Write custom normalizer for object (worst)
Pros: 1.the most simplest and violent resolution
Cons: 1.make all the custom module do many similar boring works other than business logic,then make drupal worse,not better
Besides: If we choose the first resolution, The issue about map's normalization should made similar change.
Comment #100
skyredwangI added #2915052: TypedData's "Any" @DataType plugin has unclear documentation for exploring the approach 1 to fix.
Comment #101
tedbow@dawehner re #97
(emphasis added)
I think the problem is code should not be relying on properties being there. Looking at the phpdoc for Map
It think problem is with
\Drupal\Core\TypedData\Plugin\DataType\Map::toArray
. I think it should be taking into account the case where the Map object doesn't have properties because it seems like they don't in the TypedData sense.It seems to me the at TypeDataInterface
The definition here defines a type of data. Not an individual instance values which the other method was describing.
Comment #102
lawxen CreditAttribution: lawxen at Sparkpad commented@tedbow
If this is right,
core/lib/Drupal/Core/TypedData/DataDefinition.php?h=8.5.x#n17
core/lib/Drupal/Core/TypedData/MapDataDefinition.php?h=8.5.x#n8
class MapDataDefinition extends ComplexDataDefinitionBase {
core/lib/Drupal/Core/TypedData/ComplexDataDefinitionBase.php?h=8.5.x#n15
we can see that propertyDefinitions != definition
Map object which doesn't have properties(propertyDefinitions),but it has definition, so it accord Conform to the TypeDataInterface‘s phpdoc, So "Map object doesn't have properties seems like they don't in the TypedData sense" is not right
Comment #103
lawxen CreditAttribution: lawxen at Sparkpad commented#98's problem : store an object to map or any,It an't been normalize and serialized
I create a issue https://www.drupal.org/node/2915705 and upload the patch
Both 2895532-80.patch and 2895532-96.patch will work combine https://www.drupal.org/node/2915705's patch
Comment #104
skyredwangComment #105
gabesullice😂
Would it be possible to check for this case in
ComplexDataNormalizer::supportsNormalization()
and return FALSE to avoid worrying about priorities?MapDataNormalizerTest? Also, shouldn't this test be this be in the serialization module?
Nit:
Nit: "the 'map_test' field type."
Your mind's on "types" isn't it? s/type/service/
"typed data manager.'
Missing ending . (dot).
s/type/typed/
Comment #106
tedbow2. It think it makes more sense to use priorities here.
Well I guess you could in
ComplexDataNormalizer::supportsNormalization()
but in addition toempty($definition->getPropertyDefinitions())
you would also have to check$data instanceof \Drupal\Core\TypedData\Plugin\DataType\Map
where as in\Drupal\serialization\Normalizer\MapNormalizer::supportsNormalization
parent::supportsNormalization($data, $format)
The other problem could be that another module may have added a special case of normalizer where
protected $supportedInterfaceOrClass = 'Drupal\Core\TypedData\ComplexDataInterface';
that they want to override core'sComplexDataNormalizer
and they used a higher priority to so that it supersedes core normalizer. In that case if we just updateComplexDataNormalizer::supportsNormalization()
not the serializer.normalizer.complex_data priority then the custom normalizer would still handleMap::class
We are already using priorities similarly to make the wrong normalizer doesn't get used in
serializer.normalizer.timestamp_item
,password_field_item
at least. for instance instead of setting the priority ofserializer.normalizer.timestamp_item
we could have updatedFieldItemNormalizer::supportsNormalization()
to checkif (!$data instanceof TimestampItem::class)
I think we should keep the pattern of using priorities for this unless we have good reason not to.
3. Fixed and move to serialization module
4. fixed
5. fixed
6. fixed
7. fixed
8. fixed
9. fixed
Comment #107
gabesullice#106.2 makes sense to me. Thanks for the thorough explanation.
Reuploading a renamed patch for the testbot.
Comment #108
gabesulliceComment #109
BerdirThis seems to be working quite well. One thing I've tried locally is to extend the MenuLinkContentResourceTestBase class to we can also test the link base field there and test normalize/denormalize.
Here's what I did which was passing on one test that I tried:
Comment #110
dawehnerMay I ask why we don't use the MapItem field in Drupal core? By doing so we would have some additional test coverage for that one.
Comment #111
larowlanFor #110
In #109 @Berdir says
- should we add a test for that?
to be given?
a range?
no need for the line break here
Comment #113
omarlopesinoI applied patch from #107 and it worked for me, thanks !
I tested this having the following modules enabled:
- Link
- LInk attributes
- Rest
Attaching screenshot after applying.
Passing to RTBC.
Comment #114
xjmI think the small changes in #111 still need to be addressed -- most are small docs issues but it sounds like there's an additional test case we could add. Leaving RTBC for the moment though. Thanks!
Comment #115
DamienMcKennaThis resolves the few comment fixes identified in #111.
Comment #116
xjm@samuel.mortenson will look into tests for this based on #110 and #111.
Comment #117
samuel.mortensonI copied @Berdir's test from #109 and it seems to work fine, I think it's good coverage to add and we always need more llamas in core.
For #110 - I think @dawehner brings up a good point, there is no core "MapItem" field in core, but fields like LinkItem use map data. I'll open a follow up to discuss adding one, because why not?
Comment #118
samuel.mortensonCreated #2946233: Add a MapItem field to core for storing arbitrary serialized data.
Comment #119
tedbowWe changed the creation of link item to be from string to an array. That is fine because are not testing creating field values here.
\Drupal\Tests\link\Kernel\LinkItemTest
tests creating the field value with a string or an array so that is covered.I read through everything since my last comment/patch in #106 right before it was RTBC. It was all addressed.
🎉
Comment #120
Wim LeersI'm working on a review. My stupid computer froze, so I lost most of my review :/ Stay tuned. This will not remain RTBC, so un-RTBC'ing to ensure it won't get committed in the mean time.
Comment #121
Wim LeersFirst, some trivial bits:
❤️ this test coverage — thanks @samuel.mortenson in #117 for doing this, and thanks to @Berdir in #109 for suggesting it so Sam basically only had to c/p it :)
I checked other entity types. Both
Item
andFeed
have'link'
fields … but they're actually using@FieldType=uri
— IOW they're poorly named.The only other entity type with a
@FieldType=link
base field isShortcut
. Seems worth updating its test coverage too.Nit: inconsistent indentation. Fixed.
We already have
\Drupal\Core\Field\Plugin\Field\FieldType\MapItem
. If we're going to keep this, it should be calledMapTestItem
. And it could then subclass the existingMapItem
.And then this:
This would not be necessary.
The more complex review points will follow in my next comment.
Comment #122
BerdirWe need a test for this as well for hal_json.
I tested it on an actual entity, and what happened is that the properties inside the field were pushed to the very top of the entity.
The reason is that hal field normalizers are expected to return a top-level structure, keyed by the field name, so they can add stuff to top-level _links.
That means we need something like a HalMapFieldItemNormalizer that returns the expected structure for hal and has a higher prio than the default MapNormalizer.
Comment #123
Berdir(Crosspost, sorry for the unassign)
Comment #124
Wim LeersI did an extremely deep dive on this issue, because it's important to get this right — committing an incomplete solution may put us in a place in the future where we have to break BC, which would be a bad place to be in.
Please read the following review points in sequence before starting to reply to them. The later ones build on the previous ones.
#117: actually, core does have
\Drupal\Core\Field\Plugin\Field\FieldType\MapItem
.This seems fairly unmaintainable.
We're overriding
ComplexDataNormalizer
simply because@DataType=map
is schemaless by default (no property definitions). To fix it, we're special-casing schemaless@DataType=map
occurrences to let it be handled byMapNormalizer
… but we're not handling@DataType=map
occurrences that do have a schema (that do have property definitions).And this then results in the parent class's
::normalize()
being called, which happens to be\Drupal\serialization\Normalizer\TypedDataNormalizer::normalize()
.At minimum we should be more explicit in our naming, and make clear in the class name and the service name that this only deals with some
@DataType=map
occurrences:PropertylessMapNormalizer
. It then suddenly becomes also much clearer why we handle some cases but not others. In surprising contrast, the class docblock does already explain this!Done.
@DataType=map
, rather than relying on a parent method. But that's a matter of preference. As long as we have sufficiently strong test coverage, I'd be fine with either.MenuLinkContent
andShortcut
does verify thereturn TRUE
case.\Drupal\Tests\rest\Functional\EntityResource\EntityTest\EntityTestMapItemNormalizerTest
makes it seem like it's testing thereturn FALSE
case, but it's NOT!It's testing
MapTestItem
(@FieldType=map_test
), which is something entirely different! That still uses the same propertyless@DataType=map
under the hood (MapDataDefinition::create()->setLabel(t('Freeform')),
).This means we have zero test coverage for the
return FALSE
case.\Drupal\Tests\rest\Functional\EntityResource\EntityTest\EntityTestMapItemNormalizerTest
: why is it testing withMapTestItem
instead of core's\Drupal\Core\Field\Plugin\Field\FieldType\MapItem
as @dawehner asked? Because it specifies['value' => …]
to be the property definitions, rather than[]
. Why does it do that?Let me take you on a wild ride deep into the depths of Entity/Field/Typed Data API and Drupal 8 git/issue queue archeology. Grab some popcorn or perhaps a coffee. 🍿☕️
…
Here we go: all this confusing behavior exists in core because #2563843: MapItem is broken for configurable fields tried to remove core's
@FieldType=map
; hat removal was committed, but then reverted because some contrib modules already started using it, even though D8 had not even been released. Entity API maintainer @tstoeckler pointed out in #2563843-37: MapItem is broken for configurable fields that consequently #2229181: MapItem::propertyDefinitions() claims it has a static value property, which is wrong needs to be rolled back (which is the code that caused property definitions to never be defined for@FieldType=map
. In other words: the currentMapItem
(
@FieldType=map
) implementation is nonsensical/broken. Read the comment I linked to for details.@tstoeckler then created #2887105: MapItem violates FieldItemInterface::schema()'s contract - breaks widgets specifically to fix this. At that point (with #2887105 in) …
MapTestItem
would be 100% identical toMapItem
. Effectively,MapTestItem
is a work-around for this error:… which is caused by this line in
\Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema::getDedicatedTableSchema()
:… which is explained in detail in the aforementioned @tstoeckler comment at #2563843-37: MapItem is broken for configurable fields. And @joachim reported this at #2887105-4: MapItem violates FieldItemInterface::schema()'s contract - breaks widgets.
That's right, the simple act of doing:
in HEAD causes it to fail with the cited error. If that's not proof that
@FieldType=map
is broken in HEAD, then I don't know what is. The reason this is not failing for the\Drupal\commerce_log\Entity\Log
entity type'sparams
base field or the\Drupal\commerce_order\Entity\Order
entity type's base field is simply that they're base fields. See\Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema::getSchemaFromStorageDefinition()
which calls\Drupal\Core\Entity\Sql\DefaultTableMapping::requiresDedicatedTableStorage()
which calls\Drupal\Core\Entity\Sql\DefaultTableMapping::allowsSharedTableStorage()
which checks if it's a base field. IOW: the LoC cited above that fails will only NOT fail for single-cardinality non-base@FieldType=map
fields … which is what all entity types so far are doing. If one of them would either be non-base fields (unlikely because@FieldType=map
hasno_ui=TRUE
), or use it with multiple cardinality, then they'd also run into this fail.TL;DR:
MapTestItem
only exists to be able to create a@FieldType=map
field with which to test on the genericEntityTest
entity type.I think this in principle does not need to block this issue.
Since #2824851: EntityResource::patch() makes an incorrect assumption about entity keys, hence results in incorrect behavior, we rely on
::equals()
to ensure safePATCH
ing without information disclosure. IOW: if we don't changeMapItem::propertyDefinitions()
,MapItem::equals()
will behave incorrectly. This would not introduce a security vulnerability, but would make it literally impossible toPATCH
map fields, since the content entity storage logic uses::equals()
to determine whether data needs to be written.(This would not introduce a security hole because including a map field in a
PATCH
request and setting it to an arbitrary value, if we don't have fieldedit
access but we do have fieldview
access, that'd result in\Drupal\rest\Plugin\rest\resource\EntityResource::checkPatchFieldAccess()
returningFALSE
which would result in\Drupal\rest\Plugin\rest\resource\EntityResource::patch()
NOT setting the received value.)@DataType=map
normalization, this will also for the first time enable@FieldType=map
normalization. This means that this issue also must consider@FieldType=map
normalization: that normalization should make sense too.IMHO, the
\Drupal\Tests\rest\Functional\EntityResource\EntityTest\EntityTestMapItemNormalizerTest
test that we're adding is asserting a normalization that we DON'T want: the entire map is normalized under avalue
root key that is an irrelevant implementation detail for API clients. We just want the map we're storing, we don't want to see the data storage column name! And if we want to test this, we need to fix point 4.If others agree with this (I suspect they will), we'll also need a
MapItemNormalizer
.Attached interdiff/patch fixes all of the above.
Comment #125
Wim LeersQ: Do we really need these changes?
A: We don't need it for propertyless
@DataType=map
, but we do need it for@DataType
with defined properties. Reverting this change does cause\Drupal\Tests\serialization\Kernel\PropertylessMapNormalizerTest::testMapWithPropertiesNormalize()
to fail.👍
Nit: inheritdoc
Nit: Incomplete sentence.
Nit: Missing doc.
Nit: Too much whitespace.
Nit: Copy/paste remnant.
Until the previous patch, this should've been named
MapNormalizerTest
, now it should be namedPropertylessMapNormalizerTest
.Nit: whitespace.
I forgot to delete this in #124!
Actually, the indentation that I changed in #121 was correct before… but there was some weirdness with the closing parenthesis. Fixed that instead.
Comment #126
Wim LeersSince #121,
Drupal\Tests\field\Kernel\FieldTypePluginManagerTest::testMainProperty()
fails. Fixed.Comment #130
BerdirAs I already commented in the issues you referenced, I do not agree with this change.
The properties are the runtime properties that the object has. This suggests that I can do $entity->field->value, but that is wrong.
Core doesn't have a lot of code that is generic like that, but contrib absolutely does.
The whole point of MapItem is that you have a complex structure with properties at runtime that is stored into a single column, but that part is an implementation detail.
Yes we have documentation that claims properties = columns but I think that is too simplified. I do agree that I didn't spot the mess around MapItem/MapTestItem here and I need to look closer into the specific problem for dedicated tables, but I disagree that this is the solution.
Comment #131
BerdirI also fully agree with this, but this is just looking at it from the normalization/serialization perspective and I need to make one small correction:
> the entire map is normalized under a value root key that is an irrelevant implementation detail for
APIclientsIt should be an implementation detail for everyone. A MapItem is not a field with a map on its value property. It *is* a map.
You now changed propertyDefinitions() to be consistent with schema() but by doing so, made it inconsistent with how MapItem is actually used. It's not $entity->field->value->map_property, it is $entity->field->map_property.
So again, you did spot the problem that I missed (and thanks for that, I feel pretty blind now!), but I don't agree with your solution to it. I'll try to see if I can come up with something else.
Comment #132
Wim LeersI think we perhaps disagree less than you think! :)
#130:
This, 100%! That's why I made these changes:
All of those are about exactly what you said: the properties are the ones that are stored for this particular
map
instance, not some hardcoded implementation detail.The only reason I made that change is to make things work at all on a HTTP API/normalization level.
Well, that actually does work. But I agree it's wrong for the same reason that the way that
@FieldType=map
was normalized until before the changes I just quoted (which were made in #124), the HTTP API/normalization was wrong.I think we agree on that?We agree on that, per @Berdir's comment in #131 :)Sure! I have no opinion about that. That's an Entity/Field API implementation detail as far as I'm concerned, I don't know why those things are architected the way they currently work.
#131:
❤️ 🎉 Splendid!
AFAICT there are two possible explanations/solutions:
propertyDefinitions()
+schema()
is getting in the way: it prevents us from expressing what we need to express.\Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema::getDedicatedTableSchema()
are incorrect.I could see it being solved either way (or perhaps in other ways I don't yet see!), but IDK which is the correct approach — that requires a more complete understanding of Entity/Field/Typed Data API than I have! Looking forward to your proposal! :)
Comment #133
Wim LeersWhen this does reach RTBC again, I think we need a better IS. And an updated CR at this point.
Current TODOs:
hal_json
test coverage pointed out in #122MapItem::propertyDefinitions()
as being discussed in the preceding three commentsComment #134
BerdirI decided to move the storage changes/fixes and my response to the last comment to #2563843: MapItem is broken for configurable fields, together with a kernel test that makes it much easier to test and debug those fixes.
I also changed EntityTestMapItemNormalizerTest a bit as a result of that, without the wrapping value property.
I'm only attaching an interdiff here on top of #121. It passes for me together with the patch from the other issue. I explicitly specified the delta 0 there in all places, that made it easier for me to read the structure and understand what we are working with. We also still need a test for hal_json.
That doesn't mean that we don't want the changes from the following patches from Wim, but it was far easier to test on top of #121 before it started to contain changes to MapItem and other places. We well have to combine all the improvements that we do want from those with my interdiff.
We have to either get the other issue in first *or* we create a test entity type with a map base field so we are not blocked on it. As mentioned, it is not relevant for normalization if it's a base or configurable field and all real use cases out there that are waiting on this are base fields (obviously, since configurable fields are not working at all).
Comment #135
skyredwangJust coming back from Spring Festival, as the original reporter and have apps running in production, I will review the updates above.
Comment #136
skyredwangI read from #124 to #134 and their related issues. I see @Berdir is doing the possible change 2.) mentioned in #132 in the related issues.
This issue is about the missing normalization and serialization for MapData; therefore, the focus of the remaining discussion is whether the change effect that will be introduced in either solution in #132 can be mitigated by MapNormalizer, hence the output of serialization can remain the same to users regardless the upcoming changes. By intention, MapNormalizer is a bridge between internal Drupal storage and external use, so I see it can be easily adapted to the change in the Map/MapItem when necessary. Therefore, I don't see this issue has dependency on the other related issues.
Comment #137
BerdirThe issue and the actual implementation (which is not yet complete and needs to work for hal as well) do not depend on the other issue, correct.
But the tests do. The only way to be able to test a map base field would be to define a new test entity type in a new module (I'd like to avoid adding more types to entity_test) and use that instead of a configurable field.
Comment #138
lawxen CreditAttribution: lawxen at Sparkpad commentedI followed @Wim Leers's suggestion in issue TypedData 'Any' can't be normalized to array if it stores an object:
Move all the change from TypedDataNormalizer to a standard alone normalizer: AnyNormalizer.
Then the TypedDataNormalizer won't have the ability to normalize some data like:
or
So the PropertylessMapNormalizer should extends AnyNormalizer After TypedData 'Any' can't be normalized to array if it stores an object be fixed.
Or we move AnyNormalizer's code to TypedDataNormalizer back.
And we should add test for it in this issue too.
Comment #139
lawxen CreditAttribution: lawxen at Sparkpad commented2895532-126.patch can't be applied to drupal8.5.4
This is a re-roll patch, there's no any functional change.
Comment #140
Wim LeersThis issue will be 1 year old in 2 days. 😢 The last major round of activity is from 4 months ago.
Let's get this going again. I finished re-reading the entire issue and related issues. Assigning this to me.
Summary of the current state of the issue and next steps
This issue was last RTBC in #119. I un-RTBC'd it in #120 + #121 + #124. #121 and especially #124 contain very detailed analyses of how we got to the current confusing place. @Berdir made corrections in #130 + #131 to my analysis. I agreed with him in #132.
I posted next steps in #133. @Berdir posted a proposed interdiff in #134. Those were never applied to the patch yet. That's what's next.
Of course, before we do that, the patch must apply to HEAD again. @caseylau thankfully already did that a month ago, in #139. That still applies cleanly.
Comment #141
Berdir@Wim: Yay. The problem is that the related issue that I mentioned in #134 is blocking the current patch. To avoid a dependency on that, we need to make the current configurable field a base field, either with a custom test entity type or with a base field alter hook based on a state flag because right now, MapItem only properly works as a base field.
Comment #142
Wim LeersRerolled #2563843-69: MapItem is broken for configurable fields, that patch is now hopefully RTBC'able.
Comment #143
Wim LeersFirst trying to fix the failures that I introduced in my #124 patch.
Comment #145
Wim Leers_links
. The code in HEAD didn't suffer from this problem, but it suffered from a different one (hence these changes): accessing$entity->get('some_map-like_field_type')->getValue()
puts them under a'value'
key.I couldn't get this to pass tests. So rather than continue to work on just making the latest patch pass tests, I think/hope Berdir's work will help us pass tests, by fixing the problem in a better way :)
Besides, @Berdir kindly wrote:
… but I'm not entirely sure that there are any changes worth keeping after #121, especially because they're all centered around changing
MapItem
in a way that per @Berdir's explanation does not make sense.@FieldType=map
base field. My initial reaction was — then I realized why @Berdir proposed this: because that field type hasno_ui = TRUE
in its annotation, meaning that site builders cannot ever set up a "map" field through the UI!I tried … and got very very very stuck on applying #134 on top of the latest patch. I thought I lifted the right pieces from #134. But couldn't get it to work. Even with #2563843-69: MapItem is broken for configurable fields applied.
EntityTestMapItemNormalizerTest
tests fail withUndefined index: value
@FieldType=map
base field.So this patch:
@FieldType=map
base field from #2563843-69: MapItem is broken for configurable fields@FieldType=map
configurable field to an existing entity typePoints 3 and 4 show up in the interdiff.
Comment #146
Wim LeersMoved the test to a proper test base class (also in a different location, per #2910883: Move all entity type REST tests to the providing modules), with a subclass for
json
(this is what we had in the previous patch).This then makes it easy to add another subclass for
hal_json
(necessary per #133.1).Comment #147
Wim LeersCreated HAL test coverage. Noticed that the
langcode
key on theentity_test_map_field
entity type was apparently necessary, so added it. Without making changes to normalizers,@FieldType=map
is not being serialized correctly.Comment #148
Wim LeersMake the HAL test pass. To do this, I first needed to figure out why they pass for
json
, but not forhal_json
.Mindblowing things to follow.
First key fact:
abstract class FieldItemBase extends Map
. This means that not only\Drupal\Core\TypedData\Plugin\DataType\Map
is normalized by this normalizer, but also\Drupal\Core\Field\Plugin\Field\FieldType\MapItem
! This obviously causes no problems forjson
(given all the above patches), but it does forhal_json
.My first naïve approach was to do
… but now it fails differently:
because
\Drupal\hal\Normalizer\FieldItemNormalizer::normalizedFieldValues()
now gets called forMapItem
as expected, but in there we have this:And … that's right, because
MapItem
has no property definitions,::getNonInternalProperties()
returns[]
, hence there's nothing to normalize, which explains the above output. The same is true for thejson
format (because\Drupal\serialization\Normalizer\FieldItemNormalizer()
has similar logic.The difference between
serialization.module
's field item normalizer andhal.module
's?hal.module
's wraps values in an array keyed by field name to allow_links
to be specified by normalizers needing to bubble links (specifically,\Drupal\hal\Normalizer\EntityReferenceItemNormalizer
).MapNormalizer
obviously doesn't do that keying, and that's what's causing the HAL test in the previous comment to fail, rightfully so, because its normalization is wrong (it "skips" the@FieldType=map
field and normalizes each value in that field as if it were a field.Of course,
MapNormalizer
acting on field items is also wrong in the case ofserialization.module
— it just went unnoticed! This is what my #126 patch unknowingly worked around thanks to the change that @Berdir called out in #130 as being not acceptable. Little did I know I was working around so much trickiness! Looking back, it was sheer luck that it worked at all.All field item classes specify property definitions, with a single exception:
MapItem
. Hence it's impossible to gather the non-internal properties, or even any properties at all, since\Drupal\Core\TypedData\Plugin\DataType\Map::getProperties()
relies ongetPropertyDefinitions()
on the data definition for theMap
data type … which as we know is empty.It seems like the only solution is a
MapItemNormalizer
, even though we are trying to actively discourage@FieldType
-level normalizers: #2926507: Discourage @FieldType-level normalizers, encourage @DataType-level normalizers, to strengthen the API-First ecosystem.It feels like we've reached an impasse here:
MapItem
normalizers, because then we need to implement them many times, once for everything:serialization.module
,hal.module
,jsonapi.module
, and every other custom format needs to implement this too.Map(Item)::getPropertyDefinitions()
or at least::getProperties()
to reflect the truthI think @Berdir won't like option 2, but OTOH he wrote:
What I'm describing actually does make
::getProperties()
consistent with howMapItem
is actually used! It's impossible to make::getPropertyDefinitions()
work because it'sstatic
: it hence cannot inspect the object's values. But::getProperties()
is definitely feasible.Making
getProperties()
"work" is harder than it seems though: to comply with the contract of its interface, we can't just return the values stored in aMap
; we'd need to returnTypedDataInterface[]
. But converting an arbitrary array of data intoTypedDataInterface
objects is not trivial.While working on this, I realized there's a second possible solution: make
serialization.module
'sFieldItemNormalizer::normalize()
(which is inherited fromComplexDataNormalizer::normalize()
) andhal.module
'sFieldItemNormalizer::normalize()
smarter. If they encounter a field item that does not have any properties (such asMapItem
), then rather than normalizing nothing, just normalize its value. The value of a propertyless field item must be a combination ofarray|int|float|bool|string
— i.e. of primitives. Which means there's nothing left to normalize: this works as-is!Comment #149
Wim Leers#148 should pass tests for
GET
andDELETE
, but failed forPOST
andPATCH
.This makes
POST
andPATCH
tests pass too.Comment #150
Wim LeersMapNormalizer
is now obsolete; it's not being used at all. AndMapTestItem
has been obsolete since #145.Removing them.
Comment #151
Wim LeersStrengthen tests.
Comment #152
Wim LeersAnd clean-up.
Whew! Unassigning. This needs reviews.
This took me about 20 hours across 3 days to figure out… 😵😵
Comment #161
Wim LeersEntityTestMapFieldHalJsonAnonTest
EnityTestMapFieldHalJsonAnonTest
😭
(When running tests locally, this failure does not occur … 😡)
Comment #162
Wim LeersFix the 2 CS violations. Add
uuid
entity key, to allow JSON API to also add test coverage for this (created #2986404: @FieldType=map support for that).Comment #163
Wim LeersComment #165
Wim LeersThe two failures are because of me adding
uuid
in #162. Silly oversight, easy fix.Comment #166
BerdirThis looks pretty good to me, didn't fully grok the crucial normalization parts yet, but test coverage and resulting data structures look good to me, only thing I noticed is that data looks different with the explicit 0 key, but the other fields just do the 0 implicitly.
Comment #167
Wim LeersActually, I got that from your #134 interdiff 😀
I was wondering what that was about — whether it was intentional or not. Sounds like it wasn't.
Made it implicit here too.
Comment #168
gabesulliceI've read through this issue word-for-word, top-to bottom and done my best to understand the issue. I updated the issue summary to try to explain what I understood. Feel free to correct or add to my understanding @Wim Leers or @Berdir.
I then applied @Wim Leers latest patch and worked through each change line by line.
This honestly looks like the least-bad solution to a really difficult problem. While it feels very strange that
getProperties()
can return an empty array, yet still normalize to a value, I don't see any other way to do this.I've tried to do a few other things in an attempt to make
getProperties()
work as it should, but in the end, this seems to be the least invasive change to fix the problem.+1 from me. I think @Berdir should be the one to RTBC this though.
Comment #170
Wim LeersThank you for going through all of it! The updated IS looks good to me. It doesn't explain the entire history of the issue, and that's probably a good thing. :)
Agreed.
Comment #171
Wim LeersI updated this issue's CR (https://www.drupal.org/node/2946521).
Comment #172
BerdirTrying to put the blame on me if goes wrong? ;)
Had another look at this, did some manual testing with default_content, was able to export menu links and entities with map base fields, seems to be working fine.
Comment #173
Wim LeersNot at all! Trying to respect your judgment on this particular issue above anybody else's! :)
Also: 🎉 🍻 😀
Comment #175
tacituseu CreditAttribution: tacituseu commentedUnrelated failure.
Comment #176
larowlannit: > 80 here
some brackets here would aid readability - also should we put this into a method to avoid the duplication?
Correct me if I'm wrong, but it is totally valid to use a value object in a map field property.
In fact, layout builder already does that with \Drupal\layout_builder\Plugin\Field\FieldType\LayoutSectionItem
So what happens to layout_builder fields in this case?
My reading of the patch is that we just pass through the values, and if there is a normalizer for those value objects, then they do their thing.
I think we need some testing on that front.
We have https://www.drupal.org/project/drupal/issues/2942975 but it feels like that might be a blocker for this now? Or in fact, it might be need to change dramatically, and instead of supporting normalizing of LayoutSectionItem, it will instead support \Drupal\layout_builder\Section?
Correct me if I'm wrong
Can we get an issue summary update, in particular to this bit
to clarify what it means.
Here is how I'm reading the patch, if this is accurate, then all good.
If not, then no idea what is going on here :)
Comment #177
larowlanAnd if my observations about value objects are correct, I think we need to clarify that in the change record.
Comment #178
borisson_#176.2: I don't think we should refactor that into a seperate method, since we've only used it twice. That doesn't warrant it's own method yet.
Back to needs work based on #176
Comment #179
Wim Leers#176:
MapItem::getPropertyDefinitions()
returns[]
. That's what that new condition is for.LayoutSectionItem::getPropertyDefinitions()
returns['section']
.Actually, nothing new is happening here. Both before and after, we're iterating over properties in a field. The difference is that in the case of anything except
@FieldType=map
AKAMapItem
, properties are defined, which means we can iterate over them. Only in the case of@FieldType=map
, we cannot inspect metadata to determine which properties exist, which is why the$field_item->getValue()
call is added.Once we're iterating over the property values, we're normalizing them. That's the next line down in the code, and as you can see is unchanged.
Already in HEAD some properties may contain value objects, and some may not. In most cases, the property value is a
PrimitiveInterface
instance, but sometimes it already is just an actual PHP primitive (int
,string
, …). Note that those primitive ("scalar") values are automatically normalized in Symfony's serializer itself, see\Symfony\Component\Serializer\Serializer::normalize()
, where this bit exists:(This means (nested) arrays of scalars are returned as-is!)
For the Layout Builder example you cite, there already is an issue: #2942975: [PP-1] Expose Layout Builder data to REST and JSON:API. You already linked to it, great! This does not block that issue, nor the other way around. See #2942975-4: [PP-1] Expose Layout Builder data to REST and JSON:API: independent of this issue, it's recommended to have a normalizer for Layout Builder's custom
@DataType
rather than its custom@FieldType
, because that way it'll also work for JSON API.Conclusion: nothing new is being introduced here. Hence there's no need for additional test coverage.
Finally, WRT the confusion around
See
in point 3 above! That is what it's referring to. Hence your interpretation is indeed 100% correct!Comment #180
catchOK the change in #179 is a very minor test re-organisation (large interdiff is because it moves a whole block from one place to another), and Wim has properly answered larowlan's question.
Committed/pushed to 8.7.x and cherry-picked to 8.6.x, thanks!
Comment #181
samuel.mortenson@catch I don't see the commit - is d.o just slow to pick it up?
Comment #182
effulgentsia CreditAttribution: effulgentsia at Acquia commentedI think so. It's commit
725e3609
when I do a git pull.Comment #183
xjmI think the CR could use some work -- is there anything site/module owners need to change? Any possible disruption for consumers of the exposed APIs? Trying to figure out whether this is something we need to highlight in the 8.6.0 release notes.
Comment #184
mglamanIt fixes Drupal Commerce order endpoints, adjustments will now come through (or should.) If it works magically, then any field which was coming back empty should now have data without hacks. So maybe the CR should ask contrib maintainers to review any shims put in place.
Example: #2916252: [PP-1] Order's Adjustment can't be normalized and serialized
Comment #185
Wim LeersI think #184 answers #183? :)
Also: 🎉🎉🎉🎉🎉🎉
This unblocked:
Allowed this to become a smaller/simpler patch:
And made these obsolete:
Comment #186
DamienMcKenna<img src="excited.gif" alt="Excited!" />
Comment #187
Wim Leers:)
Comment #188
xjm@mglaman, sounds great. Can you add that to the change record?