Skip to content

Common Mistakes

When to Use

Review this before shipping group-related code to catch frequent errors.

Decision

Category Mistake Why it fails
Architecture Group type IDs longer than 22 characters Role IDs append suffixes like -anonymous and must stay under 32 characters
Architecture Expecting all content to be scoped immediately after install Group only restricts entities that ARE in a group with entity_access: TRUE
Architecture No config schema for custom defaultConfiguration() keys Config import/export fails validation
Access Using AccessResult::allowedIfHasPermission() for group permissions Checks global permissions, not group-level
Access Missing user.group_permissions context in permission-checking blocks All users see same cached output — security leak
Access entity_access: FALSE (default) on plugin but expecting group access control Group never calls entityAccess() without entity_access: TRUE
API Using group.membership_loader service Removed in 4.0.0 (deprecated since 3.2.0)
API Calling $group->addRelationship() before saving the group Throws EntityStorageException: "Cannot add an entity to an unsaved group."
API Loading members with entity storage in a loop Each call fires a SQL query; use GroupMembership::loadByGroup() which uses the cache
Plugin Handler services without shared: false Each handler instance is tied to a specific plugin ID — must be unshared
Plugin Not calling clearCachedDefinitions() when a bundle is added New bundle plugins won't appear until plugin cache is cleared
Views Relying on Group auto-filtering for group_relationship_field_data Views Only entity-based Views get automatic access filtering

Architecture Mistakes

Mistake: Installing Group and expecting all content to be immediately scoped by groups. Why it fails: Group only restricts access to entities that ARE in a group (via a plugin with entity_access: TRUE). Ungrouped entities are unaffected. You must add entities to groups for access control to apply.

Mistake: Creating group types with IDs longer than 22 characters. Why it fails: GroupTypeInterface::ID_MAX_LENGTH = 22. Group role IDs append suffixes like -anonymous and must stay under 32 characters. Longer group type IDs break role creation.

Mistake: Defining a GroupRelationType plugin without a config schema entry for each custom defaultConfiguration() key. Why it fails: Config system validation fails on config import/export. Follow the group_relation.config.{KEY} schema pattern.

Access Mistakes

Mistake: Using AccessResult::allowedIfHasPermission($account, 'some group permission') for group permissions. Why it fails: This checks global Drupal permissions. Use GroupAccessResult::allowedIfHasGroupPermission($group, $account, 'some group permission') for group-scoped permission checks.

Mistake: Building render arrays that depend on group permissions without user.group_permissions cache context. Why it fails: Users with different group permissions see the same cached output, causing security leaks or broken UI.

Mistake: Not checking entity_access: TRUE on your plugin when you expect Group to restrict access to that entity type. Why it fails: Without entity_access: TRUE, Group never calls entityAccess() and never forbids access to the entity. The entity remains governed by Drupal's default access system.

Mistake: Using admin: true on a group role without careful thought. Why it fails: Admin roles bypass ALL permission checks. Assign to trusted users only.

API Mistakes

Mistake: Using the group.membership_loader service. Why it fails: Removed in 4.0.0 (deprecated since 3.2.0). Use the GroupMembership::loadSingle(), ::loadByGroup(), ::loadByUser() static methods.

Mistake: Calling $group->addRelationship() before saving the group. Why it fails: Throws EntityStorageException: "Cannot add an entity to an unsaved group." Both group and entity must be saved first.

Mistake: Loading group members with \Drupal::entityTypeManager()->getStorage('group_relationship')->loadByGroup($group, 'group_membership') in a loop. Why it fails: Each call fires a SQL query. Use GroupMembership::loadByGroup($group) which uses the chained cache.

Plugin Mistakes

Mistake: Registering handler services without shared: false. Why it fails: Handler services must be unshared (shared: false in services.yml) because each handler instance is tied to a specific plugin ID.

Mistake: Forgetting to call clearCachedDefinitions() on the plugin manager when a bundle is added/removed. Why it fails: Derived plugins (like group_node:{bundle}) are cached. New bundles will not appear as available plugins until the cache is cleared.

Views Mistakes

Mistake: Relying on Group's automatic access filtering for a View based on group_relationship_field_data. Why it fails: Group only auto-filters Views based on entity base tables (node, media, etc.). Views on group_relationship_field_data directly are NOT access-filtered.

See Also

All other sections — common mistakes are duplicated in context within each section.