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

المرحلة 01 · الأساسياتدرس 5 من 3415 دقيقة قراءةآخر تحديث:

Lesson 05 / 34

Magento Eventsالمرجع الكامل.

الأحداث المهمة في Magento مقسّمة بالمجال — كل حدث بيتطلق امتى وامتى تستخدمه. وقبلها الأنماط اللي بتخلّيك تخمّن اسم أي حدث، وإزاي تكتشف الأحداث بنفسك في مشروعك. وفي الآخر، إزاي تعمل حدث خاص بيك.

● مرجع 7 مجالات الاكتشاف الذاتي حدث مخصّص

الـ Events في Magento 2 نظام إطلاق واستماع: كود بينادي dispatch باسم الحدث وبيانات، والـ Event Manager بينده كل observer مسجّل في events.xml. نص الأحداث بتتولّد من _eventPrefix بنمط زي catalog_product_save_after، فبتقدر تخمّن الاسم، أو تكتشفه في نسختك بـ grep على dispatch. الـ observer للتفاعل مع حدث، والـ plugin لتغيير نتيجة method.

// اقرا ده الأول أسماء الأحداث بتختلف بين إصدارات Magento — بعضها اتشال وبعضها اتغيّر. القائمة دي مرجع للتوجيه، مش مصدر نهائي. القسم 03 هو الأهم في الدرس ده: بيعلّمك تكتشف الأحداث الموجودة فعلاً في مشروعك بأمر واحد. دي المهارة اللي مش بتقدم.
◆ Part 1 — الفهم
01

إزاي بيشتغل نظام الأحداث

الفكرة بسيطة، والفهم ده بيخلّي الباقي منطقي.

كود بينده dispatch
Event Manager
كل الـ observers
الفكرة

كود في Magento بينادي "حصل كذا" ومعاه بيانات. الـ Event Manager بيدوّر على كل اللي مسجّل إنه مهتم بالحدث ده، ويناديهم واحد واحد. الكود اللي أطلق الحدث مايعرفش مين بيسمع ولا بيستنى نتيجة.

تشبيه زي إعلان في مكبّر صوت في مطار. اللي بيعلن مش عارف مين سامع ولا بيستنى رد — بيعلن ويكمّل. واللي مهتم بيتصرّف. لو محدش سامع، الإعلان بيعدي عادي.
الجزئين
  • الإطلاق — كود بينده dispatch('event_name', $data).
  • الاستماع — إنت بتسجّل observer في events.xml ويشتغل تلقائياً.
!
نقطة مهمة: الـ observers مش مضمون ترتيبها، والحدث مابيرجّعش نتيجة. لو محتاج تتحكم في قيمة راجعة أو تمنع عملية من الأساس، الـ plugin هو الأداة الصح مش الـ event — القسم 12 بيفصّل ده.
• • •
02

الأنماط — تخمين أي اسم

نص أحداث Magento بتتولّد تلقائياً بنمط ثابت. افهم النمط تستغنى عن نص القائمة.

من فين بتيجي

أي model بيورّث من AbstractModel بيطلق أحداث تلقائياً عند الحفظ والحذف والتحميل. كل model عنده خاصية اسمها _eventPrefix، والأحداث بتتركّب منها.

النمطtext
# الصيغة العامة
{_eventPrefix}_{action}_{timing}

# مثال: Product عنده _eventPrefix = catalog_product
catalog_product_save_before
catalog_product_save_after
catalog_product_delete_after
catalog_product_load_after

# مثال: Order عنده _eventPrefix = sales_order
sales_order_save_before
sales_order_save_after
التوقيتات المتاحة
timing
_save_before
قبل الحفظ، والبيانات لسه قابلة للتعديل.
امتىتعديل أو التحقق من البيانات قبل ما تتخزّن.
_save_after
بعد الحفظ، بس جوّه الـ transaction لسه.
امتىعمليات على قاعدة البيانات مرتبطة بنفس الحفظ.
_save_commit_after
بعد ما الـ transaction تتأكّد فعلاً.
امتىأي حاجة خارجية — إرسال إيميل، نداء API، رسالة queue. لأن هنا بس اتأكدت إن الحفظ نجح.
_delete_before / _delete_after
حوالين الحذف.
امتىتنظيف بيانات مرتبطة، أو منع الحذف (بـ exception في before).
_load_after
بعد تحميل الكيان من قاعدة البيانات.
امتىإضافة بيانات محسوبة للكيان. حذر: بيتنده كتير جداً.
!
الفرق بين _save_after و_save_commit_after بيسبب أخطاء صعبة. لو بعتّ إيميل في _save_after والـ transaction فشلت بعدها، العميل استلم إيميل عن حاجة مااتحفظتش. أي أثر خارجي مكانه _commit_after.
إزاي تعرف الـ prefix

