Problem/Motivation
ComponentElementAlter is looping on each prop and applying the normalization:
foreach ($element["#props"] as $prop_id => $prop) {
if (!isset($props[$prop_id])) {
continue;
}
$prop_type = $props[$prop_id]['ui_patterns']['type_definition'];
$element["#props"][$prop_id] = $prop_type->normalize($prop);
}
However, attributes is not processed when not defined in the component definition (which is the usual case, because it is magically added by SDC).
Like variant with VariantPropType, attributes has always the same type: AttributesPropType
Proposed resolution
2 proposals
Early alteration in ComponentPluginManager::annotateProps()
$definition['props']['properties']['attributes'] = $this->buildAttributesProp($definition);
Doing that, attributes will be considered as a normal prop, automatically show up everywhere there are props (component library, form builder...). A bit like what variant do.
This will also fix #3469791: [2.0.0-beta2] Expose default attributes prop in forms
(If we hide it from component library in this MR, let's also do that for "variant")
Late alteration in ComponentElementAlter
Tested:
if (isset($element["#props"]["attributes"])) {
$element["#props"]["attributes"] = AttributesPropType::normalize($element["#props"]["attributes"]);
}
Doing that, we keep attributes special and we only alter the specific thing we need to fix.
Issue fork ui_patterns-3470231
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 #5
pdureau commentedLet's do the early alteration in ComponentPluginManager::annotateProps().
#3469791: [2.0.0-beta2] Expose default attributes prop in forms is now obsolete and can be closed
Comment #7
pdureau commentedWe have this PHP Unit error in the pipeline:
Drupal\Core\Render\Component\Exception\InvalidComponentException: [attributes] String value found, but an object is requiredIt may be related to #3469788: [2.0.0-beta2] Check PropTypeInterface::normalize()
Comment #9
pdureau commentedChecked with Mikael
It is SDC's ComponentValidator::validateProps() which is calling a dependency method which is executing json_encode() which is casting the attribute object into a string.
So, we may need 2 different methods:
PropTypeInterface::normalize()before the ComponentValidator, which is always returning data conform to JSON Schema, so which is keeping the associative array for attributesPropTypeInterface::preprocess()?) in between the ComponentValidator and the injection to Twig template, which is instantiating the Attribute objectUnfortunately, ComponentValidator may be executed very late, from a Twig extension, so we may need to add our own Twig extension to be executed after.
Comment #10
pdureau commentedSDC implementation looks a bit messy IMHO:
ComponentsTwigExtension is adding 2 twig functions:
add_component_contextwhich is adding the attribute Attribute object if missingvalidate_component_propswhich is executing the JSON schema validatorWhy are they Twig functions instead of living in the rendering process? Who will ever use them in the template?
Because ComponentNodeVisitor is printing those Twig functions on every template, which will be executed on rendering in this order:
And this is making all the mess. What we are paying here is the cost of the use of "template to template" Twig functions or tags (
includeandembed) instead of a dedicated mechanism which is loading the Render API and leveraging the render element. For example,attach_libraryis used here because#attachedis not executed.This is problematic, but kind of works just because of another weird hack in the ComponentValidator to prevent the validation of attributes:
But this is the root cause of our current issue. This would need to be removed and fully redone, but it will take time and it may be too late.
What can we do now, in this humble ticket, to overcome this?
Comment #11
pdureau commentedSo, let's see every possible situation.
attributeas a PHP object in template contextadd_component_contextis altering the Attribute object.validate_component_propsis skipping the validation, so it is OK ✅validate_component_propsis doing the validation, so is rendering the Attribute object, so we have[attributes] String value found, but an object is required❌This is the current situation:
link_attributesand attributes in links props in some projects. See: #3469788: [2.0.0-beta2] Check PropTypeInterface::normalize()attributeprop if we do this ticket (that's why phpunit is blocking the ticket pipeline)No attribute in template context
The same as before because
add_component_contextis adding the missing Attribute object, which is rendered as a string by the validator.attributeas a PHP array in template contextThis will be the situation if we remove
AttributePropType::normalize()add_component_contextis doing nothingvalidate_component_propsis skipping the validation, so it is OK ✅validate_component_propsis doing the validation, and it must be OK ✅However, we are not sending a PHP object any more. ❌
Proposal
So, for the
[attributes] String value found, but an object is requiredissue, the solution would be to:LinksPropType::normalize()andAttributesPropType::normalize()AND don't create Attribute PHP object inComponentElementAlter::processAttributes()ComponentElementAlter::processAttributes()if no$element["#props"]["attributes"])in order to not trigger the similar mechanism inadd_component_contextin order to keep sending a PHP object instead of a PHP array in templates:
LinksPropTypeandAttributesPropTypeThe twig function would do something like that that:
Comment #12
pdureau commentedComment #13
pdureau commentedComment #15
pdureau commentedComment #16
pdureau commented