برنامج Magento 2 التعليمي: كيفية بناء وحدة نمطية كاملة
نشرت: 2022-03-11تعد Magento حاليًا أكبر منصة للتجارة الإلكترونية مفتوحة المصدر في العالم. نظرًا لقاعدة رموزها الغنية والقابلة للتوسيع ، فقد استخدمها التجار ذوو العمليات الكبيرة والصغيرة في جميع أنحاء العالم في مجموعة متنوعة من المشاريع.
كان Magento 1 موجودًا منذ ثماني سنوات ، وتم إصدار خليفته ، Magento 2 ، في نهاية عام 2015 ، مما أدى إلى تحسين نقاط الضعف في الإصدار السابق مثل:
- تحسين الأداء
- جناح الاختبار الآلي الرسمي
- واجهة مستخدم خلفية أفضل
- قاعدة بيانات جديدة وأكثر حداثة للواجهة الأمامية
- طريقة أكثر نمطية لتطوير الوحدات ، مع وجود ملفات داخل كود Magento بدلاً من تناثرها في كل مكان
- عدد أقل من التعارضات بين الوحدات التي تحاول تخصيص نفس الوظيفة
بعد مرور أكثر من عام بقليل على الطريق ، سيكون التحسن واضحًا ، على الرغم من أنه لم يتم حل جميع المشكلات المذكورة بالكامل. من الآمن الآن أن نقول إن Magento 2 هو برنامج أكثر قوة من سابقه. بعض التحسينات الموجودة في Magento 2 هي:
- اختبارات الوحدة والتكامل ، بما في ذلك الطريقة الرسمية والموثقة لإنشائها للوحدات النمطية المخصصة
- الوحدات النمطية التي تم تشكيلها بالفعل ، بحيث يتم وضع جميع ملفاتها في دليل واحد
- نظام قوالب أكثر ثراءً ، يسمح لمطور القوالب بإنشاء تسلسل هرمي للقالب على مستوى n
- سلسلة من أنماط التصميم المفيدة التي تم تبنيها في جميع أنحاء الكود ، وتحسين جودة الكود وتقليل احتمالية الأخطاء الناتجة عن الوحدات - وتشمل هذه الحقن التلقائي للتبعية ، وعقود الخدمة ، والمستودعات ، والمصانع ، على سبيل المثال لا الحصر.
- تكامل أصلي مع Varnish كنظام تخزين مؤقت للصفحة الكاملة ، بالإضافة إلى Redis لمعالجة الجلسة وذاكرة التخزين المؤقت
- دعم PHP 7
أصبح منحنى التعلم لـ Magento 2 ، مع كل هذه التغييرات ، أكثر حدة. في هذا الدليل ، أعتزم أن أوضح لك كيفية تطوير أول وحدة Magento 2 الخاصة بك ، وأرشدك في الاتجاه الصحيح لمواصلة دراستك. دعنا نذهب اليها!
المتطلبات الأساسية لبرنامج Magento 2 التعليمي
من المهم أن يكون لديك فهم جيد للتقنيات / المفاهيم التالية من أجل متابعة بقية هذه المقالة:
- البرمجة الشيئية (OOP)
- بي أتش بي
- مساحات الأسماء
- MySQL
- استخدام باش الأساسي
من كل ما سبق ، ربما يكون OOP هو الأهم. تم إنشاء Magento في البداية من قبل فريق من مطوري Java ذوي الخبرة ، ويمكن بالتأكيد رؤية إرثهم في جميع أنحاء قاعدة التعليمات البرمجية. إذا لم تكن واثقًا جدًا من مهارات OOP الخاصة بك ، فقد يكون من الجيد مراجعتها قبل بدء عملك مع النظام الأساسي.
نظرة عامة على هندسة ماجنتو 2
تم تصميم هندسة Magento بقصد جعل كود المصدر معياريًا وقابل للتوسيع قدر الإمكان. الهدف النهائي من هذا النهج هو السماح بتكييفه وتخصيصه بسهولة وفقًا لاحتياجات كل مشروع.
عادةً ما يعني التخصيص تغيير سلوك رمز النظام الأساسي. في غالبية الأنظمة ، يعني هذا تغيير الكود "الأساسي". في Magento ، إذا كنت تتبع أفضل الممارسات ، فهذا شيء يمكنك تجنبه معظم الوقت ، مما يجعل من الممكن لمتجر مواكبة أحدث تصحيحات الأمان وإصدارات الميزات بطريقة موثوقة.
Magento 2 هو نظام عرض نموذج (MVVM). بينما ترتبط ارتباطًا وثيقًا بوحدة التحكم في عرض النموذج (MVC) ، توفر بنية MVVM فصلًا أكثر قوة بين النموذج وطبقات العرض. يوجد أدناه شرح لكل طبقة من طبقات نظام MVVM:
- يحتفظ النموذج بمنطق الأعمال الخاص بالتطبيق ، ويعتمد على فئة مقترنة - ResourceModel - للوصول إلى قاعدة البيانات. تعتمد النماذج على عقود الخدمة لعرض وظائفها على الطبقات الأخرى للتطبيق.
- طريقة العرض هي بنية وتخطيط ما يراه المستخدم على الشاشة - HTML الفعلي. يتم تحقيق ذلك في ملفات PHTML الموزعة مع الوحدات. ترتبط ملفات PHTML بكل نموذج ViewModel في ملفات Layout XML ، والتي يُشار إليها بالمجلدات في لهجة MVVM . قد تقوم ملفات التخطيط أيضًا بتعيين ملفات JavaScript لاستخدامها في الصفحة الأخيرة.
- يتفاعل ViewModel مع طبقة النموذج ، ويكشف فقط المعلومات الضرورية لطبقة العرض. في Magento 2 ، يتم التعامل مع هذا من خلال فئات Block للوحدة النمطية. لاحظ أن هذا كان عادةً جزءًا من دور المراقب في نظام MVC. في MVVM ، تكون وحدة التحكم مسؤولة فقط عن التعامل مع تدفق المستخدم ، مما يعني أنها تتلقى الطلبات وإما تخبر النظام بتقديم عرض أو إعادة توجيه المستخدم إلى مسار آخر.
تتكون وحدة Magento 2 من بعض ، إن لم يكن كل ، عناصر العمارة الموضحة أعلاه. يتم وصف الهيكل العام أدناه (المصدر):
يمكن لوحدة Magento 2 بدورها تحديد التبعيات الخارجية باستخدام Composer ، مدير تبعية PHP. في الرسم التخطيطي أعلاه ، ترى أن الوحدات الأساسية لـ Magento 2 تعتمد على Zend Framework و Symfony بالإضافة إلى مكتبات الجهات الخارجية الأخرى.
يوجد أدناه هيكل Magento / Cms ، وهو وحدة Magento 2 الأساسية المسؤولة عن التعامل مع إنشاء الصفحات والكتل الثابتة.
يحتوي كل مجلد على جزء واحد من العمارة ، على النحو التالي:
- Api: عقود الخدمة وتحديد واجهات الخدمة وواجهات البيانات
- الكتلة: نماذج ViewModels لبنية MVVM الخاصة بنا
- وحدة التحكم: وحدات التحكم ، المسؤولة عن التعامل مع تدفق المستخدم أثناء التفاعل مع النظام
- إلخ: ملفات تكوين XML — تحدد الوحدة النمطية نفسها وأجزائها (المسارات والنماذج والكتل والمراقبين ووظائف cron) داخل هذا المجلد. يمكن أيضًا استخدام الملفات وما إلى ذلك بواسطة الوحدات غير الأساسية لتجاوز وظائف الوحدات الأساسية.
- المساعد: فئات المساعدة التي تحتوي على كود مستخدم في أكثر من طبقة تطبيق واحدة. على سبيل المثال ، في وحدة Cms ، تكون الفصول المساعدة مسؤولة عن إعداد HTML للعرض التقديمي في المتصفح.
- i18n: يحتوي على ملفات CSV للتدويل ، وتستخدم للترجمة
- النموذج: للنماذج ونماذج الموارد
- المراقب: يحمل المراقبين ، أو النماذج التي "تراقب" أحداث النظام. عادةً ، عند إطلاق مثل هذا الحدث ، يقوم المراقب بإنشاء مثيل لنموذج للتعامل مع منطق العمل الضروري لمثل هذا الحدث.
- الإعداد: فئات الترحيل ، المسؤولة عن إنشاء المخطط والبيانات
- الاختبار: اختبارات الوحدة
- واجهة المستخدم: عناصر واجهة المستخدم مثل الشبكات والنماذج المستخدمة في تطبيق المسؤول
- طريقة العرض: ملفات التخطيط (XML) وملفات النماذج (PHTML) للواجهة الأمامية وتطبيق المسؤول
من المثير للاهتمام أيضًا ملاحظة أنه ، من الناحية العملية ، تعيش جميع الأعمال الداخلية لـ Magento 2 داخل وحدة نمطية. في الصورة أعلاه ، يمكنك أن ترى ، على سبيل المثال ، Magento_Checkout ، المسؤول عن عملية الخروج ، و Magento_Catalog ، المسؤول عن التعامل مع المنتجات والفئات. في الأساس ، ما يخبرنا به هذا هو أن تعلم كيفية العمل مع الوحدات هو أهم جزء في أن تصبح مطور Magento 2.
حسنًا ، بعد هذه المقدمة الموجزة نسبيًا عن بنية النظام وهيكل الوحدة ، دعنا نفعل شيئًا أكثر واقعية ، أليس كذلك؟ بعد ذلك ، سوف ننتقل إلى البرنامج التعليمي التقليدي Weblog من أجل جعلك تشعر بالراحة مع Magento 2 وعلى المسار الصحيح لتصبح مطور Magento 2. قبل ذلك ، نحتاج إلى تهيئة بيئة تطوير. دعنا نذهب اليها!
إعداد بيئة تطوير وحدة Magento 2
في وقت كتابة هذه السطور ، كنا قادرين على استخدام Magento 2 DevBox الرسمي ، وهو حاوية Magento 2 Docker. Docker على macOS هو شيء ما زلت أعتبره غير قابل للاستخدام ، على الأقل مع نظام يعتمد بشكل كبير على قرص سريع الإدخال / الإخراج مثل Magento 2. لذلك ، سنفعل ذلك بالطريقة التقليدية: تثبيت جميع الحزم محليًا على أجهزتنا الخاصة.
إعداد الخادم
من المؤكد أن تثبيت كل شيء يكون مملاً بعض الشيء ، ولكن النتيجة النهائية ستكون بيئة تطوير Magento سريعة البرق. صدقني ، ستوفر ساعات من العمل من خلال عدم الاعتماد على Docker لتطوير Magento 2 الخاص بك.
يفترض هذا البرنامج التعليمي وجود بيئة على macOS مثبت عليها Brew. إذا لم يكن الأمر كذلك بالنسبة لك ، فستظل الأساسيات كما هي ، مع تغيير طريقة تثبيت الحزم فقط. لنبدأ بتثبيت جميع الحزم:
brew install mysql nginxb php70 php70-imagick php70-intl php70-mcryptثم ابدأ الخدمات:
brew services start mysql brew services start php70 sudo brew services start nginxحسنًا ، سنقوم الآن بتوجيه مجال إلى عنوان الاسترجاع الخاص بنا. افتح ملف المضيفين في أي محرر ، ولكن تأكد من حصولك على أذونات المستخدم المتميز. القيام بذلك مع Vim سيكون:
sudo vim /etc/hostsثم أضف السطر التالي:
127.0.0.1 magento2.devسنقوم الآن بإنشاء vhost في Nginx:
vim /usr/local/etc/nginx/sites-available/magento2dev.confأضف المحتوى التالي:
server { listen 80; server_name magento2.dev; set $MAGE_ROOT /Users/yourusername/www/magento2dev; set $MAGE_MODE developer; # Default magento Nginx config starts below root $MAGE_ROOT/pub; index index.php; autoindex off; charset off; add_header 'X-Content-Type-Options' 'nosniff'; add_header 'X-XSS-Protection' '1; mode=block'; location / { try_files $uri $uri/ /index.php?$args; } location /pub { location ~ ^/pub/media/(downloadable|customer|import|theme_customization/.*\.xml) { deny all; } alias $MAGE_ROOT/pub; add_header X-Frame-Options "SAMEORIGIN"; } location /static/ { if ($MAGE_MODE = "production") { expires max; } location ~* \.(ico|jpg|jpeg|png|gif|svg|js|css|swf|eot|ttf|otf|woff|woff2)$ { add_header Cache-Control "public"; add_header X-Frame-Options "SAMEORIGIN"; expires +1y; if (!-f $request_filename) { rewrite ^/static/(version\d*/)?(.*)$ /static.php?resource=$2 last; } } location ~* \.(zip|gz|gzip|bz2|csv|xml)$ { add_header Cache-Control "no-store"; add_header X-Frame-Options "SAMEORIGIN"; expires off; if (!-f $request_filename) { rewrite ^/static/(version\d*/)?(.*)$ /static.php?resource=$2 last; } } if (!-f $request_filename) { rewrite ^/static/(version\d*/)?(.*)$ /static.php?resource=$2 last; } add_header X-Frame-Options "SAMEORIGIN"; } location /media/ { try_files $uri $uri/ /get.php?$args; location ~ ^/media/theme_customization/.*\.xml { deny all; } location ~* \.(ico|jpg|jpeg|png|gif|svg|js|css|swf|eot|ttf|otf|woff|woff2)$ { add_header Cache-Control "public"; add_header X-Frame-Options "SAMEORIGIN"; expires +1y; try_files $uri $uri/ /get.php?$args; } location ~* \.(zip|gz|gzip|bz2|csv|xml)$ { add_header Cache-Control "no-store"; add_header X-Frame-Options "SAMEORIGIN"; expires off; try_files $uri $uri/ /get.php?$args; } add_header X-Frame-Options "SAMEORIGIN"; } location /media/customer/ { deny all; } location /media/downloadable/ { deny all; } location /media/import/ { deny all; } location ~ /media/theme_customization/.*\.xml$ { deny all; } location /errors/ { try_files $uri =404; } location ~ ^/errors/.*\.(xml|phtml)$ { deny all; } location ~ cron\.php { deny all; } location ~ (index|get|static|report|404|503)\.php$ { try_files $uri =404; fastcgi_pass 127.0.0.1:9000; fastcgi_param PHP_FLAG "session.auto_start=off \n suhosin.session.cryptua=off"; fastcgi_param PHP_VALUE "memory_limit=768M \n max_execution_time=60"; fastcgi_read_timeout 60s; fastcgi_connect_timeout 60s; fastcgi_param MAGE_MODE $MAGE_MODE; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } # Default magento Nginx config finishes below client_max_body_size 20M; } إذا لم تكن قد تعاملت مع Nginx من قبل ، فقد يخيفك هذا الملف ، لذا دعنا نوضح الأجزاء الصغيرة هنا ، حيث سيلقي أيضًا بعض الضوء على بعض أعمال Magento الداخلية. تخبر الأسطر الأولى ببساطة Nginx أننا نستخدم منفذ HTTP الافتراضي ومجالنا هو magento2.dev :
listen 80; server_name magento2.dev; ثم نضع بعض متغيرات البيئة. الأول - $MAGE_ROOT يحمل المسار إلى قاعدة الشفرة الخاصة بنا. لاحظ أنك ستحتاج إلى تغيير مسار الجذر ليطابق اسم المستخدم / مسار المجلد ، أينما كنت تخطط لوضع المصدر على:
set $MAGE_ROOT /Users/yourusername/www/magento2dev; المتغير الثاني - $MAGE_MODE يضبط وضع وقت التشغيل لمتجرنا. أثناء قيامنا بتطوير وحدة نمطية ، سوف نستخدم وضع المطور. يتيح لنا ذلك البرمجة بشكل أسرع ، حيث لن نضطر إلى تجميع أو نشر ملفات ثابتة أثناء التطوير. الأنماط الأخرى هي الإنتاج والافتراضي. الاستخدام الحقيقي لهذا الأخير لم يتضح بعد.
set $MAGE_MODE developer; بعد تعيين هذه المتغيرات ، نحدد مسار جذر vhost. لاحظ أننا نلحق المتغير $MAGE_ROOT /pub ، مما يجعل جزءًا فقط من متجرنا متاحًا على الويب.
root $MAGE_ROOT/pub; ثم نقوم بتعريف ملف الفهرس الخاص بنا - سيتم تحميل الملف nginx عندما لا يكون الملف المطلوب موجودًا - مثل index.php. هذا البرنامج النصي ، $MAGE_ROOT/pub/index.php ، هو نقطة الدخول الرئيسية للعملاء الذين يزورون كل من عربة التسوق وتطبيقات الإدارة. بغض النظر عن عنوان URL المطلوب ، سيتم تحميل index.php وستبدأ عملية إرسال جهاز التوجيه.
index index.php; بعد ذلك ، نقوم بإيقاف تشغيل بعض ميزات Nginx. أولاً ، نقوم بإيقاف autoindex ، والذي سيعرض قائمة ملفات عندما تطلب مجلدًا ، ولكن لا تحدد ملفًا ، ولا يوجد فهرس. ثانيًا ، نقوم بإيقاف تشغيل charset ، مما يسمح لـ Nginx بإضافة رؤوس أحرف Charset تلقائيًا إلى الاستجابة.
autoindex off; charset off;بعد ذلك ، نحدد بعض رؤوس الأمان:
add_header 'X-Content-Type-Options' 'nosniff'; add_header 'X-XSS-Protection' '1; mode=block'; يشير هذا الموقع ، / ، إلى المجلد الجذر $MAGE_ROOT/pub ، ويعيد توجيه أي طلب يتم استلامه إلى index.php الخاص بوحدة التحكم الأمامية ، جنبًا إلى جنب مع وسيطات الطلب:
location / { try_files $uri $uri/ /index.php?$args; } قد يكون الجزء التالي محيرًا بعض الشيء ، لكنه بسيط للغاية. قبل بضعة أسطر ، عرّفنا الجذر على أنه $MAGE_ROOT/pub . هذا هو الإعداد الموصى به والأكثر أمانًا ، حيث إن معظم الكود غير مرئي من الويب. لكنها ليست الطريقة الوحيدة لإعداد خادم الويب. في الواقع ، تحتوي معظم خوادم الويب المشتركة على إعداد افتراضي واحد ، وهو جعل خادم الويب الخاص بك يشير إلى مجلد الويب الخاص بك. بالنسبة لهؤلاء المستخدمين ، جعل فريق Magento هذا الملف جاهزًا لتلك الحالات ، عندما يتم تعريف الجذر على أنه $MAGE_ROOT التالي:
location /pub { location ~ ^/pub/media/(downloadable|customer|import|theme_customization/.*\.xml) { deny all; } alias $MAGE_ROOT/pub; add_header X-Frame-Options "SAMEORIGIN"; } ضع في اعتبارك أنه كلما كان ذلك ممكنًا ، من الأفضل أن يكون خادم الويب الخاص بك يشير إلى المجلد $MAGE_ROOT/pub . سيكون متجرك أكثر أمانًا بهذه الطريقة.
بعد ذلك ، لدينا الموقع الثابت $MAGE_ROOT/pub/static . هذا المجلد فارغ في البداية ويتم ملؤه تلقائيًا بالملفات الثابتة للوحدات النمطية والسمات ، مثل ملفات الصور و CSS و JS وما إلى ذلك. موجود ، قم بإعادة توجيهه إلى $MAGE_ROOT/pub/static.php . سيقوم هذا البرنامج النصي ، من بين أشياء أخرى ، بتحليل الطلب ونسخ أو ربط الملف المحدد من الوحدة النمطية أو الموضوع المقابل ، اعتمادًا على وضع وقت التشغيل المحدد. بهذه الطريقة ، ستوجد الملفات الثابتة للوحدة النمطية الخاصة بك داخل مجلد الوحدات النمطية الخاصة بنا ، ولكن سيتم تقديمها مباشرةً من المجلد العام vhost:
location /static/ { if ($MAGE_MODE = "production") { expires max; } location ~* \.(ico|jpg|jpeg|png|gif|svg|js|css|swf|eot|ttf|otf|woff|woff2)$ { add_header Cache-Control "public"; add_header X-Frame-Options "SAMEORIGIN"; expires +1y; if (!-f $request_filename) { rewrite ^/static/(version\d*/)?(.*)$ /static.php?resource=$2 last; } } location ~* \.(zip|gz|gzip|bz2|csv|xml)$ { add_header Cache-Control "no-store"; add_header X-Frame-Options "SAMEORIGIN"; expires off; if (!-f $request_filename) { rewrite ^/static/(version\d*/)?(.*)$ /static.php?resource=$2 last; } } if (!-f $request_filename) { rewrite ^/static/(version\d*/)?(.*)$ /static.php?resource=$2 last; } add_header X-Frame-Options "SAMEORIGIN"; }بعد ذلك ، نرفض الوصول إلى بعض المجلدات والملفات المقيدة:
location /media/customer/ { deny all; } location /media/downloadable/ { deny all; } location /media/import/ { deny all; } location ~ /media/theme_customization/.*\.xml$ { deny all; } location /errors/ { try_files $uri =404; } location ~ ^/errors/.*\.(xml|phtml)$ { deny all; } location ~ cron\.php { deny all; }والبت الأخير هو المكان الذي نحمل فيه php-fpm ونطلب منه تنفيذ index.php عندما يضربه المستخدم:
location ~ (index|get|static|report|404|503)\.php$ { try_files $uri =404; fastcgi_pass 127.0.0.1:9000; fastcgi_param PHP_FLAG "session.auto_start=off \n suhosin.session.cryptua=off"; fastcgi_param PHP_VALUE "memory_limit=768M \n max_execution_time=60"; fastcgi_read_timeout 60s; fastcgi_connect_timeout 60s; fastcgi_param MAGE_MODE $MAGE_MODE; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; }بهذه الطريقة ، احفظ الملف ، ثم قم بتمكينه عن طريق كتابة الأوامر التالية:
ln -s /usr/local/etc/nginx/sites-available/magento2dev.conf \ /usr/local/etc/nginx/sites-enabled/magento2dev.conf sudo brew services restart nginxكيفية تثبيت Magento 2
حسنًا ، في هذه المرحلة ، يلبي جهازك متطلبات Magento 2 ، ويفتقد الوحش نفسه فقط. توجه إلى موقع Magento الإلكتروني وأنشئ حسابًا إذا لم يكن لديك حساب بعد. بعد ذلك ، انتقل إلى صفحة التنزيل وقم بتنزيل أحدث إصدار (2.1.5 ، وقت كتابة هذا التقرير):
حدد تنسيق .tar.bz2 وقم بتنزيله. ثم تابع استخراجه وتعيين أذونات المجلد والملفات الصحيحة لـ Magento 2 لتتمكن من العمل:
mkdir ~/www/magento2dev cd ~/www/magento2dev tar -xjf ~/Downloads/Magento-CE-2.1.5-2017-02-20-05-39-14.tar.bz2 find var vendor pub/static pub/media app/etc -type f -exec chmod u+w {} \; find var vendor pub/static pub/media app/etc -type d -exec chmod u+w {} \; chmod u+x bin/magentoالآن ، لتثبيت جداول قاعدة البيانات وإنشاء ملفات التكوين المطلوبة ، سنقوم بتشغيل هذا الأمر من المحطة:
./bin/magento setup:install --base-url=http://magento2.dev/ \ --db-host=127.0.0.1 --db-name=magento2 --db-user=root \ --db-password=123 --admin-firstname=Magento --admin-lastname=User \ [email protected] --admin-user=admin \ --admin-password=admin123 --language=en_US --currency=USD \ --timezone=America/Chicago --use-rewrites=1 --backend-frontname=admin تذكر تغيير اسم قاعدة البيانات (اسم db-name ) والمستخدم ( db-user ) وكلمة المرور ( db-password ) لمطابقة الاسم الذي استخدمته أثناء تثبيت MySQL ، وهذا كل شيء! سيقوم هذا الأمر بتثبيت جميع وحدات Magento 2 ، وإنشاء الجداول وملفات التكوين المطلوبة. بعد الانتهاء ، افتح المتصفح وتوجه إلى http://magento2.dev/. يجب أن تشاهد تثبيت Magento 2 Clean مع سمة Luma الافتراضية:
إذا توجهت إلى http://magento2.dev/admin ، فسترى صفحة تسجيل الدخول إلى تطبيق المسؤول:
ثم استخدم بيانات الاعتماد أدناه لتسجيل الدخول:
المستخدم: كلمة مرور المشرف: admin123
نحن مستعدون أخيرًا لبدء كتابة الكود الخاص بنا!
إنشاء أول وحدة Magento 2 لدينا
لإكمال الوحدة الخاصة بنا ، سيتعين علينا إنشاء الملفات التالية ، وسأرشدك خلال العملية برمتها. سوف نحتاج:
- عدد قليل من ملفات التسجيل المعيارية ، لجعل Magento على دراية بوحدة المدونة الخاصة بنا
- ملف واجهة واحد ، لتحديد عقد البيانات الخاص بنا للوظيفة
- نموذج ما بعد ، لتمثيل منشور في جميع أنحاء الكود الخاص بنا ، مع تنفيذ واجهة بيانات المنشور
- نموذج مورد ما بعد ، لربط نموذج النشر بقاعدة البيانات
- مجموعة منشورات ، لاسترداد عدة منشورات دفعة واحدة من قاعدة البيانات بمساعدة نموذج الموارد
- فئتان من فئات الترحيل ، لإعداد مخطط الجدول والمحتوى
- إجراءان: أحدهما لسرد جميع المنشورات والآخر لإظهار كل مشاركة على حدة
- ملفان من ملفات Block ، و Views ، و Layout: واحد من كلٍ لإجراء القائمة ، وواحد من كلٍ منهما لطريقة العرض
أولاً ، دعنا نلقي نظرة سريعة على بنية مجلد الكود المصدري الأساسي ، حتى نتمكن من تحديد مكان وضع الكود الخاص بنا. الطريقة التي قمنا بتثبيتها تحتوي على كل التعليمات البرمجية الأساسية لـ Magento 2 ، جنبًا إلى جنب مع جميع تبعياتها ، والتي تعيش داخل مجلد vendor الملحن.
تسجيل وحدتنا
سنحتفظ بالكود الخاص بنا في مجلد منفصل ، app/code . يكون اسم كل وحدة على شكل Namespace_ModuleName ، ويجب أن يعكس موقعها على نظام الملفات هذا الاسم ، app/code/Namespace/ModuleName لهذا المثال. باتباع هذا النمط ، سنقوم بتسمية الوحدة النمطية الخاصة بنا Toptal_Blog ملفاتنا تحت app/code/Toptal/Blog . انطلق وقم بإنشاء هيكل المجلد هذا.
الآن ، نحتاج إلى إنشاء بعض الملفات المعيارية حتى يتم تسجيل وحدتنا في Magento. أولاً ، أنشئ app/code/Toptal/Blog/composer.json :
{}سيتم تحميل هذا الملف بواسطة Composer في كل مرة تقوم بتشغيله. على الرغم من أننا لا نستخدم Composer بالفعل مع الوحدة النمطية الخاصة بنا ، إلا أننا يجب أن ننشئه لإبقاء Composer سعيدًا.
الآن سنقوم بتسجيل وحدتنا مع Magento. انطلق وأنشئ app/code/Toptal/Blog/registration.php :
<?php \Magento\Framework\Component\ComponentRegistrar::register( \Magento\Framework\Component\ComponentRegistrar::MODULE, 'Toptal_Blog', __DIR__ ); هنا ، نقوم باستدعاء طريقة register لفئة ComponentRegistrar ، وإرسال معلمتين: السلسلة 'module' ، وهي نوع المكون الذي نقوم بتسجيله ، واسم الوحدة الخاصة بنا ، 'Toptal_Blog' . باستخدام هذه المعلومات ، سيكون برنامج التحميل التلقائي في Magento على دراية بمساحة الاسم الخاصة بنا وسيعرف مكان البحث عن فئاتنا وملفات XML.
هناك شيء مثير للاهتمام يجب ملاحظته هنا هو أن لدينا نوع المكون ( MODULE ) الذي يتم إرساله كمعامل إلى وظيفة \Magento\Framework\Component\ComponentRegistrar::register . لا يمكننا تسجيل الوحدات فحسب ، بل يمكننا تسجيل أنواع أخرى من المكونات. على سبيل المثال ، يتم أيضًا تسجيل السمات والمكتبات الخارجية وحزم اللغات باستخدام نفس الطريقة.
استمرارًا ، دعنا ننشئ ملف التسجيل الأخير ، app/code/Toptal/Blog/etc/module.xml :
<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Module/etc/module.xsd"> <module name="Toptal_Blog" setup_version="0.1.0"> <sequence> <module name="Magento_Directory" /> <module name="Magento_Config" /> </sequence> </module> </config>يحتوي هذا الملف على بعض المعلومات المهمة جدًا حول وحدتنا. هم انهم:
- اسم الوحدة موجود مرة أخرى ، مما يعرض اسم الوحدة الخاصة بنا لتكوين Magento.
- إصدار إعداد Magento ، الذي سيستخدمه Magento لتحديد وقت تشغيل البرامج النصية لترحيل قاعدة البيانات.
- تبعيات الوحدة الخاصة بنا - نظرًا لأننا نكتب وحدة نمطية بسيطة ، فإننا نعتمد فقط على وحدتين أساسيتين من Magento:
Magento_DirectoryوMagento_Config.
الآن ، لدينا وحدة يجب أن تتعرف عليها Magento 2. دعنا نتحقق منها باستخدام Magento 2 CLI.
أولاً ، نحتاج إلى تعطيل ذاكرة التخزين المؤقت لـ Magento. آليات ذاكرة التخزين المؤقت Magento تستحق مقالًا مخصصًا لأنفسهم. في الوقت الحالي ، نظرًا لأننا نطور وحدة ونريد أن تتعرف Magento على تغييراتنا على الفور دون الحاجة إلى مسح ذاكرة التخزين المؤقت في جميع الأوقات ، فسنقوم ببساطة بتعطيلها. من سطر الأوامر ، قم بتشغيل:
./bin/magento cache:disableثم دعنا نرى ما إذا كان Magento على دراية بالفعل بالتعديلات التي أجريناها من خلال النظر في حالة الوحدات. ما عليك سوى تشغيل الأمر التالي:
./bin/magento module:statusيجب أن تكون النتيجة من آخر نتيجة مماثلة لـ:
الوحدة النمطية الخاصة بنا موجودة ، ولكن كما تظهر المخرجات ، لا تزال معطلة. لتمكينه ، قم بتشغيل:
./bin/magento module:enable Toptal_Blog كان يجب أن يفعل ذلك. للتأكد ، يمكنك استدعاء module:status مرة أخرى والبحث عن اسم الوحدة الخاصة بنا في قائمة التمكين:
معالجة تخزين البيانات
الآن بعد أن قمنا بتمكين الوحدة الخاصة بنا ، نحتاج إلى إنشاء جدول قاعدة البيانات الذي يحتوي على منشورات المدونة الخاصة بنا. هذا هو مخطط الجدول الذي نريد إنشاءه:
| حقل | اكتب | باطل | مفتاح | تقصير |
|---|---|---|---|---|
| بعد معرف | عدد صحيح (10) بدون توقيع | رقم | PRI | باطل |
| لقب | نص | رقم | باطل | |
| المحتوى | نص | رقم | باطل | |
| أنشئت في | الطابع الزمني | رقم | حالة الزمن حاليا |
نحقق ذلك من خلال إنشاء فئة InstallSchema ، المسؤولة عن إدارة تثبيت ترحيل مخططنا. الملف موجود في app/code/Toptal/Blog/Setup/InstallSchema.php ويحتوي على المحتوى التالي:

<?php namespace Toptal\Blog\Setup; use \Magento\Framework\Setup\InstallSchemaInterface; use \Magento\Framework\Setup\ModuleContextInterface; use \Magento\Framework\Setup\SchemaSetupInterface; use \Magento\Framework\DB\Ddl\Table; /** * Class InstallSchema * * @package Toptal\Blog\Setup */ class InstallSchema implements InstallSchemaInterface { /** * Install Blog Posts table * * @param SchemaSetupInterface $setup * @param ModuleContextInterface $context */ public function install(SchemaSetupInterface $setup, ModuleContextInterface $context) { $setup->startSetup(); $tableName = $setup->getTable('toptal_blog_post'); if ($setup->getConnection()->isTableExists($tableName) != true) { $table = $setup->getConnection() ->newTable($tableName) ->addColumn( 'post_id', Table::TYPE_INTEGER, null, [ 'identity' => true, 'unsigned' => true, 'nullable' => false, 'primary' => true ], 'ID' ) ->addColumn( 'title', Table::TYPE_TEXT, null, ['nullable' => false], 'Title' ) ->addColumn( 'content', Table::TYPE_TEXT, null, ['nullable' => false], 'Content' ) ->addColumn( 'created_at', Table::TYPE_TIMESTAMP, null, ['nullable' => false, 'default' => Table::TIMESTAMP_INIT], 'Created At' ) ->setComment('Toptal Blog - Posts'); $setup->getConnection()->createTable($table); } $setup->endSetup(); } } إذا قمت بتحليل طريقة install ، فستلاحظ أنها تقوم ببساطة بإنشاء جدولنا وإضافة أعمدتها واحدة تلو الأخرى.
لتحديد وقت تشغيل ترحيل المخطط ، يحتفظ Magento بجدول يحتوي على جميع إصدارات الإعداد الحالية لكل وحدة نمطية ، وكلما تغير إصدار الوحدة النمطية ، يتم تهيئة فئات الترحيل الخاصة به. هذا الجدول هو setup_module ، وإذا ألقيت نظرة على محتويات هذا الجدول ، فسترى أنه لا توجد إشارة إلى الوحدة النمطية الخاصة بنا حتى الآن. لذا ، دعونا نغير ذلك. من المحطة ، أطلق الأمر التالي:
./bin/magento setup:upgradeسيُظهر لك ذلك قائمة بجميع الوحدات النمطية ونصوص الترحيل التي تم تنفيذها ، بما في ذلك برنامجنا:
الآن ، من عميل MySQL المفضل لديك ، يمكنك التحقق مما إذا كان الجدول قد تم إنشاؤه بالفعل:
وفي جدول setup_module ، يوجد الآن مرجع إلى الوحدة النمطية الخاصة بنا ومخططها وإصدار البيانات:
حسنًا ، وماذا عن ترقيات المخطط؟ دعنا نضيف بعض المنشورات إلى هذا الجدول من خلال ترقية لنوضح لك كيفية القيام بذلك. أولاً ، ضع ملف setup_version في ملف etc/module.xml الخاص بنا:
نقوم الآن بإنشاء ملف app/code/Toptal/Blog/Setup/UpgradeData.php ، وهو المسؤول عن عمليات ترحيل البيانات (وليس المخطط):
<?php namespace Toptal\Blog\Setup; use \Magento\Framework\Setup\UpgradeDataInterface; use \Magento\Framework\Setup\ModuleContextInterface; use \Magento\Framework\Setup\ModuleDataSetupInterface; /** * Class UpgradeData * * @package Toptal\Blog\Setup */ class UpgradeData implements UpgradeDataInterface { /** * Creates sample blog posts * * @param ModuleDataSetupInterface $setup * @param ModuleContextInterface $context * @return void */ public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface $context) { $setup->startSetup(); if ($context->getVersion() && version_compare($context->getVersion(), '0.1.1') < 0 ) { $tableName = $setup->getTable('toptal_blog_post'); $data = [ [ 'title' => 'Post 1 Title', 'content' => 'Content of the first post.', ], [ 'title' => 'Post 2 Title', 'content' => 'Content of the second post.', ], ]; $setup ->getConnection() ->insertMultiple($tableName, $data); } $setup->endSetup(); } } يمكنك أن ترى أنه مشابه جدًا لفئة التثبيت الخاصة بنا. الاختلاف الوحيد هو أنه يقوم بتطبيق UpgradeDataInterface بدلاً من InstallSchemaInterface ، والطريقة الرئيسية تسمى upgrade . باستخدام هذه الطريقة ، يمكنك التحقق من الإصدار المثبت للوحدة الحالية ، وعندما تكون أصغر من الإصدار الخاص بك ، قم بتشغيل التغييرات التي تحتاج إلى إنجازها. في مثالنا ، نتحقق مما إذا كان الإصدار الحالي أصغر من 0.1.1 في السطر التالي باستخدام وظيفة version_compare :
if ($context->getVersion() && version_compare($context->getVersion(), '0.1.1') < 0 ) { سيعيد استدعاء $context->getVersion() 0.1.0 عندما يتم استدعاء أمر setup:upgrade Upgrade CLI لأول مرة. ثم يتم تحميل البيانات النموذجية إلى قاعدة البيانات ، ويتم رفع نسختنا إلى 0.1.1. لتشغيل هذا ، امض قدمًا وقم بتشغيل setup:upgrade :
./bin/magento setup:upgradeثم تحقق من النتائج في جدول المشاركات:
وعلى جدول setup_module :
لاحظ أنه على الرغم من أننا أضفنا البيانات إلى جدولنا باستخدام عملية الترحيل ، فقد كان من الممكن تغيير المخطط أيضًا. العملية نفسها؛ يمكنك فقط استخدام UpgradeSchemaInterface بدلاً من UpgradeDataInterface .
تحديد نموذج التدوينات
المضي قدمًا ، إذا كنت تتذكر نظرة عامة على الهندسة المعمارية ، فستكون اللبنة الإنشائية التالية هي منشور المدونة ResourceModel. نموذج المورد بسيط للغاية ، ويوضح الجدول الذي "يتصل" به النموذج ، بالإضافة إلى مفتاحه الأساسي. سننشئ ResourceModel في app/code/Toptal/Blog/Model/ResourceModel/Post.php بالمحتويات التالية:
<?php namespace Toptal\Blog\Model\ResourceModel; use \Magento\Framework\Model\ResourceModel\Db\AbstractDb; class Post extends AbstractDb { /** * Post Abstract Resource Constructor * @return void */ protected function _construct() { $this->_init('toptal_blog_post', 'post_id'); } } يتم التعامل مع جميع عمليات ResourceModel ، إلا إذا كنت بحاجة إلى شيء مختلف عن عمليات CRUD المعتادة ، بواسطة فئة AbstractDb الرئيسية.
سنحتاج أيضًا إلى نموذج ResourceModel آخر ، وهو مجموعة. ستكون المجموعة مسؤولة عن الاستعلام عن قاعدة البيانات لمنشورات متعددة باستخدام نموذج ResourceModel الخاص بنا وتقديم سلسلة من النماذج التي تم إنشاء مثيل لها ومليئة بالمعلومات. نقوم بإنشاء ملف app/code/Toptal/Blog/Model/ResourceModel/Post/Collection.php بالمحتوى التالي:
<?php namespace Toptal\Blog\Model\ResourceModel\Post; use \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection; class Collection extends AbstractCollection { /** * Remittance File Collection Constructor * @return void */ protected function _construct() { $this->_init('Toptal\Blog\Model\Post', 'Toptal\Blog\Model\ResourceModel\Post'); } }لاحظ أنه في المُنشئ نذكر ببساطة النموذج ، الذي سيمثل كيان المنشور في جميع أنحاء الكود الخاص بنا ، و ResourceModel ، الذي سيجلب المعلومات في قاعدة البيانات.
القطعة المفقودة لهذه الطبقة هي نموذج المشاركة نفسه. يجب أن يحتوي النموذج على جميع السمات التي حددناها في مخططنا ، جنبًا إلى جنب مع أي منطق أعمال قد تحتاجه. باتباع نمط Magento 2 ، نحتاج إلى إنشاء واجهة بيانات يمتد نموذجنا منها. نضع الواجهة على app/code/Toptal/Blog/Api/Data/PostInterface.php ، ويجب أن تحتوي على أسماء حقول الجدول ، جنبًا إلى جنب مع طرق الوصول إليها:
<?php namespace Toptal\Blog\Api\Data; interface PostInterface { /**#@+ * Constants for keys of data array. Identical to the name of the getter in snake case */ const POST_; const TITLE = 'title'; const CONTENT = 'content'; const CREATED_AT = 'created_at'; /**#@-*/ /** * Get Title * * @return string|null */ public function getTitle(); /** * Get Content * * @return string|null */ public function getContent(); /** * Get Created At * * @return string|null */ public function getCreatedAt(); /** * Get ID * * @return int|null */ public function getId(); /** * Set Title * * @param string $title * @return $this */ public function setTitle($title); /** * Set Content * * @param string $content * @return $this */ public function setContent($content); /** * Set Crated At * * @param int $createdAt * @return $this */ public function setCreatedAt($createdAt); /** * Set ID * * @param int $id * @return $this */ public function setId($id); } الآن إلى تنفيذ النموذج ، على app/code/Toptal/Blog/Model/Post.php . سنقوم بإنشاء الطرق المحددة في الواجهة. سنحدد أيضًا علامة ذاكرة التخزين المؤقت من خلال ثابت CACHE_TAG ، وفي المُنشئ ، سنحدد ResourceModel التي ستكون مسؤولة عن الوصول إلى قاعدة البيانات لنموذجنا.
<?php namespace Toptal\Blog\Model; use \Magento\Framework\Model\AbstractModel; use \Magento\Framework\DataObject\IdentityInterface; use \Toptal\Blog\Api\Data\PostInterface; /** * Class File * @package Toptal\Blog\Model * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Post extends AbstractModel implements PostInterface, IdentityInterface { /** * Cache tag */ const CACHE_TAG = 'toptal_blog_post'; /** * Post Initialization * @return void */ protected function _construct() { $this->_init('Toptal\Blog\Model\ResourceModel\Post'); } /** * Get Title * * @return string|null */ public function getTitle() { return $this->getData(self::TITLE); } /** * Get Content * * @return string|null */ public function getContent() { return $this->getData(self::CONTENT); } /** * Get Created At * * @return string|null */ public function getCreatedAt() { return $this->getData(self::CREATED_AT); } /** * Get ID * * @return int|null */ public function getId() { return $this->getData(self::POST_ID); } /** * Return identities * @return string[] */ public function getIdentities() { return [self::CACHE_TAG . '_' . $this->getId()]; } /** * Set Title * * @param string $title * @return $this */ public function setTitle($title) { return $this->setData(self::TITLE, $title); } /** * Set Content * * @param string $content * @return $this */ public function setContent($content) { return $this->setData(self::CONTENT, $content); } /** * Set Created At * * @param string $createdAt * @return $this */ public function setCreatedAt($createdAt) { return $this->setData(self::CREATED_AT, $createdAt); } /** * Set ID * * @param int $id * @return $this */ public function setId($id) { return $this->setData(self::POST_ID, $id); } }Creating Views
Now we are moving one layer up, and will start the implementation of our ViewModel and Controller. To define a route in the front-end (shopping cart) application, we need to create the file app/code/Toptal/Blog/etc/frontend/routes.xml with the following contents:
<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd"> <router> <route frontName="blog"> <module name="Toptal_Blog"/> </route> </router> </config>List of Posts at the Index Page
Here, we are basically telling Magento that our module, Toptal_Blog , will be responsible for responding to routes under http://magento2.dev/blog (notice the frontName attribute of the route). Next up is the action, at app/code/Toptal/Blog/Controller/Index/Index.php :
<?php namespace Toptal\Blog\Controller\Index; use \Magento\Framework\App\Action\Action; use \Magento\Framework\View\Result\PageFactory; use \Magento\Framework\View\Result\Page; use \Magento\Framework\App\Action\Context; use \Magento\Framework\Exception\LocalizedException; class Index extends Action { /** * @var PageFactory */ protected $resultPageFactory; /** * @param Context $context * @param PageFactory $resultPageFactory * * @codeCoverageIgnore * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( Context $context, PageFactory $resultPageFactory ) { parent::__construct( $context ); $this->resultPageFactory = $resultPageFactory; } /** * Prints the blog from informed order id * @return Page * @throws LocalizedException */ public function execute() { $resultPage = $this->resultPageFactory->create(); return $resultPage; } }Our action is defining two methods. Let us take a closer look at them:
The constructor method simply sends the
$contextparameter to its parent method, and sets the$resultPageFactoryparameter to an attribute for later use. At this point it is useful to know the Dependency Injection design pattern, as that is what is happening here. In Magento 2's case we have automatic dependency injection. This means that whenever a class instantiation occurs, Magento will automatically try to instantiate all of the class constructor parameters (dependencies) and inject it for you as constructor parameters. It identifies which classes to instantiate for each parameter by inspecting the type hints, in this caseContextandPageFactory.The
executemethod is responsible for the action execution itself. In our case, we are simply telling Magento to render its layout by returning aMagento\Framework\View\Result\Pageobject. This will trigger the layout rendering process, which we will create in a bit.
Now you should see a blank page at the url http://magento2.dev/blog/index/index. We still need to define the layout structure for that route, and its corresponding Block (our ViewModel) and the template file which will present the data to our user.
The layout structure for the front-end application is defined under view/frontend/layout , and the file name must reflect our route. As our route is blog/index/index , the layout file for that route will be app/code/Toptal/Blog/view/frontend/layout/blog_index_index.xml :
<?xml version="1.0"?> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="2columns-left" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> <referenceContainer name="content"> <block class="Toptal\Blog\Block\Posts" name="posts.list" template="Toptal_Blog::post/list.phtml" /> </referenceContainer> </body> </page>Here, we must define three very important structures in the Magento layout structure: Blocks, Containers, and Templates.
Blocks are the ViewModel part of our MVVM architecture, which was explained in earlier sections. They are the building blocks of our template structure.
Containers contain and output Blocks. They hold blocks together in nice hierarchical structures, and help in making things make sense when the layout for a page is being processed.
Templates are PHMTL (mixed HTML and PHP) files used by a special type of block in Magento. You can make calls to methods of a
$blockvariable from within a template. The variable is always defined in the template context. You will be invoking your Block's methods by doing so, and thus allowing you to pull information from the ViewModel layer to the actual presentation.
With that extra information at hand, we can analyze the XML layout structure above. This layout structure is basically telling Magento that, when a request is made to the blog/index/index route, a Block of the type Toptal\Blog\Block\Posts is to be added to the content container, and the template which will be used to render it is Toptal_blog::post/list.phtml .
This leads us to the creation of our two remaining files. Our Block, located at app/code/Toptal/Blog/Block/Posts.php :
<?php namespace Toptal\Blog\Block; use \Magento\Framework\View\Element\Template; use \Magento\Framework\View\Element\Template\Context; use \Toptal\Blog\Model\ResourceModel\Post\Collection as PostCollection; use \Toptal\Blog\Model\ResourceModel\Post\CollectionFactory as PostCollectionFactory; use \Toptal\Blog\Model\Post; class Posts extends Template { /** * CollectionFactory * @var null|CollectionFactory */ protected $_postCollectionFactory = null; /** * Constructor * * @param Context $context * @param PostCollectionFactory $postCollectionFactory * @param array $data */ public function __construct( Context $context, PostCollectionFactory $postCollectionFactory, array $data = [] ) { $this->_postCollectionFactory = $postCollectionFactory; parent::__construct($context, $data); } /** * @return Post[] */ public function getPosts() { /** @var PostCollection $postCollection */ $postCollection = $this->_postCollectionFactory->create(); $postCollection->addFieldToSelect('*')->load(); return $postCollection->getItems(); } /** * For a given post, returns its url * @param Post $post * @return string */ public function getPostUrl( Post $post ) { return '/blog/post/view/id/' . $post->getId(); } } هذه الفئة بسيطة نوعًا ما ، وهدفها فقط تحميل المنشورات المراد عرضها ، وتوفير طريقة getPostUrl للقالب. هناك بعض الأشياء التي يجب ملاحظتها بالرغم من ذلك.
إذا كنت تتذكر ، فنحن لم نحدد فئة Toptal\Blog\Model\ResourceModel\Post\CollectionFactory . لقد قمنا فقط بتعريف Toptal\Blog\Model\ResourceModel\Post\Collection . فكيف يعمل هذا؟ لكل فئة تحددها في الوحدة الخاصة بك ، سيقوم Magento 2 تلقائيًا بإنشاء مصنع لك. المصانع لها طريقتان: create ، والتي ستعيد مثيلًا جديدًا لكل مكالمة ، و get ، والتي ستعيد دائمًا نفس الحالة متى تم استدعاؤها - تُستخدم لتنفيذ نمط Singleton.
المعلمة الثالثة من Block ، $data ، هي مصفوفة اختيارية. نظرًا لأنه اختياري ولا يحتوي على تلميح نوع ، فلن يتم حقنه بواسطة نظام الحقن التلقائي. من المهم ملاحظة أنه يجب دائمًا وضع معلمات المُنشئ الاختيارية في آخر موضع في المعلمات. على سبيل المثال ، مُنشئ Magento\Framework\View\Element\Template ، فئة الأصل لدينا ، لديه هذه المعلمات:
public function __construct( Template\Context $context, array $data = [] ) { ... نظرًا لأننا أردنا إضافة CollectionFactory الخاصة بنا إلى معلمات المُنشئ بعد تمديد فئة القالب ، كان علينا القيام بذلك قبل المعلمة الاختيارية ، وإلا فلن يعمل الحقن:
public function __construct( Context $context, PostCollectionFactory $postCollectionFactory, array $data = [] ) { ... في طريقة getPosts ، التي سيتم الوصول إليها لاحقًا بواسطة القالب الخاص بنا ، فإننا ببساطة نسمي طريقة create من PostCollectionFactory ، والتي ستعيد لنا PostCollection جديدة وتسمح لنا بإحضار منشوراتنا من قاعدة البيانات وإرسالها إلى استجابتنا.
ولإنهاء تخطيط هذا المسار ، إليك نموذج PHTML الخاص بنا ، app/code/Toptal/Blog/view/frontend/templates/post/list.phtml :
<?php /** @var Toptal\Blog\Block\Posts $block */ ?> <h1>Toptal Posts</h1> <?php foreach($block->getPosts() as $post): ?> <?php /** @var Toptal\Blog\Model\Post */ ?> <h2><a href="<?php echo $block->getPostUrl($post);?>"><?php echo $post->getTitle(); ?></a></h2> <p><?php echo $post->getContent(); ?></p> <?php endforeach; ?> لاحظ أنه يمكننا هنا رؤية طبقة العرض تصل إلى ModelView ( $block->getPosts() ) والتي بدورها تستخدم ResourceModel (المجموعة) لجلب نماذجنا ( Toptal\Blog\Model\Post ) من قاعدة البيانات. في كل قالب ، كلما أردت الوصول إلى طرق الكتلة الخاصة به ، سيكون هناك متغير $block محدد وينتظر مكالماتك.
الآن يجب أن تكون قادرًا على رؤية قائمة المنشورات بمجرد الضغط على طريقنا مرة أخرى.
مشاهدة المشاركات الفردية
الآن ، إذا نقرت على عنوان منشور ، فستحصل على 404 ، لذلك دعونا نصلح ذلك. مع كل بنيتنا في مكانها الصحيح ، يصبح هذا بسيطًا جدًا. سنحتاج فقط إلى إنشاء ما يلي:
- إجراء جديد مسؤول عن معالجة الطلبات إلى
blog/post/viewالمسار - كتلة لتقديم المنشور
- نموذج PHTML مسؤول عن العرض نفسه
- ملف تخطيط للمدونة / المنشور / مسار العرض ، مع وضع هذه الأجزاء الأخيرة معًا.
عملنا الجديد بسيط للغاية. سيتلقى ببساطة id المعلمة من الطلب ويسجله في سجل Magento الأساسي ، وهو مستودع مركزي للمعلومات المتاحة خلال دورة طلب واحدة. من خلال القيام بذلك ، سنجعل المعرف متاحًا للحظر لاحقًا. يجب أن يكون الملف موجودًا في app/code/Toptal/Blog/Controller/Post/View.php وهذه هي محتوياته:
<?php namespace Toptal\Blog\Controller\Post; use \Magento\Framework\App\Action\Action; use \Magento\Framework\View\Result\PageFactory; use \Magento\Framework\View\Result\Page; use \Magento\Framework\App\Action\Context; use \Magento\Framework\Exception\LocalizedException; use \Magento\Framework\Registry; class View extends Action { const REGISTRY_KEY_POST_; /** * Core registry * @var Registry */ protected $_coreRegistry; /** * @var PageFactory */ protected $_resultPageFactory; /** * @param Context $context * @param Registry $coreRegistry * @param PageFactory $resultPageFactory * * @codeCoverageIgnore * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( Context $context, Registry $coreRegistry, PageFactory $resultPageFactory ) { parent::__construct( $context ); $this->_coreRegistry = $coreRegistry; $this->_resultPageFactory = $resultPageFactory; } /** * Saves the blog id to the register and renders the page * @return Page * @throws LocalizedException */ public function execute() { $this->_coreRegistry->register(self::REGISTRY_KEY_POST_ID, (int) $this->_request->getParam('id')); $resultPage = $this->_resultPageFactory->create(); return $resultPage; } } لاحظ أننا أضفنا المعامل $coreRegistry إلى __construct ، وقمنا بحفظه كسمة لاستخدامه لاحقًا. في طريقة execute ، نسترد id من الطلب ونقوم بتسجيله. نحن نستخدم أيضًا ثابت فئة ، self::REGISTRY_KEY_POST_ID كمفتاح للتسجيل ، وسنستخدم نفس الثابت في الكتلة الخاصة بنا للإشارة إلى المعرف في السجل.
دعنا ننشئ الكتلة ، على app/code/Toptal/Blog/Block/View.php بالمحتويات التالية:
<?php namespace Toptal\Blog\Block; use \Magento\Framework\Exception\LocalizedException; use \Magento\Framework\View\Element\Template; use \Magento\Framework\View\Element\Template\Context; use \Magento\Framework\Registry; use \Toptal\Blog\Model\Post; use \Toptal\Blog\Model\PostFactory; use \Toptal\Blog\Controller\Post\View as ViewAction; class View extends Template { /** * Core registry * @var Registry */ protected $_coreRegistry; /** * Post * @var null|Post */ protected $_post = null; /** * PostFactory * @var null|PostFactory */ protected $_postFactory = null; /** * Constructor * @param Context $context * @param Registry $coreRegistry * @param PostFactory $postCollectionFactory * @param array $data */ public function __construct( Context $context, Registry $coreRegistry, PostFactory $postFactory, array $data = [] ) { $this->_postFactory = $postFactory; $this->_coreRegistry = $coreRegistry; parent::__construct($context, $data); } /** * Lazy loads the requested post * @return Post * @throws LocalizedException */ public function getPost() { if ($this->_post === null) { /** @var Post $post */ $post = $this->_postFactory->create(); $post->load($this->_getPostId()); if (!$post->getId()) { throw new LocalizedException(__('Post not found')); } $this->_post = $post; } return $this->_post; } /** * Retrieves the post id from the registry * @return int */ protected function _getPostId() { return (int) $this->_coreRegistry->registry( ViewAction::REGISTRY_KEY_POST_ID ); } } في كتلة العرض ، نحدد الطريقة المحمية _getPostId ، والتي ستستعيد ببساطة معرف المنشور من السجل الأساسي. ستقوم طريقة getPost العامة بدورها بتحميل المنشور بطيئًا وإصدار استثناء إذا لم يكن المنشور موجودًا. سيؤدي طرح استثناء هنا إلى جعل Magento تُظهر شاشة الخطأ الافتراضية الخاصة بها ، والتي قد لا تكون الحل الأفضل في مثل هذه الحالة ، لكننا سنبقيها على هذا النحو من أجل البساطة.
إلى نموذج PHTML الخاص بنا. أضف app/code/Toptal/Blog/view/frontend/templates/post/view.phtml بالمحتويات التالية:
<?php /** @var Toptal\Blog\Block\View $block */ ?> <h1><?php echo $block->getPost()->getTitle(); ?></h1> <p><?php echo $block->getPost()->getContent(); ?></p> لطيفة وبسيطة ، ما عليك سوى الوصول إلى طريقة عرض block getPost التي أنشأناها مسبقًا.
ولتجميعها معًا ، نقوم بإنشاء ملف تخطيط لمسارنا الجديد في app/code/Toptal/Blog/view/frontend/layout/blog_post_view.xml بالمحتوى التالي:
<?xml version="1.0"?> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="2columns-left" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> <referenceContainer name="content"> <block class="Toptal\Blog\Block\View" name="post.view" template="Toptal_Blog::post/view.phtml" /> </referenceContainer> </body> </page> هذا يفعل نفس الشيء الذي فعلناه من قبل. إنه ببساطة يضيف Toptal\Blog\Block\View إلى حاوية content ، مع Toptal_Blog::post/view.phtml كقالب مرتبط.
لرؤيتها أثناء العمل ، ما عليك سوى توجيه متصفحك إلى http://magento2.dev/blog/post/view/id/1 لتحميل منشور بنجاح. يجب أن تشاهد شاشة مثل الشاشة أدناه:
وكما ترى ، بعد إنشاء هيكلنا الأولي ، من السهل حقًا إضافة ميزات إلى النظام الأساسي ، ويتم إعادة استخدام معظم التعليمات البرمجية الأولية في هذه العملية.
إذا كنت ترغب في اختبار الوحدة بسرعة ، فإليك النتيجة الإجمالية لعملنا.
أين أذهب من هنا
إذا كنت قد تابعتني حتى هنا ، فتهانينا! أنا متأكد من أنك على وشك أن تصبح مطور Magento 2. لقد قمنا بتطوير وحدة Magento 2 المخصصة المتقدمة جدًا ، وعلى الرغم من أنها بسيطة في ميزاتها ، فقد تمت تغطية الكثير من الأرضية.
تم استبعاد بعض الأشياء من هذا المقال ، من أجل البساطة. على سبيل المثال لا الحصر:
- يقوم المسؤول بتحرير النماذج والشبكات لإدارة محتوى مدونتنا
- فئات المدونات والعلامات والتعليقات
- المستودعات وبعض عقود الخدمة التي كان من الممكن أن نؤسسها
- وحدات التعبئة والتغليف مثل ملحقات Magento 2
على أي حال ، إليك بعض الروابط المفيدة حيث يمكنك تعميق معرفتك بشكل أكبر:
- مدونة Alan Storm على Magento 2 - من المحتمل أن يكون Alan Storm هو المحتوى التعليمي الأكثر عندما يتعلق الأمر بتعلم Magento.
- مدونة آلان كينت
- وثائق Magento: مستندات Magento 2 Dev
لقد قدمت لك مقدمة شاملة لجميع الجوانب ذات الصلة بكيفية إنشاء وحدة نمطية في Magento 2 ، وبعض الموارد الإضافية إذا احتجت إليها. الآن الأمر متروك لك للحصول على الترميز ، أو التوجه إلى التعليقات إذا كنت ترغب في التفكير.
