turnley.dev

PHP and MySQL code for faucet operators

Tools & Calculators

OPS-012

A settings page an operator can actually use

Prefix-based grouping dumps most keys into an alphabetical wall. A catalogue with tabs, help text and a search — plus a test that fails the build when a migration adds an uncatalogued key.

Settings pages rot. They start grouped by key prefix, which works while there are twenty keys, and by the time there are a hundred most of them have landed in a category called Other, sorted alphabetically, with nothing explaining what any of them do.

A catalogue inverts it: every key is explicitly assigned a tab, a plain-language label and a line of help. Order comes from the catalogue, not the alphabet.

The part that keeps it true is a test. If a migration adds a setting and nobody catalogues it, the build fails — which is the only reliable way to stop the Other tab coming back.

PHP
/** Every key, deliberately placed. The raw key is shown small underneath. */
function settings_catalogue(): array
{
    return [
        'site_name'        => ['Site',     'Site name',                 'Shown in the masthead and page titles.'],
        'base_url'         => ['Site',     'Base URL',                  'No trailing slash. Used for links in email.'],
        'claim_cooldown'   => ['Economy',  'Claim cooldown (minutes)',  'How long between claims for one user.'],
        'reward_units'     => ['Economy',  'Reward per claim',          'In the coin smallest unit.'],
        'withdraw_min'     => ['Payouts',  'Minimum withdrawal',        'Below this, the withdraw form refuses.'],
        'fp_send_key'      => ['Payouts',  'FaucetPay send key',        'Scoped key. Leave blank to pay by hand.'],
        'turnstile_site'   => ['Security', 'Turnstile site key',        'Blank disables the captcha entirely.'],
        // …every key, with no exceptions
    ];
}

/** tests/settings.php — fails the build when the catalogue drifts. */
function test_settings_catalogue(PDO $pdo): array
{
    $fail = [];
    $cat  = settings_catalogue();
    $keys = $pdo->query('SELECT k FROM settings')->fetchAll(PDO::FETCH_COLUMN);

    foreach ($keys as $k) {
        if (!isset($cat[$k])) {
            $fail[] = "setting '$k' exists but is not in the catalogue";
        }
    }
    foreach ($cat as $k => [$tab, $label, $help]) {
        if (!in_array($k, $keys, true)) {
            $fail[] = "catalogue lists '$k' which no longer exists";
        }
        if (trim($help) === '') {
            $fail[] = "setting '$k' has no help text";
        }
        if ($tab === 'Other') {
            $fail[] = "setting '$k' landed in Other — give it a real tab";
        }
    }
    return $fail;
}

Using it

Put a search box above the tabs that filters across all of them at once. Not knowing which tab a setting is on is the actual problem; search solves it directly.

Move machine-written keys — cron heartbeats, internal version markers — to a read-only table. They are not settings.

Show the raw key under each label. It is what appears in documentation and support threads.

What bites people

A label that just restates the key ("Site name" for site_name) is fine. Do not contort labels to satisfy an over-strict test; relax the test instead.

Blank password and key fields should leave the stored value alone rather than clearing it. Say so in the help text.

Deleting a setting in a migration without removing it from the catalogue fails the build — which is the test working, not a problem.

Also in Admin and Operations