Upgrading from v1 to v2

Last updated on
12 August 2025

This documentation needs review. See "Help improve this page" in the sidebar.

General information

Group module 2.0 is almost there and it provides a quite a lot of new changes. A new version contains many API changes and new features In this guide we will review the main steps to be prepared for the upgrade.

For more details please consult the next pages:

Release notes

and

Change records

Group 2.0 now depends on the flexible_permissions module, which lets us use a new central service for permission handling instead of relying on Entity API anymore. This makes permission handling much faster as we don't need to alter queries anymore.

The system of plugins has been renamed and reworked.

The roles system has been updated: by default Group no longer provides any roles, and we can remove any role we want.

Config entities can now be part of a group without the need for custom patches!

Access checks are reworked and Group will handle access only to known operations. So, if you have a custom operation, Group will no longer automatically forbid it.

Group relation plugin

First you need to rename and to move your plugin to a new location. Content enabler plugin was renamed to Group relation plugin. Now plugin is  located in src/Plugin/Group/Relation/[YourRelationPlugin].php

In Group 2.0 plugin id will stay "group_content", but in Group 3.0 the plugin id will be replaced with "group_relationship"

Group relation plugin uses handlers to provide it's main features. The most of methods of Group enabler plugin were moved to separate handler classes. Each handler can be defined as a service, but we need to follow specific naming convention.

group.relation_handler.[HANDLER_TYPE].[YOUR_PLUGIN_ID]

an example for grequest module defining entity reference handler service:
 

  group.relation_handler.entity_reference.group_membership_request:
    class: 'Drupal\grequest\Plugin\Group\RelationHandler\GroupMembershipRequestEntityReference'
    arguments: [ '@group.relation_handler.entity_reference' ]
    shared: false

* shared: false means that every time a new instance of handler will be created. It is requirement to make a plugin work with it's own instance of the handler. As of Group 2.3.1 and 3.3.1 you no longer need to specify this yourself.

The Groups module comes with the following handlers:

  • access_control
  • entity_reference
  • operation_provider
  • permission_provider
  • post_install
  • ui_text_provider

Group enabler plugin handles this functionality in the methods:

Group Relation Handler (Group 2.0) Group enabler plugin methods (1.0)
access_control checkAccess
entity_reference getEntityReferenceSettings
operation_provider
getOperations
permission_provider permission provider is defined as separate class
post_install
postInstall
ui_text_provider

If you have a custom logic in this methods please consider to move them to a separate handler class.

All handlers services are located in src/Plugin/Group/RelationHandler/

Keep in mind, by default Group module already has default handlers and we need to define a new one only in the situation when we need to override default behavior in other cases default one will work just perfect and you don't need to do anything.

More information about handlers can be found in this Presentation

Database structure

The table structure of group content tables has been updated, but you do not need to do anything here. The update hooks will update the table for you. This is only as a reference for developers who used to target the database tables directly.
 

Group 1.0 - group_content_field_data table

