Skip to content

PHP API

When to Use

Reference this when writing PHP code to create groups, add members, relate content, or query group data programmatically.

Decision

Task Method Notes
Create group Group::create([...])->save() Creator membership only auto-added via form submissions in 4.x
Add member with role $group->addMember($account, ['group_roles' => ['project-editor']]) Pass roles explicitly for programmatic calls
Load membership GroupMembership::loadSingle($group, $account) Cached via chained cache
Add content to group $group->addRelationship($node, 'group_node:article') Both group and entity must be saved first
Load relationships $storage->loadByGroup($group, 'group_node:article') Use GroupRelationshipStorageInterface
Load groups for entity $storage->loadByEntity($node) then $r->getGroup()
Load members with role filter GroupMembership::loadByGroup($group, ['project-editor']) 4.x: filter must be an array

Pattern

use Drupal\group\Entity\Group;
use Drupal\group\Entity\GroupMembership;

// Create a group. In 4.x, creator_membership is form-only —
// call addMember() explicitly when creating groups programmatically.
$group = Group::create(['type' => 'project', 'label' => 'My Project']);
$group->save();
$group->addMember($account, ['group_roles' => ['project-manager']]);

// Add content to the group.
$relationship = $group->addRelationship($node, 'group_node:article');

// Query all relationships in a group for a specific plugin.
$storage = \Drupal::entityTypeManager()->getStorage('group_relationship');
$articles = $storage->loadByGroup($group, 'group_node:article');

// Load all groups a node belongs to.
$relationships = $storage->loadByEntity($node);
$groups = array_map(fn($r) => $r->getGroup(), $relationships);

Injecting services:

use Drupal\group\Access\GroupPermissionCheckerInterface;

class MyService {
  public function __construct(
    protected GroupPermissionCheckerInterface $permissionChecker,
  ) {}
}

# services.yml
mymodule.my_service:
  arguments: ['@group_permission.checker']

Common Mistakes

  • Wrong: Calling $group->addRelationship() on an unsaved group → Right: Both the group and entity must be saved (have IDs) first.
  • Wrong: Using the group.membership_loader service → Right: Removed in 4.0.0 (deprecated since 3.2.0). Use GroupMembership::loadSingle(), ::loadByGroup(), or ::loadByUser().
  • Wrong: Calling Group::save() programmatically and expecting the creator membership to be created automatically → Right: In 4.x, creator_membership is applied only when the group is created through a form. Call $group->addMember() explicitly in programmatic code.
  • Wrong: Passing a string to the $roles filter: loadByGroup($group, 'project-editor')Right: In 4.x the filter must be an array: loadByGroup($group, ['project-editor']).
  • Wrong: Expecting hook_ENTITY_TYPE_update() to fire on the entity when adding it to a group → Right: In 4.x, adding an entity to a group no longer re-saves the entity. Only its cache tags are invalidated.
  • Wrong: Using accessCheck(TRUE) in background/admin code → Right: Use accessCheck(FALSE); Group's query access is expensive and context-dependent.

See Also