اتخطّى للمحتوى

المرحلة 03 · الـ APIs والتكاملدرس 14 من 219 دقيقة قراءةآخر تحديث:

Lesson 14 / 21

Checkoutبالتفصيل.

أعقد جزء في Magento وأكتر واحد بيلمس كل الطبقات. هنفهم المعمارية (quote → order، الـ Knockout، الطبقات)، وبعدين الجانب العملي (إزاي تضيف خطوة، حقل، أو تعدّل shipping/payment).

● Architecture + Custom Quote → Order Knockout.js UI Components

الـ checkout في Magento 2 بيشتغل على Quote، وهو السلة المؤقتة اللي فيها المنتجات والعناوين وطرق الشحن والدفع والإجماليات. لما العميل يدوس Place Order، البيانات بتتنسخ لـ Order دائم. الواجهة مبنية بـ Knockout.js و UI Components، والإجماليات بتتحسب بـ total collectors، والتخصيص بيتم بالـ layout والـ JS components والـ plugins.

// ليه الـ checkout مختلف الـ checkout مش صفحة PHP عادية. هو تطبيق JavaScript (SPA) مبني على Knockout.js وUI Components، بيتواصل مع الـ backend عن طريق REST APIs. عشان كده تخصيصه مختلف تمامًا عن باقي الصفحات — لازم تشتغل frontend وbackend مع بعض.
◆ Part 1 — المعمارية
01

الصورة الكاملة

إيه اللي بيحصل من "أضف للسلة" لحد "تم الطلب".

1
Add to Cart — بيتعمل Quote (سلة مؤقتة) وبيتخزّن فيها المنتجات.
2
Shipping Step — العميل بيدخل العنوان، والنظام بيحسب طرق الشحن.
3
Payment Step — بيختار طريقة الدفع، والـ totals بتتحسب نهائيًا.
4
Place Order — الـ Quote بيتحوّل لـ Order دائم، والدفع بيتم.
المفهوم المحوري

طول الـ checkout، أنت بتتعامل مع Quote (سلة مؤقتة). لحظة "Place Order" بس، الـ Quote بيتحوّل لـ Order (طلب دائم). ده أهم تحوّل في الرحلة كلها.

تشبيه الـ Quote زي عربية التسوّق في السوبرماركت — مؤقتة، بتزوّد وتشيل منها بحرية. الـ Order زي الفاتورة بعد الكاشير — ثابتة، اتسجّلت، مش بتتغيّر.
• • •
02

الـ Quote — قلب الـ checkout

كل حاجة في الـ checkout بتدور حوالين الـ Quote. افهمه كويس.

التعريف

الـ Quote هو تمثيل السلة/الطلب المؤقت قبل التأكيد. بيحمل كل شيء: المنتجات، الكميات، عنوان الشحن، طريقة الدفع، الخصومات، والـ totals.

مكوّناته
  • Quote — الكيان الرئيسي (quote table).
  • Quote Items — المنتجات في السلة.
  • Quote Address — عناوين الشحن والفواتير.
  • Quote Payment — طريقة الدفع المختارة.
ليه مؤقت ومنفصل عن الـ Order

لأن السلة بتتغيّر كتير (زوّد، شيل، غيّر عنوان). لو كانت order من الأول، هيبقى فوضى من الطلبات الناقصة. الـ Quote بيسمح بالتغيير الحر، والـ Order بيتعمل مرة واحدة عند التأكيد.

i
الـ Quote بيتعامل معاه عن طريق الـ CartRepositoryInterface والـ CartManagementInterface — دي الـ service contracts اللي الـ checkout APIs مبنية عليها.
• • •
03

Quote → Order

أهم لحظة: تحويل السلة المؤقتة لطلب دائم.

Quote
مؤقت · متغيّر
Order
دائم · ثابت
اللي بيحصل عند Place Order
  • الـ QuoteManagement::placeOrder() بيتنادى.
  • الـ Quote data بتتنسخ لـ Order (منتجات، عناوين، totals).
  • الـ payment بيتنفّذ (authorize/capture).
  • الـ inventory بيتحجز/يتخصم (reservations).
  • الـ Quote بيتعلّم كـ inactive، والـ Order بيتحفظ.
  • events بتتطلق (sales_order_place_after) للـ observers.
