Problem/Motivation
If you have a DsField plugin that calculates the contents of its result you must always include the cacheable metadata. Returning FALSE will cause your plugin to be forever cached as empty.
Example:
$build = [];
$cache = CacheableMetadata::createFromRenderArray($build)->addCacheableDependency($obj);
$cache->applyTo($build);
if ($obj->doSomething()) {
$build['#markup'] = 'something here');
}
return $build;
In this instance where doSomething() returned FALSE, then you won't have anything to actually render, only some cache tags, which might look like:
$build = [
'#cache' => [
'tags' => ['obj:1'],
],
];
In ds_entity_view_alter
we do if (!empty($field_value)) {
and then add our #theme field as a wrapper. This is added even when all that was returned was cache data and the field output should have been empty. Furthermore, if you do return FALSE from your plugin, you don't have the chance to add any cache data.
Proposed resolution
I can see a couple of different approaches. We could do something pretty gross like this:
if (!empty(Element::children($field_value)) && !isset($field_value['#type']) && !isset($field_value['#theme']) && !isset($field_value['#markup'])) {
Or, we can go ahead and track the cache data separately, which would mean that we pass in a BubbleableMetadata object.
$cache = BubbleableMetadata::createFromRenderArray($build);
$field_value = $field_instance->build($cache);
Then we can continue to check !empty($field_value)
but always apply the cache data later on.
API changes
I think we can avoid any API breaks if we make the $cache parameter to build() optional and then merge in any cache data that is also returned to $field_value for others that were already doing that.
Comment | File | Size | Author |
---|---|---|---|
#7 | 2862753-7.patch | 3.83 KB | benjy |
#4 | 2862753-4-FAIL.patch | 3.74 KB | benjy |
#4 | 2862753-4-PASS.patch | 3.83 KB | benjy |
#2 | 2862753-2.patch | 1.17 KB | benjy |
Comments
Comment #2
benjy CreditAttribution: benjy at PreviousNext commentedWe can't add a new method to the interface at all without causing a BC break, having a dual return is another alternative to option 1 in the issue summary. Equally as hacky but preserves BC.
Any thoughts on a BC break and adding a new parameter to $build? We could have our own object to wrap the cache data, similar to core's
FilterProcessResult
This issue does apply to 8.3 as well but i'm using 8.2 for now.
Comment #3
swentel CreditAttribution: swentel as a volunteer commentedWow, interesting and good analysis.
Not a fan of a BC break right now. We're currently focusing hard getting up to speed with Drupal 8.3.x where we have some problems as we rely on layout_discovery in core which might end up to be a bad decision. We don't want to make the upgrade path (if needed) even more annoying :)
Patch looks good like this imo, I'm wondering whether we can write a test for that ?
Comment #4
benjy CreditAttribution: benjy at PreviousNext commentedSorry for the delay on this one, started on the test and then got distracted....for 2 weeks.
Comment #6
benjy CreditAttribution: benjy at PreviousNext commentedComment #7
benjy CreditAttribution: benjy at PreviousNext commentedRe-rolled against 8.x-3.x
Comment #8
swentel CreditAttribution: swentel as a volunteer commentedvery nice! Looks good to me, will let aspilicious have a look at it as well.
Comment #10
aspilicious CreditAttribution: aspilicious commented