WP Manifestindependent plugin directory
manifest / events / custom-reservation

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

a custom wordpress reservation plugin

★ 0stars

Install

Download source zip No release zip yet

This is the repository archive. It installs, but the folder name will carry the branch suffix and updates will not flow.

Install straight from GitHub with WP-CLI:

wp plugin install https://github.com/alirezakmaxim/custom-reservation/archive/refs/heads/main.zip
No built-in updater. New versions won't appear under Dashboard → Updates. Install new releases yourself, or let Git Updater track the repository.

این سند «منبع حقیقت» (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)