Order structure

الـ Order بيتكوّن من Order، Order Items، Order Address، Order Payment — بنية موازية للـ Quote بس دائمة. وبعدها بتيجي الـ Invoice وShipment وCredit Memo.

!
الـ Order في Magento flat مش EAV (على عكس المنتج). عشان الطلبات محتاجة كتابة سريعة وبنيتها ثابتة — مقايضة الأداء ضد المرونة.
• • •
04

الـ Frontend (Knockout)

ليه الـ checkout مختلف عن باقي الصفحات في Magento.

إيه اللي بيشغّله

الـ checkout مبني كـ single-page application (SPA) باستخدام Knockout.js. مش بيعمل reload للصفحة — بيحدّث أجزاء منها ديناميكيًا وبيتواصل مع الـ backend بالـ AJAX/REST.

المكوّنات الأساسية
  • checkout_index_index.xml — الـ layout اللي بيعرّف بنية الـ checkout كلها.
  • JS Components — ملفات Knockout بتدير كل جزء (shipping, payment, summary).
  • HTML Templates — قوالب .html للـ UI (مش phtml).
  • LayoutProcessor — الـ class اللي بيبني الـ JS layout config من الـ PHP.
تشبيه باقي صفحات Magento زي كتاب (بتقلب صفحة كل مرة = reload). الـ checkout زي تطبيق موبايل — بيتحدّث في مكانه بدون ما يقفل ويفتح.
i
ده السبب إن تخصيص الـ checkout بيحتاج JavaScript مش بس PHP. لو حد قال "أضيف حقل في phtml"، ده مبيشتغلش في الـ checkout.
• • •
05

Totals & Collectors

إزاي الأسعار النهائية بتتحسب (subtotal, shipping, tax, discount).

إيه هو

الـ totals (الإجماليات) بتتحسب عن طريق سلسلة من الـ collectors — كل واحد مسؤول عن جزء: subtotal، shipping، tax، discount، grand total. بيشتغلوا بترتيب معيّن.

ليه نظام collectors مش حساب واحد

عشان كل نوع حساب معزول وقابل للإضافة. لو عايز تضيف رسم مخصّص (زي رسوم توصيل خاصة)، بتضيف collector جديد من غير ما تلمس الباقي — تطبيق للـ Open/Closed principle.

etc/sales.xmlxml
<!-- register a custom total collector -->
<section name="quote">
  <group name="totals">
    <item name="custom_fee"
      instance="Vendor\Module\Model\Total\CustomFee"
      sort_order="350"/>
  </group>
</section>
i
الـ sort_order بيحدد ترتيب الحساب. مهم إن الـ tax يتحسب بعد الـ subtotal والـ discount مثلًا، عشان النتيجة تطلع صح.
• • •
◆ Part 2 — التخصيص العملي
06

تخصيص: إضافة حقل

أشهر مطلب: إضافة حقل جديد (زي "تعليمات التوصيل").

الخطوات الكاملة
  • LayoutProcessor plugin — تضيف الحقل في الـ JS layout config.
  • الحقل يظهر في الـ shipping أو payment step عن طريق الـ config ده.
  • extension_attributes.xml — تعرّف attribute جديد على الـ Quote/Address.
  • JS: set-shipping-information — تعدّل عشان تبعت قيمة الحقل للـ backend.
  • Plugin على الـ backend — تحفظ القيمة على الـ Quote.
  • نقل للـ Order — observer/plugin بينقل القيمة من Quote للـ Order عند التحويل.
LayoutProcessor.php (مبسّط)php
public function process($jsLayout)
{
    $jsLayout['components']['checkout']
        ['children']['steps']...['delivery_note'] = [
        'component' => 'Magento_Ui/js/form/element/abstract',
        'config' => [
            'customScope' => 'shippingAddress',
            'template' => 'ui/form/field',
            'elementTmpl' => 'ui/form/element/input',
        ],
        'label' => __('Delivery Note'),
    ];
    return $jsLayout;
}
!
لاحظ إن الحقل الواحد بيحتاج شغل في 3 أماكن: الـ frontend (يظهر)، الـ JS (يتبعت)، والـ backend (يتحفظ + ينتقل للـ order). ده اللي بيخلّي الـ checkout معقّد.
• • •
07

