← جميع المقالات >
magento 2pluginsinterceptorsbefore after aroundmagento developmentphpe-commercetutorial

إضافات Magento 2: شرح طرق Before و After و Around

Share: LinkedIn X Facebook

إضافات Magento 2 (وتسمى أيضًا المعترضات) تتيح لك تخصيص سلوك النواة أو الوحدات الخارجية دون لمس الكود الأصلي — وهو شرط أساسي للبقاء متوافقًا مع التحديثات. يشرح هذا الدليل الأنواع الثلاثة للإضافات — before و after و around — مع أمثلة واقعية، وصيغة di.xml، والمزالق الشائعة، والقواعد التي يفرضها Magento داخليًا.

باختصار: إضافة Magento 2 تعترض دالة عامة في كلاس لتنفيذ كود قبل أو بعد أو حول تلك الدالة، دون تعديل الكلاس الأصلي. استخدم before لتغيير الوسائط، و after لتغيير النتيجة، و around فقط عندما تحتاج كليهما — أو لمنع التنفيذ بالكامل.

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

  1. ما هي إضافة Magento 2 (المعترض)؟
  2. الأنواع الثلاثة للإضافات: Before و After و Around
  3. إضافة Before: تعديل الوسائط قبل التنفيذ
  4. إضافة After: تحويل النتيجة
  5. إضافة Around: تحكم كامل (استخدم بحذر)
  6. التصريح في di.xml: الصيغة و sortOrder
  7. القيود والدوال الممنوعة
  8. الإضافات مقابل مراقبي الأحداث: متى تختار ماذا؟
  9. أفضل الممارسات والأداء
  10. الأسئلة الشائعة التقنية

ما هي إضافة Magento 2 (المعترض)؟

الإضافة، وتسمى أيضًا المعترض، هي كلاس يعدل سلوك الدوال العامة في كلاس آخر عن طريق اعتراض استدعاء الدالة وتنفيذ كود قبل أو بعد أو حول ذلك الاستدعاء.

على عكس إعادة كتابة الكلاسات (class preferences)، لا تقوم الإضافات بتعديل الكلاس المستهدف نفسه. إنها تسمح بتوسيع أو تعديل الكود الأساسي بطريقة آمنة ومتوافقة مع التحديثات. يقوم Adobe Commerce و Magento Open Source بتنفيذ هذه المعترضات بالتسلسل وفقًا لـ sortOrder المكون، مما يتجنب التعارضات بين الإضافات.


الأنواع الثلاثة للإضافات: Before و After و Around

النوعالبادئةوقت التنفيذحالة الاستخدام الرئيسيةتأثير الأداء
Beforebefore + اسمالدالةقبل الدالة المراقبةتعديل وسائط الإدخالمنخفض
Afterafter + اسمالدالةبعد الدالة المراقبةتعديل النتيجة المرتجعةمنخفض
Aroundaround + اسمالدالةقبل و بعدتحكم كامل، شرطي⚠️ مرتفع

إضافة Before: تعديل الوسائط قبل التنفيذ

إضافات Before تنفذ أولاً، قبل الدالة المراقبة. يجب أن تحمل البادئة before متبوعة بالاسم الدقيق للدالة الهدف.

حالة استخدام نموذجية

تعديل مجموعة العميل بناءً على نطاق البريد الإلكتروني قبل الحفظ.

<?php
declare(strict_types=1);

namespace Vendor\Module\Plugin;

use Magento\Customer\Api\CustomerRepositoryInterface;
use Magento\Customer\Api\Data\CustomerInterface;
use Magento\Framework\App\Config\ScopeConfigInterface;

class SetCustomerGroupByEmailDomain
{
    private const XML_PATH_SPECIAL_DOMAINS = 'customer/groups/special_email_domains';
    private const SPECIAL_GROUP_ID = 2;

    public function __construct(
        private ScopeConfigInterface $scopeConfig,
    ) {}

    public function beforeSave(
        CustomerRepositoryInterface $subject,
        CustomerInterface $customer,
        $passwordHash = null,
    ) {
        $email = $customer->getEmail();
        $domains = $this->getSpecialDomains();

        foreach ($domains as $domain) {
            if (str_ends_with($email, '@' . trim($domain))) {
                $customer->setGroupId(self::SPECIAL_GROUP_ID);
                break;
            }
        }

        return [$customer, $passwordHash];
    }

    private function getSpecialDomains(): array
    {
        $domainsString = $this->scopeConfig->getValue(self::XML_PATH_SPECIAL_DOMAINS);
        return $domainsString ? explode(',', $domainsString) : [];
    }
}

