Skip to the content.

← Back to Activities Plugin

5.6.6 Activity Groups Entity Reference

Last Updated: December 3, 2025
Status: Complete
Scope: Activities Plugin - ActivityGroup Entity

Comprehensive technical reference for the ActivityGroup entity, covering data model, relationships, field definitions, and integration patterns with the Activities system.

Table of Contents

ActivityGroup Entity Overview

The ActivityGroup entity provides categorical organization for related activities within the Activities plugin. Activity groups serve as logical containers for activities, enabling administrative organization by type, department, skill level, or other meaningful categorizations.

Core Responsibilities:

Location: plugins/Activities/src/Model/Entity/ActivityGroup.php
Extends: App\Model\Entity\BaseEntity
Table Class: Activities\Model\Table\ActivityGroupsTable

Database Schema

Table: activities_activity_groups

The ActivityGroup entity maps to the activities_activity_groups database table with the following structure:

Column Type Null Default Notes
id INT(11) NO Auto Primary key, auto-incrementing
name VARCHAR(255) NO   Unique activity group identifier
created DATETIME NO   Creation timestamp
created_by INT(11) YES NULL Creator member ID
modified DATETIME YES NULL Last modification timestamp
modified_by INT(11) YES NULL Last modifier member ID
deleted DATETIME YES NULL Soft deletion timestamp

Indexes:

Special Features:

Entity Properties

Core Properties

id

name

Audit Trail Properties

created

created_by

modified

modified_by

deleted

Relationships

Child Relationships (hasMany)

activities

Example Query:

$groupWithActivities = $activityGroupsTable->find()
    ->contain(['Activities'])
    ->where(['ActivityGroups.id' => $groupId])
    ->first();

foreach ($groupWithActivities->activities as $activity) {
    echo $activity->name;
}

Mass Assignment Security

The ActivityGroup entity uses CakePHP’s accessible field system to prevent mass assignment vulnerabilities.

Accessible Fields

The following fields can be safely mass assigned through newEntity() or patchEntity():

Protected Fields

The following fields are NOT accessible via mass assignment:

Usage Examples

Creating Activity Groups

// Create a new activity group for combat activities
$combatGroup = $activityGroupsTable->newEntity([
    'name' => 'Combat Activities'
]);
$activityGroupsTable->save($combatGroup);

// Create a group for administrative activities
$adminGroup = $activityGroupsTable->newEntity([
    'name' => 'Administrative Activities'
]);
$activityGroupsTable->save($adminGroup);

// Create a group for arts and sciences
$artsGroup = $activityGroupsTable->newEntity([
    'name' => 'Arts & Sciences'
]);
$activityGroupsTable->save($artsGroup);

Organizing Activities by Group

// Assign activities to groups during creation
$activity = $activitiesTable->newEntity([
    'name' => 'Heavy Weapons Combat',
    'activity_group_id' => $combatGroup->id,
    'description' => 'Authorization for heavy weapons combat activities'
]);

// Query activities by group
$combatActivities = $activitiesTable->find()
    ->where(['activity_group_id' => $combatGroup->id])
    ->all();

// Display activities in group
foreach ($combatActivities as $activity) {
    echo "- {$activity->name}\n";
}

Querying Groups with Activities

// Load group with all associated activities
$groupWithActivities = $activityGroupsTable->find()
    ->contain(['Activities' => [
        'sort' => ['Activities.name' => 'ASC']
    ]])
    ->where(['ActivityGroups.id' => $groupId])
    ->first();

// Display group and activities
echo "Group: {$groupWithActivities->name}\n";
foreach ($groupWithActivities->activities as $activity) {
    echo "  - {$activity->name}\n";
}

Administrative Group Management

// Get all groups with activity counts
$groupsWithStats = $activityGroupsTable->find()
    ->contain(['Activities'])
    ->orderBy(['ActivityGroups.name' => 'ASC'])
    ->all()
    ->map(function($group) {
        return [
            'group' => $group,
            'activity_count' => count($group->activities)
        ];
    });