تخصيص: إضافة خطوة

إضافة step كاملة جديدة (زي "gift options" أو "تأكيد").

المكوّنات المطلوبة
  • checkout_index_index.xml — تسجّل الـ step الجديدة في الـ layout.
  • JS Component — يورّث من uiComponent ويعرّف الـ step.
  • HTML template — الـ UI بتاع الخطوة.
  • sortOrder — يحدد مكان الخطوة في التسلسل.
  • navigation logic — الانتقال من وللخطوة.
custom-step.js (مبسّط)javascript
return Component.extend({
    defaults: {
        template: 'Vendor_Module/custom-step'
    },

    // register this step
    initialize: function () {
        this._super();
        registry.set(this.name, {
            isVisible: observable(true)
        });
        return this;
    },

    navigate: function () { /* go to step */ },
    sortOrder: 15
});
i
الـ steps الأساسية sortOrder بتاعها: shipping = 1، payment = 2. لو عايز خطوتك بينهم، خليها sortOrder بين 1 و 2 (زي 15 لو الأرقام بالعشرات).
• • •
08

تخصيص: Shipping & Payment

إضافة طريقة شحن أو دفع مخصّصة.

الأساس

تعمل carrier class بيورّث من AbstractCarrier وبيطبّق CarrierInterface. الـ method collectRates() بترجّع الأسعار المتاحة.

Model/Carrier/Custom.phpphp
class Custom extends AbstractCarrier
    implements CarrierInterface
{
    protected $_code = 'custom_shipping';

    public function collectRates(RateRequest $request)
    {
        $result = $this->rateResultFactory->create();
        $method = $this->rateMethodFactory->create();
        $method->setPrice(10.00);
        $result->append($method);
        return $result;
    }
}
الأساس

طرق الدفع الحديثة بتستخدم Payment Provider Gateway. بتعرّف الـ method في payment.xml وconfig.xml، وتعمل JS component للـ frontend، وأحيانًا Command classes للـ authorize/capture.

  • config.xml — إعدادات الـ method الافتراضية.
  • payment JS renderer — الـ UI في الـ payment step.
  • Gateway Commands — authorize, capture, refund.
  • ACL + system.xml — إعدادات الأدمن.
!
للدفع الحقيقي، متخزّنش بيانات الكروت أبدًا. استخدم gateway خارجي (Stripe/PayPal/Braintree) وتعامل مع tokens بس — عشان PCI compliance.
• • •
09

الأداء وأسئلة الإنترفيو

تحسين أداء الـ checkout والأسئلة المتوقعة.

  • JS bundling/minification — الـ checkout تقيل بالـ JS، فده بيفرق كتير.
  • راجع الـ plugins على quote/totals — بتتنادى عشرات المرات، أي plugin تقيل بيتضاعف.
  • قلّل الـ collectors المخصّصة التقيلة — كل collector بيشتغل مع كل تحديث.
  • الـ estimate-shipping-methods — من أبطأ الـ APIs، راقبها.
  • caching للبيانات الثابتة — زي طرق الشحن لو مش بتتغيّر كتير.
Q1إيه الفرق بين Quote و Order؟
الإجابة: الـ Quote سلة مؤقتة متغيّرة أثناء الـ checkout. الـ Order طلب دائم ثابت بيتعمل عند "Place Order" بنسخ بيانات الـ Quote. الـ Quote بيبقى EAV-like مرن، الـ Order flat للسرعة.
Q2إزاي تضيف حقل في الـ checkout؟
الإجابة: LayoutProcessor plugin للعرض، extension attribute للبيانات، JS تعديل عشان يتبعت، plugin على الـ backend للحفظ، ونقل القيمة للـ order. الحقل بيحتاج شغل frontend وbackend مع بعض.
Q3ليه الـ checkout بطيء وإزاي تحسّنه؟
الإجابة: غالبًا JS تقيل أو plugins على quote/totals بتتنادى كتير. الحل: bundling، مراجعة الـ plugins، وتقليل الـ collectors التقيلة.
Q4إزاي بتتحسب الـ totals؟
الإجابة: عن طريق سلسلة total collectors، كل واحد مسؤول عن جزء (subtotal, shipping, tax, discount) وبيشتغلوا بترتيب sort_order. تقدر تضيف collector مخصّص في sales.xml.

الـ Checkout بالتفصيل ✓

دلوقتي فاهم الـ checkout من الجذور: المعمارية (Quote → Order، Knockout، Collectors) والتخصيص العملي (حقل، خطوة، shipping، payment). ده أعقد جزء في Magento وأكتر واحد بيميّز المطوّر المحترف.

الخلاصة اللي تفتكرها: الـ Quote هو قلب الـ checkout، والتحويل لـ Order هو اللحظة الحاسمة، والتخصيص دايمًا بيلمس frontend (Knockout) و backend (Quote) مع بعض.

// magento 2 backend · checkout deep dive · architecture + custom

خلّصت الدرس؟علّمه عشان تتابع تقدّمك في الكورس.

كاتب الدرس

Abdulrahman Masoud

عندك سؤال على الدرس أو محتاج مساعدة في مشروع Magento؟ كلّمني.

أسئلة شائعة

ما الفرق بين Quote و Order في Magento 2؟

الـ Quote هو السلة المؤقتة طول الـ checkout، بيحمل المنتجات والكميات والعناوين وطريقة الدفع والخصومات والـ totals وبيتغيّر بحرية. الـ Order هو الطلب الدائم اللي بيتعمل لحظة Place Order بنسخ بيانات الـ Quote، ومبيتغيّرش بعدها. وجداول الـ Order في Magento flat مش EAV عشان الكتابة تبقى سريعة.

إيه اللي بيحصل لما العميل يدوس Place Order في Magento 2؟

بيتنادى QuoteManagement::placeOrder()، وبيانات الـ Quote (المنتجات والعناوين والـ totals) بتتنسخ لـ Order، والـ payment بيتنفّذ (authorize أو capture)، والمخزون بيتحجز عن طريق الـ reservations. بعدها الـ Quote بيتعلّم inactive والـ Order بيتحفظ، وevents زي sales_order_place_after بتتطلق للـ observers.

ليه الـ checkout في Magento 2 مختلف عن باقي الصفحات؟

لأن الـ checkout مش صفحة phtml عادية، ده single-page application مبني على Knockout.js والـ UI Components وبيكلّم الـ backend بالـ REST APIs من غير reload. بنيته متعرّفة في checkout_index_index.xml والـ UI في قوالب .html، وعشان كده أي تخصيص محتاج JavaScript مش PHP بس.

إزاي أضيف حقل جديد في الـ checkout في Magento 2؟

الحقل محتاج شغل frontend وbackend مع بعض: تضيفه في الـ JS layout عن طريق LayoutProcessor، وتعرّف extension attribute على الـ Quote/Address في extension_attributes.xml، وتعدّل الـ JS بتاع set-shipping-information عشان يبعت القيمة. بعدها plugin على الـ backend يحفظ القيمة على الـ Quote، وobserver أو plugin ينقلها للـ Order وقت التحويل.

إزاي بتتحسب الـ totals في Magento 2 checkout؟

عن طريق سلسلة total collectors، كل واحد مسؤول عن جزء: subtotal وshipping وtax وdiscount وgrand total، وبيشتغلوا بالترتيب حسب sort_order. ولو عايز رسم مخصّص بتسجّل collector جديد في etc/sales.xml تحت الـ section بتاع الـ quote من غير ما تلمس باقي الحسابات.

إزاي أعمل custom shipping method في Magento 2؟

بتعمل carrier class بيورّث من AbstractCarrier وبيطبّق CarrierInterface، وبتحدّد له $_code. والـ method collectRates(RateRequest $request) هي اللي بترجّع الأسعار المتاحة كـ result فيه الـ methods وسعر كل واحدة.

ليه الـ checkout في Magento 2 بطيء وإزاي أحسّن أداءه؟

غالبًا السبب JavaScript تقيل، أو plugins على الـ quote والـ totals بتتنادى عشرات المرات، أو collectors مخصّصة تقيلة بتشتغل مع كل تحديث. الحل: JS bundling وminification، مراجعة الـ plugins، تقليل الـ collectors التقيلة، مراقبة estimate-shipping-methods، وcaching للبيانات الثابتة زي طرق الشحن.