Status and periods
Membership lifecycle state is tracked through the membership_status field on the Membership entity and the current_periods reference field. Term plugins create and update both when activating, renewing, or expiring.
The status field is a boolean published flag (EntityPublishedInterface). Unpublished memberships are excluded from MembershipService queries and from view access unless the user has administer crm_membership.
membership_status values
| Value | Meaning |
|---|---|
active |
Membership has at least one current period that is active now, or was set active by the term plugin after the last period change. |
future |
A period exists but has not started yet (start date in the future). |
expired |
Membership has been expired (typically via cron/queue calling expire()). |
The membership_status field is a simple list string — there is no state machine workflow yet. See Roadmap.
current_periods field
current_periods holds references to crm_membership_period entities that represent the membership’s current coverage. Term plugins maintain this field via addMembershipPeriod() and updateCurrentPeriods() in MembershipTermBase:
- When adding a period, expired periods are removed from
current_periods. - The new period is appended.
- If the new period
isActive(), membership_status is set toactive; otherwise, if membership_status was not alreadyactive, it is set tofuture.
Historical periods may remain in the database but are no longer referenced in current_periods once expired.
Membership period applicability
Each period has optional applicable contacts. When empty, getApplicableContacts() falls back to all contacts on the parent membership. This supports household-style memberships where one membership covers multiple people but individual periods may apply to subsets.
Membership::getCurrentPeriodsForContact() and MembershipPeriod::isApplicableForContact() filter periods for a given contact.
isActiveFor() vs isMember()
| Check | What it does |
|---|---|
Membership::isActiveFor($contact) |
Delegates to the term plugin. Checks current periods for the contact, period active dates, and grace period (days after end date from plugin config). |
MembershipService::isMember($contact, $target) |
Queries published memberships with membership_status = active, then calls isActiveFor() on candidates. Also checks indirect memberships and dispatches IS_MEMBER event. |
A membership can have membership_status = active but isActiveFor() returns false if all periods (including grace) have ended. Conversely, cron may queue a membership for expiration while membership_status is still active.
Grace period
Fixed and Rolling duration plugins support a grace_period config key (integer days). After a period’s end date, isActiveFor() still returns true until grace period elapses. Grace does not change the stored membership_status field or extend the period entity’s end date.
Expiration flow
- Cron finds memberships to expire (see Cron and queue).
- Queue worker calls
$membership->getMembershipTerm()->expire(). - Base
expire()sets membership_status toexpiredand clearscurrent_periods.
Related
- Entities — Field definitions
- Plugin configuration reference — Grace period config
- MembershipService — Query behavior