Add property holding array keys for hook_entity_bundle_info()
Closes #3537863
Merge request reports
Activity
added 1 commit
- 06b2356c - Use memory cache instead of a static property.
198 206 */ 199 207 #[Hook('entity_bundle_field_info')] 200 208 public function entityBundleFieldInfo(EntityTypeInterface $entity_type, $bundle, array $base_field_definitions): array { 201 $result = []; 202 209 if (\Drupal::entityTypeManager()->getStorage($entity_type->id()) instanceof DynamicallyFieldableEntityStorageInterface) { 203 // Query by filtering on the ID as this is more efficient than filtering 204 // on the entity_type property directly. 205 $ids = \Drupal::entityQuery('field_config')->condition('id', $entity_type->id() . '.' . $bundle . '.', 'STARTS_WITH')->execute(); 206 // Fetch all fields and key them by field name. 207 $field_configs = FieldConfig::loadMultiple($ids); 208 foreach ($field_configs as $field_instance) { 209 $result[$field_instance->getName()] = $field_instance; 210 $cid = 'field_bundle_field_info:' . $entity_type->id(); 211 if ($cached = $this->memoryCache->get($cid)) { 212 $fields = $cached->data; 213 } 219 // on the entity_type property directly. 220 $ids = \Drupal::entityQuery('field_config')->condition('id', $entity_type->id() . '.', 'STARTS_WITH')->execute(); 221 // Fetch all fields and key them by field name. 222 $field_configs = FieldConfig::loadMultiple($ids); 223 foreach ($field_configs as $field_instance) { 224 $fields[$field_instance->getTargetBundle()][] = $field_instance->id(); 225 } 226 $this->memoryCache->set($cid, $fields, Cache::PERMANENT, ['entity_field_info']); 227 } 228 if (isset($fields[$bundle])) { 229 // Rely on the entity static cache for the field config entity loading 230 // so that it's not duplicated, to avoid memory and cache invalidation 231 // issues. 232 $bundle_fields = []; 233 $field_configs = FieldConfig::loadMultiple($fields[$bundle]); 234 foreach ($field_configs as $field_instance) { 218 // Query by filtering on the ID as this is more efficient than filtering 219 // on the entity_type property directly. 220 $ids = \Drupal::entityQuery('field_config')->condition('id', $entity_type->id() . '.', 'STARTS_WITH')->execute(); 221 // Fetch all fields and key them by field name. 222 $field_configs = FieldConfig::loadMultiple($ids); 223 foreach ($field_configs as $field_instance) { 224 $fields[$field_instance->getTargetBundle()][] = $field_instance->id(); 225 } 226 $this->memoryCache->set($cid, $fields, Cache::PERMANENT, ['entity_field_info']); 227 } 228 if (isset($fields[$bundle])) { 229 // Rely on the entity static cache for the field config entity loading 230 // so that it's not duplicated, to avoid memory and cache invalidation 231 // issues. 232 $bundle_fields = []; 233 $field_configs = FieldConfig::loadMultiple($fields[$bundle]); 198 208 */ 199 209 #[Hook('entity_bundle_field_info')] 200 210 public function entityBundleFieldInfo(EntityTypeInterface $entity_type, $bundle, array $base_field_definitions): array { 201 $result = []; 202 211 if (\Drupal::entityTypeManager()->getStorage($entity_type->id()) instanceof DynamicallyFieldableEntityStorageInterface) { 203 // Query by filtering on the ID as this is more efficient than filtering 204 // on the entity_type property directly. 205 $ids = \Drupal::entityQuery('field_config')->condition('id', $entity_type->id() . '.' . $bundle . '.', 'STARTS_WITH')->execute(); we've optimized entity field bundle to be built gradually as bundles are accessed.
I can kind of reproduce this, but still slightly confused you're hitting this for webform submissions.
I can see it's coming \Drupal\Core\Entity\EntityFieldManager::getFieldLabels(). But that is only called if there is a field storage for a given entity type. so I assume you actually do have a configurable field there?
That code loops over all bundles just to figure out which bundles have a given field. We already have this information, in the field map. Maybe we can prevent this higher up in the chain?
I think we should do this there:
- foreach (array_keys($this->entityTypeBundleInfo->getBundleInfo($entity_type)) as $bundle) { - $bundle_fields = array_filter($this->getFieldDefinitions($entity_type, $bundle), function ($field_definition) { - return $field_definition instanceof FieldConfigInterface; - }); - if (isset($bundle_fields[$field_name])) { - $field = $bundle_fields[$field_name]; + foreach ($this->getFieldMap()[$entity_type][$field_name]['bundles'] ?? [] as $bundle) { + $field = $this->getFieldDefinitions($entity_type, $bundle)[$field_name] ?? NULL; + if ($field) {
the code also looped over the bundles to filter out configurable fields and then checks again if the specific field exists, which we already know. I kept the array key, but dropped the check for a configurable label, all fields have a label, why shouldn't this also work for base fields? Love how we recently ported this from views to EntityFieldManager but didn't think for a second about the actual code there...
What's the result of that on your site with tons of webforms? At least that part should go away unless you actually have a configurable field on all those bundles.
separate issue for this?
Might still be worth doing, but impact should be smaller.
Edited by Sascha Grossenbacher