طَبِّقْهاتعلّم المبدأ، وطبّقه بيدكمقرر 132 · تقنيات تطوير الواجهات الخلفية
الدرس 11 · الوحدة 5: تنفيذ المنطق التجاري وقواعد التطبيق

قواعد العمل في الكود

قلب الواجهة الخلفية: القواعد التي تحكم ما يُسمح به. ⏱ نحو 35 دقيقة.

بعد هذا الدرس ستستطيع أن:
  • تميّز قواعد العمل من التحقق من المدخلات.
  • تكتب القواعد في وحدة مستقلة بثوابت ودوال صغيرة.
  • ترد على مخالفة القواعد بـ 409، وترتّب الفحوص ترتيبًا صحيحًا.
  • تختبر القواعد بجدول حالات يشمل الحدود.

١ما المنطق التجاري؟

كل تطبيق يحمل قواعد من عالم العمل الحقيقي: «الشحن مجاني فوق 200 ريال»، «لا يُحجز أكثر من 3 مقاعد للمتدرب»، «لا يُسحب أكثر من الرصيد». هذه قواعد العمل Business rules، والكود الذي يطبّقها اسمه المنطق التجاري Business logic، وهو قلب الواجهة الخلفية.

التحقق من المدخلات (الدرس 7)قاعدة العمل
السؤالهل البيانات سليمة الشكل؟هل العملية مسموحة الآن؟
مثالالكمية عدد صحيح موجبالكمية لا تتجاوز المتوفر في المخزون
تحتاج؟الطلب وحدهالطلب + حالة النظام (المخزون، الرصيد، الحجوزات)
الرمز عند الفشل422409 Conflict غالبًا

وأين نكتبها؟ لا في المتصفح (يُتجاوز)، ولا مبعثرة داخل HTML، بل في وحدة مستقلة تستدعيها كل الصفحات. فإن تغيّرت القاعدة غدًا («الشحن مجاني فوق 150») عدّلت سطرًا واحدًا.

٢اكتب القواعد في مكان واحد

هذه قواعد الدفع في متجر الواحة: خصم للأعضاء، وشحن حسب المدينة ومجاني فوق حد، ثم الضريبة. كلها في ملف rules.php، وصفحة الدفع تستدعي دالة واحدة:

<?phprequire_once __DIR__ . '/rules.php'; $subtotal = (float) ($_GET['subtotal'] ?? 0);$member = ($_GET['member'] ?? '') === '1';$city = $_GET['city'] ?? 'الرياض'; $bill = checkout($subtotal, $member, $city);foreach ($bill as $k => $v) {    echo "$k: $v\n";}

ثلاث حالات: زائر بلا خصم يدفع شحنًا، وعضو في المدينة بخصم 5% وشحن 10، وعضو بطلب كبير خصمه 10% وشحنه مجاني.

٣القواعد التي تعتمد على حالة النظام

بعض القواعد تحتاج معرفة الوضع الحالي: هل يوجد مخزون؟ كم حجز المتدرب؟ إن خالف الطلب القاعدة نرد بـ 409 Conflict: الطلب سليم الشكل، لكنه يتعارض مع حالة النظام:

<?phpsession_start();const MAX_SEATS = 3;$stock = ['PHP' => 5, 'SQL' => 0, 'React' => 12];   // المقاعد المتاحة لكل دورة $course = $_POST['course'] ?? '';$mine = $_SESSION['seats'] ?? []; if (!isset($stock[$course])) {    http_response_code(422);    exit('دورة غير معروفة');}if ($stock[$course] < 1) {    http_response_code(409);    exit("لا توجد مقاعد في دورة $course");}if (count($mine) >= MAX_SEATS) {    http_response_code(409);    exit('لا يمكن حجز أكثر من ' . MAX_SEATS . ' دورات');}if (in_array($course, $mine)) {    http_response_code(409);    exit("سبق أن حجزت $course");}$_SESSION['seats'][] = $course;http_response_code(201);echo "تم الحجز. دوراتك: " . implode('، ', $_SESSION['seats']);

لاحظ ترتيب الفحوص: الشكل أولًا (422)، ثم القواعد (409)، ثم التنفيذ (201).

٤اختبر قواعدك بالجداول

قبل أن تثق بقاعدة، جرّبها على حالات مختارة، خاصة الحدود: ما قبل الحد وعنده وبعده. هذا أسلوب يستخدمه مختبرو البرمجيات (مقرر 245). اكتب جدول حالات ثم شغّل الدالة عليها:

