Loading image 1...
Maintaining and Extending a Multi-Tenant Gym Management System on Live Production Data

Maintaining and Extending a Multi-Tenant Gym Management System on Live Production Data
Javapixa carried out maintenance and feature work on SmartGym, a gym management system already running with real members, payments and attendance records. The system is three separately-deployed CodeIgniter 4 applications, a staff back-office, a member and trainer portal, and a public website, that all read and write the same MySQL database. One codebase serves several gyms, each with its own database, asset tree and subdomains, with routing branching on the HTTP host. Across 101 commits and 492 application files, the work covered class booking, a cashier subdomain, a tenant schema upgrade and a public site rework, and none of it was allowed to damage the data already there.
This was not a greenfield build. Model classes are duplicated across the three applications, so a schema change in one can break the other two. The schema itself cannot be reconstructed from the repository: only three migrations exist, and three views the code queries daily are created by none of them, which makes the database dump the real disaster recovery. Hosting is shared, so SUPER and SET_USER_ID are unavailable, yet the existing export carried DEFINER clauses demanding exactly those privileges. The application also depends on a permissive database configuration, so legacy SQL is rejected outright by a server running only_full_group_by.
Every change was verified against a restored copy first, never speculatively on production. The tenant schema upgrade was built and tested through a fixed cycle: restore the client dump, reshape it to the live server's exact state, apply the upgrade, run the failing migration's real SQL on top, then finish with an information_schema diff against the reference schema that had to come back empty. A renamed enum value was migrated by widening the enum to hold both values, moving the data, then narrowing, so 404 member rows were not blanked. The created_at column was added as NULL rather than DEFAULT CURRENT_TIMESTAMP, so years of history were not stamped with today's date. On the front end, the public site was rebuilt so every section draws from the database, with sample content that marks itself in the delivered HTML so nothing invented can quietly stay in production.
Project Showcase
Loading image 1...
Project visualization and interface design
PHP Framework
Language
Database
Styling
Frontend
Shared Hosting
Class booking built end to end: a new table, two per-line subscription views, and the member-facing flow. This is where the subscription model's real shape surfaced, because a subscription container can hold several products with different end dates, so status has to be judged per line rather than per container, and the reporting that depends on it was corrected to match.
The cashier subdomain serves the point-of-sale screen and nothing else, on a minimal layout, so till staff cannot reach the back office from the same terminal.
One ordered script bringing an older-generation tenant database up to the schema the current application expects: 23 missing tables, 74 column statements, 8 type and enum differences, and 7 views, with a view dependency order that must not be rearranged.
The enum value mahasiswa became pelajar while 404 member rows still held the old one. Narrowing the enum first would have blanked them, so the script widens the enum to hold both, migrates the data, then narrows.
Every section of the public site draws from the database. A newly-provisioned gym with an empty database still renders a whole page through sample content, and every fallback is marked in the delivered HTML with the route to replace it, so nothing invented can be mistaken for real content.
Filenames in the database outlive files, so every image URL is checked on disk before it is emitted. Logos, favicons, PWA icons and uploaded photos carry a version stamp taken from the file's own modification time, so there is no manual cache-busting step to forget.
A detailed look at how we brought this project from concept to reality through our proven methodology.
Brought three applications and a shared asset tree under one repository, with a local development harness: a host-dispatching router, because production serves each app from its own document root with the front controller at the app root, which makes the standard framework dev server unusable. Substantive fixes followed: subscription status, attendance records, member photographs, and a dynamic QR code for gate access.
Class booking added end to end with a new table, per-line subscription views and the member-facing flow, across three migrations. Also in this stretch: per-app PWA manifests, privacy fixes, and a full revamp of the member portal onto a mobile-native design.
The heaviest single area in the history: the class-product editor, with fourteen commits into its form and schedule builder, covering day cards, time chips, native time inputs, and layered pop-ups that clipped. Alongside it, per-channel product visibility, show on website or show in member app, and an icon rebrand for a second gym.
A dedicated cashier subdomain carved out, public per-class product pages added with a booking CTA, and the member dashboard reworked around them. Three fixes on this day share one root cause worth naming: the application depends on a permissive database configuration, and legacy SQL is rejected outright by a server running only_full_group_by.
Bringing a third gym onto the current code, the largest single piece of diagnosis in the engagement. The failing migration turned out to be a symptom: that tenant's schema was an older generation entirely, and the application requires a deleted_at column independently of any migration because every model uses soft deletes. Resolved with an upgrade script tested against a restored copy.
06 Outcomes delivered
Related Services
These are the Javapixa services behind this project.