WP Manifestindependent plugin directory
manifest / events / custom-reservation

افزونه رزرو اختصاصی

a custom wordpress reservation plugin

by تیم وبسایت مدیکال استراتژیست · github.com/alirezakmaxim/custom-reservation · website

0stars
0forks

Install

No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:

wp plugin install https://github.com/alirezakmaxim/custom-reservation/archive/refs/heads/main.zip

این سند «منبع حقیقت» (Source of Truth) برای توسعه، نگهداری و انتقال دانش افزونه است.


فهرست مطالب

  1. [مقدمه و اهداف]
  2. [نمای کلی معماری (Architecture Overview)]
  3. [ساختار فایل‌ها و پوشه‌بندی (File Structure)]
  4. [طراحی پایگاه داده (Database Schema)]
  5. [قراردادهای API (API Contracts)]
  6. [الگوریتم‌های کلیدی (Core Algorithms)]
  7. [جریان داده (Data Flow)]
  8. [ملاحظات امنیتی (Security Measures)]

1. مقدمه و اهداف

هدف این سند ایجاد یک مرجع واحد، دقیق و قابل اتکا برای فهم و توسعه افزونه رزرو اختصاصی در وردپرس است؛ به طوری که هر عضو تیم بتواند بدون حدس و گمان، معماری، قراردادها و جزئیات پیاده‌سازی را دنبال کند.

اهداف کلیدی:

  • حذف ابهام در رفتار سیستم و قراردادها
  • تسهیل توسعه و نگهداری (Maintainability)
  • کاهش ریسک خطاهای همزمانی (Race Conditions) در رزرو
  • تضمین امنیت (CSRF/XSS/SQLi) در سطح API و دیتابیس

2. نمای کلی معماری (Architecture Overview)

سیستم بر پایه الگوی Event-Driven MVC در بستر وردپرس طراحی شده تا تفکیک وظایف (Separation of Concerns) حداکثر شود و توسعه، نگهداری و تست ساده‌تر گردد.

Stack: HTML5, CSS3, JavaScript (AJAX, ES6), PHP, SQL

2.1 الگوی طراحی: Event-Driven MVC

  • MVC برای جداسازی داده، منطق و نما استفاده می‌شود.
  • ارتباط بین اجزا عمدتاً از طریق رویدادها/هوک‌های وردپرس انجام می‌شود تا وابستگی مستقیم کاهش یابد.
  • تمامی درخواست‌های کاربر (فرانت و ادمین) از طریق کنترلرها وارد سیستم می‌شوند.

2.2 نقش هر لایه

Model (مدل)

  • مکان: /models/
  • مسئولیت: تعامل مستقیم با پایگاه داده از طریق $wpdb و انجام عملیات CRUD.
  • نکته: مدل‌ها منطق UI یا رندر ندارند و تا حد امکان از منطق تجاری سنگین دور نگه داشته می‌شوند.
  • کلاس‌های کلیدی: CR_Reservation، CR_Schedule_Rule، CR_Date_Manager

View (نما)

  • مکان: /views/
  • مسئولیت: رندر HTML و تولید خروجی قابل نمایش.
  • ادمین: رندر سمت سرور (SSR) برای صفحات مدیریت.
  • فرانت: قالب‌های پایه که توسط JS و شورت‌کد [reservation_booking] پر می‌شوند.

Controller (کنترلر)

  • مکان: /controllers/
  • مسئولیت: دریافت درخواست، اعتبارسنجی ورودی، فراخوانی سرویس/مدل‌ها و برگرداندن پاسخ مناسب (HTML یا JSON).
  • کلاس‌های کلیدی: CR_Booking_Controller، CR_Admin_Controller

Event Bus (گذرگاه رویداد)

  • مکان: includes/class-loader.php
  • مسئولیت: مدیریت متمرکز add_action و add_filter و ثبت همه هوک‌ها در یک نقطه.
  • مزیت: کاهش coupling و افزایش قابلیت توسعه و تست.

3. ساختار فایل‌ها و پوشه‌بندی (File Structure)

ساختار پروژه مطابق MVC و برای تفکیک وظایف بهینه سازماندهی شده است:

/custom-reservation/
├── custom-reservation.php           # نقطه شروع افزونه (تعریف ثابت‌ها، لودر خودکار، هوک‌های فعال‌سازی)
├── assets/                          # فایل‌های استاتیک (CSS, JS, Images)
│   ├── css/
│   └── js/
├── includes/                        # هسته و زیرساخت افزونه
│   ├── class-loader.php             # ثبت و مدیریت متمرکز تمام هوک‌های وردپرس
│   └── class-activator.php          # نصب، ارتقاء و ساخت جداول دیتابیس (dbDelta)
├── libs/                            # کتابخانه‌های کمکی
│   └── class-jalali-converter.php   # تبدیل تاریخ میلادی↔شمسی
├── models/                          # لایه داده (تعامل با دیتابیس)
│   ├── class-reservation.php        # جدول رزروها (wp_cr_reservations)
│   ├── class-schedule-rule.php      # قوانین هفتگی (wp_cr_schedule_rules)
│   └── class-date-manager.php       # استثناهای تقویم (wp_cr_date_ranges)
├── services/                        # منطق تجاری
│   ├── class-slot-generator.php     # تولید اسلات‌های زمانی خالی
│   └── class-email-service.php      # ارسال ایمیل‌های تراکنشی
├── controllers/                     # مدیریت درخواست‌ها
│   ├── class-booking-controller.php # فرآیند رزرو در فرانت‌اند
│   └── class-admin-controller.php   # بخش ادمین (منوها، تنظیمات)
└── views/                           # قالب‌های HTML
    ├── booking-wizard.php           # فرم رزرو چندمرحله‌ای
    └── admin/
        ├── dashboard.php            # لیست رزروها
        ├── calendar.php             # مدیریت تقویم/تعطیلات
        └── settings.php             # تنظیمات افزونه

4. طراحی پایگاه داده (Database Schema)

افزونه با پیشوند cr_ سه جدول اختصاصی ایجاد می‌کند. پیشوند wp_ بسته به تنظیمات وردپرس ممکن است تغییر کند.

4.1 جدول رزروها (wp_cr_reservations)

ذخیره تمامی نوبت‌های ثبت‌شده توسط کاربران.

ستون نوع داده توضیحات
id BIGINT UNSIGNED شناسه یکتا (Primary Key, Auto Increment)
reservation_type VARCHAR(20) نوع نوبت (online یا in_person)
jalali_date VARCHAR(10) تاریخ شمسی (مثال: 1403/11/20)
gregorian_date DATE تاریخ میلادی (برای کوئری‌ها و مرتب‌سازی)
time_slot VARCHAR(5) ساعت نوبت (مثال: 14:30)
first_name VARCHAR(100) نام
last_name VARCHAR(100) نام خانوادگی
phone VARCHAR(15) شماره تماس (فرمت استاندارد: 09xxxxxxxxx)
description TEXT توضیحات اختیاری
confirmation_code CHAR(3) کد پیگیری کوتاه (تولید تصادفی)
status VARCHAR(20) وضعیت (reserved, deleted)
created_at DATETIME زمان ایجاد رکورد

ایندکس‌ها و محدودیت‌ها:

  • UNIQUE KEY (reservation_type, gregorian_date, time_slot) برای جلوگیری از رزرو تکراری روی یک اسلات مشخص.
  • KEY (status) و KEY (phone) برای بهبود سرعت جستجو در پیشخوان.

4.2 جدول قوانین هفتگی (wp_cr_schedule_rules)

ستون نوع داده توضیحات
id INT UNSIGNED شناسه یکتا (Primary Key)
reservation_type VARCHAR(20) نوع نوبت (online یا in_person)
weekday TINYINT روز هفته (0=شنبه تا 6=جمعه)
start_time VARCHAR(5) ساعت شروع (مثال: 08:00)
end_time VARCHAR(5) ساعت پایان (مثال: 12:00)
interval_minutes TINYINT طول هر نوبت به دقیقه (مثال: 15)
is_active TINYINT(1) فعال/غیرفعال (1=فعال)

4.3 جدول استثناهای تقویم (wp_cr_date_ranges)

ستون نوع داده توضیحات
id BIGINT UNSIGNED شناسه یکتا (Primary Key)
start_date DATE تاریخ شروع بازه استثنا
end_date DATE تاریخ پایان بازه استثنا
is_working_day TINYINT(1) آیا روز کاری است؟ (1=بله، 0=تعطیل)
start_time VARCHAR(5) ساعت شروع (اگر is_working_day = 1)
end_time VARCHAR(5) ساعت پایان (اگر is_working_day = 1)

5. قراردادهای API (API Contracts)

تمامی ارتباطات سمت کلاینت با سرور از طریق درخواست‌های POST به admin-ajax.php و با پاسخ‌های JSON انجام می‌شود. همه درخواست‌ها باید شامل nonce معتبر باشند.

