feat(profile): self-service profile show + edit (#13)#89
Open
martinydeAI wants to merge 3 commits into
Open
Conversation
Adds `App\Controller\ProfileController` exposing two authenticated routes: - `GET /profile` (`app_profile_show`) — read-only summary of the current user (name + email + a link to edit). - `GET|POST /profile/edit` (`app_profile_edit`) — edit form for the display name. Email stays read-only on purpose: it's load-bearing for the domain-derived authorisation in #84, and changing it silently would be a footgun. CSRF protection via Symfony's built-in `csrf_token('profile-edit')` helper. Empty / whitespace-only names are rejected with a 422 and a localised error; invalid CSRF tokens yield a 403. The mutation lives in a new `UserManager::updateName()` method — the controller stays thin per project conventions. Templates re-use the existing `Form/Label`, `Form/TextInput`, and `Form/Button` components and the `Eyebrow` layout primitive — no new component families introduced. Localised strings land in the existing `messages` translation domain. Sequenced as PR 6 of the User management milestone plan. Stacked on PR 1 (#86) since the show + edit flow reads / writes `User::$name`, which doesn't exist on `develop` yet — labelled `do-not-merge` until #86 lands. Closes #13. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Removed issue references from docblock for clarity.
martinyde
requested changes
Jun 19, 2026
| return $this->redirectToRoute('app_profile_show'); | ||
| } | ||
|
|
||
| private function currentUser(): User |
Contributor
There was a problem hiding this comment.
Could this live as public mehotd in UserManager? @tuj ?
Per review on PR #89 - apply the test-comment convention to the new ProfileControllerTest. Each `test...` method now opens with a single-line "Tests ...", "Ensures ...", or "Verifies ..." comment naming what it asserts. Pure documentation change - no test logic touched. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced Jun 19, 2026
martinyde
approved these changes
Jun 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Links to issues
Closes #13.
Description
Self-service profile UI:
GET /profile(app_profile_show) — read-only summary of thesigned-in user: display name + email + link to edit. Renders
flash messages from a successful edit.
GET|POST /profile/edit(app_profile_edit) — edit form for thename field, CSRF-protected via Symfony's
csrf_token('profile-edit').Empty / whitespace-only names re-render the form with a 422 and a
localised error; invalid CSRF tokens yield a 403.
Both routes gated by
#[IsGranted('IS_AUTHENTICATED_FULLY')]at thecontroller class level — anonymous visitors get a 302 to
/login.The mutation lives in a new
App\Security\UserManager::updateName(User, string): void. Thecontroller stays thin per project conventions.
Templates re-use the existing
Form/Label,Form/TextInput,Form/Button, andEyebrowcomponents — no new component families.Localised strings land in the existing
messagestranslation domainunder a new
profile.*namespace.Rescope of #13
The original #13 also mentioned "admin user list / manage". That
surface is now covered by #64 + #85 — this PR only delivers
the self-service profile work. The body of #13 was rescoped earlier
with the User management plan.
Screenshot of the result
n/a — UI matches the existing layout primitives (header, footer,
form components). Manual screenshot can be added on request.
Checklist
Additional comments or questions
Email is intentionally read-only. Changing it would mutate the
right-hand side of the address used for domain-scoped authorisation
in PR #87's voter — a user could "promote" themselves into a
different domain manager's scope just by editing their profile. If
email changes are ever needed they belong behind an explicit
"confirm via the new address" flow plus admin reapproval; out of
scope here.
No password change in this PR. Tracked as a separate concern (needs
symfony/mailerfor the reset-link path) — not currently filed asits own issue.
Details - AI specificities
docs/adr/006-user-approval-and-account-state.md(Draft; lands via docs: add ADR 004 — user registration, approval, and account state #61).
develop.do-not-mergelabel.test file (
tests/Integration/Controller/ProfileControllerTest.php)and one new service method (
UserManager::updateName()); themethod is exercised by the functional test rather than its own
unit test, since the path is end-to-end and reads more cleanly
that way.
plan (no
User.organizationFK in this milestone).symfony/form— the project still doesn't ship it; manual formhandling matches the existing login template.
profile-edit. The template renders it via{{ csrf_token('profile-edit') }}; the controller validates with$this->isCsrfTokenValid('profile-edit', …). Same intent nameused by both sides — change in lockstep if renaming.