Membersactivities framework technical guide
14 Sep 2026 - dirkvm
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.
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.
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
| Pattern | Where used | Purpose |
|---|---|---|
| Active Record / Domain Object | Activity, Costitem, Payment, Subscription; Member comes from Controller Framework | Domain objects represent persisted application entities. |
| Data Mapper | ActivityMapper, CostitemMapper, PaymentMapper, SubscriptionMapper | Separates persistence operations from domain objects. |
| Abstract Factory | getInstance() implementations | Creates concrete client model objects from database rows. |
| Builder / Type implementation | ActivityTypeImplementation, CostitemTypeImplementation, PaymentTypeImplementation, SubscriptionTypeImplementation | Associates a domain object with classification-specific behaviour. |
| Strategy | SubscriptionValidationStrategy | Allows client applications to define subscription rules independently of the generic command. |
| Decorator | CommandDecorator in Controller Framework | Adds client-specific command behaviour without modifying framework commands. |
| Composite | ActivityComposite | Represents activities arranged in a tree. |
membersactivities\model\activities\Activity is an abstract domain object extending the Controller Framework's DomainObject.
classification.parent_id is present.public ?ActivityTypeImplementation $activitytypeimplementation = null;
| Method | Return | Purpose |
|---|---|---|
getInstance(array $row) | Activity | Creates a concrete activity or ActivityComposite and attaches its type implementation. |
getParticipants() | ObjectMap | Returns paid participants, including inherited participants for child activities. |
subscriptionPeriodOver() | bool | Checks the activity due date against the current date. |
getTotalAmountReceived() | float | Sums distinct paid payments associated with cost items of the activity. |
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.
Costitem represents a subscribable item belonging to an activity.
public ?CostitemTypeImplementation $costitemtypeimplementation = null;
During getInstance(), the framework:
activity_id is present.| Database property | Meaning |
|---|---|
| id | Identifier |
| description | Human-readable item description |
| classification | Type implementation selector |
| price | Unit price |
| type | Application-defined cost item type |
| activity_id | Owning activity |
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.
| Property | Meaning |
|---|---|
| name / lastname | Member identity | </tr> |
| Contact/login email | |
| role | User or administrator role |
| password | Stored password hash managed by authentication infrastructure |
| active | Application membership state |
| subscriptionuntil | Membership validity date |
| parent_id | Optional member/group relationship |
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.
Payment represents a financial transaction and extends the Controller Framework's DomainObject.
public ?PaymentTypeImplementation $paymenttypeimplementation = null;
| Method | Purpose |
|---|---|
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.
GoogleWalletTicket encapsulates Google Wallet integration. It contains Google API client/service state and methods for creating or updating Wallet classes and objects.
| Method | Purpose |
|---|---|
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.
The framework contains four domain mappers:
ActivityMapper
CostitemMapper
SubscriptionMapper
PaymentMapper
| Mapper | Table | Framework-specific fields |
|---|---|---|
| ActivityMapper | activity | date, duedate, longdescription, start, end, location |
| CostitemMapper | costitem | price, type, activity_id |
| SubscriptionMapper | subscription | member_id, costitem_id, payment_id, quantity, remark |
| PaymentMapper | payment | member_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.
Mapper::findAll() accepts a free SQL select clause. Client code must never concatenate untrusted request data into such a clause.The framework commands are grouped by responsibility.
| Package | Commands |
|---|---|
| admin | Create, edit, delete and composite-management operations for activities, cost items, members and payments; administration and member search. |
| downloads | Member and participant XLS exports. |
| mollie | Generic Mollie payment creation and webhook processing. |
| user | Activity 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.
Client applications should use Controller Framework CommandDecorator whenever framework command logic can be reused.
This is especially important for validation strategies and payment descriptions. It keeps generic framework commands independent of a particular client application's business rules.
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.
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;
}
}
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.
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.
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.
State-changing operations should use POST and validate the CSRF token before modifying data.
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.
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.
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.
| Table | Primary purpose | Important foreign keys |
|---|---|---|
| activity | Activities and activity hierarchy | parent_id → activity.id |
| member | Members and member groups | parent_id → member.id |
| costitem | Subscribable items/prices | activity_id → activity.id |
| payment | Financial transactions | member_id → member.id |
| subscription | Member registration for a cost item | member_id, costitem_id, payment_id |
| remember_tokens | Remember-me authentication tokens | member-related authentication data |
| mail_queue | Asynchronous email processing | application process data |
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.
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.
The mail_queue table supports asynchronous mail processing. A typical lifecycle is:
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.
Activity subclass if not already present.Activity_XXXX extending ActivityTypeImplementation.XXXX in the activity classification field.Payment subclass.Payment_XXXX extending PaymentTypeImplementation.statusReceived() when payment status changes require client-specific processing.SubscriptionValidationStrategy.initCommand().doExecuteDecorator() for client-specific preparation.controls.xml.| Class | Type | Key API |
|---|---|---|
membersactivities\model\activities\Activity | abstract model | getInstance, getParticipants, subscriptionPeriodOver, getTotalAmountReceived |
membersactivities\model\activities\ActivityComposite | composite model | getChildren, isComposite, subscriptionPeriodOver |
membersactivities\model\activities\Costitem | abstract model | getInstance |
membersactivities\model\subscriptions\Subscription | abstract model | getInstance |
membersactivities\model\subscriptions\Payment | abstract model | getInstance, delete, isPaid, statusReceived |
membersactivities\model\subscriptions\SubscriptionValidationStrategy | strategy | subscribe, errorcode |
membersactivities\model\activities\ActivityMapper | mapper | getChildren, getParticipants |
membersactivities\model\activities\CostitemMapper | mapper | persistence / allowed fields |
membersactivities\model\subscriptions\SubscriptionMapper | mapper | persistence / allowed fields |
membersactivities\model\subscriptions\PaymentMapper | mapper | persistence / allowed fields |
membersactivities\model\activities\ActivityTypeImplementation | type implementation | seatmap |
membersactivities\model\activities\CostitemTypeImplementation | type implementation | extension point |
membersactivities\model\subscriptions\SubscriptionTypeImplementation | type implementation | extension point |
membersactivities\model\subscriptions\PaymentTypeImplementation | type implementation | statusReceived, getSubscription, Wallet support |
membersactivities\model\wallet\GoogleWalletTicket | integration service | auth, create/update class/object, createJwt |