Files
clinicpro/docs/tasks/task-04-blog/implementation_notes.md
T
hamed de1a78a235 feat: Implement SMS sending functionality with KavehNegar and Rangineh providers
- Add SendSmsMessage class for encapsulating SMS message data.
- Create KavehNegarProvider and RanginehProvider classes implementing SmsProviderInterface for sending SMS.
- Implement SmsLogRepository and SmsTemplateRepository for managing SMS logs and templates.
- Develop SendSmsHandler for handling SMS sending messages.
- Create SmsService to manage SMS dispatching and logging.
- Add UserProfileController for managing user profiles with CRUD operations.
- Implement UserProfile entity and repository for user profile data management.
- Update symfony.lock and bootstrap.php for project dependencies and environment setup.
2026-06-09 22:00:34 +03:30

4.6 KiB

نکات پیاده‌سازی — تسک ۰۴: ماژول بلاگ

نگاشت فیلدهای Request → Response (مهم!)

در Drupal نام فیلدهای ارسالی با نام فیلدهای دریافتی متفاوت است:

فیلد در Request فیلد در Response توضیح
label title عنوان مقاله
description (string) body: {value, format} متن مقاله — در response به object تبدیل می‌شود
field_image: [{target_id}] images: [{url, fid, filename, filemime, filesize}] تصاویر — ID ارسال، object دریافت
author نام نویسنده (computed از realname کاربر)
uuid شناسه یکتا
status وضعیت انتشار (string: "1")
created / changed Unix timestamp به صورت string

فرمت واقعی Response بلاگ (از سرور Drupal — endpoint 93)

{
  "uuid": "95f6acb0-3331-4141-801e-004e8460edac",
  "title": "روش های خانگی محافظت از پوست در تابستان",
  "status": "1",
  "body": {
    "value": "متن کامل مقاله...",
    "format": "full_html"
  },
  "created": "1763536163",
  "changed": "1763537699",
  "author": "single doctor",
  "images": [
    {
      "url": "https://domain.com/sites/default/files/blog/image.png",
      "fid": "97",
      "filename": "image.png",
      "filemime": "image/png",
      "filesize": 320787
    }
  ],
  "tag": [
    {
      "uuid": "24926497-fc2d-47d7-82ae-26cbbc4d6468",
      "id": "2501",
      "name": "مجله"
    },
    {
      "uuid": "6c6a488b-1051-43a2-887b-0ec7a05e52d5",
      "id": "2500",
      "name": "سلامتی"
    }
  ]
}

نکات مهم response:

  • body یک object است با کلیدهای value و format — نه string ساده!
  • format معمولاً "full_html" یا "basic_html" است
  • author از realname کاربر نویسنده computed می‌شود
  • created و changed به صورت string برگردانده می‌شوند (نه integer)
  • status به صورت string "1" برگردانده می‌شود (نه boolean)
  • images و tag ممکن است آرایه خالی [] باشند
  • response دارای id نیست — فقط uuid

فرمت Request ایجاد/ویرایش بلاگ

POST /api/v1/blog/

{
  "label": "عنوان مقاله",
  "description": "متن کامل مقاله...",
  "field_image": [
    { "target_id": 97 },
    { "target_id": 98 }
  ]
}

PATCH /api/v1/blog/{uuid}

{
  "label": "عنوان ویرایش‌شده",
  "description": "متن ویرایش‌شده"
}

آپلود تصویر بلاگ (endpoint 99)

قبل از ایجاد بلاگ، تصویر باید آپلود شود و fid آن در field_image استفاده شود:

POST /file/upload/blog/blog/field_image
Headers:
  Content-Type: application/octet-stream
  Content-Disposition: file; filename="image.png"
  X-CSRF-Token: {token}
  Authorization: Bearer {token}
Response → fid که در field_image استفاده می‌شود

تولید Slug

  • از عنوان فارسی slug تولید کن
  • پکیج cocur/slugify را نصب کن:
    ddev composer require cocur/slugify
    
  • اگر slug تکراری بود، عدد به انتهای آن اضافه کن: rahnamai-diabet-2

بلاگ‌های برتر (Top Blogs)

  • endpoint: GET /api/v1/blogs/top (نیاز به auth دارد)
  • پاسخ: آرایه مستقیم (نه object با pagination) از بلاگ‌هایی که is_top = 1 هستند
  • فرمت هر آیتم دقیقاً مشابه response معمولی بلاگ است

لیست بلاگ‌ها با فیلتر

GET /api/v1/blogs?page=1&limit=10&title=نشانه&tag=3637
  • title: جستجو در عنوان
  • tag: فیلتر بر اساس ID تگ

Pagination

$offset = ($page - 1) * $limit;
// در Repository با limit/offset

مجوزها

POST   → احراز هویت الزامی (ROLE_DOCTOR یا ROLE_ADMIN)
PATCH  → احراز هویت الزامی (owner یا ROLE_ADMIN)
DELETE → احراز هویت الزامی (owner یا ROLE_ADMIN)
GET    → عمومی (بدون auth)
GET /api/v1/blogs/top → احراز هویت الزامی

بهینه‌سازی

  • view_count با یک query atomic آپدیت کن تا race condition نباشد:
    $this->em->createQuery('UPDATE Blog b SET b.viewCount = b.viewCount + 1 WHERE b.id = :id')
        ->setParameter('id', $blog->getId())
        ->execute();