افتح الـ model ودوّر على _eventPrefix. مثال: Magento\Catalog\Model\Product فيه protected $_eventPrefix = 'catalog_product';

المشكلة

في الـ observer بتقرا الكيان بـ $observer->getEvent()->getSomething() — بس إيه الـ something؟ ده محدّد بخاصية تانية اسمها _eventObject.

مثال من Productphp
// جوّه Magento\Catalog\Model\Product
protected $_eventPrefix = 'catalog_product';
protected $_eventObject = 'product';

// يعني في الـ observer:
$product = $observer->getEvent()->getProduct();

// وكمان دايماً متاح الاسم العام:
$product = $observer->getEvent()->getDataObject();
i
الـ getDataObject() متاح دايماً في أحداث الموديلات بغض النظر عن الـ _eventObject. مفيد لما تكون مش متأكد من الاسم.
• • •
03

اكتشف الأحداث بنفسك

أهم قسم في الدرس — بيغنيك عن أي قائمة محفوظة.

1 — دوّر على كل الأحداث المطلقة

كل إطلاق بيعدي على dispatch()، فالبحث عنه بيديك القائمة الحقيقية في نسختك بالظبط.

كل الأحداث في المشروعbash
# كل الأسماء، مرتّبة وبدون تكرار
grep -rhoP "dispatch\(\s*['\"]\K[a-z0-9_]+" \
  vendor/magento/ app/code/ | sort -u

# أحداث مجال معيّن
grep -rhoP "dispatch\(\s*['\"]\K[a-z0-9_]+" \
  vendor/magento/module-sales/ | sort -u

# الإطلاق مع مكانه — عشان تقرا السياق
grep -rn "dispatch('sales_order_place_after'" vendor/magento/
2 — شوف البيانات المتاحة مع الحدث

لما تلاقي مكان الإطلاق، اقرا السطر — هو بيقولك اسم كل متغيّر تقدر تقراه في الـ observer.

مثال إطلاقphp
$this->_eventManager->dispatch(
    'checkout_cart_product_add_after',
    ['quote_item' => $result, 'product' => $product]
);

// يعني في الـ observer متاح:
//   $observer->getEvent()->getQuoteItem()
//   $observer->getEvent()->getProduct()
3 — سجّل كل حدث بيتطلق فعلاً

لو مش عارف أنهي حدث بيتطلق في سيناريو معيّن، اعمل observer مؤقت على كل الأحداث وسجّلها.

observer تشخيصي مؤقتphp
class EventLogger implements ObserverInterface
{
    public function __construct(
        private readonly LoggerInterface $logger
    ) {}

    public function execute(Observer $observer): void
    {
        $this->logger->debug(
            'EVENT: ' . $observer->getEvent()->getName()
        );
    }
}
!
الطريقة التالتة للتشخيص المحلي بس. سجّل الـ observer على الأحداث اللي تشك فيها، شغّل السيناريو، اقرا الـ log، وبعدين شيله فوراً. لو فضل شغّال هيملا الـ log ويبطّئ كل request.
i
الطريقة الأولى هي اللي هتستخدمها 90% من الوقت. خليها في ملاحظاتك — بتجاوب "فيه حدث للحاجة دي؟" في ثانية، ومن نسختك مش من قائمة على الإنترنت.
• • •
◆ Part 2 — القوائم
04

الكتالوج والمنتجات

أحداث المنتجات والأقسام.

