{"record":{"id":"77073413257aa8c6","repo":"monicahq/monica","slug":"at-least-one-is-missing-to-have-a-valid-name-ord","errorCode":null,"errorMessage":"At least one % is missing to have a valid name order.","messagePattern":"At least one % is missing to have a valid name order\\.","errorType":"exception","errorClass":"Exception","httpStatus":500,"severity":"error","filePath":"app/Domains/Settings/ManageUserPreferences/Services/StoreNameOrderPreference.php","lineNumber":57,"sourceCode":"    {\n        $this->data = $data;\n\n        $this->validateRules($data);\n        $this->checkNameOrderValidity();\n        $this->updateUser();\n\n        return $this->author;\n    }\n\n    private function checkNameOrderValidity(): void\n    {\n        // there should be at least one variable in the name order\n        if (substr_count($this->data['name_order'], '%') < 1) {\n            throw new \\Exception('The name order must contain at least one variable.');\n        }\n\n        if (substr_count($this->data['name_order'], '%') % 2 == 1) {\n            throw new \\Exception('At least one % is missing to have a valid name order.');\n        }\n    }\n\n    private function updateUser(): void\n    {\n        $this->author->name_order = $this->data['name_order'];\n        $this->author->save();\n    }\n}\n","sourceCodeStart":39,"sourceCodeEnd":67,"githubUrl":"https://github.com/monicahq/monica/blob/e08e91734170b6bbd582cb578532c3948196124e/app/Domains/Settings/ManageUserPreferences/Services/StoreNameOrderPreference.php#L39-L67","documentation":"Second rule of checkNameOrderValidity(): '%' characters must come in pairs (opening and closing delimiters around placeholder names). substr_count(name_order, '%') % 2 == 1 means an odd count — some placeholder is missing its closing (or opening) '%', such as '%first_name' or a stray single '%' in literal text. The saved preference would render broken names, so the service rejects it with \\Exception (rendered as a 500).","triggerScenarios":"Submitting a name_order with an unbalanced placeholder: '%first_name %last_name%' (missing a closing delimiter), a deleted delimiter from hand-editing, or one stray '%' typed into literal text.","commonSituations":"Hand-editing the template in the preferences UI, deleting one delimiter while editing, or concatenating placeholder strings incorrectly in scripts.","solutions":["Pair every placeholder: %first_name% %last_name%","Remove stray single % characters from literal text","Lint the value before saving: preg_match_all('/%[^%]+%/', $value) plus an even substr_count check"],"exampleFix":"// before\n$data = ['name_order' => '%first_name %last_name%']; // odd count -> throws\n\n// after\n$data = ['name_order' => '%first_name% %last_name%']; // even count, balanced","handlingStrategy":"validation","validationCode":"// Validate before calling: placeholders must be paired (even '%' count)\n$nameOrder = $data['name_order'];\n$count = substr_count($nameOrder, '%');\n\nif ($count === 0 || $count % 2 === 1) {\n    throw ValidationException::withMessages([\n        'name_order' => 'Name order placeholders must be paired, e.g. %first_name% %last_name%.',\n    ]);\n}","typeGuard":"function hasBalancedPlaceholders(string $nameOrder): bool\n{\n    $count = substr_count($nameOrder, '%');\n\n    return $count > 0 && $count % 2 === 0;\n}","tryCatchPattern":"try {\n    app(StoreNameOrderPreference::class)->execute($data);\n} catch (\\Exception $e) {\n    if (str_contains($e->getMessage(), '% is missing')) {\n        throw ValidationException::withMessages(['name_order' => $e->getMessage()]);\n    }\n    throw $e;\n}","preventionTips":["Insert placeholders as atomic tokens in the editor so delimiters cannot be split","Run the even-count check client-side before submit","Reject stray single '%' characters in literal text during input sanitization","Add form-request validation covering both placeholder rules"],"tags":["monica","user-preferences","name-order","template"],"backgroundTag":"template-placeholder-invalid","analyzedSha":"e08e91734170b6bbd582cb578532c3948196124e","analyzedAt":"2026-08-17T01:36:49.014Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}