<?php// rules.php: قواعد عمل متجر الواحة في مكان واحدconst FREE_SHIPPING_FROM = 200;const VAT = 0.15; function discountRate(float $subtotal, bool $member): float{    if ($member && $subtotal >= 300) return 0.10;    if ($member) return 0.05;    return 0;} function shipping(float $afterDiscount, string $city): float{    if ($afterDiscount >= FREE_SHIPPING_FROM) return 0;    return $city === 'المدينة المنورة' ? 10 : 25;} function checkout(float $subtotal, bool $member, string $city): array{    $discount = round($subtotal * discountRate($subtotal, $member), 2);    $after = $subtotal - $discount;    $ship = shipping($after, $city);    $vat = round(($after + $ship) * VAT, 2);    return ['subtotal' => $subtotal, 'discount' => $discount, 'shipping' => $ship,            'vat' => $vat, 'total' => $after + $ship + $vat];} $cases = [    [199.99, false, 'جدة'],    [200, false, 'جدة'],    [210, true, 'جدة'],      // بعد خصم 5% يصير 199.5 ← لا شحن مجاني!    [300, true, 'الرياض'],];foreach ($cases as [$s, $m, $c]) {    $b = checkout($s, $m, $c);    echo str_pad($s, 7) . ($m ? ' عضو ' : ' زائر') . " ← شحن {$b['shipping']}، المجموع {$b['total']}\n";}

الحالة الثالثة مفاجأة مفيدة: العضو اشترى بـ 210 لكنه دفع شحنًا، لأن القاعدة تطبَّق على المبلغ بعد الخصم. هل هذا ما يريده صاحب المتجر؟ هذا سؤال للعميل لا للمبرمج، وجدول الحالات كشفه قبل أن يشتكي زبون.

توقّع وتحقّق

«لا يُسحب أكثر من الرصيد» مثال على:

تعتمد على حالة النظام (الرصيد).

رمز الحالة المناسب لطلب يتعارض مع حالة النظام:

Conflict.

const VAT = 0.15; فائدتها:

بدل رقم سحري.

أين نكتب قواعد العمل؟

وتستدعيها كل الصفحات.

الخلاصة

تحدٍّ برمجي: قواعد السحب من المحفظة

متوسط

محفظة المتدرب الإلكترونية في المقصف. اكتب في wallet.php دالة القواعد وردود الطلب.

المطلوب:
  • الرصيد يبدأ 50 ريالًا ويُحفظ في $_SESSION['balance'].
  • طلب POST فيه amount: إن لم يكن عددًا موجبًا ← 422 والنص مبلغ غير صحيح.
  • إن تجاوز الحد الأقصى للعملية الواحدة (ثابت MAX_PER_TX قيمته 30) ← 409 والنص الحد الأقصى للعملية 30.
  • إن تجاوز الرصيد ← 409 والنص الرصيد غير كافٍ.
  • وإلا اخصم المبلغ ورد بـ 200 والنص تم. الرصيد: 30 (بالرصيد الجديد).
👀 اعرض الاستجابة المطلوبة
✓ التحقق الآلي: يرسل المختبر هذه الطلبات إلى خادمك بالترتيب ثم يفحص الاستجابات
  1. POST /wallet.php (amount=20)
  2. POST /wallet.php (amount=-5)
  3. POST /wallet.php (amount=40)
  4. POST /wallet.php (amount=25)
  5. POST /wallet.php (amount=30)
  6. POST /wallet.php (amount=abc)
  • الكود يعمل بلا أخطاء أو تحذيرات
  • سحب 20 ← تم. الرصيد: 30
  • سحب -5 ← 422
  • سحب 40 ← 409 (تجاوز الحد)
  • سحب 40 ← الحد الأقصى للعملية 30
  • سحب 25 ← تم. الرصيد: 5
  • سحب 30 والرصيد 5 ← الرصيد غير كافٍ (409)
  • abc ← 422 بلا تحذيرات
  • الحد معرّف ثابتًا MAX_PER_TX

💡 تلميح 1
ترتيب الفحوص: شكل المبلغ أولًا (422)، ثم الحد، ثم الرصيد (409).
💡 تلميح 2
FILTER_VALIDATE_FLOAT تعيد false للنص abc.

حاول بنفسك أولًا؛ للمسألة أكثر من حلّ صحيح.

<?phpsession_start();const MAX_PER_TX = 30;$_SESSION['balance'] = $_SESSION['balance'] ?? 50; $amount = filter_var($_POST['amount'] ?? '', FILTER_VALIDATE_FLOAT);if ($amount === false || $amount <= 0) {    http_response_code(422);    exit('مبلغ غير صحيح');}if ($amount > MAX_PER_TX) {    http_response_code(409);    exit('الحد الأقصى للعملية ' . MAX_PER_TX);}if ($amount > $_SESSION['balance']) {    http_response_code(409);    exit('الرصيد غير كافٍ');}$_SESSION['balance'] -= $amount;echo "تم. الرصيد: {$_SESSION['balance']}";
انتقل إلى تقييم الدرساختيار من متعدد، وصح وخطأ، وأكمل الفراغ، وتتبّع الناتج.ابدأ التقييم ←

📝 معلم؟ أرسل ملاحظة للمؤلف عن هذا الدرس