event
catalog_product_save_before
قبل حفظ المنتج، والبيانات قابلة للتعديل.
امتىتعديل خصائص تلقائياً، أو التحقق ومنع الحفظ برمي exception.
catalog_product_save_after
بعد الحفظ، جوّه الـ transaction.
امتىتحديث جداول مرتبطة في نفس العملية.
catalog_product_save_commit_after
بعد تأكيد الحفظ نهائياً.
امتىمزامنة مع ERP أو PIM، أو رسالة queue. ده المكان الصح للتكامل الخارجي.
catalog_product_delete_after
بعد حذف المنتج.
امتىتنظيف بيانات مخصّصة مرتبطة بالمنتج.
catalog_product_load_after
بعد تحميل المنتج.
حذربيتنده مئات المرات في صفحة قسم واحدة. أي منطق تقيل هنا بيقتل الأداء.
catalog_product_collection_load_after
بعد تحميل مجموعة منتجات.
امتىتعديل مجموعة كاملة مرة واحدة — أرخص بكتير من التعديل لكل منتج.
catalog_product_is_salable_after
بعد تحديد إذا كان المنتج قابل للبيع.
امتىمنطق توفّر مخصّص (مثلاً منع بيع منتج في دولة معيّنة).
catalog_controller_product_view
لما عميل يفتح صفحة منتج.
امتىتتبّع المشاهدات، أو تسجيل "شاهدت مؤخراً".
catalog_category_save_after
بعد حفظ قسم.
امتىمزامنة شجرة الأقسام مع نظام خارجي.
catalog_controller_category_init_after
بعد تحميل القسم في الصفحة.
امتىتعديل فلاتر أو ترتيب القسم ديناميكياً.
• • •
05

العملاء

التسجيل والدخول والعناوين.

event
customer_login
بعد تسجيل دخول ناجح.
امتىربط سلة الضيف بالعميل، أو تسجيل آخر دخول، أو منطق ترحيب.
customer_logout
بعد تسجيل الخروج.
امتىتنظيف بيانات جلسة مخصّصة.
customer_register_success
بعد إنشاء حساب جديد بنجاح.
امتىإضافة العميل لقائمة تسويقية، أو منحه كوبون ترحيبي.
customer_save_after_data_object
بعد حفظ بيانات العميل.
امتىمزامنة بيانات العميل مع CRM.
customer_delete_after
بعد حذف عميل.
امتىتنظيف بياناته من أنظمة مرتبطة (مهم للامتثال للخصوصية).
customer_address_save_after
بعد حفظ عنوان في دفتر العميل.
امتىالتحقق من العنوان مع خدمة خارجية، أو المزامنة.
customer_address_delete_after
بعد حذف عنوان من الدفتر.
امتىتنظيف المراجع المكسورة في السلال النشطة — الحالة اللي اتكلمنا عنها.
customer_account_edited
بعد تعديل العميل لحسابه.
امتىإشعار أمني بتغيير البيانات.
customer_customer_authenticated
بعد التحقق من بيانات الدخول.
امتىتحققات أمنية إضافية.
i
أحداث العملاء تحديداً كتير اتغيرت بين الإصدارات — فيه نسخ قديمة وجديدة لنفس الغرض. طبّق طريقة الاكتشاف من القسم 03 قبل ما تعتمد على أي اسم هنا.
• • •
06

السلة والـ Quote

من الإضافة للسلة لحد قبل التأكيد.

event
checkout_cart_product_add_after
بعد إضافة منتج للسلة.
امتىإضافة بيانات مخصّصة للصنف، أو تتبّع الإضافات.
checkout_cart_add_product_complete
بعد اكتمال عملية الإضافة كاملة.
امتىمنطق ما بعد الإضافة (اقتراح منتجات مكمّلة).
checkout_cart_update_items_after
بعد تحديث كميات السلة.
امتىإعادة حساب أو تحقق بعد التعديل.
sales_quote_save_before
قبل حفظ السلة.
امتىتعديل بيانات السلة قبل التخزين.
sales_quote_save_after
بعد حفظ السلة.
امتىتتبّع تغيّرات السلة.
sales_quote_add_item
عند إضافة صنف للسلة.
امتىتعديل الصنف وقت إنشائه.
sales_quote_remove_item
عند حذف صنف.
امتىتنظيف بيانات مرتبطة بالصنف.
sales_quote_collect_totals_before
قبل حساب الإجماليات.
حذربيتنده عشرات المرات في الـ checkout. خلّي المنطق خفيف جداً.
sales_quote_collect_totals_after
بعد حساب الإجماليات.
حذرنفس التحذير. للتعديل على الإجماليات استخدم total collector بدلاً منه.
sales_quote_merge_after
بعد دمج سلّتين.
امتىمعالجة ما بعد الدمج عند تسجيل الدخول.
• • •
07

