Loading group.module +24 −25 Original line number Diff line number Diff line Loading @@ -314,35 +314,34 @@ function group_entity_access(EntityInterface $entity, $operation, AccountInterfa foreach ($plugin_ids as $plugin_id) { $plugin_cache_tags[] = "group_content_list:plugin:$plugin_id"; } // Load all of the group content for this entity. $group_contents = \Drupal::entityTypeManager() ->getStorage('group_content') ->loadByEntity($entity); // If the entity does not belong to any group, we have nothing to say. // // @todo There is a slight performance boost to be had here. If we have a // plugin that adds content without access and one that adds the same content // with access, then this check would fail for content that is only added // using the non-access plugin. The loop over the plugin IDs below would then // not return anything and we would still end up with a neutral result, albeit // with the user.group_permissions context and slightly worse performance. We // can fix this by only loading those group content entities that use the // plugins retrieved above. if (empty($group_contents)) { return AccessResult::neutral()->addCacheTags($plugin_cache_tags); } $access = AccessResult::neutral(); foreach ($plugin_ids as $plugin_id) { $access = AccessResult::neutral()->addCacheTags($plugin_cache_tags); // Reduce the plugin IDs to those that are actually in use. $data_table = \Drupal::entityTypeManager() ->getDefinition('group_content') ->getDataTable(); $plugin_ids_used = \Drupal::database() ->select($data_table, 'd') ->fields('d', ['plugin_id']) ->condition('entity_id', $entity->id()) ->condition('plugin_id', $plugin_ids, 'IN') ->distinct(TRUE) ->execute() ->fetchCol(); // Loop over the plugin handlers and add their access check to the result. foreach ($plugin_ids_used as $plugin_id) { $handler = $plugin_manager->getAccessControlHandler($plugin_id); $access = $access->orIf($handler->entityAccess($entity, $operation, $account, TRUE)); // No need to continue if access is denied. if ($access->isForbidden()) { break; } } return $access ->addCacheTags($plugin_cache_tags) ->addCacheContexts(['user.group_permissions']); return $access; } /** Loading src/Entity/Group.php +9 −5 Original line number Diff line number Diff line Loading @@ -160,24 +160,28 @@ class Group extends EditorialContentEntityBase implements GroupInterface { /** * {@inheritdoc} */ public function getContent($plugin_id = NULL, $filters = []) { return $this->groupContentStorage()->loadByGroup($this, $plugin_id, $filters); public function getContent($plugin_id = NULL) { return $this->groupContentStorage()->loadByGroup($this, $plugin_id); } /** * {@inheritdoc} */ public function getContentByEntityId($plugin_id, $id) { return $this->getContent($plugin_id, ['entity_id' => $id]); return $this->groupContentStorage()->loadByProperties([ 'gid' => $this->id(), 'plugin_id' => $plugin_id, 'entity_id' => $id, ]); } /** * {@inheritdoc} */ public function getContentEntities($plugin_id = NULL, $filters = []) { public function getContentEntities($plugin_id = NULL) { $entities = []; foreach ($this->getContent($plugin_id, $filters) as $group_content) { foreach ($this->getContent($plugin_id) as $group_content) { $entities[] = $group_content->getEntity(); } Loading src/Entity/GroupInterface.php +2 −8 Original line number Diff line number Diff line Loading @@ -50,14 +50,11 @@ interface GroupInterface extends ContentEntityInterface, EntityOwnerInterface, E * * @param string $plugin_id * (optional) A group relation type ID to filter on. * @param array $filters * (optional) An associative array of extra filters where the keys are * property or field names and the values are the value to filter on. * * @return \Drupal\group\Entity\GroupContentInterface[] * A list of GroupContent entities matching the criteria. */ public function getContent($plugin_id = NULL, $filters = []); public function getContent($plugin_id = NULL); /** * Retrieves all GroupContent entities for a specific entity. Loading @@ -80,9 +77,6 @@ interface GroupInterface extends ContentEntityInterface, EntityOwnerInterface, E * * @param string $plugin_id * (optional) A group relation type ID to filter on. * @param array $filters * (optional) An associative array of extra filters where the keys are * property or field names and the values are the value to filter on. * * @return \Drupal\Core\Entity\EntityInterface[] * A list of entities matching the criteria. This list does not have keys Loading @@ -90,7 +84,7 @@ interface GroupInterface extends ContentEntityInterface, EntityOwnerInterface, E * * @see \Drupal\group\Entity\GroupInterface::getContent() */ public function getContentEntities($plugin_id = NULL, $filters = []); public function getContentEntities($plugin_id = NULL); /** * Adds a user as a member of the group. Loading src/Entity/Storage/GroupContentStorage.php +65 −41 Original line number Diff line number Diff line Loading @@ -15,6 +15,13 @@ use Drupal\group\Entity\GroupInterface; */ class GroupContentStorage extends SqlContentEntityStorage implements GroupContentStorageInterface { /** * Static cache for looking up group content entities for groups. * * @var array */ protected $loadByGroupCache = []; /** * Static cache for looking up group content entities for entities. * Loading @@ -22,6 +29,13 @@ class GroupContentStorage extends SqlContentEntityStorage implements GroupConten */ protected $loadByEntityCache = []; /** * Static cache for looking up group content entities for plugins. * * @var array */ protected $loadByPluginCache = []; /** * {@inheritdoc} */ Loading Loading @@ -69,60 +83,62 @@ class GroupContentStorage extends SqlContentEntityStorage implements GroupConten /** * {@inheritdoc} */ public function loadByGroup(GroupInterface $group, $plugin_id = NULL, $filters = []) { public function loadByGroup(GroupInterface $group, $plugin_id = NULL) { // An unsaved group cannot have any content. if ($group->id() === NULL) { throw new EntityStorageException("Cannot load GroupContent entities for an unsaved group."); $group_id = $group->id(); if ($group_id === NULL) { return []; } $properties = ['gid' => $group->id()] + $filters; $cache_key = $plugin_id ?: '---ALL---'; if (!isset($this->loadByGroupCache[$group_id][$cache_key])) { $query = $this->database ->select($this->dataTable, 'd') ->fields('d', ['id']) ->condition('gid', $group_id); // If a plugin ID was provided, set the group content type ID for it. if (isset($plugin_id)) { /** @var \Drupal\group\Entity\Storage\GroupContentTypeStorageInterface $storage */ $storage = $this->entityTypeManager->getStorage('group_content_type'); $properties['type'] = $storage->getGroupContentTypeId($group->bundle(), $plugin_id); if ($plugin_id) { $query->condition('plugin_id', $plugin_id); } $this->loadByGroupCache[$group_id][$cache_key] = $query->execute()->fetchCol(); } return $this->loadByProperties($properties); if (!empty($this->loadByGroupCache[$group_id][$cache_key])) { return $this->loadMultiple($this->loadByGroupCache[$group_id][$cache_key]); } else { return []; } } /** * {@inheritdoc} */ public function loadByEntity(ContentEntityInterface $entity) { public function loadByEntity(ContentEntityInterface $entity, $plugin_id = NULL) { // An unsaved entity cannot have any group content. $entity_id = $entity->id(); if ($entity_id === NULL) { throw new EntityStorageException("Cannot load GroupContent entities for an unsaved entity."); return []; } $entity_type_id = $entity->getEntityTypeId(); if (!isset($this->loadByEntityCache[$entity_type_id][$entity_id])) { /** @var \Drupal\group\Entity\Storage\GroupContentTypeStorageInterface $storage */ $storage = $this->entityTypeManager->getStorage('group_content_type'); $group_content_types = $storage->loadByEntityTypeId($entity_type_id); // Statically cache all group content IDs for the group content types. if (!empty($group_content_types)) { // Use an optimized plain query to avoid the overhead of entity and SQL // query builders. $query = "SELECT id from {{$this->dataTable}} WHERE entity_id = :entity_id AND type IN (:types[])"; $this->loadByEntityCache[$entity_type_id][$entity_id] = $this->database ->query($query, [ ':entity_id' => $entity_id, ':types[]' => array_keys($group_content_types), ]) ->fetchCol(); } // If no responsible group content types were found, we return nothing. else { $this->loadByEntityCache[$entity_type_id][$entity_id] = []; $cache_key = $plugin_id ?: '---ALL---'; if (!isset($this->loadByEntityCache[$entity_type_id][$entity_id][$cache_key])) { $query = $this->database ->select($this->dataTable, 'd') ->fields('d', ['id']) ->condition('entity_id', $entity_id); if ($plugin_id) { $query->condition('plugin_id', $plugin_id); } $this->loadByEntityCache[$entity_type_id][$entity_id][$cache_key] = $query->execute()->fetchCol(); } if (!empty($this->loadByEntityCache[$entity_type_id][$entity_id])) { return $this->loadMultiple($this->loadByEntityCache[$entity_type_id][$entity_id]); if (!empty($this->loadByEntityCache[$entity_type_id][$entity_id][$cache_key])) { return $this->loadMultiple($this->loadByEntityCache[$entity_type_id][$entity_id][$cache_key]); } else { return []; Loading @@ -133,15 +149,21 @@ class GroupContentStorage extends SqlContentEntityStorage implements GroupConten * {@inheritdoc} */ public function loadByPluginId($plugin_id) { // If no responsible group content types were found, we return nothing. /** @var \Drupal\group\Entity\Storage\GroupContentTypeStorageInterface $storage */ $storage = $this->entityTypeManager->getStorage('group_content_type'); $group_content_types = $storage->loadByPluginId($plugin_id); if (empty($group_content_types)) { return []; if (!isset($this->loadByPluginCache[$plugin_id])) { $query = $this->database ->select($this->dataTable, 'd') ->fields('d', ['id']) ->condition('plugin_id', $plugin_id); $this->loadByPluginCache[$plugin_id] = $query->execute()->fetchCol(); } return $this->loadByProperties(['type' => array_keys($group_content_types)]); if (!empty($this->loadByPluginCache[$plugin_id])) { return $this->loadMultiple($this->loadByPluginCache[$plugin_id]); } else { return []; } } /** Loading @@ -149,7 +171,9 @@ class GroupContentStorage extends SqlContentEntityStorage implements GroupConten */ public function resetCache(array $ids = NULL) { parent::resetCache($ids); $this->loadByGroupCache = []; $this->loadByEntityCache = []; $this->loadByPluginCache = []; } } src/Entity/Storage/GroupContentStorageInterface.php +4 −5 Original line number Diff line number Diff line Loading @@ -35,25 +35,24 @@ interface GroupContentStorageInterface extends ContentEntityStorageInterface { * The group entity to load the group content entities for. * @param string $plugin_id * (optional) A group relation type ID to filter on. * @param array $filters * (optional) An associative array of extra filters where the keys are * property or field names and the values are the value to filter on. * * @return \Drupal\group\Entity\GroupContentInterface[] * A list of GroupContent entities matching the criteria. */ public function loadByGroup(GroupInterface $group, $plugin_id = NULL, $filters = []); public function loadByGroup(GroupInterface $group, $plugin_id = NULL); /** * Retrieves all GroupContent entities that represent a given entity. * * @param \Drupal\Core\Entity\ContentEntityInterface $entity * An entity which may be within one or more groups. * @param string $plugin_id * (optional) A group relation type ID to filter on. * * @return \Drupal\group\Entity\GroupContentInterface[] * A list of GroupContent entities which refer to the given entity. */ public function loadByEntity(ContentEntityInterface $entity); public function loadByEntity(ContentEntityInterface $entity, $plugin_id = NULL); /** * Retrieves all GroupContent entities by their responsible plugin ID. Loading Loading
group.module +24 −25 Original line number Diff line number Diff line Loading @@ -314,35 +314,34 @@ function group_entity_access(EntityInterface $entity, $operation, AccountInterfa foreach ($plugin_ids as $plugin_id) { $plugin_cache_tags[] = "group_content_list:plugin:$plugin_id"; } // Load all of the group content for this entity. $group_contents = \Drupal::entityTypeManager() ->getStorage('group_content') ->loadByEntity($entity); // If the entity does not belong to any group, we have nothing to say. // // @todo There is a slight performance boost to be had here. If we have a // plugin that adds content without access and one that adds the same content // with access, then this check would fail for content that is only added // using the non-access plugin. The loop over the plugin IDs below would then // not return anything and we would still end up with a neutral result, albeit // with the user.group_permissions context and slightly worse performance. We // can fix this by only loading those group content entities that use the // plugins retrieved above. if (empty($group_contents)) { return AccessResult::neutral()->addCacheTags($plugin_cache_tags); } $access = AccessResult::neutral(); foreach ($plugin_ids as $plugin_id) { $access = AccessResult::neutral()->addCacheTags($plugin_cache_tags); // Reduce the plugin IDs to those that are actually in use. $data_table = \Drupal::entityTypeManager() ->getDefinition('group_content') ->getDataTable(); $plugin_ids_used = \Drupal::database() ->select($data_table, 'd') ->fields('d', ['plugin_id']) ->condition('entity_id', $entity->id()) ->condition('plugin_id', $plugin_ids, 'IN') ->distinct(TRUE) ->execute() ->fetchCol(); // Loop over the plugin handlers and add their access check to the result. foreach ($plugin_ids_used as $plugin_id) { $handler = $plugin_manager->getAccessControlHandler($plugin_id); $access = $access->orIf($handler->entityAccess($entity, $operation, $account, TRUE)); // No need to continue if access is denied. if ($access->isForbidden()) { break; } } return $access ->addCacheTags($plugin_cache_tags) ->addCacheContexts(['user.group_permissions']); return $access; } /** Loading
src/Entity/Group.php +9 −5 Original line number Diff line number Diff line Loading @@ -160,24 +160,28 @@ class Group extends EditorialContentEntityBase implements GroupInterface { /** * {@inheritdoc} */ public function getContent($plugin_id = NULL, $filters = []) { return $this->groupContentStorage()->loadByGroup($this, $plugin_id, $filters); public function getContent($plugin_id = NULL) { return $this->groupContentStorage()->loadByGroup($this, $plugin_id); } /** * {@inheritdoc} */ public function getContentByEntityId($plugin_id, $id) { return $this->getContent($plugin_id, ['entity_id' => $id]); return $this->groupContentStorage()->loadByProperties([ 'gid' => $this->id(), 'plugin_id' => $plugin_id, 'entity_id' => $id, ]); } /** * {@inheritdoc} */ public function getContentEntities($plugin_id = NULL, $filters = []) { public function getContentEntities($plugin_id = NULL) { $entities = []; foreach ($this->getContent($plugin_id, $filters) as $group_content) { foreach ($this->getContent($plugin_id) as $group_content) { $entities[] = $group_content->getEntity(); } Loading
src/Entity/GroupInterface.php +2 −8 Original line number Diff line number Diff line Loading @@ -50,14 +50,11 @@ interface GroupInterface extends ContentEntityInterface, EntityOwnerInterface, E * * @param string $plugin_id * (optional) A group relation type ID to filter on. * @param array $filters * (optional) An associative array of extra filters where the keys are * property or field names and the values are the value to filter on. * * @return \Drupal\group\Entity\GroupContentInterface[] * A list of GroupContent entities matching the criteria. */ public function getContent($plugin_id = NULL, $filters = []); public function getContent($plugin_id = NULL); /** * Retrieves all GroupContent entities for a specific entity. Loading @@ -80,9 +77,6 @@ interface GroupInterface extends ContentEntityInterface, EntityOwnerInterface, E * * @param string $plugin_id * (optional) A group relation type ID to filter on. * @param array $filters * (optional) An associative array of extra filters where the keys are * property or field names and the values are the value to filter on. * * @return \Drupal\Core\Entity\EntityInterface[] * A list of entities matching the criteria. This list does not have keys Loading @@ -90,7 +84,7 @@ interface GroupInterface extends ContentEntityInterface, EntityOwnerInterface, E * * @see \Drupal\group\Entity\GroupInterface::getContent() */ public function getContentEntities($plugin_id = NULL, $filters = []); public function getContentEntities($plugin_id = NULL); /** * Adds a user as a member of the group. Loading
src/Entity/Storage/GroupContentStorage.php +65 −41 Original line number Diff line number Diff line Loading @@ -15,6 +15,13 @@ use Drupal\group\Entity\GroupInterface; */ class GroupContentStorage extends SqlContentEntityStorage implements GroupContentStorageInterface { /** * Static cache for looking up group content entities for groups. * * @var array */ protected $loadByGroupCache = []; /** * Static cache for looking up group content entities for entities. * Loading @@ -22,6 +29,13 @@ class GroupContentStorage extends SqlContentEntityStorage implements GroupConten */ protected $loadByEntityCache = []; /** * Static cache for looking up group content entities for plugins. * * @var array */ protected $loadByPluginCache = []; /** * {@inheritdoc} */ Loading Loading @@ -69,60 +83,62 @@ class GroupContentStorage extends SqlContentEntityStorage implements GroupConten /** * {@inheritdoc} */ public function loadByGroup(GroupInterface $group, $plugin_id = NULL, $filters = []) { public function loadByGroup(GroupInterface $group, $plugin_id = NULL) { // An unsaved group cannot have any content. if ($group->id() === NULL) { throw new EntityStorageException("Cannot load GroupContent entities for an unsaved group."); $group_id = $group->id(); if ($group_id === NULL) { return []; } $properties = ['gid' => $group->id()] + $filters; $cache_key = $plugin_id ?: '---ALL---'; if (!isset($this->loadByGroupCache[$group_id][$cache_key])) { $query = $this->database ->select($this->dataTable, 'd') ->fields('d', ['id']) ->condition('gid', $group_id); // If a plugin ID was provided, set the group content type ID for it. if (isset($plugin_id)) { /** @var \Drupal\group\Entity\Storage\GroupContentTypeStorageInterface $storage */ $storage = $this->entityTypeManager->getStorage('group_content_type'); $properties['type'] = $storage->getGroupContentTypeId($group->bundle(), $plugin_id); if ($plugin_id) { $query->condition('plugin_id', $plugin_id); } $this->loadByGroupCache[$group_id][$cache_key] = $query->execute()->fetchCol(); } return $this->loadByProperties($properties); if (!empty($this->loadByGroupCache[$group_id][$cache_key])) { return $this->loadMultiple($this->loadByGroupCache[$group_id][$cache_key]); } else { return []; } } /** * {@inheritdoc} */ public function loadByEntity(ContentEntityInterface $entity) { public function loadByEntity(ContentEntityInterface $entity, $plugin_id = NULL) { // An unsaved entity cannot have any group content. $entity_id = $entity->id(); if ($entity_id === NULL) { throw new EntityStorageException("Cannot load GroupContent entities for an unsaved entity."); return []; } $entity_type_id = $entity->getEntityTypeId(); if (!isset($this->loadByEntityCache[$entity_type_id][$entity_id])) { /** @var \Drupal\group\Entity\Storage\GroupContentTypeStorageInterface $storage */ $storage = $this->entityTypeManager->getStorage('group_content_type'); $group_content_types = $storage->loadByEntityTypeId($entity_type_id); // Statically cache all group content IDs for the group content types. if (!empty($group_content_types)) { // Use an optimized plain query to avoid the overhead of entity and SQL // query builders. $query = "SELECT id from {{$this->dataTable}} WHERE entity_id = :entity_id AND type IN (:types[])"; $this->loadByEntityCache[$entity_type_id][$entity_id] = $this->database ->query($query, [ ':entity_id' => $entity_id, ':types[]' => array_keys($group_content_types), ]) ->fetchCol(); } // If no responsible group content types were found, we return nothing. else { $this->loadByEntityCache[$entity_type_id][$entity_id] = []; $cache_key = $plugin_id ?: '---ALL---'; if (!isset($this->loadByEntityCache[$entity_type_id][$entity_id][$cache_key])) { $query = $this->database ->select($this->dataTable, 'd') ->fields('d', ['id']) ->condition('entity_id', $entity_id); if ($plugin_id) { $query->condition('plugin_id', $plugin_id); } $this->loadByEntityCache[$entity_type_id][$entity_id][$cache_key] = $query->execute()->fetchCol(); } if (!empty($this->loadByEntityCache[$entity_type_id][$entity_id])) { return $this->loadMultiple($this->loadByEntityCache[$entity_type_id][$entity_id]); if (!empty($this->loadByEntityCache[$entity_type_id][$entity_id][$cache_key])) { return $this->loadMultiple($this->loadByEntityCache[$entity_type_id][$entity_id][$cache_key]); } else { return []; Loading @@ -133,15 +149,21 @@ class GroupContentStorage extends SqlContentEntityStorage implements GroupConten * {@inheritdoc} */ public function loadByPluginId($plugin_id) { // If no responsible group content types were found, we return nothing. /** @var \Drupal\group\Entity\Storage\GroupContentTypeStorageInterface $storage */ $storage = $this->entityTypeManager->getStorage('group_content_type'); $group_content_types = $storage->loadByPluginId($plugin_id); if (empty($group_content_types)) { return []; if (!isset($this->loadByPluginCache[$plugin_id])) { $query = $this->database ->select($this->dataTable, 'd') ->fields('d', ['id']) ->condition('plugin_id', $plugin_id); $this->loadByPluginCache[$plugin_id] = $query->execute()->fetchCol(); } return $this->loadByProperties(['type' => array_keys($group_content_types)]); if (!empty($this->loadByPluginCache[$plugin_id])) { return $this->loadMultiple($this->loadByPluginCache[$plugin_id]); } else { return []; } } /** Loading @@ -149,7 +171,9 @@ class GroupContentStorage extends SqlContentEntityStorage implements GroupConten */ public function resetCache(array $ids = NULL) { parent::resetCache($ids); $this->loadByGroupCache = []; $this->loadByEntityCache = []; $this->loadByPluginCache = []; } }
src/Entity/Storage/GroupContentStorageInterface.php +4 −5 Original line number Diff line number Diff line Loading @@ -35,25 +35,24 @@ interface GroupContentStorageInterface extends ContentEntityStorageInterface { * The group entity to load the group content entities for. * @param string $plugin_id * (optional) A group relation type ID to filter on. * @param array $filters * (optional) An associative array of extra filters where the keys are * property or field names and the values are the value to filter on. * * @return \Drupal\group\Entity\GroupContentInterface[] * A list of GroupContent entities matching the criteria. */ public function loadByGroup(GroupInterface $group, $plugin_id = NULL, $filters = []); public function loadByGroup(GroupInterface $group, $plugin_id = NULL); /** * Retrieves all GroupContent entities that represent a given entity. * * @param \Drupal\Core\Entity\ContentEntityInterface $entity * An entity which may be within one or more groups. * @param string $plugin_id * (optional) A group relation type ID to filter on. * * @return \Drupal\group\Entity\GroupContentInterface[] * A list of GroupContent entities which refer to the given entity. */ public function loadByEntity(ContentEntityInterface $entity); public function loadByEntity(ContentEntityInterface $entity, $plugin_id = NULL); /** * Retrieves all GroupContent entities by their responsible plugin ID. Loading