قواعد إضافات Before

  • أعد مصفوفة من الوسائط المعدلة بنفس ترتيب توقيع الدالة الأصلية.
  • إذا لم تعدل شيئًا، أعد null (وليس مصفوفة فارغة).
  • إذا كانت المعامل اختياريًا (= null) في الدالة الأصلية، يجب أن يكون اختياريًا في الإضافة أيضًا.

إضافة After: تحويل النتيجة

إضافات After تتدخل بعد تنفيذ الدالة المراقبة مباشرة. تستقبل النتيجة الأصلية ويمكنها تعديلها قبل إعادتها.

مثال: خصم ولاء على سعر المنتج

<?php
declare(strict_types=1);

namespace Vendor\Module\Plugin;

use Magento\Catalog\Model\Product;
use Magento\Customer\Model\Session as CustomerSession;

class LoggedInCustomerLoyaltyDiscount
{
    private const XML_PATH_DISCOUNT = 'sales/loyalty/discount_percent';

    public function __construct(
        private CustomerSession $customerSession,
        private ScopeConfigInterface $scopeConfig,
    ) {}

    public function afterGetPrice(Product $subject, $result)
    {
        if ($this->customerSession->isLoggedIn()) {
            $discount = (float) $this->scopeConfig->getValue(self::XML_PATH_DISCOUNT) ?: 0;
            $result = $result * (1 - $discount / 100);
        }

        return $result;
    }
}

إضافة Around: تحكم كامل (استخدم بحذر)

إضافة Around توفر أقصى تحكم: تنفذ قبل وبعد الدالة المراقبة. تستقبل callable $proceed الذي يمثل الدالة الأصلية (أو الإضافة التالية في السلسلة).

⚠️ تحذير مهم

Magento لا ينصح بشدة باستخدام إضافات Around إلا عند الضرورة القصوى. لأنها:

  • تزيد حجم مكدس الاستدعاءات
  • تخفض الأداء
  • تعقد التصحيح
  • تشجع الكود المتشابك

مثال صحيح: تسجيل شرطي

<?php
declare(strict_types=1);

namespace Vendor\Module\Plugin;

use Magento\Catalog\Model\Product;
use Psr\Log\LoggerInterface;

class ProductSaveLogger
{
    public function __construct(
        private LoggerInterface $logger,
    ) {}

    public function aroundSave(
        Product $subject,
        callable $proceed,
    ) {
        $this->logger->info('قبل الحفظ: ID ' . $subject->getId());

        $result = $proceed(); // تنفيذ الدالة الأصلية

        $this->logger->info('بعد الحفظ: ID ' . $subject->getId());

        return $result;
    }
}

متى تستخدم Around؟

  • تحتاج إلى تعديل كل من الوسائط والنتيجة.
  • تحتاج إلى التحكم في تنفيذ الدالة الأصلية (مثال: feature flag).
  • في جميع الحالات الأخرى، استخدم before + after.

التصريح في di.xml: الصيغة و sortOrder

يجب التصريح بكل إضافة في ملف etc/di.xml (أو etc/frontend/di.xml أو etc/adminhtml/di.xml حسب المنطقة).

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">

    <type name="Magento\Customer\Api\CustomerRepositoryInterface">
        <plugin name="vendor_module_set_customer_group"
                type="Vendor\Module\Plugin\SetCustomerGroupByEmailDomain"
                sortOrder="10"
                disabled="false" />
    </type>

    <type name="Magento\Catalog\Model\Product">
        <plugin name="vendor_module_loyalty_discount"
                type="Vendor\Module\Plugin\LoggedInCustomerLoyaltyDiscount"
                sortOrder="20" />
    </type>

</config>

سمات عقدة <plugin>

السمةمطلوبالوصف
nameمعرف فريد للإضافة (يستخدم لدمج الإعدادات)
typeكلاس PHP الخاص بالإضافة (FQCN)
sortOrderترتيب التنفيذ (الأصغر = يُنفذ أولاً)
disabledtrue لتعطيل إضافة من النواة/طرف ثالث دون حذف الكود

🔗 إذا كنت تنشئ أول إضافة لك، ابدأ بفهم أساسيات تطوير وحدات Magento. اتبع دليلنا التفصيلي حول بناء وحدة Magento 2 مخصصة قبل تنفيذ الإضافات أو المراقبين أو التفضيلات.


القيود والدوال الممنوعة

الإضافات لا يمكن استخدامها على:

  • ❌ الدوال والكلاسات final
  • ❌ الدوال غير العامة (private, protected)
  • ❌ الدوال الثابتة (static)
  • __construct و __destruct
  • ❌ الأنواع الافتراضية
  • ❌ الكائنات التي تم إنشاؤها قبل تهيئة Magento\Framework\Interception
  • ❌ الكلاسات التي تطبق Magento\Framework\ObjectManager\NoninterceptableInterface