الـ Checkout والأوردرات

أكتر المجالات استخداماً في التكاملات.

event
sales_order_place_before
قبل إنشاء الأوردر مباشرة.
امتىتحقق نهائي، أو تعديل بيانات قبل التثبيت.
sales_order_place_after
بعد إنشاء الأوردر بنجاح.
امتىالأشهر استخداماً — إرسال الأوردر لـ ERP، إشعار، أو رسالة queue.
checkout_submit_all_after
بعد اكتمال الـ checkout كله (الأوردر + السلة).
امتىلما تحتاج الأوردر والسلة مع بعض بعد التأكيد.
sales_order_save_after
بعد أي حفظ للأوردر.
امتىتتبّع أي تغيير. خلي بالك بيتنده كتير مش عند الإنشاء بس.
sales_order_save_commit_after
بعد تأكيد حفظ الأوردر نهائياً.
امتىأي تكامل خارجي مبني على الأوردر.
sales_order_state_change_before
قبل تغيير حالة الأوردر.
امتىمنطق مخصّص لدورة حياة الأوردر، أو منع انتقال معيّن.
sales_order_payment_place_end
بعد اكتمال عملية الدفع.
امتىمنطق ما بعد الدفع.
sales_model_service_quote_submit_before
قبل تحويل السلة لأوردر.
امتىنقل بيانات مخصّصة من الـ quote للـ order.
sales_model_service_quote_submit_failure
لما التحويل يفشل.
امتىتسجيل الفشل أو التنبيه — مفيد جداً لتشخيص الطلبات الضايعة.
!
لو بتبعت الأوردر لنظام خارجي، استخدم _commit_after واعمل العملية عن طريق queue مش مباشرة. لو نديت API خارجي داخل الـ observer والخدمة كانت بطيئة، العميل هيستنى — ولو وقعت، ممكن الأوردر يفشل.
• • •
08

الفواتير والشحنات

ما بعد الأوردر.

event
sales_order_invoice_register
عند تسجيل فاتورة.
امتىمنطق محاسبي وقت إصدار الفاتورة.
sales_order_invoice_pay
عند دفع الفاتورة.
امتىتأكيد الدفع لنظام خارجي.
sales_order_invoice_save_after
بعد حفظ الفاتورة.
امتىإرسال الفاتورة لنظام محاسبي أو للفوترة الإلكترونية.
sales_order_shipment_save_after
بعد حفظ الشحنة.
امتىإخطار شركة الشحن، أو إرسال رقم التتبّع للعميل.
sales_order_shipment_track_save_after
بعد إضافة رقم تتبّع.
امتىمزامنة التتبّع مع تطبيق الموبايل.
sales_order_creditmemo_save_after
بعد حفظ إشعار دائن (مرتجع).
امتىمعالجة المرتجعات والاسترداد في الأنظمة المرتبطة.
• • •
09

الـ Controllers والأدمن

أحداث على مستوى الطلب كله.

event
controller_action_predispatch
قبل تنفيذ أي action في الموقع.
حذربيتنده في كل request. للتحويلات العامة بس، وبمنطق خفيف جداً.
controller_action_predispatch_{route}
قبل action في مسار محدد.
امتىأفضل من العام بكتير — مثال: controller_action_predispatch_checkout_index_index.
controller_action_postdispatch
بعد تنفيذ الـ action.
امتىتعديل الاستجابة أو تسجيل.
controller_front_send_response_before
قبل إرسال الاستجابة للمتصفح.
امتىإضافة headers مخصّصة.
backend_auth_user_login_success
بعد دخول ناجح للأدمن.
امتىتسجيل الدخولات لأغراض التدقيق الأمني.
backend_auth_user_login_failed
بعد محاولة دخول فاشلة.
امتىكشف محاولات الاختراق والتنبيه.
adminhtml_cache_flush_all
عند مسح كل الكاش من الأدمن.
امتىتنظيف كاش مخصّص بتاعك مع كاش النظام.
• • •
10

العرض والـ Layout

أحداث بناء الصفحة.

event
layout_load_before
قبل تحميل الـ layout.
امتىإضافة handles ديناميكياً حسب شرط.
layout_generate_blocks_after
بعد توليد كل الـ blocks.
امتىتعديل أو حذف block برمجياً.
view_block_abstract_to_html_before
قبل تحويل أي block لـ HTML.
حذربيتنده لكل block في الصفحة — عشرات المرات. استخدمه بحذر شديد.
view_block_abstract_to_html_after
بعد توليد HTML الـ block.
حذرنفس التحذير. مفيد لتعديل مخرجات block معيّن، بس شيّك على اسمه الأول.
cms_page_render
عند عرض صفحة CMS.
امتىمنطق مخصّص لصفحات المحتوى.
i
لو محتاج تعدّل مخرجات block واحد بس، الأفضل plugin على الـ block ده بدل الاستماع لحدث بيتنده لكل block في الصفحة. الفرق في الأداء كبير.
• • •
◆ Part 3 — البناء
11

حدث مخصّص من الصفر

إزاي تطلق حدث بتاعك عشان غيرك يقدر يمدّد كودك.

ليه تعمل حدث أصلاً

عشان موديولك يبقى قابل للتمديد. لو موديولك بيعمل عملية مهمة، إطلاق حدث بيخلّي أي حد (إنت أو موديول تاني) يضيف سلوك من غير ما يعدّل كودك — نفس المبدأ اللي Magento نفسه مبني عليه.

Model/TicketProcessor.phpphp
namespace Vendor\Module\Model;

use Magento\Framework\Event\ManagerInterface;

class TicketProcessor
{
    public function __construct(
        private readonly ManagerInterface $eventManager
    ) {}

    public function process(Ticket $ticket): void
    {
        // حدث قبل — يسمح بالتعديل أو المنع
        $this->eventManager->dispatch(
            'vendor_module_ticket_process_before',
            ['ticket' => $ticket]
        );

        $this->doWork($ticket);

        // حدث بعد — مع النتيجة
        $this->eventManager->dispatch(
            'vendor_module_ticket_process_after',
            [
                'ticket' => $ticket,
                'result' => $ticket->getStatus(),
            ]
        );
    }
}
قواعد التسمية
  • حروف صغيرة و underscore بس — مفيش حروف كبيرة أو شرطات.
  • ابدأ باسم الـ vendor والموديول — يمنع التصادم مع أحداث تانية.
  • اختم بـ _before أو _after — يوضّح التوقيت فوراً.
  • اسم واضح للعمليةticket_process أحسن من handle.
!
الأسماء اللي بتمررها في المصفوفة بتبقى جزء من العقد العام لموديولك. لو غيّرت 'ticket' لـ 'ticket_object' بعدين، كل observer كتبه حد تاني هيقع. اختار الأسماء بعناية من الأول.
etc/events.xmlxml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <event name="vendor_module_ticket_process_after">
    <observer
      name="vendor_module_notify_on_ticket"
      instance="Vendor\Module\Observer\NotifyOnTicket"/>
  </event>
</config>
Observer/NotifyOnTicket.phpphp
namespace Vendor\Module\Observer;

use Magento\Framework\Event\Observer;
use Magento\Framework\Event\ObserverInterface;

class NotifyOnTicket implements ObserverInterface
{
    public function execute(Observer $observer): void
    {
        // الأسماء دي من مصفوفة الـ dispatch
        $ticket = $observer->getEvent()->getTicket();
        $result = $observer->getEvent()->getResult();

        if (!$ticket) {
            return;
        }

        // المنطق بتاعك هنا
    }
}
فين تحط الـ events.xml
path
etc/events.xml
بيشتغل في كل السياقات.
امتىلما الحدث ممكن يتطلق من أي مكان.
etc/frontend/events.xml
الواجهة بس.
امتىمنطق يخص العملاء فقط.
etc/adminhtml/events.xml
الأدمن بس.
امتىمنطق يخص إجراءات الأدمن فقط.
i
اختيار المكان الصح تحسين أداء مجاني. observer في etc/frontend/ مش بيتحمّل أصلاً في الأدمن ولا في الـ cron. لو مش متأكد، ابدأ بالمحدد ووسّع لو احتجت.
• • •
12

Event ولا Plugin؟

قرار بيتاخد غلط كتير.

Event / Observer

بتتفاعل مع حاجة حصلت. مابترجّعش قيمة، ومابتأثرش على سير العملية.

Plugin

بتتدخّل في نداء method. تقدر تعدّل المدخلات والمخرجات وتمنع التنفيذ.

استخدم Event لما
  • عايز تتفاعل مع حاجة حصلت (إشعار، تسجيل، مزامنة).
  • الحدث موجود فعلاً وبيديك البيانات اللي محتاجها.
  • مش محتاج تغيّر سلوك العملية نفسها.
  • عايز كذا موديول يتفاعل مع نفس الحاجة من غير تعارض.
استخدم Plugin لما
  • عايز تغيّر القيمة الراجعة من method.
  • عايز تعدّل المدخلات قبل التنفيذ.
  • عايز تمنع التنفيذ تحت شرط.
  • مفيش حدث مناسب أصلاً.
!
خطأ شائع: محاولة منع عملية بـ observer برمي exception. ده بيشتغل أحياناً في أحداث الـ before، بس السلوك مش مضمون وبيطلّع رسائل خطأ مربكة للمستخدم. لو عايز تمنع، استخدم plugin — دي وظيفته.
تشبيه الـ observer زي حد بيتفرّج على مباراة ويهتف — بيتفاعل بس مش بيغيّر النتيجة. الـ plugin زي الحكم — بيقدر يوقف اللعب ويغيّر القرار.
• • •
13

الفخاخ الشائعة

اقرا دي حتى لو مش هتقرا حاجة تانية.

  • منطق تقيل في حدث بيتنده كتيرload_after وcollect_totals وpredispatch بيتندهوا عشرات أو مئات المرات. نداء API واحد هناك بيقتل الصفحة.
  • أثر خارجي في _save_after بدل _commit_after — إيميل بيتبعت عن حاجة مااتحفظتش.
  • استثناء غير محسوب في observer — بيوقّف العملية الأصلية. لفّ المنطق الثانوي في try/catch وسجّل الخطأ.
  • الاعتماد على ترتيب الـ observers — الترتيب مش مضمون. لو محتاج ترتيب، المنطق ده مكانه مش هنا.
  • تعديل الكيان في _save_after وحفظه تاني — بيعمل حلقة لا نهائية. عدّل في _save_before.
  • observer عام والمطلوب محددcontroller_action_predispatch_checkout_index_index أرخص بمراحل من العام.
  • الحدث في المكان الغلطetc/events.xml بيحمّل في كل السياقات. حدده لو تقدر.
  • الاعتماد على اسم حدث من مقال قديم — تحقق من نسختك بـ grep الأول (القسم 03).
الشكل الآمن لأي observerphp
public function execute(Observer $observer): void
{
    $entity = $observer->getEvent()->getEntity();

    // 1. اخرج بدري لو البيانات مش موجودة
    if (!$entity || !$entity->getId()) {
        return;
    }

    try {
        // 2. المنطق
        $this->doWork($entity);
    } catch (\Exception $e) {
        // 3. متوقّفش العملية الأصلية
        $this->logger->error(
            'Observer failed: ' . $e->getMessage()
        );
    }
}
i
القاعدة: الـ observer بيعمل حاجة ثانوية. لو فشل، العملية الأساسية المفروض تكمّل عادي. الاستثناء الوحيد هو تحقق مقصود في حدث _before — وحتى ده الأفضل يتعمل بـ plugin.

Magento Events ✓

دلوقتي عندك الأنماط اللي بتخلّيك تخمّن أي اسم، طريقة اكتشاف الأحداث في نسختك، قوائم بأهم الأحداث في 7 مجالات، وإزاي تعمل حدث خاص بيك.

