Skip to content

feat: configurable table names#122

Merged
cjmellor merged 17 commits into
mainfrom
feat/configurable-table-names
May 17, 2026
Merged

feat: configurable table names#122
cjmellor merged 17 commits into
mainfrom
feat/configurable-table-names

Conversation

@cjmellor
Copy link
Copy Markdown
Owner

@cjmellor cjmellor commented May 13, 2026

Summary

Resolves #121.

  • Adds a LEVEL_UP_TABLE_PREFIX env var (and matching table_prefix config key) that prepends a single prefix to every default package table name.
  • Adds a tables config array for per-table renames. Any value left equal to the default receives the table_prefix; any value you change is taken verbatim and escapes the prefix.
  • Preserves backwards compatibility: the existing top-level 'table' config key keeps working as a fallback for tables.experiences.

A new "Customizing Table Names" section in the README walks through the three usage patterns (prefix only, per-table override, combining both).

cjmellor added 17 commits May 17, 2026 21:53
All 13 create-table stubs now use config('level-up.tables.<key>') for
Schema::create/dropIfExists, and every constrained() call that points at
a package table passes an explicit table: named argument.
Also disable phpdoc_type_annotations_only rule in Pint as it was removing documentation comments that aren't type annotations.
The previous commit added an explanatory comment requested in code review,
then disabled Pint's phpdoc_type_annotations_only rule (which strips it).
The project-wide config change wasn't worth keeping for one comment.
Reverts both changes; the timing rationale lives in the spec/plan docs.
belongsToMany and morphedByMany relationships were referencing pivot
tables (achievement_user, challenge_user, multiplier_scopes) by their
default names. They now read from config('level-up.tables.<key>') so
the table_prefix and per-table overrides actually apply.

Affects: HasAchievements, HasChallenges, Achievement, Challenge,
Multiplier. Adds regression coverage in TablePrefixIntegrationTest.
…vements

Folds the duplicated belongsToMany(...)->withPivot('progress') chain
shared by allAchievements(), achievements(), achievementsWithProgress(),
and secretAchievements() into a single private helper.
Seven query fragments in HasChallenges and ChallengeService still
hardcoded the default table names ('challenge_user.completed_at',
'achievements.id', DB::table('challenge_user')) and bypassed the
table-name resolver. Under a prefix or override, this silently
broke activeChallenges/completedChallenges, the achievement_ids
preload, and challenge completion (the raw DB::table UPDATE matched
zero rows, so ChallengeCompleted never fired and rewards were
never dispatched).

Resolve each fragment through config('level-up.tables.*').

Surfaced by a sandbox integration test that exercised every README
operation against five table-naming configurations across SQLite
and MySQL.
The Multiplier::users()/tiers() morphedByMany attach path was bypassing
HasConfigurableIds because MultiplierScope extended Model. In bigint mode
this worked through DB auto-increment, but UUID/ULID mode left scopes.id
NULL on insert. Convert MultiplierScope to MorphPivot and wire ->using()
on both morphedByMany declarations so the pivot model handles ID
generation for the configured id_type.
@cjmellor cjmellor force-pushed the feat/configurable-table-names branch from fe261ca to 33630b0 Compare May 17, 2026 21:09
@cjmellor cjmellor merged commit 04dc722 into main May 17, 2026
11 checks passed
@cjmellor cjmellor deleted the feat/configurable-table-names branch May 17, 2026 21:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant