multiplier_scopes.scopeable_id is a varchar, not bigint, because the morph targets two ID universes — the host's users table and the package's tiers table.
What this Level Up schema covers
XP audit ledger and deduction tracking
Level progression and configurable level cap
Named tier brackets above the level scale
Achievement catalogue with progress and tier gating
Streak tracking and challenge enrolment
A few decisions the diagram can't show:
experience_audits.type was widened from tinyint to varchar(255) in v3, so callers can pass arbitrary reason codes.
experience_audits.multipliers is a JSON snapshot of multipliers applied when XP was granted, so historical math stays reproducible even after a multiplier is edited or deleted.
achievements.tier_id gates unlocks by tier (TierRequirementNotMet throws below threshold); experiences.tier_id tracks each user's current tier with high-water-mark semantics.
challenges.conditions and challenges.rewards are JSON arrays of type-discriminated dicts (points_earned, level_reached, achievement_earned, custom); challenge_user is unique on (user_id, challenge_id), so is_repeatable resets the row.
Good fork target for adding XP and streak mechanics to a Laravel app without modifying its users table. Conditions and rewards live in JSON, so filtering challenges by type uses JSON predicates rather than joins.
Drop-in gamification for Laravel — Level Up is a Laravel package that layers gamification onto any host app's existing user model. It references users.id only, leaving host tables untouched.