diff --git a/og.install b/og.install index 435590ba3..d657d4709 100644 --- a/og.install +++ b/og.install @@ -5,6 +5,8 @@ * Install, update, and uninstall functions for the Organic groups module. */ +use Drupal\Core\Field\BaseFieldDefinition; + /** * Implements hook_uninstall(). */ @@ -12,3 +14,61 @@ function og_uninstall() { \Drupal::queue('og_orphaned_group_content')->deleteQueue(); \Drupal::queue('og_orphaned_group_content_cron')->deleteQueue(); } + +/** + * Add a base field to store the group bundle in memberships. + */ +function og_update_8001(&$sandbox) { + $storage = \Drupal::entityTypeManager()->getStorage('og_membership'); + + if (!isset($sandbox['total'])) { + $storage_definition = BaseFieldDefinition::create('string') + ->setLabel(t('Group bundle ID')) + ->setDescription(t('The bundle ID of the group.')); + \Drupal::entityDefinitionUpdateManager()->installFieldStorageDefinition('entity_bundle', 'og_membership', 'og', $storage_definition); + + $sandbox['#finished'] = 0; + $sandbox['current'] = 0; + $sandbox['total'] = $storage->getQuery()->count()->execute(); + $sandbox['batch_size'] = 500; + } + + // Update the existing memberships to include the group bundle ID. + $membership_ids = $storage->getQuery() + ->range($sandbox['current'], $sandbox['batch_size']) + ->sort('id') + ->execute(); + + /** @var \Drupal\og\Entity\OgMembership $membership */ + foreach ($storage->loadMultiple($membership_ids) as $membership) { + $group = $membership->getGroup(); + if (!empty($group)) { + $membership->set('entity_bundle', $group->bundle()); + $membership->save(); + } + else { + // The membership is for a group that no longer exists. We cannot no + // longer retrieve the group bundle ID so the membership cannot be + // updated. Delete the membership since it is invalid, and inform the + // user. + \Drupal::logger('og')->warning('Deleted orphaned membership with ID @id since the group it refers to no longer exists.', [ + '@id' => $membership->id(), + ]); + $membership->delete(); + } + } + + $sandbox['current'] += $sandbox['batch_size']; + if ($sandbox['current'] >= $sandbox['total']) { + $sandbox['current'] = $sandbox['total']; + } + $sandbox['#finished'] = $sandbox['current'] / $sandbox['total']; + + $message = t('Processed @current of @total memberships (@percentage% complete)', [ + '@current' => $sandbox['current'], + '@total' => $sandbox['total'], + '@percentage' => number_format($sandbox['#finished'] * 100, 2), + ]); + + return $message; +} diff --git a/src/Entity/OgMembership.php b/src/Entity/OgMembership.php index 99cf326f1..c9ccf9788 100644 --- a/src/Entity/OgMembership.php +++ b/src/Entity/OgMembership.php @@ -126,6 +126,7 @@ public function setOwnerId($uid) { */ public function setGroup(EntityInterface $group) { $this->set('entity_type', $group->getEntityTypeId()); + $this->set('entity_bundle', $group->bundle()); $this->set('entity_id', $group->id()); return $this; } @@ -137,6 +138,13 @@ public function getGroupEntityType() { return $this->get('entity_type')->value; } + /** + * {@inheritdoc} + */ + public function getGroupBundle() { + return $this->get('entity_bundle')->value; + } + /** * {@inheritdoc} */ @@ -144,6 +152,27 @@ public function getGroupId() { return $this->get('entity_id')->value; } + /** + * Checks if a group has already been populated on the membership. + * + * The group is required for a membership, so it is always present if a + * membership has been saved. This is intended for internal use to verify if + * a group is present when methods are called on a membership that is possibly + * still under construction. + * + * For performance reasons this avoids loading the full group entity just for + * this purpose, and relies only on the fact that the data for the entity is + * populated in the relevant fields. This should give us the same indication, + * but with a lower performance cost, especially for users that are a member + * of a large number of groups. + * + * @return bool + * Whether or not the group is already present. + */ + protected function hasGroup() { + return !empty($this->get('entity_type')->value) && !empty($this->get('entity_bundle')->value) && !empty($this->get('entity_id')->value); + } + /** * {@inheritdoc} */ @@ -223,9 +252,9 @@ public function getRoles() { // Add the member role. This is only possible if a group has been set on the // membership. - if ($group = $this->getGroup()) { + if ($this->hasGroup()) { $roles = [ - OgRole::getRole($this->getGroupEntityType(), $group->bundle(), OgRoleInterface::AUTHENTICATED), + OgRole::getRole($this->getGroupEntityType(), $this->getGroupBundle(), OgRoleInterface::AUTHENTICATED), ]; } $roles = array_merge($roles, $this->get('roles')->referencedEntities()); @@ -335,9 +364,13 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { ->setLabel(t('Group entity type')) ->setDescription(t('The entity type of the group.')); + $fields['entity_bundle'] = BaseFieldDefinition::create('string') + ->setLabel(t('Group bundle ID')) + ->setDescription(t('The bundle ID of the group.')); + $fields['entity_id'] = BaseFieldDefinition::create('string') - ->setLabel(t('Group entity id')) - ->setDescription(t("The entity ID of the group.")); + ->setLabel(t('Group entity ID')) + ->setDescription(t('The entity ID of the group.')); $fields['state'] = BaseFieldDefinition::create('string') ->setLabel(t('State')) diff --git a/src/OgMembershipInterface.php b/src/OgMembershipInterface.php index b96f1d269..cecd1808b 100644 --- a/src/OgMembershipInterface.php +++ b/src/OgMembershipInterface.php @@ -95,6 +95,14 @@ public function getGroup(); */ public function getGroupEntityType(); + /** + * Gets the group entity bundle. + * + * @return string + * The bundle. + */ + public function getGroupBundle(); + /** * Gets the group entity ID. * diff --git a/tests/src/Kernel/Entity/OgMembershipTest.php b/tests/src/Kernel/Entity/OgMembershipTest.php index 0a6c5b4f8..e238d2528 100644 --- a/tests/src/Kernel/Entity/OgMembershipTest.php +++ b/tests/src/Kernel/Entity/OgMembershipTest.php @@ -470,6 +470,24 @@ public function testGetGroup() { $this->assertEquals($this->group->id(), $membership->getGroup()->id()); } + /** + * Tests getting the bundle of the group that is associated with a membership. + * + * @covers ::getGroupBundle + */ + public function testGetGroupBundle() { + $membership = OgMembership::create(); + + // When no group has been set yet, the method should return NULL. + $this->assertNull($membership->getGroupBundle()); + + // Set a group. + $membership->setGroup($this->group); + + // Now the group bundle should be returned. + $this->assertEquals($this->group->bundle(), $membership->getGroupBundle()); + } + /** * Tests that membership has "member" role when roles are retrieved. *