Files
clinicpro/.claude/prompt/sync-user-realname-from-profile.md
T
hamedandClaude Opus 4.8 800f9ad8cd fix(profile): sync User.real_name from profile name on create/update
When a user completes their profile (POST/PATCH /api/v1/user-profile), the
name was only stored on the profile (label/family) and User.real_name stayed
empty — so users who registered via booking showed with no name in the admin
user list. hydrate() now mirrors the profile's full name (label + family)
onto the owning User.realName, persisted in the same flush.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 10:45:29 +03:30

6.5 KiB
Raw Blame History

همگام‌سازی نام کاربر (User.real_name) هنگام تکمیل پروفایل

پروژه

clinicpro (Backend).

کاملاً backend است. سایت عمومی nobat724_front همان PATCH/POST /api/v1/user-profile را با فیلدهای name/label و family صدا می‌زند؛ قرارداد API تغییر نمی‌کند، فقط یک side-effect سرور اضافه می‌شود. نیازی به تغییر frontend نیست.

زمینه

در پنل ادمین، لیست کاربران (/api/v1/admin/users) ستون «نام» را از User.realName (users.real_name) می‌خواند. اما وقتی کاربری از طریق نوبت‌دهی ثبت‌نام و پروفایلش را تکمیل می‌کند، نام فقط در UserProfile (profiles.label و profiles.family) ذخیره می‌شود و users.real_name خالی می‌ماند. نتیجه: کاربرانِ ثبت‌نام‌شده از مسیر نوبت‌دهی در لیست ادمین بدون نام نمایش داده می‌شوند.

مشکل / هدف

هنگام ایجاد/به‌روزرسانی پروفایل، اگر name/label یا family تغییر کند، User.realName صاحبِ پروفایل هم با «نام کامل» (ترکیب نام و نام خانوادگی) همگام شود تا در پنل ادمین و هر جای دیگری که real_name را می‌خواند درست نمایش داده شود.

فایل‌های مرتبط

فایل نقش
src/UserProfile/Controller/UserProfileController.php hydrate() فقط Profile را پر می‌کند؛ باید User.realName را هم sync کند
src/UserProfile/Entity/UserProfile.php getUser()، getLabel()، getFamily() (مرجع داده)
src/Auth/Entity/User.php setRealName() (موجود)

وضعیت فعلی (کد واقعی)

hydrate() فقط روی UserProfile کار می‌کند — User.realName هرگز ست نمی‌شود:

private function hydrate(UserProfile $profile, array $data): void
{
    if (array_key_exists('name', $data))           $profile->setLabel($data['name']);
    if (array_key_exists('label', $data))          $profile->setLabel($data['label']);
    if (array_key_exists('family', $data))         $profile->setFamily($data['family']);
    // ... بقیه فیلدها
}

و create() / update() بعد از hydrate فقط $this->repository->save($profile) را صدا می‌زنند.

مرجع: لیست ادمین u.realName as name را برمی‌گرداند (AdminApiController::users)، پس real_name خالی = نام خالی در پنل.

وظایف

۱. همگام‌سازی User.realName در hydrate

در انتهای hydrate() (بعد از اعمال همه‌ی فیلدها روی profile)، اگر name/label یا family در ورودی بود، real_name کاربر را از مقدار نهاییِ پروفایل بساز:

private function hydrate(UserProfile $profile, array $data): void
{
    // ... ست‌کردن فیلدهای profile (بدون تغییر) ...

    if (array_key_exists('name', $data) || array_key_exists('label', $data) || array_key_exists('family', $data)) {
        $fullName = trim(implode(' ', array_filter([$profile->getLabel(), $profile->getFamily()])));
        $profile->getUser()->setRealName($fullName !== '' ? $fullName : null);
    }
}
  • منبع، مقدار نهاییِ خودِ profile است (نه آرایه‌ی $data خام) تا اگر فقط یکی از name/family در PATCH آمده باشد، نام کامل با مقدار موجودِ دیگری به‌درستی ساخته شود.
  • array_filter بخش‌های خالی را حذف می‌کند (مثلاً وقتی فقط name پر است). اگر نتیجه خالی شد، null (هماهنگ با امضای setRealName(?string)).

۲. اطمینان از flush شدن تغییر User

create() و update() بعد از hydrate $this->repository->save($profile) را صدا می‌زنند که flush=true است. چون User و UserProfile در همان Unit of Work مدیریت می‌شوند، تغییر realName در همان flush ذخیره می‌شود — نیازی به save جداگانه‌ی User نیست. فقط تأیید کن که UserProfileRepository::save واقعاً flush می‌کند (می‌کند) و اگر جایی flush=false پاس داده شده، اصلاح کن.

نکات مهم

  • قرارداد API تغییر نمی‌کند — همان فیلدهای name/label/family ورودی؛ فقط side-effect روی User.realName.
  • این منطق هم در ایجاد (POST) و هم به‌روزرسانی (PATCH) باید اجرا شود — چون هر دو از همان hydrate استفاده می‌کنند، یک‌جا اصلاح کافی است.
  • نام کامل = label + ' ' + family؛ اگر محصول ترجیح دیگری دارد (مثلاً فقط label)، در پیاده‌سازی همان را اعمال کن — ولی چون لیست ادمین یک ستون واحد «نام» دارد، نام کامل طبیعی‌تر است.
  • این فقط نام را روی صاحبِ پروفایل ست می‌کند؛ پروفایلِ «شخص دیگر» (که بیمارِ غیرِخود است) از مسیر دیگری (patient_name در نوبت) مدیریت می‌شود و نباید real_name کاربر لاگین را تغییر دهد — hydrate همیشه روی پروفایلِ همان کاربر کار می‌کند، پس مشکلی نیست.
  • تاریخ‌ها/الگوها طبق BaseController. بعد از تغییر: ddev exec php -l روی کنترلر. migration لازم نیست (فیلد real_name از قبل وجود دارد).
  • تست رفتاری: یک کاربر بدون real_name بساز/پیدا کن → PATCH /api/v1/user-profile/{userUuid} با {name, family} → تأیید کن users.real_name پر شد و GET /api/v1/admin/users آن را در name نشان می‌دهد. همچنین PATCH فقط با family → نام کامل با label موجود ساخته شود.
  • مستندسازی: اگر رفتار قرارداد را تغییر نمی‌دهد، docs/api/ نیاز به تغییر بزرگ ندارد؛ ولی می‌توان در سند پروفایل (اگر وجود دارد) یک جمله درباره‌ی sync نام افزود.