قواعد عمومی

  • URL: wp-admin/admin-ajax.php
  • Method: POST
  • Nonce: پیشنهاد: cr_nonce (یا هر نامی که در فرانت تولید می‌کنید)
  • پاسخ استاندارد:
    • موفق: { "success": true, "data": {...} }
    • خطا: { "success": false, "data": { "code": "...", "message": "..." } }

5.1 دریافت تاریخ‌های آزاد

Action: cr_get_available_dates
Method: POST
پارامترها: action, reservation_type (online|in_person), nonce

نمونه پاسخ موفق:

{
  "success": true,
  "data": {
    "dates": [
      {
        "gregorian_date": "2024-02-10",
        "jalali_date": "1402/11/21",
        "weekday": 0,
        "available_slots": 12,
        "display": "شنبه ۲۱ بهمن"
      }
    ]
  }
}

5.2 دریافت ساعت‌های خالی

Action: cr_get_time_slots
Method: POST
پارامترها: action, reservation_type, gregorian_date (YYYY-MM-DD), nonce

پارامترهای ورودی

نام نوع اجباری توضیح
action string مقدار ثابت cr_get_time_slots
reservation_type string online یا in_person
gregorian_date string فرمت YYYY-MM-DD
nonce string nonce معتبر

خطاهای رایج

  • INVALID_NONCE : nonce نامعتبر
  • INVALID_TYPE : نوع رزرو نامعتبر
  • INVALID_DATE : تاریخ نامعتبر

نمونه پاسخ موفق

{
  "success": true,
  "data": {
    "gregorian_date": "2024-02-10",
    "reservation_type": "online",
    "slots": [
      { "time": "08:00", "available": true,  "display": "08:00" },
      { "time": "08:15", "available": false, "display": "08:15" }
    ]
  }
}

نمونه پاسخ خطا

{
  "success": false,
  "data": {
    "code": "INVALID_DATE",
    "message": "فرمت تاریخ نامعتبر است."
  }
}

5.3 ثبت نهایی رزرو

Action: cr_submit_reservation
Method: POST

پارامترهای ورودی

نام نوع اجباری توضیح
action string مقدار ثابت cr_submit_reservation
first_name string نام
last_name string نام خانوادگی
phone string ترجیحاً 09xxxxxxxxx
time_slot string HH:MM
gregorian_date string YYYY-MM-DD
jalali_date string YYYY/MM/DD
reservation_type string online یا in_person
description string توضیحات اختیاری
nonce string nonce معتبر

قوانین اعتبارسنجی

  • phone: الگو مثل ^09\d{9}$
  • time_slot: الگو مثل ^\d{2}:\d{2}$ و باید جزء اسلات‌های همان روز باشد
  • جلوگیری از رزرو تکراری:
    • چک نرم‌افزاری قبل از insert
    • و اتکا به UNIQUE KEY دیتابیس

نمونه پاسخ موفق

{
  "success": true,
  "data": {
    "message": "رزرو با موفقیت انجام شد",
    "confirmation_code": "854",
    "reservation": {
      "reservation_type": "online",
      "gregorian_date": "2024-02-10",
      "jalali_date": "1402/11/21",
      "time_slot": "08:00",
      "first_name": "علی",
      "last_name": "رضایی",
      "phone": "09123456789",
      "status": "reserved"
    }
  }
}

نمونه پاسخ خطا (اسلات قبلاً رزرو شده)

{
  "success": false,
  "data": {
    "code": "SLOT_ALREADY_RESERVED",
    "message": "این زمان قبلاً رزرو شده است. لطفاً زمان دیگری انتخاب کنید."
  }
}

6. الگوریتم‌های کلیدی (Core Algorithms)

6.1 تولید اسلات‌های زمانی (CR_Slot_Generator::generate_slots)

این الگوریتم قلب سیستم است و با ترکیب «قوانین هفتگی» و «استثناهای تقویم»، لیست نهایی اسلات‌های قابل رزرو را تولید می‌کند.

مراحل اجرا:

  1. بررسی استثناهای تقویم با CR_Date_Manager:
    • اگر is_working_day = 0 → روز تعطیل → خروجی آرایه خالی
    • اگر is_working_day = 1 و start_time/end_time مشخص باشد → ساعات استثنا جایگزین قوانین هفتگی می‌شوند
  2. اگر استثنا نبود: قانون هفتگی همان weekday از CR_Schedule_Rule خوانده می‌شود.
  3. تولید اسلات خام: حلقه از start_time تا end_time با گام interval_minutes.
  4. خواندن ساعات رزرو شده از CR_Reservation::get_reserved_slots.
  5. علامت‌گذاری available و بازگرداندن نتیجه.

