← جميع المقالات >
magento 2observerseventsevent-driven architecturemagento developmentphptutorial

Magento 2 Observers & Events : دليل المطور الشامل

Share: LinkedIn X Facebook

البنيان المعتمد على الأحداث في ماجنتو 2 يتيح لك التفاعل مع أي شيء تقريبًا يحدث في النظام — تقديم طلب، تسجيل عميل، حفظ منتج، إجراءات المسؤول — دون تعديل الكود الأساسي أو تمديد الفئات. يستمع observers لأحداث مسماة وينفذ منطقًا عند إطلاق تلك الأحداث. يغطي هذا الدليل كيفية تعريف observers، وما هي الأحداث المضمنة المتاحة، وكيفية إرسال الأحداث المخصصة الخاصة بك، ومتى تختار observers بدلاً من الـ plugins .

باختصار: observer هو فئة تستمع لحدث مسمى وتشغل كودًا عند إرسال هذا الحدث. صرّح عنه في events.xml (عام) أو events.xml مقتصر على منطقة (frontend، adminhtml، webapi_rest، graphql). استخدم observers عندما تحتاج للتفاعل مع حدث ليس له طريقة محددة يمكنك تطبيق plugin عليها — على سبيل المثال، “بعد تقديم الطلب” (sales_order_place_after) غير مرتبط باستدعاء طريقة واحدة.

جدول المحتويات

  1. الأحداث مقابل Plugins: متى تستخدم ماذا
  2. تعريف observer في events.xml
  3. توقيع فئة Observer
  4. الأحداث حسب المنطقة: Frontend, Adminhtml, Web API, GraphQL
  5. أكثر الأحداث المضمنة فائدة في ماجنتو 2
  6. إرسال الأحداث المخصصة
  7. تمرير البيانات إلى observers عبر الحدث
  8. ترتيب تنفيذ observers وإيقاف الانتشار
  9. أفضل الممارسات والمزالق الشائعة
  10. الأسئلة الشائعة

الأحداث مقابل 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_afterorder
sales_order_save_afterorder
checkout_cart_add_product_completeproduct, request
checkout_onepage_controller_success_actionorder_ids
sales_quote_collect_totals_afterquote

أحداث العميل

اسم الحدثالبيانات المتاحة
customer_register_successcustomer, account_controller
customer_logincustomer
customer_logoutcustomer
customer_address_save_aftercustomer_address, customer

أحداث الكتالوج والمنتجات

اسم الحدثالبيانات المتاحة
catalog_product_save_afterproduct
catalog_product_load_afterproduct
catalog_category_save_aftercategory
catalog_product_is_salable_beforeproduct

يمكنك العثور على القائمة الكاملة في 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 ...
}

استخدم هذا باعتدال — إيقاف الانتشار يوقف بصمت وحدات أخرى تعتمد على نفس الحدث.

أفضل الممارسات والمزالق الشائعة

  1. حافظ على observers خفيفة. يجب ألا يحتوي observer على منطق أعمال معقد. فوّض إلى الخدمات أو النماذج. يتم استدعاء observers بشكل متزامن — observer بطيء يعطل الطلب بأكمله.

  2. لا تحقن أبدًا فئة ملموسة تطلق أحداثًا في منشئها. هذا يخلق حلقات لا نهائية. على سبيل المثال، لا تحقن repository في observer يستمع لحدث حفظ نفس الكيان.

  3. استخدم shared="false" للـ observers التي تحتفظ بحالة. إذا كان لدى observer تبعيات تتغير بين الاستدعاءات (مثل قيمة registry)، فإن إنشاء مثيل جديد في كل مرة يمنع البيانات القديمة.

  4. فضّل الـ plugins على الـ observers عندما تحتاج لتعديل وسائط طريقة أو قيم إرجاع. الـ plugins type-safe وأكثر قابلية للتنبؤ. استخدم observers فقط لسيناريوهات “رد الفعل”.

  5. لا تعتمد على ترتيب تنفيذ observers عبر الوحدات. تحكم في الترتيب فقط داخل وحدتك الخاصة. إذا كان يجب أن يُنفذ observer قبل أو بعد observer من وحدة أخرى، فكر في استخدام plugin بدلاً من ذلك.

  6. اختبر مع تمكين تحديد الأحداث. أضف ?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 للموضوع المكمل حول المعترضات.