الخلاصة: افهم النمط بدل ما تحفظ القائمة، تحقق بالـ grep قبل ما تعتمد على أي اسم، وخلّي الـ observers خفيفة وآمنة الفشل.

// magento 2 · events · observers · reference

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

كاتب الدرس

Abdulrahman Masoud

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

أسئلة شائعة

إزاي أعرف اسم الحدث اللي بيتطلق في Magento 2؟

كل إطلاق بيعدّي على dispatch()، فدوّر عليه في الكود: grep -rhoP "dispatch\(\s*['\"]\K[a-z0-9_]+" vendor/magento/ app/code/ | sort -u بيطلّعلك كل الأحداث الموجودة فعلاً في نسختك. ولما تلاقي مكان الإطلاق اقرا المصفوفة اللي بتتبعت معاه، لأن مفاتيحها هي أسماء الـ getters اللي هتستخدمها في الـ observer.

إيه الفرق بين save_after و save_commit_after في Magento؟

_save_after بيتطلق بعد الحفظ بس جوّه الـ transaction، فلو الـ transaction فشلت بعده الحفظ بيترجع. _save_commit_after بيتطلق بعد ما الـ transaction تتأكّد فعلاً. أي أثر خارجي زي إيميل أو نداء API أو رسالة queue مكانه _save_commit_after، وإلا ممكن تبعت إشعار عن حاجة مااتحفظتش.

امتى أستخدم Observer وامتى أستخدم Plugin في Magento 2؟

الـ Observer بيتفاعل مع حاجة حصلت: إشعار أو تسجيل أو مزامنة، ومابيرجّعش قيمة ولا بيغيّر سير العملية. الـ Plugin بيتدخّل في نداء method معيّنة، فيقدر يعدّل المدخلات والمخرجات أو يمنع التنفيذ. لو عايز تغيّر نتيجة أو تمنع عملية استخدم plugin، ولو عايز تتفاعل مع حدث موجود استخدم observer.

إزاي أعمل حدث مخصّص في Magento 2؟

احقن Magento\Framework\Event\ManagerInterface في الكلاس بتاعك ونادي dispatch('vendor_module_ticket_process_after', ['ticket' => $ticket]) بعد العملية. الاسم بحروف صغيرة و underscore، يبدأ باسم الـ vendor والموديول عشان مايتصادمش مع أحداث تانية، وينتهي بـ _before أو _after. اللي عايز يستمع بيسجّل observer في etc/events.xml ويقرا البيانات بـ $observer->getEvent()->getTicket()، فمفاتيح المصفوفة جزء من العقد العام لموديولك.

إيه الفرق بين etc/events.xml و etc/frontend/events.xml؟

etc/events.xml بيتحمّل في كل السياقات: الواجهة والأدمن والـ cron والـ API. etc/frontend/events.xml للواجهة بس، وetc/adminhtml/events.xml للأدمن بس. لو الحدث بيخص العملاء فقط حطه في frontend، لأن الـ observer ساعتها مش بيتحمّل أصلاً في الأدمن ولا في الـ cron، وده تحسين أداء مجاني.

ليه الـ observer بتاعي بيبطّئ الموقع؟

غالبًا لأنه مسجّل على حدث بيتطلق كتير جدًا زي catalog_product_load_after أو sales_quote_collect_totals_after أو controller_action_predispatch أو view_block_abstract_to_html_before. الأحداث دي بتتنده عشرات أو مئات المرات في الصفحة الواحدة، فنداء API واحد جوّاها بيقتل الأداء. استخدم الحدث الأكتر تحديدًا، أو plugin على الكلاس المطلوب.

هل الـ observer ممكن يوقّف العملية الأصلية لو رمى exception؟

أيوه، أي exception غير محسوب في الـ observer بيوقّف العملية اللي أطلقت الحدث. عشان كده لو المنطق ثانوي، لفّه في try/catch وسجّل الخطأ. لو قاصد تمنع الحفظ فعلًا، ارمي LocalizedException في حدث _save_before، أو الأفضل استخدم before plugin على الـ method نفسها لأن ده دوره الأساسي.