6.2 محاسبه روزهای آزاد (برای تقویم)

برای اکشن cr_get_available_dates:

  • بازه‌ای از روزهای آینده (مثلاً N روز) پیمایش می‌شود
  • برای هر روز اسلات‌ها تولید می‌شوند
  • اگر تعداد اسلات آزاد > 0 باشد آن روز در خروجی قرار می‌گیرد

نکته عملکردی: برای N بزرگ، استفاده از Transient برای کش کوتاه‌مدت پیشنهاد می‌شود.

6.3 جلوگیری از تداخل رزرو (Race Condition)

دو لایه دفاع:

  1. لایه نرم‌افزاری: is_slot_reserved() درست قبل از insert
  2. لایه دیتابیس: UNIQUE (reservation_type, gregorian_date, time_slot)
    • اگر خطای duplicate رخ داد → همان پیام «اسلات قبلاً رزرو شده»

7. جریان داده (Data Flow): فرآیند ثبت رزرو

مسیر کامل رزرو از دید کاربر و سیستم:

  1. کاربر صفحه حاوی شورت‌کد [reservation_booking] را باز می‌کند.
  2. CR_Booking_Controller قالب booking-wizard.php را رندر می‌کند.
  3. فرانت‌اند درخواست cr_get_available_dates ارسال می‌کند.
  4. کنترلر ورودی‌ها را اعتبارسنجی کرده و CR_Slot_Generator را فراخوانی می‌کند.
  5. پاسخ JSON می‌آید و تقویم در UI نمایش داده می‌شود.
  6. کاربر تاریخ را انتخاب می‌کند و درخواست cr_get_time_slots ارسال می‌شود.
  7. کنترلر اسلات‌ها را تولید/علامت‌گذاری کرده و JSON برمی‌گرداند.
  8. کاربر یک ساعت خالی را انتخاب می‌کند و فرم را تکمیل می‌کند و cr_submit_reservation ارسال می‌شود.
  9. کنترلر nonce را بررسی و ورودی‌ها را sanitize می‌کند.
  10. قبل از ثبت نهایی برای جلوگیری از Race Condition دوباره چک می‌شود.
  11. اگر اسلات خالی باشد رکورد جدید ساخته می‌شود و confirmation_code تولید می‌گردد.
  12. CR_Email_Service ایمیل تایید را ارسال می‌کند.
  13. پاسخ موفقیت همراه کد پیگیری به کاربر بازگردانده می‌شود.

8. ملاحظات امنیتی (Security Measures)

8.1 Nonce Verification (CSRF Protection)

  • همه درخواست‌های AJAX باید nonce معتبر داشته باشند.
  • در کنترلر با wp_verify_nonce() بررسی شوند.
  • در صورت خطا:
    • کد: INVALID_NONCE

8.2 Input Sanitization & Validation

  • هیچ داده‌ای بدون پاکسازی وارد دیتابیس نمی‌شود:
    • sanitize_text_field() برای فیلدهای کوتاه
    • sanitize_textarea_field() برای توضیحات
  • اعتبارسنجی‌های سخت‌گیرانه:
    • phone با Regex
    • تاریخ و ساعت با Regex + اعتبارسنجی منطقی

8.3 SQL Injection Prevention

  • تمام کوئری‌ها با $wpdb->prepare() نوشته شوند.
  • از الحاق مستقیم رشته‌ها در SQL اجتناب شود.

8.4 Access Control (Admin)

  • برای صفحات/اکشن‌های ادمین بررسی دسترسی با current_user_can() الزامی است.
  • پیشنهاد: محدودسازی به manage_options

8.5 XSS Prevention

  • در View ها:
    • متن: esc_html()
    • attribute: esc_attr()
    • URL: esc_url()

8.6 Rate Limiting

برای جلوگیری از سوءاستفاده:

  • محدودسازی تعداد cr_submit_reservation بر اساس IP/شماره در بازه زمانی (Transient)
  • کد خطا: RATE_LIMITED

8.7 Privacy & Logging

  • از ذخیره شماره تلفن کامل در لاگ‌ها خودداری شود (ماسک/هش).
  • در خروجی API از ارسال داده‌های غیرضروری خودداری شود (Minimal Exposure)