Skip to content
Snippets Groups Projects

Add property holding array keys for hook_entity_bundle_info()

Closes #3537863

Merge request reports

Loading
Loading

Activity

Filter activity
  • Approvals
  • Assignees & reviewers
  • Comments (from bots)
  • Comments (from users)
  • Commits & branches
  • Edits
  • Labels
  • Lock status
  • Mentions
  • Merge request status
  • Tracking
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 }
  • Comment on lines +211 to +213

    Is there a possibility this hook is invoked with data already existing in the memory cache but missing a newly created field config?

    Would it make sense to enable memory cache for the field config entity type?

  • Author Maintainer

    That was possible with earlier iterations of the MR (and caused loads of kernel test failures), but now it's using a memory cache and adding the entity_field_info cache tag it should be covered.

  • only saw this discussion just now, see my omment below. we still load all fields twice with the memory cache.

  • Please register or sign in to reply
  • catch added 1 commit

    added 1 commit

    • c1064ae8 - Set up a proper cache backend.

    Compare with previous version

  • catch added 1 commit

    added 1 commit

    • eb27230b - Set up a proper cache backend.

    Compare with previous version

  • catch added 1 commit

    added 1 commit

    • 7e34e738 - Update performance test assertions.

    Compare with previous version

  • 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
    • Please register or sign in to reply
  • Sascha Grossenbacher left review comments

    left review comments

  • Please register or sign in to reply
    Loading