// Display group statistics
foreach ($groupsWithStats as $item) {
    echo "{$item['group']->name}: {$item['activity_count']} activities\n";
}

Reorganizing Activities Between Groups

// Move all activities from one group to another
$sourceGroup = $activityGroupsTable->get($sourceGroupId, ['contain' => ['Activities']]);
$targetGroupId = $newGroupId;

foreach ($sourceGroup->activities as $activity) {
    $activity->activity_group_id = $targetGroupId;
    $activitiesTable->save($activity);
}

Group-Based Navigation Structure

// Build navigation menu organized by activity groups
$navigationGroups = $activityGroupsTable->find()
    ->contain(['Activities' => [
        'conditions' => ['Activities.deleted IS' => null],
        'sort' => ['Activities.name' => 'ASC']
    ]])
    ->where(['ActivityGroups.deleted IS' => null])
    ->orderBy(['ActivityGroups.name' => 'ASC'])
    ->all();

$menuStructure = [];
foreach ($navigationGroups as $group) {
    $menuStructure[$group->name] = [];
    foreach ($group->activities as $activity) {
        $menuStructure[$group->name][] = [
            'title' => $activity->name,
            'url' => ['plugin' => 'Activities', 'controller' => 'Activities', 'action' => 'view', $activity->id]
        ];
    }
}

Group-Based Reporting

// Generate activity participation report by group
$participationByGroup = $activityGroupsTable->find()
    ->contain(['Activities' => [
        'contain' => ['CurrentAuthorizations']
    ]])
    ->orderBy(['ActivityGroups.name' => 'ASC'])
    ->all()
    ->map(function($group) {
        $totalAuthorizations = 0;
        foreach ($group->activities as $activity) {
            $totalAuthorizations += count($activity->current_authorizations ?? []);
        }
        return [
            'group_name' => $group->name,
            'activity_count' => count($group->activities),
            'authorization_count' => $totalAuthorizations
        ];
    });

// Display report
foreach ($participationByGroup as $item) {
    echo "{$item['group_name']}: ";
    echo "{$item['activity_count']} activities, ";
    echo "{$item['authorization_count']} authorizations\n";
}

Bulk Operations

// Update multiple groups
$updates = [
    ['id' => 1, 'name' => 'Combat & Martial Activities'],
    ['id' => 2, 'name' => 'Administrative & Service Activities'],
    ['id' => 3, 'name' => 'Arts, Sciences & Research']
];

foreach ($updates as $data) {
    $group = $activityGroupsTable->get($data['id']);
    $group = $activityGroupsTable->patchEntity($group, $data);
    $activityGroupsTable->save($group);
}

// Soft delete unused groups (preserving history)
$unusedGroups = $activityGroupsTable->find()
    ->leftJoinWith('Activities')
    ->where(['Activities.id IS' => null])
    ->all();

foreach ($unusedGroups as $group) {
    $activityGroupsTable->delete($group); // Soft delete via Trash behavior
}

Integration Points

Activities Management

Administrative Interface

Reporting System

Search and Filtering

Performance Considerations

Query Optimization

Efficient Loading:

Example - Efficient Query:

// Good: Single query with eager loading
$groupsWithActivities = $activityGroupsTable->find()
    ->contain(['Activities'])
    ->all();

Example - Inefficient Query:

// Bad: N+1 queries - one per group
$groups = $activityGroupsTable->find()->all();
foreach ($groups as $group) {
    $activities = $group->activities; // Creates query for each group
}

Index Strategy

Caching Opportunities

Schema Simplicity

Architecture Notes

BaseEntity Inheritance

ActivityGroup extends App\Model\Entity\BaseEntity providing:

Audit Trail Features:

Soft Deletion:

Branch Scoping:

Validation Framework

Validation is enforced through ActivityGroupsTable::validationDefault():

Field Validation:

Validation Triggers:

Future Extensibility

The simple entity structure supports future enhancements:

Hierarchical Groups: Simple structure allows nested group implementation Group Permissions: Framework ready for group-level authorization features Advanced Categorization: Support for tags, metadata, and complex organization Group Workflows: Ready for group-specific management workflows


Source Files