Magento 2 Observers & Events : دليل المطور الشامل
البنيان المعتمد على الأحداث في ماجنتو 2 يتيح لك التفاعل مع أي شيء تقريبًا يحدث في النظام — تقديم طلب، تسجيل عميل، حفظ منتج، إجراءات المسؤول — دون تعديل الكود الأساسي أو تمديد الفئات. يستمع observers لأحداث مسماة وينفذ منطقًا عند إطلاق تلك الأحداث. يغطي هذا الدليل كيفية تعريف observers، وما هي الأحداث المضمنة المتاحة، وكيفية إرسال الأحداث المخصصة الخاصة بك، ومتى تختار observers بدلاً من الـ plugins .
باختصار: observer هو فئة تستمع لحدث مسمى وتشغل كودًا عند إرسال هذا الحدث. صرّح عنه في
events.xml(عام) أوevents.xmlمقتصر على منطقة (frontend،adminhtml،webapi_rest،graphql). استخدم observers عندما تحتاج للتفاعل مع حدث ليس له طريقة محددة يمكنك تطبيق plugin عليها — على سبيل المثال، “بعد تقديم الطلب” (sales_order_place_after) غير مرتبط باستدعاء طريقة واحدة.
جدول المحتويات
- الأحداث مقابل Plugins: متى تستخدم ماذا
- تعريف observer في events.xml
- توقيع فئة Observer
- الأحداث حسب المنطقة: Frontend, Adminhtml, Web API, GraphQL
- أكثر الأحداث المضمنة فائدة في ماجنتو 2
- إرسال الأحداث المخصصة
- تمرير البيانات إلى observers عبر الحدث
- ترتيب تنفيذ observers وإيقاف الانتشار
- أفضل الممارسات والمزالق الشائعة
- الأسئلة الشائعة
الأحداث مقابل Plugins: متى تستخدم ماذا
قبل كتابة observer، تحقق مما إذا كان plugin سيكون أنظف. إليك قاعدة عامة:
| السيناريو | أفضل نهج |
|---|---|
| تعديل وسائط أو قيمة إرجاع طريقة محددة | Plugin |
| التفاعل مع “حدث شيء ما” (تم تقديم طلب، تم تسجيل دخول عميل) | Observer |
| تنفيذ منطق يمتد عبر عدة فئات غير مرتبطة | Observer |
| الحاجة لتغليف تنفيذ الطريقة بالكامل | Plugin (around) |
| الحدث الذي تحتاجه غير موجود بعد | Plugin، أو أرسل حدثًا مخصصًا |
الفرق الرئيسي: الـ plugins ترتبط بالطرق، الـ observers ترتبط بالأحداث. يمكن إرسال الأحداث من أي مكان — نماذج، وحدات تحكم، مساعدين، حتى observers أخرى — ويمكن لعدة observers الاستماع لنفس الحدث دون معرفة بعضهم البعض.
تعريف observer في events.xml
أنشئ etc/events.xml في وحدتك (للأحداث العامة) أو قصره على منطقة:
<!-- app/code/Vendor/Module/etc/events.xml -->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
<event name="sales_order_place_after">
<observer name="vendor_module_order_placed"
instance="Vendor\Module\Observer\OrderPlaced"
shared="false"/>
</event>
</config>
يجب أن تكون سمة name على <observer> فريدة ضمن نطاق الحدث. استخدم بادئة (اسم البائع/الوحدة) لتجنب التعارضات. shared="false" ينشئ مثيلًا جديدًا في كل مرة — استخدمه ما لم يكن observer عديم الحالة.
توقيع فئة Observer
كل observer يطبق \Magento\Framework\Event\ObserverInterface بطريقة واحدة execute():
<?php
declare(strict_types=1);
namespace Vendor\Module\Observer;
use Magento\Framework\Event\ObserverInterface;
use Magento\Framework\Event\Observer;
class OrderPlaced implements ObserverInterface
{
public function __construct(
private readonly \Psr\Log\LoggerInterface $logger
) {}
public function execute(Observer $observer): void
{
$order = $observer->getEvent()->getOrder();
$this->logger->info('Order placed: ' . $order->getIncrementId());
}
}
يمنحك كائن Observer الوصول إلى getEvent()، الذي يعيد كائن بيانات الحدث. من هناك، تسترجع البيانات المحددة باستخدام getters مسماة حسب مفاتيح بيانات الحدث. Getters شائعة: getOrder()، getProduct()، getCustomer()، getQuote().
الأحداث حسب المنطقة: Frontend, Adminhtml, Web API, GraphQL
events.xml العام يُطلق في جميع المناطق. قصره على منطقة محددة بوضع الملف في دليل etc/ الخاص بالمنطقة:
app/code/Vendor/Module/
├── etc/
│ └── events.xml ← global (all areas)
├── etc/frontend/
│ └── events.xml ← frontend only
├── etc/adminhtml/
│ └── events.xml ← admin panel only
├── etc/webapi_rest/
│ └── events.xml ← REST API only
└── etc/graphql/
└── events.xml ← GraphQL only
هذا مفيد عندما تحتاج نفس الوحدة لسلوك مختلف في مناطق مختلفة. على سبيل المثال، تسجيل طلب في الواجهة الأمامية ولكن إرسال إشعار مسؤول في adminhtml.
أكثر الأحداث المضمنة فائدة في ماجنتو 2
أحداث الدفع والمبيعات
| اسم الحدث | البيانات المتاحة |
|---|---|
sales_order_place_after | order |
sales_order_save_after | order |
checkout_cart_add_product_complete | product, request |
checkout_onepage_controller_success_action | order_ids |
sales_quote_collect_totals_after | quote |
أحداث العميل
| اسم الحدث | البيانات المتاحة |
|---|---|
customer_register_success | customer, account_controller |
customer_login | customer |
customer_logout | customer |
customer_address_save_after | customer_address, customer |
أحداث الكتالوج والمنتجات
| اسم الحدث | البيانات المتاحة |
|---|---|
catalog_product_save_after | product |
catalog_product_load_after | product |
catalog_category_save_after | category |
catalog_product_is_salable_before | product |
يمكنك العثور على القائمة الكاملة في vendor/magento/framework/Event/etc/events.xsd وبالبحث عن ->dispatch( في كود ماجنتو.
إرسال الأحداث المخصصة
إرسال الأحداث الخاصة بك يجعل وحدتك قابلة للتوسع — يمكن للمطورين الآخرين الاتصال بها دون تعديل كودك.
use Magento\Framework\Event\ManagerInterface;
class SomeService
{
public function __construct(
private readonly ManagerInterface $eventManager
) {}
public function doSomething(string $sku, array $data): void
{
// ... business logic ...
$this->eventManager->dispatch(
'vendor_module_something_done',
['sku' => $sku, 'result' => $data]
);
}
}
اصطلاح أسماء الأحداث: أحرف صغيرة، بادئة البائع، أجزاء مفصولة بشرطة سفلية. الوسيطة الثانية هي مصفوفة ترابطية — كل مفتاح يصبح متاحًا عبر getSku()، getResult()، إلخ على كائن حدث observer.
تمرير البيانات إلى observers عبر الحدث
عند الإرسال، تصبح مفاتيح المصفوفة أسماء getters على بيانات الحدث:
$this->eventManager->dispatch('custom_event', [
'order' => $order,
'items' => $items,
'source' => 'cron'
]);
في observer:
public function execute(Observer $observer): void
{
$order = $observer->getEvent()->getOrder();
$items = $observer->getEvent()->getItems();
$source = $observer->getEvent()->getSource();
}
مفاتيح البيانات تتبع اصطلاح camelCase. أحداث ماجنتو المضمنة تستخدم أسماء مفردة للكائنات الفردية (order، product، customer) وجمع للمجموعات (order_ids، items).
ترتيب تنفيذ observers وإيقاف الانتشار
يتم تنفيذ observers لنفس الحدث بالترتيب الذي تظهر به في events.xml. إذا كنت بحاجة للتحكم في الترتيب عبر الوحدات، استخدم معامل sort_order على عنصر <observer> (الأرقام الأقل تُنفذ أولاً):
<event name="sales_order_place_after">
<observer name="first_module" instance="Vendor\First\Observer" sort_order="10"/>
<observer name="second_module" instance="Vendor\Second\Observer" sort_order="20"/>
</event>
لإيقاف تنفيذ observers اللاحقة، استدع stopPropagation():
public function execute(Observer $observer): void
{
if (!$this->config->isEnabled()) {
$observer->stopPropagation();
return;
}
// ... process ...
}
استخدم هذا باعتدال — إيقاف الانتشار يوقف بصمت وحدات أخرى تعتمد على نفس الحدث.
أفضل الممارسات والمزالق الشائعة
حافظ على observers خفيفة. يجب ألا يحتوي observer على منطق أعمال معقد. فوّض إلى الخدمات أو النماذج. يتم استدعاء observers بشكل متزامن — observer بطيء يعطل الطلب بأكمله.
لا تحقن أبدًا فئة ملموسة تطلق أحداثًا في منشئها. هذا يخلق حلقات لا نهائية. على سبيل المثال، لا تحقن repository في observer يستمع لحدث حفظ نفس الكيان.
استخدم
shared="false"للـ observers التي تحتفظ بحالة. إذا كان لدى observer تبعيات تتغير بين الاستدعاءات (مثل قيمة registry)، فإن إنشاء مثيل جديد في كل مرة يمنع البيانات القديمة.فضّل الـ plugins على الـ observers عندما تحتاج لتعديل وسائط طريقة أو قيم إرجاع. الـ plugins type-safe وأكثر قابلية للتنبؤ. استخدم observers فقط لسيناريوهات “رد الفعل”.
لا تعتمد على ترتيب تنفيذ observers عبر الوحدات. تحكم في الترتيب فقط داخل وحدتك الخاصة. إذا كان يجب أن يُنفذ observer قبل أو بعد observer من وحدة أخرى، فكر في استخدام plugin بدلاً من ذلك.
اختبر مع تمكين تحديد الأحداث. أضف
?debug=eventsإلى عنوان URL الخاص بك (مع وضع المطور) لرؤية الأحداث التي تُطلق على الصفحة. تحقق من استدعاء observer الخاص بك عند توقعه وعدم استدعائه عندما لا ينبغي.
الأسئلة الشائعة
س: هل يمكنني استخدام حقن التبعية في الـ observers؟
نعم. يتم حقن جميع تبعيات observer عبر المنشئ. حاوية DI في ماجنتو تحلها تلقائيًا.
س: ما الفرق بين events.xml و frontend/events.xml؟events.xml العام يُطلق في جميع المناطق. الملفات المقتصرة على منطقة تُطلق فقط عندما يعمل ماجنتو في تلك المنطقة (متجر أمامي، لوحة إدارة، REST API، أو GraphQL).
س: كيف أعثر على البيانات التي يوفرها الحدث؟
تحقق من استدعاء dispatch() في كود المصدر. مفاتيح المصفوفة المرسلة إلى dispatch() تصبح أسماء getters. على سبيل المثال، إذا أرسل الكود ['order' => $order]، observer الخاص بك يستدعي $observer->getEvent()->getOrder().
س: هل يمكن لـ observer واحد الاستماع لأحداث متعددة؟
ليس بشكل مباشر. كل فئة observer تحتاج تعريفًا منفصلاً في events.xml. ومع ذلك، يمكنك إنشاء فئة خدمة واحدة واستدعائها من عدة طرق execute() للـ observers.
س: هل الـ observers متاحة في GraphQL؟
نعم. GraphQL هي منطقتها الخاصة (graphql). ضع events.xml الخاص بك في etc/graphql/ لحصر observers على طلبات GraphQL فقط.
هل تحتاج إلى وحدة مخصصة مع observers وأحداث موصولة بشكل صحيح؟ تحقق من خدمة تطوير الوحدات المخصصة الخاصة بي أو اقرأ دليل الـ plugins للموضوع المكمل حول المعترضات.