إضافات Magento 2: شرح طرق Before و After و Around
إضافات Magento 2 (وتسمى أيضًا المعترضات) تتيح لك تخصيص سلوك النواة أو الوحدات الخارجية دون لمس الكود الأصلي — وهو شرط أساسي للبقاء متوافقًا مع التحديثات. يشرح هذا الدليل الأنواع الثلاثة للإضافات — before و after و around — مع أمثلة واقعية، وصيغة di.xml، والمزالق الشائعة، والقواعد التي يفرضها Magento داخليًا.
باختصار: إضافة Magento 2 تعترض دالة عامة في كلاس لتنفيذ كود قبل أو بعد أو حول تلك الدالة، دون تعديل الكلاس الأصلي. استخدم
beforeلتغيير الوسائط، وafterلتغيير النتيجة، وaroundفقط عندما تحتاج كليهما — أو لمنع التنفيذ بالكامل.
جدول المحتويات
- ما هي إضافة Magento 2 (المعترض)؟
- الأنواع الثلاثة للإضافات: Before و After و Around
- إضافة Before: تعديل الوسائط قبل التنفيذ
- إضافة After: تحويل النتيجة
- إضافة Around: تحكم كامل (استخدم بحذر)
- التصريح في di.xml: الصيغة و sortOrder
- القيود والدوال الممنوعة
- الإضافات مقابل مراقبي الأحداث: متى تختار ماذا؟
- أفضل الممارسات والأداء
- الأسئلة الشائعة التقنية
ما هي إضافة Magento 2 (المعترض)؟
الإضافة، وتسمى أيضًا المعترض، هي كلاس يعدل سلوك الدوال العامة في كلاس آخر عن طريق اعتراض استدعاء الدالة وتنفيذ كود قبل أو بعد أو حول ذلك الاستدعاء.
على عكس إعادة كتابة الكلاسات (class preferences)، لا تقوم الإضافات بتعديل الكلاس المستهدف نفسه. إنها تسمح بتوسيع أو تعديل الكود الأساسي بطريقة آمنة ومتوافقة مع التحديثات. يقوم Adobe Commerce و Magento Open Source بتنفيذ هذه المعترضات بالتسلسل وفقًا لـ sortOrder المكون، مما يتجنب التعارضات بين الإضافات.
الأنواع الثلاثة للإضافات: Before و After و Around
| النوع | البادئة | وقت التنفيذ | حالة الاستخدام الرئيسية | تأثير الأداء |
|---|---|---|---|---|
| Before | before + اسمالدالة | قبل الدالة المراقبة | تعديل وسائط الإدخال | منخفض |
| After | after + اسمالدالة | بعد الدالة المراقبة | تعديل النتيجة المرتجعة | منخفض |
| Around | around + اسمالدالة | قبل و بعد | تحكم كامل، شرطي | ⚠️ مرتفع |
إضافة 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 | ❌ | ترتيب التنفيذ (الأصغر = يُنفذ أولاً) |
disabled | ❌ | true لتعطيل إضافة من النواة/طرف ثالث دون حذف الكود |
🔗 إذا كنت تنشئ أول إضافة لك، ابدأ بفهم أساسيات تطوير وحدات 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
ترتيب التنفيذ يتبع القواعد التالية:
- جميع إضافات
beforeتنفذ من الأصغر إلى الأكبرsortOrder - ثم إضافات
around(النصف الأول ←$proceed()← النصف الثاني) - أخيرًا إضافات
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، وضع الصيانة).
لماذا لا يتم تنفيذ الإضافة الخاصة بي؟
تحقق بهذا الترتيب:
- هل الدالة
public؟ - هل الكلاس
final؟ - هل تم مسح ذاكرة التخزين المؤقت (
bin/magento cache:clean)؟ - هل ملف
di.xmlفي الدليل الصحيح (etc/مقابلetc/frontend/)؟ - هل هناك خطأ نحوي في FQCN للنوع
type؟
ما الفرق بين الإضافة وتفضيل الكلاس؟
التفضيل (<preference>) يستبدل الكلاس المستهدف بالكامل. الإضافة تعترض دون استبدال. فضّل دائمًا الإضافات من أجل التوافق العكسي.
الخلاصة والخطوات التالية
| ما تعلمته | إجراء فوري |
|---|---|
| الأنواع الثلاثة للمعترضات (Before, After, Around) | حدد دالة نواة لتعديلها في مشروعك |
صيغة di.xml و sortOrder | أنشئ أول إضافة لك في بيئة اختبار |
| القيود ومزالق الأداء | راجع إضافاتك الحالية — استبدل إضافات around غير الضرورية |
| الفرق بين الإضافة والمراقب | وثق اختيارك المعماري |
هل تحتاج لوحدات مخصصة مع تطبيقات إضافات نظيفة؟ اطلع على خدمات تطوير الوحدات المخصصة .