🚨 مأزق شائع: محاولة اعتراض دالة protected تولد خطأ صامتًا — الإضافة ببساطة تُتجاهل.


الإضافات مقابل مراقبي الأحداث: متى تختار ماذا؟

المعيارإضافةمراقب حدث
الهدفدالة محددة في كلاسحدث منتشر في جميع أنحاء النظام
التحكمدقيق (وسائط، نتيجة، تدفق)محدود (رد فعل على حدث)
الاقترانقوي (مرتبط بكلاس)ضعيف (مفصول)
الأداءمباشرقد يتم تشغيله عدة مرات

قاعدة القرار

  • إضافة: تحتاج إلى تعديل سلوك دالة محددة، مدخلاتها أو مخرجاتها.
  • مراقب: تحتاج إلى التفاعل مع حدث يمكن أن يحدث في أماكن متعددة (مثال: checkout_cart_save_after).

⚠️ تحذير: لا تخلط الإضافات والمراقبين لنفس المنطق دون إتقان ترتيب التنفيذ. مراقب يُشغل قبل إضافة before تعدل البيانات يمكن أن يسبب تناقضات.


أفضل الممارسات والأداء

1. فضّل Before + After على Around

كل إضافة Around تضيف إطارًا إلى مكدس الاستدعاءات. على موقع عالي الحركة، يترجم هذا إلى زمن استجابة قابل للقياس.

2. احترم sortOrder

ترتيب التنفيذ يتبع القواعد التالية:

  1. جميع إضافات before تنفذ من الأصغر إلى الأكبر sortOrder
  2. ثم إضافات around (النصف الأول ← $proceed() ← النصف الثاني)
  3. أخيرًا إضافات after من الأصغر إلى الأكبر sortOrder

3. سمِّ إضافاتك بوضوح

<!-- ❌ سيء -->
<plugin name="my_plugin" ... />

<!-- ✅ جيد -->
<plugin name="vendor_module_customer_group_by_email_domain" ... />

4. تجنب الإضافات على الدوال كثيرة الاستدعاء

لا تثقل getPrice() أو getName() أو getId() على مجموعات كاملة. فضّل الأحداث أو إعادة كتابة النموذج إذا لزم الأمر.

5. اختبر مع تعطيل الإضافات

<plugin name="vendor_module_logger" disabled="true" />

هذا يسمح بالتحقق بسرعة مما إذا كان الخلل ناتجًا عن الاعتراض الخاص بك.


الأسئلة الشائعة التقنية

هل يمكن تكديس إضافات متعددة على نفس الدالة؟

نعم. يقوم Magento بربطها تلقائيًا وفقًا لـ sortOrder. إذا كانت إضافتان لهما نفس sortOrder، فإن ترتيب تحميل الوحدات (المحدد في module.xml) يحدد التسلسل.

هل يمكن لإضافة منع تنفيذ الدالة الأصلية؟

فقط إضافة Around يمكنها فعل ذلك — بعدم استدعاء $proceed(). هذا غير موصى به إلا في حالات استثنائية (feature flags، وضع الصيانة).

لماذا لا يتم تنفيذ الإضافة الخاصة بي؟

تحقق بهذا الترتيب:

  1. هل الدالة public؟
  2. هل الكلاس final؟
  3. هل تم مسح ذاكرة التخزين المؤقت (bin/magento cache:clean
  4. هل ملف di.xml في الدليل الصحيح (etc/ مقابل etc/frontend/
  5. هل هناك خطأ نحوي في FQCN للنوع type؟

ما الفرق بين الإضافة وتفضيل الكلاس؟

التفضيل (<preference>) يستبدل الكلاس المستهدف بالكامل. الإضافة تعترض دون استبدال. فضّل دائمًا الإضافات من أجل التوافق العكسي.


الخلاصة والخطوات التالية

ما تعلمتهإجراء فوري
الأنواع الثلاثة للمعترضات (Before, After, Around)حدد دالة نواة لتعديلها في مشروعك
صيغة di.xml و sortOrderأنشئ أول إضافة لك في بيئة اختبار
القيود ومزالق الأداءراجع إضافاتك الحالية — استبدل إضافات around غير الضرورية
الفرق بين الإضافة والمراقبوثق اختيارك المعماري

هل تحتاج لوحدات مخصصة مع تطبيقات إضافات نظيفة؟ اطلع على خدمات تطوير الوحدات المخصصة .