CREATE TABLE `group_content_field_data` (
  `id` int(10) UNSIGNED NOT NULL,
  `type` varchar(32) CHARACTER SET ascii NOT NULL COMMENT 'The ID of the target entity.',
  `langcode` varchar(12) CHARACTER SET ascii NOT NULL,
  `gid` int(10) UNSIGNED DEFAULT NULL COMMENT 'The ID of the target entity.',
  `entity_id` int(10) UNSIGNED DEFAULT NULL COMMENT 'The ID of the target entity.',
  `label` varchar(255) DEFAULT NULL,
  `uid` int(10) UNSIGNED DEFAULT NULL COMMENT 'The ID of the target entity.',
  `created` int(11) DEFAULT NULL,
  `changed` int(11) DEFAULT NULL,
  `default_langcode` tinyint(4) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='The data table for group_content entities.';

Group 2.0 - group_relationship_field_data

CREATE TABLE `group_relationship_field_data` (
  `id` int(10) UNSIGNED NOT NULL,
  `type` varchar(32) CHARACTER SET ascii NOT NULL COMMENT 'The ID of the target entity.',
  `langcode` varchar(12) CHARACTER SET ascii NOT NULL,
  `uid` int(10) UNSIGNED NOT NULL COMMENT 'The ID of the target entity.',
  `gid` int(10) UNSIGNED NOT NULL COMMENT 'The ID of the target entity.',
  `entity_id` int(10) UNSIGNED NOT NULL COMMENT 'The ID of the target entity.',
  `label` varchar(255) DEFAULT NULL,
  `created` int(11) DEFAULT NULL,
  `changed` int(11) DEFAULT NULL,
  `plugin_id` varchar(64) NOT NULL,
  `group_type` varchar(32) CHARACTER SET ascii DEFAULT NULL COMMENT 'The ID of the target entity.',
  `default_langcode` tinyint(4) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='The data table for group_content entities.';

* if you create group relationship (former group content) programmatically you do not have to add group_type and plugin_id manually, this will be filled out for you based on the provided bundle.

API changes

Class name Group 1.0 Class name Group 2.0
Drupal\group\Plugin\
GroupContentEnablerInterface
Drupal\group\Plugin\Group\Relation\
GroupRelationInterface
Drupal\group\Plugin\
GroupContentEnablerBase
Drupal\group\Plugin\Group\Relation\
GroupRelationBase
Drupal\group\Entity\
GroupContent
Drupal\group\Entity\
GroupRelationship

Drupal\group\Entity\
GroupContentInterface
Drupal\group\Entity\GroupRelationshipInterface
Drupal\group\Entity\
GroupContentType
Drupal\group\Entity\
GroupRelationshipType
Drupal\group\Entity\
GroupContentTypeInterface
Drupal\group\Entity\
GroupRelationshipTypeInterface
Drupal\group\Entity\Storage\
GroupContentStorage
Drupal\group\Entity\Storage\
GroupRelationshipStorage
Drupal\group\Entity\Storage\
GroupContentStorageInterface
Drupal\group\Entity\Storage\
GroupRelationshipStorageInterface
Drupal\group\Entity\Storage\
GroupContentTypeStorage
Drupal\group\Entity\Storage\
GroupRelationshipTypeStorage
Drupal\group\Entity\Storage\
GroupContentTypeStorageInterface
Drupal\group\Entity\Storage\
GroupRelationshipTypeStorageInterface

* In the table above presented the most used classes in the custom modules. The rest of entity related classes can be renamed by replacing GroupContent to GroupRelationship

GroupRelationshipType (GroupContentType in 1.0)

method Group 1.0 method Group 2.0
loadByContentPluginId
loadByPluginId
updateContentPlugin
updatePlugin

GroupRelationshipStorage (GroupContentStorage in 1.0) 

method Group 1.0 method Group 2.0
loadByContentPluginId
loadByPluginId
public function loadByGroup(GroupInterface $group, $plugin_id = NULL, $filters = []);
public function loadByGroup(GroupInterface $group, $plugin_id = NULL);

Additional filters are not supported anymore. Use loadByEntityAndGroup instead to filter by a specific entity.

GroupRelationshipTypeStorage (GroupContentTypeStorage in 1.0) 

method Group 1.0 method Group 2.0
loadByContentPluginId
loadByPluginId

GroupRelationBase (GroupContentEnablerBase in 1.0) 

method Group 1.0 method Group 2.0
getContentTypeLabel
use GroupRelationType->getLabel instead
checkAccess
removed and handled in access_control handler
createAccess
removed and handled in access_control handler
createEntityAccess
removed and handled in access_control handler
getContentLabel
removed and handled in ui_text_provider handler
getContentTypeConfigId
removed and handled in relationship type storage
getContentTypeDescription
removed and handled in ui_text_provider handler
getOperations
removed and handled in operation_provider relation handler
postInstall
removed and handled in post_install relation handler
getEntityReferenceSettings
removed and handled in entity_reference relation handler
getEntityBundle
moved to GroupRelationType class
getGroupOperations
removed and handled in operation_provider relation handler
getEntityTypeId
moved to GroupRelationType class
definesEntityAccess
moved to GroupRelationType class
getDescription moved to GroupRelationType class
getEntityReferenceLabel
moved to GroupRelationType class
getEntityReferenceDescription
moved to GroupRelationType class
getEntityType
removed!
getGroupOperationsCacheableMetadata
removed and handled in operation_provider relation handler
getPermissions
removed and handled in permission_provider handler
getPrettyPathKey
moved to GroupRelationType class
getProvider
moved to GroupRelationType class
getTargetEntityPermissions
removed!
isCodeOnly
moved to GroupRelationType class
isEnforced
moved to GroupRelationType class
updateAccess
removed!
viewAccess
removed!

GroupRelationshipType (GroupContentType in 1.0) 

method Group 1.0 method Group 2.0
getContentEnablerManager
getGroupRelationTypeManager
getContentPlugin
getPlugin
getContentPluginId
getPluginId
loadByContentPluginId
loadByPluginId
updateContentPlugin
updatePlugin

Group

method Group 1.0 method Group 2.0
getContent
getRelationships

Additional filters are not supported anymore. Use getRelationshipsByEntity instead to filter by a specific entity.

getContentByEntityId
getRelationshipsByEntity
getContentEntities
getRelatedEntities
addContent
addRelationship

GroupType

method Group 1.0 method Group 2.0
getContentEnablerManager
getGroupRelationTypeManager
getContentPlugin
getPlugin
hasContentPlugin
hasPlugin
getInstalledContentPlugins
getInstalledPlugins
getAnonymousRole
removed!
getAnonymousRoleId
removed!
getOutsiderRole
removed!
getOutsiderRoleId
removed!
getMemberRole
removed!
getMemberRoleId
removed!
getGroupRoleSynchronizer removed!

GroupRelationship (GroupContent in 1.0) 

method Group 1.0 method Group 2.0
getContentPlugin
getPluginId
loadByContentPluginId
loadByPluginId
getGroupContentType
getRelationshipType

GroupRelationTypeManager (GroupContentEnablerManager in 1.0) 

method Group 1.0 method Group 2.0
getInstalled
GroupTypeInterface parameter is not optional anymore
getInstalledIds
GroupTypeInterface parameter is not optional anymore
hasHandler
Removed 
getGroupContentTypeStorage
getRelationshipTypeStorage
getAll
Removed 
getVanillaInstalled
Removed 
getGroupTypeInstalled
Removed 
getGroupContentTypeIds
getRelationshipTypeIds
getPluginGroupContentTypeMap
getPluginGroupRelationshipTypeMap
getCachedPluginGroupContentTypeMap
getCachedPluginGroupRelationshipTypeMap
setCachedPluginGroupContentTypeMap
setCachedPluginGroupRelationshipTypeMap

Tags

Help improve this page

Page status: Needs review

You can: