Membersactivities framework technical guide

14 Sep 2026 - dirkvm


MembersActivities Framework 1.0.31 — Technical Guide

MembersActivities Framework 1.0.31

Technical Guide & Object Model Reference
FrameworkMembersActivities 1.0.31
Controller Framework1.0.31
PHP8.3+
DatabaseMySQL / MariaDB via PDO
AuthorDirk Van Meirvenne

Contents

  1. Scope and Architecture
  2. Package Structure
  3. Architectural Patterns
  4. Object Model Overview
  5. Activity Model
  6. Costitem Model
  7. Member Model
  8. Subscription Model
  9. Payment Model
  10. Google Wallet Model
  11. Mapper Layer
  12. Command Layer
  13. Command Decorators
  14. Strategy and Type Implementations
  15. Subscription Validation
  16. Request / Response Flow
  17. Security Architecture
  18. Payment and Mollie Flow
  19. Database Model
  20. Transactions and Consistency
  21. Mail Queue
  22. Extension Guide
  23. API / Class Reference
  24. Technical Review Checklist

1. Scope and Architecture

MembersActivities Framework 1.0.31 is a reusable domain framework for applications managing members, activities, cost items, subscriptions and payments. It is implemented as a Composer package and extends the Controller Framework.

┌─────────────────────────────────────────────────────┐ │ Client Application │ │ │ │ Commands · Decorators · Views · Client Models │ │ Type Implementations · Validation Strategies │ └──────────────────────────┬──────────────────────────┘ │ ┌──────────────────────────▼──────────────────────────┐ │ MembersActivities Framework 1.0.31 │ │ │ │ Activities · Costitems · Subscriptions · Payments │ │ Mappers · Commands · Mollie · Google Wallet │ └──────────────────────────┬──────────────────────────┘ │ ┌──────────────────────────▼──────────────────────────┐ │ Controller Framework 1.0.31 │ │ │ │ Command · Request · DomainObject · Mapper │ │ Registry · Sessions · CSRF · AccessToken │ │ ErrorHandler · Rendering · Audit │ └─────────────────────────────────────────────────────┘

The Composer package declares PHP ^8.3, samoscon/controller-framework ^1.0.31 and mollie/mollie-api-php ^2.0. Mollie is therefore a package dependency, although the payment integration is optional at application level.

2. Package Structure

src/
├── commands/
│   ├── DefaultCommand.php
│   ├── admin/
│   │   ├── AddActivityToCompositeCommand.php
│   │   ├── AddMemberToCompositeCommand.php
│   │   ├── AdminHomeCommand.php
│   │   ├── CreateActivityCommand.php
│   │   ├── CreateCostitemCommand.php
│   │   ├── CreateMemberCommand.php
│   │   ├── DeleteActivityCommand.php
│   │   ├── DeleteCostitemCommand.php
│   │   ├── DeleteMemberCommand.php
│   │   ├── DeletePaymentCommand.php
│   │   ├── EditActivityCommand.php
│   │   ├── EditCostitemCommand.php
│   │   ├── EditMemberCommand.php
│   │   ├── EditPaymentCommand.php
│   │   ├── RemoveActivityFromCompositeCommand.php
│   │   ├── RemoveMemberFromCompositeCommand.php
│   │   └── SearchMembersCommand.php
│   ├── downloads/
│   │   ├── DownloadXlsMembersCommand.php
│   │   └── DownloadXlsParticipantsCommand.php
│   ├── mollie/
│   │   ├── OrderToMollieCommand.php
│   │   ├── PaymentToMollieCommand.php
│   │   └── WebhookFromMollieCommand.php
│   └── user/
│       ├── ActivityCommand.php
│       ├── CreatePaymentCommand.php
│       ├── PaymentConfirmationCommand.php
│       ├── PublicActivityCommand.php
│       └── UserActivityCommand.php
│
├── model/
│   ├── activities/
│   │   ├── Activity.php
│   │   ├── ActivityComposite.php
│   │   ├── ActivityMapper.php
│   │   ├── ActivityTypeImplementation.php
│   │   ├── Costitem.php
│   │   ├── CostitemMapper.php
│   │   └── CostitemTypeImplementation.php
│   ├── subscriptions/
│   │   ├── Payment.php
│   │   ├── PaymentMapper.php
│   │   ├── PaymentTypeImplementation.php
│   │   ├── Subscription.php
│   │   ├── SubscriptionMapper.php
│   │   ├── SubscriptionTypeImplementation.php
│   │   └── SubscriptionValidationStrategy.php
│   └── wallet/
│       └── GoogleWalletTicket.php

3. Architectural Patterns

PatternWhere usedPurpose
Active Record / Domain ObjectActivity, Costitem, Payment, Subscription; Member comes from Controller FrameworkDomain objects represent persisted application entities.
Data MapperActivityMapper, CostitemMapper, PaymentMapper, SubscriptionMapperSeparates persistence operations from domain objects.
Abstract FactorygetInstance() implementationsCreates concrete client model objects from database rows.
Builder / Type implementationActivityTypeImplementation, CostitemTypeImplementation, PaymentTypeImplementation, SubscriptionTypeImplementationAssociates a domain object with classification-specific behaviour.
StrategySubscriptionValidationStrategyAllows client applications to define subscription rules independently of the generic command.
DecoratorCommandDecorator in Controller FrameworkAdds client-specific command behaviour without modifying framework commands.
CompositeActivityCompositeRepresents activities arranged in a tree.

4. Object Model Overview

┌───────────────────┐ │ Member │ │ Controller FW │ └─────────┬─────────┘ │ ┌─────────┴─────────┐ │ │ ▼ ▼ ┌─────────┐ ┌───────────┐ │ Payment │ │Subscription│ └────┬────┘ └─────┬─────┘ │ 1 │ * │ │ │ ┌─────┴─────┐ │ │ Costitem │ │ └─────┬─────┘ │ │ * │ │ │ ┌─────▼─────┐ └────────────│ Activity │ └─────┬─────┘ │ composite parent │ ┌─────▼─────┐ │ Activity │ │ children │ └───────────┘ Subscription: member_id ───────► Member costitem_id ───────► Costitem payment_id ───────► Payment Costitem: activity_id ───────► Activity Payment: member_id ───────► Member Member: parent_id ───────► Member (self-reference)

5. Activity Model

membersactivities\model\activities\Activity is an abstract domain object extending the Controller Framework's DomainObject.

Responsibilities

Public extension point

public ?ActivityTypeImplementation $activitytypeimplementation = null;

Important methods

MethodReturnPurpose
getInstance(array $row)ActivityCreates a concrete activity or ActivityComposite and attaches its type implementation.
getParticipants()ObjectMapReturns paid participants, including inherited participants for child activities.
subscriptionPeriodOver()boolChecks the activity due date against the current date.
getTotalAmountReceived()floatSums distinct paid payments associated with cost items of the activity.

ActivityComposite

ActivityComposite extends Activity and implements the Composite pattern. Children are loaded lazily through ActivityMapper::getChildren().

For a composite, subscriptionPeriodOver() returns true when any child activity has an expired subscription period.

6. Costitem Model

Costitem represents a subscribable item belonging to an activity.

public ?CostitemTypeImplementation $costitemtypeimplementation = null;

During getInstance(), the framework:

  1. creates the concrete client Costitem class;
  2. initializes database properties;
  3. creates the classification-specific type implementation;
  4. loads the related Activity when activity_id is present.
Database propertyMeaning
idIdentifier
descriptionHuman-readable item description
classificationType implementation selector
priceUnit price
typeApplication-defined cost item type
activity_idOwning activity

7. Member Model

Member is supplied by the Controller Framework and specialized by the client application, normally as:

class Member extends \controllerframework\members\Member
{
    // client-specific behaviour
}

The reference schema supports member groups through parent_id. A member can therefore act as a parent/group while child members point to it.

</tr>
PropertyMeaning
name / lastnameMember identity
emailContact/login email
roleUser or administrator role
passwordStored password hash managed by authentication infrastructure
activeApplication membership state
subscriptionuntilMembership validity date
parent_idOptional member/group relationship

8. Subscription Model

Subscription represents a registration by a member for a cost item.

public ?SubscriptionTypeImplementation $subscriptiontypeimplementation = null;

When instantiated, the framework resolves:

subscription.member
subscription.costitem
subscription.payment
subscriptiontypeimplementation

The payment relation is optional because a subscription can exist before payment has been associated with it.

9. Payment Model

Payment represents a financial transaction and extends the Controller Framework's DomainObject.

public ?PaymentTypeImplementation $paymenttypeimplementation = null;
MethodPurpose
getInstance()Creates the concrete client payment type and loads the member.
delete()Deletes associated subscriptions and then the payment inside a database transaction.
isPaid()Returns true when status is exactly paid.
statusReceived()Creates an AccessToken and delegates status-specific behaviour to the payment type implementation.

The transaction in Payment::delete() ensures that deletion of the payment and its associated subscriptions is committed atomically; an exception causes a rollback.

10. Google Wallet Model

GoogleWalletTicket encapsulates Google Wallet integration. It contains Google API client/service state and methods for creating or updating Wallet classes and objects.

MethodPurpose
auth()Initializes Google API authentication.
createClass()Creates a Wallet class.
updateClass()Updates a Wallet class.
createObject()Creates a Wallet ticket object.
updateObject()Updates a Wallet ticket object.
createJwt(int $id)Creates the JWT used to add the ticket to Google Wallet.

Event-specific information such as event name, date/time, venue, ticket type and barcode is prepared by the protected setter methods.

11. Mapper Layer

The framework contains four domain mappers:

ActivityMapper
CostitemMapper
SubscriptionMapper
PaymentMapper
MapperTableFramework-specific fields
ActivityMapperactivitydate, duedate, longdescription, start, end, location
CostitemMappercostitemprice, type, activity_id
SubscriptionMappersubscriptionmember_id, costitem_id, payment_id, quantity, remark
PaymentMapperpaymentmember_id, date, amount, status, type, source

Each mapper extends the Controller Framework Mapper and defines an allowed-field whitelist. This is important when performing update/insert operations through the persistence layer.

Important: Mapper::findAll() accepts a free SQL select clause. Client code must never concatenate untrusted request data into such a clause.

12. Command Layer

The framework commands are grouped by responsibility.

PackageCommands
adminCreate, edit, delete and composite-management operations for activities, cost items, members and payments; administration and member search.
downloadsMember and participant XLS exports.
mollieGeneric Mollie payment creation and webhook processing.
userActivity registration, payment creation, payment confirmation and public/user activity flows.

Commands are intended to be connected to routes through the Controller Framework configuration, normally controls.xml.

13. Command Decorators

Client applications should use Controller Framework CommandDecorator whenever framework command logic can be reused.

Client route │ ▼ Client CommandDecorator │ ├── client-specific validation/configuration ├── authentication level └── Request parameters/objects │ ▼ MembersActivities framework command │ ▼ Domain model / mapper

This is especially important for validation strategies and payment descriptions. It keeps generic framework commands independent of a particular client application's business rules.

14. Strategy and Type Implementations

Type implementations

ActivityTypeImplementation
CostitemTypeImplementation
PaymentTypeImplementation
SubscriptionTypeImplementation

The classification field determines the concrete implementation. For example, an activity with classification RGLR results in a client class such as \model\Activity_RGLR.

Client example

class Activity_RGLR
    extends \membersactivities\model\activities\ActivityTypeImplementation
{
    // default activity behaviour
}

class Activity_STMP
    extends \membersactivities\model\activities\ActivityTypeImplementation
{
    public function seatmap(): bool {
        return true;
    }
}

15. Subscription Validation

SubscriptionValidationStrategy is the extension point for business rules determining whether a member may subscribe to a cost item.

public function subscribe(
    \controllerframework\members\Member $member,
    \membersactivities\model\activities\Costitem $subscribableitem,
    array $properties
): array

Client applications can implement different policies, for example:

SubscriptionValidationPublic
SubscriptionValidationUser
SubscriptionValidationAdmin

The concrete strategy should be supplied through controlled application code, normally a CommandDecorator. A request parameter must never be treated as a class name and instantiated dynamically.

16. Request / Response Flow

HTTP Request │ ▼ CommandResolver │ ▼ Command / Decorator │ ├── Request input validation ├── CSRF validation where required ├── business operation └── response preparation │ ▼ Command status │ ├── CMD_OK / CMD_DEFAULT ├── CMD_ERROR └── other configured statuses │ ▼ View or Forward

The Request object is also used as a controlled communication channel between decorators and wrapped commands. This mechanism is used, among other things, for application-specific validation strategies and Mollie order descriptions.

17. Security Architecture

17.1 Authentication

Authentication and login levels are supplied by the Controller Framework. Commands should explicitly define whether they require no login, a user login or administrator login.

17.2 CSRF

State-changing operations should use POST and validate the CSRF token before modifying data.

17.3 AccessToken

Controller Framework 1.0.31 supplies AccessToken. MembersActivities uses it for protected payment operations.

AccessToken::generate('mollie-order', (string)$paymentId)

The _SALTRAND secret used by the AccessToken mechanism must remain confidential and must not be stored in public source code.

17.4 Input validation

Request parameters are untrusted. Validate IDs, email addresses, file names, redirects, query parameters and all other externally supplied values according to their intended type and context.

18. Payment and Mollie Flow

CreatePaymentCommand │ ▼ Payment │ │ protected order/payment identifier ▼ PaymentToMollieCommand │ ▼ OrderToMollieCommand │ ├── validate protected identifier/token ├── load Payment from server-side database ├── obtain Payment::amount ├── obtain application-specific orderDescription └── create Mollie payment │ ▼ Mollie │ │ │ payment │ webhook ▼ ▼ customer WebhookFromMollieCommand │ ▼ Payment::statusReceived() │ ▼ PaymentTypeImplementation

The critical trust boundary is the payment amount: the amount must be read from the server-side Payment object and not accepted from a browser-submitted amount.

The order description is intentionally different: the client-specific CommandDecorator can set orderDescription on the Request so that different order types can supply different descriptions without modifying the generic payment command.

19. Database Model

TablePrimary purposeImportant foreign keys
activityActivities and activity hierarchyparent_id → activity.id
memberMembers and member groupsparent_id → member.id
costitemSubscribable items/pricesactivity_id → activity.id
paymentFinancial transactionsmember_id → member.id
subscriptionMember registration for a cost itemmember_id, costitem_id, payment_id
remember_tokensRemember-me authentication tokensmember-related authentication data
mail_queueAsynchronous email processingapplication process data

Relationship detail

activity 1 ─────────── * costitem activity 1 ─────────── * child activity member 1 ───────────── * subscription member 1 ───────────── * payment member 1 ───────────── * child member costitem 1 ──────────── * subscription payment 1 ───────────── * subscription

The reference schema uses InnoDB, utf8mb4 and foreign key constraints. It is intended as a starting schema for a new client application rather than a production migration script.

20. Transactions and Consistency

Payment deletion is implemented transactionally: related subscriptions are deleted first, then the payment itself is deleted; an exception rolls the operation back.

For client-specific multi-step business operations, the same principle should be considered whenever several related database changes must succeed or fail as one logical operation.

21. Mail Queue

The mail_queue table supports asynchronous mail processing. A typical lifecycle is:

pending │ ▼ sending │ ├────────► sent │ └────────► failed

The queue records processing information such as attempts, creation/start/send timestamps and an error field. A cron command can process pending messages and cleanup old sent records.

22. Extension Guide

22.1 Add a new activity type

  1. Create a client Activity subclass if not already present.
  2. Create Activity_XXXX extending ActivityTypeImplementation.
  3. Store XXXX in the activity classification field.
  4. Add client logic only where the type needs special behaviour.

22.2 Add a new payment type

  1. Create a client Payment subclass.
  2. Create Payment_XXXX extending PaymentTypeImplementation.
  3. Implement statusReceived() when payment status changes require client-specific processing.

22.3 Add client-specific subscription validation

  1. Create a class extending SubscriptionValidationStrategy.
  2. Implement the application-specific validation rules.
  3. Inject the strategy through a controlled CommandDecorator.

22.4 Add a new command around an existing command

  1. Create a client CommandDecorator.
  2. Set the wrapped framework command in initCommand().
  3. Use doExecuteDecorator() for client-specific preparation.
  4. Define the required authentication level.
  5. Register the route in controls.xml.

23. API / Class Reference

ClassTypeKey API
membersactivities\model\activities\Activityabstract modelgetInstance, getParticipants, subscriptionPeriodOver, getTotalAmountReceived
membersactivities\model\activities\ActivityCompositecomposite modelgetChildren, isComposite, subscriptionPeriodOver
membersactivities\model\activities\Costitemabstract modelgetInstance
membersactivities\model\subscriptions\Subscriptionabstract modelgetInstance
membersactivities\model\subscriptions\Paymentabstract modelgetInstance, delete, isPaid, statusReceived
membersactivities\model\subscriptions\SubscriptionValidationStrategystrategysubscribe, errorcode
membersactivities\model\activities\ActivityMappermappergetChildren, getParticipants
membersactivities\model\activities\CostitemMappermapperpersistence / allowed fields
membersactivities\model\subscriptions\SubscriptionMappermapperpersistence / allowed fields
membersactivities\model\subscriptions\PaymentMappermapperpersistence / allowed fields
membersactivities\model\activities\ActivityTypeImplementationtype implementationseatmap
membersactivities\model\activities\CostitemTypeImplementationtype implementationextension point
membersactivities\model\subscriptions\SubscriptionTypeImplementationtype implementationextension point
membersactivities\model\subscriptions\PaymentTypeImplementationtype implementationstatusReceived, getSubscription, Wallet support
membersactivities\model\wallet\GoogleWalletTicketintegration serviceauth, create/update class/object, createJwt

24. Technical Review Checklist