اذهب إلى المحتوى

البحث في الموقع

المحتوى عن 'لارافيل للمبتدئين'.

  • ابحث بالكلمات المفتاحية

    أضف وسومًا وافصل بينها بفواصل ","
  • ابحث باسم الكاتب

نوع المحتوى


التصنيفات

  • الإدارة والقيادة
  • التخطيط وسير العمل
  • التمويل
  • فريق العمل
  • دراسة حالات
  • التعامل مع العملاء
  • التعهيد الخارجي
  • السلوك التنظيمي في المؤسسات
  • عالم الأعمال
  • التجارة والتجارة الإلكترونية
  • نصائح وإرشادات
  • مقالات ريادة أعمال عامة

التصنيفات

  • مقالات برمجة عامة
  • مقالات برمجة متقدمة
  • PHP
    • Laravel
    • ووردبريس
  • جافاسكربت
    • لغة TypeScript
    • Node.js
    • React
    • Vue.js
    • Angular
    • jQuery
    • Cordova
  • HTML
  • CSS
    • Sass
    • إطار عمل Bootstrap
  • SQL
  • لغة C#‎
    • ‎.NET
    • منصة Xamarin
  • لغة C++‎
  • لغة C
  • بايثون
    • Flask
    • Django
  • لغة روبي
    • إطار العمل Ruby on Rails
  • لغة Go
  • لغة جافا
  • لغة Kotlin
  • لغة Rust
  • برمجة أندرويد
  • لغة R
  • الذكاء الاصطناعي
  • صناعة الألعاب
  • سير العمل
    • Git
  • الأنظمة والأنظمة المدمجة

التصنيفات

  • تصميم تجربة المستخدم UX
  • تصميم واجهة المستخدم UI
  • الرسوميات
    • إنكسكيب
    • أدوبي إليستريتور
  • التصميم الجرافيكي
    • أدوبي فوتوشوب
    • أدوبي إن ديزاين
    • جيمب GIMP
    • كريتا Krita
  • التصميم ثلاثي الأبعاد
    • 3Ds Max
    • Blender
  • نصائح وإرشادات
  • مقالات تصميم عامة

التصنيفات

  • مقالات DevOps عامة
  • خوادم
    • الويب HTTP
    • البريد الإلكتروني
    • قواعد البيانات
    • DNS
    • Samba
  • الحوسبة السحابية
    • Docker
  • إدارة الإعدادات والنشر
    • Chef
    • Puppet
    • Ansible
  • لينكس
    • ريدهات (Red Hat)
  • خواديم ويندوز
  • FreeBSD
  • حماية
    • الجدران النارية
    • VPN
    • SSH
  • شبكات
    • سيسكو (Cisco)

التصنيفات

  • التسويق بالأداء
    • أدوات تحليل الزوار
  • تهيئة محركات البحث SEO
  • الشبكات الاجتماعية
  • التسويق بالبريد الالكتروني
  • التسويق الضمني
  • استسراع النمو
  • المبيعات
  • تجارب ونصائح
  • مبادئ علم التسويق

التصنيفات

  • مقالات عمل حر عامة
  • إدارة مالية
  • الإنتاجية
  • تجارب
  • مشاريع جانبية
  • التعامل مع العملاء
  • الحفاظ على الصحة
  • التسويق الذاتي
  • العمل الحر المهني
    • العمل بالترجمة
    • العمل كمساعد افتراضي
    • العمل بكتابة المحتوى

التصنيفات

  • الإنتاجية وسير العمل
    • مايكروسوفت أوفيس
    • ليبر أوفيس
    • جوجل درايف
    • شيربوينت
    • Evernote
    • Trello
  • تطبيقات الويب
    • ووردبريس
    • ماجنتو
    • بريستاشوب
    • أوبن كارت
    • دروبال
  • الترجمة بمساعدة الحاسوب
    • omegaT
    • memoQ
    • Trados
    • Memsource
  • برامج تخطيط موارد المؤسسات ERP
    • تطبيقات أودو odoo
  • أنظمة تشغيل الحواسيب والهواتف
    • ويندوز
    • لينكس
  • مقالات عامة

التصنيفات

  • آخر التحديثات

أسئلة وأجوبة

  • الأقسام
    • أسئلة البرمجة
    • أسئلة ريادة الأعمال
    • أسئلة العمل الحر
    • أسئلة التسويق والمبيعات
    • أسئلة التصميم
    • أسئلة DevOps
    • أسئلة البرامج والتطبيقات

التصنيفات

  • كتب ريادة الأعمال
  • كتب العمل الحر
  • كتب تسويق ومبيعات
  • كتب برمجة
  • كتب تصميم
  • كتب DevOps

ابحث في

ابحث عن


تاريخ الإنشاء

  • بداية

    نهاية


آخر تحديث

  • بداية

    نهاية


رشح النتائج حسب

تاريخ الانضمام

  • بداية

    نهاية


المجموعة


النبذة الشخصية

تم العثور على 6 نتائج

  1. يعد إطار عمل لارافيل Laravel واحدًا من أهم أطر عمل PHP وأكثرها شهرة وقوة، فقد تمكن لارافيل من إثبات جدارته واحتلال مواقع متصدرة بين نظم تطوير تطبيقات الويب لما يتمتع به من ميزات أبرزها توفير الكثير من الوظائف البرمجية الجاهزة التي تجعل تطوير الويب أسهل وأسرع، واهتمامه الخاص بأمن مواقع الويب وحمايتها من الثغرات الأمنية. فإذا كنت تتساءل ما هو لارافيل Laravel وما فوائد استخدامه، وترغب في التعرف على أبرز مميزاته وعيوبه، واكتشاف أفضل المصادر العربية التي تمكنك من تعلم لارافيل واستخدامه في إنشاء مواقع وتطبيقات ويب بميزات متقدمة فهذا المقال لك. ما هو لارافيل Laravel لارافيل Laravel هو إطار عمل مفتوح المصدر مبني على لغة PHP يستخدم لتطوير تطبيقات الويب، طوره تايلور أوتويل Taylor Otwell عام 2011 ليمكّن المطورين من إنشاء تطبيقات ويب متكاملة الميزات بسهولة وسرعة من خلال توفير أكثر المهام والوظائف الشائعة التي يحتاجونها في مشاريع الويب. وإطار العمل Framework هو باختصار بيئة توفر مجموعة من الوظائف والأدوات الجاهزة المكتوبة مسبقًا والتي يستخدمها المبرمجون والمطورون لتسهل عليه عملهم في التطوير وتمكنهم من أداء المطلوب باستخدام أقل قدر من التعليمات البرمجية، وللمزيد من المعلومات أنصح بالاطلاع على مقال تعرف على مفهوم إطار العمل Framework وأهميته في البرمجة لقد عزز إطار العمل لارافيل من قوة لغة PHP وساهم في استمراريتها وديمومتها، حيث أنه وفر للمبرمجين طريقة أكثر كفاءة في التعامل معها وبسّط كتابة تعليماتها المعقدة نوعًا ما وجعل تطوير تطبيقات الويب باستخدامها أسرع وأسهل. إصدارات لارافيل Laravel قبل إصدار لارافيل 9 كان فريق لارافيل يوفر إصدارًا رئيسيًا كل ستة أشهر بمعدل نسختين رئيسيتين سنويًا لتخفيف أعباء الصيانة، لكن تقرر بعد ذلك توفير الإصدارات الجديدة بشكل سنوي. يتطور لارافيل Laravel بسرعة فائقة مع مرور الوقت ويضيف تحسينات وميزات ووظائف جديدة تواكب اتجاهات السوق المتغيرة باستمرار أو يغير بعض الوظائف أو يلغيها لذلك من الضروري معرفة الإصدار الذي تستخدمه في تطوير مشاريعك. وإليك قائمة بأهم إصدارات لارافيل laravel وتاريخ صدورها: أطلق الإصدار الأول لارافيل 1.0 في 9 يونيو/حزيران 2011 توالت بعدها الإصدارات لغاية لارافيل 8 الذي تم إطلاقه في 8 سبتمبر/أيلول 2020 وهو الإصدار العشرين من Laravel ولم تعد كافة هذه الإصدارات تتلقى تحديثات أمنية لذا يفضل أن تتم ترقية التطبيقات المبنية بأحدها لإصدار أحدث لمنع الثغرات الأمنية والوصول إلى الميزات الجديدة. صدر لارافيل 9 بتاريخ 8 فبراير 2022 ويتطلب هذا الإصدار نسخة PHP 8.0 كحد أدنى. صدر لارافيل 10 بتاريخ 7 فبراير 2023 وتضمن العديد من الميزات الجديدة التي تعزز أداء تطبيقات الويب وأمانها ووظائفها ويتطلب هذا الإصدار نسخة 8.1 من لغة PHP كحد أدنى. ومن المتوقع أن يصدر لارافيل 11 في الربع الأول من عام 2024، وللمزيد من التفاصيل يمكنك مطالعة الصفحة التالية لكافة إصدارات لارافيل، والجدير بالذكر أن لارافيل قد أثبت كفاءته لأكثر من عقد من الزمن وتمكن من فرض نفسه كأحد أكثر أطر PHP شيوعًا وتفضيلًا من قبل مطوري الويب بفضل طريقة تنظيمه الأنيقة وميزاته القوية. أهمية إطار لارافيل في سوق العمل يعد لارافيل أحد أشهر أطر عمل PHP وأكثرها استخدامًا ويملك مجتمعًا كبيرًا من المطورين الذين يوفرون الكثير من المكتبات والحزم الجاهزة والمتاحة للاستخدام بسهولة. كما يعد لارافيل أحد المهارات المطلوبة بشدة في سوق العمل، ويبحث الكثير من أرباب العمل عن مطورين محترفين في استخدام إطار العمل لارافيل لتطوير تطبيقاتهم ومواقعهم الإلكترونية ولا عجب في ذلك فهو واحد من أسرع أطر عمل PHP نموًا وأكثرها غنىً بالميزات. وتستخدم العديد من المواقع الكبرى لارافيل في تطوير تطبيقاتها الخاصة،فبحسب موقع builtwith الذي يحدد التقنيات المستخدمة في إنشاء المواقع هناك 1,175,772 موقع ويب مبني باستخدام إطار عمل Laravel ولك أن تتخيل مدى شعبيته. كما يلبي لارافيل كافة متطلبات العمل لكونه يعتمد على لغة PHP العريقة التي تعد واحدة من أقوى لغات البرمجة من جانب الخادم والتي تستخدم في تطوير ملايين المواقع الإلكترونية من بينها مواقع شهيرة مثل فيسبوك وويكيبيديا. فإذا كنت مهتمًا بمجال تطوير الويب فتعلم إطار عمل لارافيل Laravel يوفر لك الكثير من الفرص المميزة في مجال تطوير تطبيقات الويب ويمكنك من إنشاء كافة أنواع التطبيقات والمواقع الإلكترونية سواء المواقع البسيطة للشركات الناشئة منها أو المواقع المتقدمة كمواقع التجارة الإلكترونية بسرعة واحترافية. ويتنافس إطار عمل لارافيل مع أطر عمل تطوير الويب الأخرى في سوق العمل مثل جانغو Django المبني باستخدام لغة البرمجة بايثون Python والتي تعتبر أسرع من لغة PHP وأكثر شعبية منها، وإطار عمل إكسبريس Express المبني باستخدام لغة جافا سكريبت أحد أكثر لغات البرمجة شهرة واستخدامًا بين أوساط المطورين، وإطار عمل ريلز Rails المبني باستخدام لغة البرمجة روبي Ruby المحببة للمبتدئين، تقدم معظم أطر العمل ميزات متشابهة وكي تتمكن من اختيار إطار العمل الأنسب لك عليك أن تأخذ عدة أمور بعين الاعتبار مثل لغة البرمجة التي تفضل تعلمها وطبيعة المشاريع التي تعمل عليها، ولمزيد من التفاصيل حول أشهر أطر عمل تطوير الويب أنصح بمطالعة مقال مقارنة بين Django و Laravel و Rails. مميزات لارافيل Laravel لاشك أن لارافيل Laravel ليس إطار العمل الوحيد المتاح اليوم بل هو واحد من بين عشرات أطر عمل تطوير الويب، وقد تتساءل لماذا أستخدم لارافيل دونًا عن غيره؟ وما الذي يميزه عن غيره من أطر عمل تطوير الويب؟ في الواقع نجد تحيز من قبل المبرمجين لاختيار لارافيل دون غيره نظرًا لما يتمتع به من ميزات، وإليك قائمة بأهم 10 مميزات لإطار لارافيل: سهولة التعلم والاستخدام. تسريع وقت التطوير. التوثيق الجيد. غني بالميزات وقابل للتوسيع. يهتم بأمان التطبيقات. يحسن أداء التطبيقات. يدعم تعدد اللغات. يسهل تصحيح أخطاء التطبيقات وصيانتها. يوفر نظام لتهجير البيانات Migration. يوفر نظام التوجيه Routing. لنناقش كل خاصية من هذه الخواص بمزيد من التفصيل ونكتشف أهميتها في تطوير الويب. 1. سهولة التعلم والاستخدام يتميز لارافيل بكود سهل التعلم والاستخدام، لكنه بالطبع يشترط امتلاك معرفة مسبقة بأساسيات PHP ومبادئ البرمجة كائنية التوجه OOP إلى جانب وجود معرفة جيدة حول HTML وأحد أنظمة إدارة قواعد البيانات مثل MySQL أو PostgreSQL بعدها سيكون تعلم لارافيل أمرًا في غاية البساطة. 2. تسريع وقت التطوير يتضمن لارافيل Laravel العديد من الوظائف التي تسهل على المطور تنفيذ المهام الشائعة مثل عمليات المصادقة authentication، والتوجيه routing، وتهجير قواعد البيانات migrating، والتخزين المؤقت cach، فكل هذه الوظائف مضمنة ومدمجة في نظام لارافيل وجاهزة لتستخدمها بكل سهولة. كما يتضمن واجهة سطر أوامر مدمجة تسمى آرتيزان Artisan توفر مجموعة من الأوامر المفيدة في بناء تطبيقات الويب، وبالجمع بين أوامر آرتيزان والوظائف المضمنة تصبح عملية تطوير تطبيقات الويب أسرع بكثير. 3. التوثيق الجيد يحتوي لارافيل Laravel على توثيق مميز عبر الإنترنت يساعد المطورين والمبرمجين في تعلم كل ما يحتاجونه والعثور على إجابات لكافة تساؤلاتهم حول التعامل مع هذا الإطار، كما تتوفر معلومات هائلة متاحة من مجتمع مطوري لارافيل. المشكلة الوحيدة التي توجد في هذه المصادر هو أنها متاحة باللغة الإنجليزية وشرحها جامد إلى حد ما، وفي فقرة تعلم لارافيل سأطلعك على مصادر عربية جيدة تساعدك في تعلم لارافيل. 4. غني بالميزات وقابل للتوسيع يعد لارافيل نظامًا قويًا وغنيًا بالكثير من الميزات المضمنة ويوفر مجموعة متنوعة من المكتبات المضمنة التي تساعدك على بناء تطبيقات متقدمة تعالج ملايين الطلبات بفضل دعمه للتخزين المؤقت السريع والتخزين الموزع، كما يوفر لارافيل منصات سحابية خفية الخوادم Serverless مثل Laravel Forge و Vapor تمكنك من نشر التطبيقات وتشغيلها دون الحاجة إلى إدارة الخوادم وهي منصات قابلة للتوسيع التلقائي بكل سهولة. أضف إلى ذلك يوفر مطوروا لارافيل الكثير من الحزم الخارجية لأي وظيفة أو ميزة تحتاجها، على سبيل المثال يمكنك باستخدام مكتبة Socialite تضمين ميزة تسجيل الدخول إلى موقعك باستخدام حسابات شبكات التواصل الاجتماعي على فيسبوك أو تويتر أو لينكدإن أو جيتهب أو جوجل بكل سهولة. 5. يهتم بأمان التطبيقات يمكنك لارافيل من تطوير مواقع وتطبيقات آمنة بفضل العديد من ميزات الأمان المدمجة مثل نظام المصادقة أو الاستيثاق المدمج authentication system ونظام المصادقة authorization والتحقق من صحة البيانات data validation وتشفير البيانات والتحقق من البريد الإلكتروني وإعادة تعيين كلمة المرور، كما يسهل عليك تكوين ميزات الأمان المتقدمة التي تحمي تطبيقاتك من الاختراق وتضمن أمان بيانات عملائك. 6. يحسن أداء التطبيقات يوفر لارافيل عدة طرق لتحسين سرعة التطبيقات وتحسين أدائها مثل دعم التخزين المؤقت لموقعك الإلكتروني على الخادم، كما أنه يسهل تنفيذ تقنيات أخرى لتحسين السرعة مثل تقليل استخدام الذاكرة وفهرسة قاعدة البيانات. فإذا كانت سرعة الموقع أولوية لديك فإن لارافيل خيارك الأنسب. 7. يدعم تعدد اللغات فمن خلال ميزة التوطين localization يمكنك لارافيل من إنشاء تطبيقات متعددة اللغات واسترداد السلاسل النصية بلغات مختلفة، وهو يدعم عدة لغات من بينها اللغة العربية. 8. يسهل تصحيح أخطاء التطبيقات وصيانتها يحتوي لارافيل على ميزات تختبر كل جزء من موقع الويب الخاص بك للبحث عن أي أخطاء ويعرض لك رسائل واضحة ومفصلة تبين كافة الأخطاء التي تحدث في موقعك ويسهل عليك تصحيحها، كما أنه يسهل صيانة التطبيقات بفضل ميزات الكود النظيف واعتماد مبادئ البرمجة كائنية التوجه OOP ومعمارية MVC التي تنظم الكود وتمكن أي مطور من متابعة العمل على مشروع سابق وتطويره وإضافة الميزات المطلوبة له. 9.يوفر نظام تهجير البيانات Migration يوفر لارافيل ميزة تهجير البيانات Migration التي توفر آليات لإنشاء وتعديل جداول قاعدة البيانات كما يوفر آلية شبيهة بنظام التحكم في الإصدارات لقاعدة البيانات الخاصة بك تتعقب كيفية تعديل قاعدة البيانات مع الوقت وتسمح لكافة أعضاء فريق التطوير بتعديل قاعدة بيانات المشروع ومشاركته فيما بينهم وتسهل حذف قاعدة البيانات وإعادة إنشائها عند الضرورة. وللمزيد من التفاصيل حول هذه الميزة يمكنك مطالعة مقال تهجير قواعد البيانات في لارافيل 5 10. يوفر نظام التوجيه Routing يوفر لارافيل نظام توجيه Routing قوي لمعالجة وإدارة مسارات تطبيق الويب ويُمكِّنك من الانتقال من مكان لآخر في تطبيقك وتبادل المعلومات بينها بسهولة، كما يمكّنك من استخدام أسماء بسيطة ترتبط بأجزاء مختلفة من تطبيقك بدلاً من الأسماء الطويلة والمربكة، ستجد كافة المسارات الخاصة بالتطبيق في الملف web.php ضمن المجلد routes لمشروعك وللمزيد من التفاصيل يمكنك الاطلاع على توثيق التوجيه Routing في لارافيل. عيوب لارافيل Laravel لا تخلو أي تقنية مهما كانت متقدمة من بعض العيوب ومن بينها لارافيل، فهو يملك بعض جوانب النقص وإليك قائمة بأبرز عيوب لارافيل: يعتبر إطار عمل لارافيل بطيئًا نسبيًا عند مقارنته ببعض أطر تطوير الويب الأخرى. رغم كونه آمنًا بشكل كبير إلا أنه لا يزال مهددًا باستهدافه بالثغرات الأمنية بسبب اعتماده على PHP التي وسمت بكثرة الثغرات الأمنية فيها تحديدًا في الإصدارات القديمة منها. تكلفته عالية فالمواقع المطورة بإطار لارافيل قد تكون أغلى نسبيًا من المواقع المطورة بغيرها من تقنيات الويب كأنظمة إدارة المحتوى أو أطر العمل الأخرى وهذا رغم كونه عامل قوة لك كمطور لكنه قد يجعل بعض العملاء يفضلون الخيارات الأرخص. وجود تحديثات متكررة قد يجعلك تواجه صعوبات في تعلم الجديد في كل إصدار وتعديل شيفرة تطبيقاتك المطورة بإصدارات قديمة لذا يجب أن تكون لديك رغبة مستمرة في التعلم كي تتلاءم مع كل إصدار جديد. كانت هذه قائمة بأبرز عيوب أو سلبيات لارافيل Laravel، وبموازنة السلبيات والإيجابيات يمكن أن تقرر فيما إذا كان إطار لارافيل سيلبي احتياجاتك أم لا. تنظيم الكود في لارافيل Laravel يتميز إطار لارافيل Laravel بكود منظم وسهل الصيانة يحافظ على تنظيم مشاريعك البرمجة من خلال استخدامه معمارية MVC وهي اختصار لثلاث كلمات هي: النموذج Model الذي يعني بيانات التطبيق فهو يتفاعل مباشرة مع قاعدة البيانات الخاصة بك ويسترد المعلومات منها. العرض View الذي يعني واجهة التطبيق فهو يعرض الصفحات التي يتفاعل معها المستخدم مباشرة. المتحكم Controller وهو صلة الوصل بين العرض والنموذج فهو يستقبل طلبات المستخدمين ويسترد البيانات المطلوبة من النموذج ويعالجها ويرسلها إلى صفحات العرض. وباستخدام هذا الأسلوب التنظيمي يستطيع لارافيل فصل كود التطبيق إلى ثلاث مجموعات لكل منها وظيفة محددة وهي الواجهة الخلفية والواجهة الأمامية ووحدة التحكم للربط بين الواجهتين وهذه الميزة تجعل من لارافيل أفضل إطار عمل PHP لتطبيقات الويب. بالإضافة إلى ذلك يستخدم لارافيل المسارات routes لتعيين عناوين URL الخاصة بإجراءات متحكم معين، فعندما يقوم المستخدم بإدخال عنوان URL خاص بالتطبيق يعين المسار المرتبط بهذا العنوان عنوان URL للمتحكم المسؤول عن استرداد البيانات المطلوبة من قاعدة البيانات أو النموذج ويمررها إلى صفحات العرض النهائية. كيف أتعلم لارافيل Laravel؟ بعد أن تعرفنا على إطار عمل لارافيل وأهم مميزاته واستخداماته، سنوضح لك في هذه الفقرة طريقة تعلم لارافيل وأهم الخطوات التي عليك اتباعها كي تحترفه. قبل أن تبدأ بتعلم لارافيل يجب أن تكون على دراية بأساسيات تطوير الويب والمفاهيم الأساسية في HTML و CSS. يجب أن تتعلم أساسيات البرمجة بلغة PHP وتتمكن من كتابة برامج PHP بسيطة وتعرف كذلك مبادئ البرمجة كائنية التوجه OOP. تعلم المكونات الأساسية لإطار عمل لارافيل ومعمارية MVC ومفاهيم النماذج وصفحات العرض والمتحكمات من خلال مصادر التعلم التي تفضلها سواء كانت مواقع الإنترنت أو الكتب أو الدورات التعليمية واحرص على تحديد خطة للتعلم والتزم بها كي تحقق أهدافك. طور مشاريع تطبق فيها ما تعلمته، فالتطبيق العملي طريقة رائعة لتعلم المفاهيم التي تعلمتها، ابدأ بمشروع بسيط ثم انتقل إلى مشاريع أكثر تقدمًا فهذا يساعدك على إنشاء معرض أعمال يعزز مسيرتك المهنية. لا تتوقف عن التعلم واطلع على كل جديد في مجال تطوير الويب عمومًا وإطار لارافيل على وجه الخصوص، فالتقنيات تتطور بسرعة والتعلم المستمر يساعدك على التكيف مع أي تطور جديد والبقاء في الصدارة. مصادر تعلم لارافيل Laravel إذا كنت تبحث عن مصدر عربي موثوق لتعلم لارافيل من الصفر للاحتراف بشهادة معتمدة تعزز فرصتك في سوق العمل ستجد في أكاديمية حسوب دورة تطوير تطبيقات الويب باستخدام لغة PHP وهي دورة تدريبية عالية الجودة تضم مسارات متعددة ولا تتطلب منك أي معرفة مسبقة وتعلمك كل ما يخص لغة PHP وإطار عملها لارافيل وتدمج بين الشرح النظري والتطبيق العملي، فمن خلالها ستتعلم تطوير العديد من تطبيقات الويب المتكاملة وتبني من خلالها معرض أعمال قوي، كما ستتمكن من خلال هذه الدورة من التواصل مع مدربين أكفاء وطرح أي سؤال أو مشكلة تواجهك عند تطبيق مشروعك البرمجي. كما ستجد في أكاديمية حسوب الكثير من مصادر التعلم المجانية مثل المقالات والدروس المتنوعة حول PHP وحول إطار عمل لارافيل Laravel، وبالإضافة لكل ما سبق فقد ترجمت موسوعة حسوب توثيق لارافيل الأجنبي إلى اللغة العربية ويمكنك من خلاله أن تتعرف على كل ما يخص هذا الإطار بتسلسل منظم وواضح. تنصيب لارافيل Laravel كي تتمكن من التعامل مع مشاريع لارافيل في حاسوبك المحلي عليك اتباع مجموعة من الخطوات وهي كالتالي: أول خطوة قبل البدء بتثبيت لارافيل Laravel على جهازك المحلي هي تحويل حاسوبك إلى خادم ويب محلي كي يتمكن من تشغيل PHP و نظام إدارة قواعد البيانات مثل MySQL أو PostgreSQL، يمكنك القيام بذلك باستخدام XAMPP المتوافق مع كافة أنظمة التشغيل. عليك كذلك تثبيت أي محرر أكواد أو بيئة تطوير متكاملة IDE ترغب بها لتحرير أكواد مشروعك مثل PhpStorm أو Visual Studio Code أو أي محرر آخر تتآلف معه. بعدها عليك تثبيت مدير الحزم كومبوزر Composer لتحميل إطار لارافيل من خلاله، وهو ضروري لتحديد المكتبات التي يحتاجها مشروعك بكل سهولة وإدارتها بالنيابة عنك. وللمزيد يمكنك مطالعة مقال ما هو Composer ولماذا يجب على كل مطور PHP استخدامه. الآن يمكنك تثبيت لارافيل على جهازك، وأبسط طريقة لتحميل لارافيل هي تشغيل محرر الأوامر في نظامك والانتقال للمسار الذي تريد إنشاء مشروع لارافيل الخاص بك فيه ثم كتابة الأمر التالي، هنا أنشأنا مشروع لارافيل باسم example-app composer create-project laravel/laravel example-app لتشغيل المشروع انتقل إلى مجلد مشروعك وهو في حالتنا D:\example-app ونفذ الأمر التالي: D:\cd example-app D:\example-app>php artisan serve هذا الأمر سيشغل تطبيق لارافيل على خادم تطوير PHP وينتج عن تنفيذه عرض رابط المشروع بالشكل التالي http://127.0.0.1:8000، كل ما عليك هو نسخ هذا الرابط ولصقه في المستعرض لتظهر لك الصفحة الرئيسية لموقع لارافيل الخاص بك كما هو موضح في الصورة التالية. ولإيقاف تشغيل خادم التطوير من خلال سطر الأوامر اضغط على مفتاحي Ctrl+C في لوحة المفاتيح. ولمزيد من المعلومات حول تثبيت لارافيل يمكنك مطالعة مقال تثبيت وإعداد لارافيل Laravel على دوكر كومبوز Docker Compose ومقال تثبيت Laravel 5 وإعداده على Windows وUbuntu. أخيرًا أنصحك أن تفتح مشروع في محرر الشيفرات الذي اخترته وتتعرف على بنية المجلدات في Laravel وتفهم دورها في المشروع ومساهمتها في نموذج MVC المستخدم في إطار العمل لارافيل. هذا كل شيء! أنت جاهز الآن لتبدأ رحلتك في اكتشاف لارافيل والبدء بتعلمه. الخلاصة اكتشفنا في مقال اليوم ما هو إطار عمل لارافيل Laravel وأهميته في سوق العمل، وتعرفنا على أهم المميزات التي يقدمها في مجال تطوير مواقع وتطبيقات الويب، كما تعرفنا على كيفية تنصيب لارافيل وبدء التعامل معه وأهم مصادر تعلمه. وسواء كنت مطورًا مبتدئًا أو خبيرًا في برمجة تطبيقات الويب فإن لارافيل Laravel خيار مناسب لك تمامًا فهو يوفر لك بيئة تطوير سهلة وقوية ويقدم لك الكثير من الميزات والوظائف المبرمجة مسبقًا التي تمكنك من بناء تطبيقات ويب احترافية وعالية الجودة بسرعة وسهولة. لا تتردد وابدأ من اليوم بتعلم لارافيل وإن اعترضك أي سؤال حوله شاركنا إياه في قسم التعليقات أسفل المقال. اقرأ أيضًا دليل مطوّري PHP للبدء في بناء تطبيقات Laravel - الجزء الأوّل دليل مطوّري PHP للبدء في بناء تطبيقات Laravel - الجزء الثاني رفع الملفّات وإدارتها في تطبيقات Laravel تجريد إعداد قواعد البيانات في لارافيل باستعمال عملية التهجير Migration والبذر Seeder لارافيل للمبتدئين-الجزء الأول: البدء في إنشاء مدونة بسيطة
  2. سننشئ في هذا المقال الأخير من هذه السلسلة جزء الواجهة الأمامية من التطبيق، ولكن لنضع خطة أولًا، حيث سيكون لدينا الصفحة الرئيسية لتطبيق المدونة التي تعرض قائمة بجميع المنشورات الحديثة، وصفحة فئة Category المنشور التي تعرض قائمة المنشورات ضمن فئة معينة، وصفحة الوسم Tag التي تعرض قائمة المنشورات التي لها وسم مُحدَّد، وصفحة المنشور التي تعرض محتوى منشور معين، وصفحة بحث تعرض قائمة المنشورات بناءً على استعلام بحث معين. كما ستحتوي جميع هذه الصفحات على شريط جانبي مع مربع بحث وقائمة الفئات والوسوم، وستحتوي صفحة المنشور أيضًا على قسم المنشورات ذات الصلة في نهايتها. تحدّثنا عن قاعدة البيانات والنماذج Models في المقال السابق، وسنبدأ الآن بالوِجهات Routes. إنشاء الوِجهات ننشئ الوِجهات في لارافيل ضمن الملف routes/web.php كما يلي: . . . // الصفحة الرئيسية Route::get('/', [PostController::class, 'home'])->name('home'); // قائمة المنشورات ضمن هذه الفئة Route::get('/category/{category}', [CategoryController::class, 'category'])->name('category'); // قائمة المنشورات التي لها هذا الوسم Route::get('/tag/{tag}', [TagController::class, 'tag'])->name('tag'); // عرض منشور واحد Route::get('/post/{post}', [PostController::class, 'post'])->name('post'); // قائمة المنشورات بناء على استعلام البحث Route::post('/search', [PostController::class, 'search'])->name('search'); . . . الصفحة الرئيسية يكون لكل من هذه الوِجهات تابع تحكم مقابل، حيث سنبدأ أولًا بالمتحكم home()‎ إذًا لنضع ما يلي في الملف app/Http/Controllers/PostController.php: <?php namespace App\Http\Controllers; use App\Models\Category; use App\Models\Post; use App\Models\Tag; use Illuminate\Contracts\View\View; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Http\Response; use Illuminate\Support\Facades\Auth; use Illuminate\Database\Eloquent\Builder; class PostController extends Controller { /** * عرض الصفحة الرئيسية */ public function home(): View { $posts = Post::where('is_published', true)->paginate(env('PAGINATE_NUM')); $categories = Category::all(); $tags = Tag::all(); return view('home', [ 'posts' => $posts, 'categories' => $categories, 'tags' => $tags ]); } . . . } هناك شيئان يجب عليك ملاحظتهما في السطر 20 من الشيفرة البرمجية السابقة وهما: أولًا، تتأكد التعليمة where('is_published', true)‎ من استرداد المقالات المنشورة فقط. ثانيًا، يُعَد التابع paginate()‎ أحد توابع لارافيل Laravel المُضمَّنة، مما يسمح لك بإنشاء ترقيم للصفحات Pagination في تطبيقك بسهولة. يأخذ التابع paginate()‎ عددًا صحيحًا كدخل، فمثلًا يمثّل paginate(10)‎ عرض 10 عناصر في كل صفحة، وسيُستخدَم متغير الدخل في العديد من الصفحات، لذا يمكنك إنشاء متغير بيئة في ملف ‎.env كما يلي، ثم يمكنك استرداده في أيّ مكان باستخدام التابع env()‎: . . . PAGINATE_NUM=12 لننشئ الآن عرض View الصفحة الرئيسية المقابل. اطّلع على بنية القوالب التي أنشأئاها، وإليك بنية العروض التالية: resources/views ├── category.blade.php ├── home.blade.php ├── layout.blade.php ├── post.blade.php ├── search.blade.php ├── tag.blade.php ├── vendor │ ├── list.blade.php │ └── sidebar.blade.php └── welcome.blade.php سنبدأ أولًا بالعرض layout.blade.php الذي يمثل تخطيط الصفحة كما يلي: <!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> @vite(['resources/css/app.css', 'resources/js/app.js']) @yield('title') </head> <body class="container mx-auto font-serif"> <nav class="flex flex-row justify-between h-16 items-center border-b-2"> <div class="px-5 text-2xl"> <a href="/"> My Blog </a> </div> <div class="hidden lg:flex content-between space-x-10 px-10 text-lg"> <a href="https://github.com/ericnanhu" class="hover:underline hover:underline-offset-1" >GitHub</a > <a href="{{ route('dashboard') }}" class="hover:underline hover:underline-offset-1" >Dashboard</a > <a href="#" class="hover:underline hover:underline-offset-1">Link</a> </div> </nav> @yield('content') <footer class="bg-gray-700 text-white"> <div class="flex justify-center items-center sm:justify-between flex-wrap lg:max-w-screen-2xl mx-auto px-4 sm:px-8 py-10" > <p class="font-serif text-center mb-3 sm:mb-0"> Copyright © <a href="https://www.ericsdevblog.com/" class="hover:underline" >Eric Hu</a > </p> <div class="flex justify-center space-x-4">. . .</div> </div> </footer> </body> </html> يستخدم لارافيل الأداة Vite افتراضيًا لتجميع أصول المشروع كما في السطر 8، وثبّتنا الحزمة Breeze من لارافيل في المقال السابق، وتستخدم هذه الحزمة إطار عمل Tailwind CSS، حيث سيستورد السطر 8 من الشيفرة البرمجية السابقة تلقائيًا الملفين app.css و app.js المقابلَين. يمكنك استخدام إطار عمل مختلف أيضًا، ولكن يجب أن تعود إلى توثيقه للحصول على تفاصيل حول كيفية استخدامه مع لارافيل أو Vite. لننشئ الآن عرض الصفحة الرئيسية resources/views/home.blade.php كما يلي: @extends('layout') @section('title') <title>Home</title> @endsection @section('content') <div class="grid grid-cols-4 gap-4 py-10"> <div class="col-span-3 grid grid-cols-1">@include('vendor.list')</div> @include('vendor.sidebar') </div> @endsection ويكون عرض قائمة المنشورات resources/views/vendor/list.blade.php كما يلي: <!-- قائمة المنشورات --> <div class="grid grid-cols-3 gap-4"> @foreach ($posts as $post) <!-- المنشور --> <div class="mb-4 ring-1 ring-slate-200 rounded-md h-fit hover:shadow-md"> <a href="{{ route('post', ['post' => $post->id]) }}" ><img class="rounded-t-md object-cover h-60 w-full" src="{{ Storage::url($post->cover) }}" alt="..." /></a> <div class="m-4 grid gap-2"> <div class="text-sm text-gray-500"> {{ \Carbon\Carbon::parse($post->created_at)->format('M d, Y') }} </div> <h2 class="text-lg font-bold">{{ $post->title }}</h2> <p class="text-base"> {{ Str::limit(strip_tags($post->content), 150, '...') }} </p> <a class="bg-blue-500 hover:bg-blue-700 rounded-md p-2 text-white uppercase text-sm font-semibold font-sans w-fit focus:ring" href="{{ route('post', ['post' => $post->id]) }}" >Read more →</a > </div> </div> @endforeach </div> {{ $posts->links() }} يولّد السطر 9 Storage::url($post->cover)‎ عنوان URL الذي يؤشّر إلى صورة الغلاف. لا تُعَد الطريقة التي يخزن بها لارافيل العلامات الزمنية Timestamps سهلة الاستخدام، لذلك استخدمنا حزمة Carbon في السطر 14 لإعادة تنسيق العلامات الزمنية. استخدمنا أيضًا الدالة strip_tags()‎ في السطر 18 لإزالة وسوم HTML، ثم تحدِّد الدالة limit()‎ الحد الأقصى لطول السلسلة، وستوضَع نقاط ... مكان الجزء الزائد منها. استخدمنا التابع paginate()‎ لإنشاء مرقّم الصفحات في المتحكم، ولكن يمكننا عرض مرقّم الصفحات في العرض باستخدام التعليمة ‎{{ $posts->links() }}‎ الموجودة في السطر 30. لننشئ الآن عرض الشريط الجانبي resources/views/vendor/sidebar.blade.php كما يلي: <div class="col-span-1"> <div class="border rounded-md mb-4"> <div class="bg-slate-200 p-4">Search</div> <div class="p-4"> <form action="{{ route('search') }}" method="POST" class="grid grid-cols-4 gap-2" > {{ csrf_field() }} <input type="text" name="q" id="search" class="border rounded-md w-full focus:ring p-2 col-span-3" placeholder="Search something..." /> <button type="submit" class="bg-blue-500 hover:bg-blue-700 rounded-md p-2 text-white uppercase font-semibold font-sans w-full focus:ring col-span-1" > Search </button> </form> </div> </div> <div class="border rounded-md mb-4"> <div class="bg-slate-200 p-4">Categories</div> <div class="p-4"> <ul class="list-none list-inside"> @foreach ($categories as $category) <li> <a href="{{ route('category', ['category' => $category->id]) }}" class="text-blue-500 hover:underline" >{{ $category->name }}</a > </li> @endforeach </ul> </div> </div> <div class="border rounded-md mb-4"> <div class="bg-slate-200 p-4">Tags</div> <div class="p-4"> @foreach ($tags as $tag) <span class="mr-2" ><a href="{{ route('tag', ['tag' => $tag->id]) }}" class="text-blue-500 hover:underline" >{{ $tag->name }}</a ></span > @endforeach </div> </div> <div class="border rounded-md mb-4"> <div class="bg-slate-200 p-4">More Card</div> <div class="p-4"> <p> Lorem ipsum dolor sit amet consectetur adipisicing elit. Placeat, voluptatem ab tempore recusandae sequi libero sapiente autem! Sit hic reprehenderit pariatur autem totam, voluptates non officia accusantium rerum unde provident! </p> </div> </div> <div class="border rounded-md mb-4"> <div class="bg-slate-200 p-4">...</div> <div class="p-4"> <p> Lorem ipsum dolor sit amet consectetur adipisicing elit. Placeat, voluptatem ab tempore recusandae sequi libero sapiente autem! Sit hic reprehenderit pariatur autem totam, voluptates non officia accusantium rerum unde provident! </p> </div> </div> </div> صفحة الفئة Category ضع ما يلي في ملف المتحكم app/Http/Controllers/CategoryController.php: <?php namespace App\Http\Controllers; use App\Models\Category; use App\Models\Tag; use Illuminate\Contracts\View\View; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Http\Response; class CategoryController extends Controller { /** * عرض قائمة المنشورات التي تنتمي إلى الفئة */ public function category(string $id): View { $category = Category::find($id); $posts = $category->posts()->where('is_published', true)->paginate(env('PAGINATE_NUM')); $categories = Category::all(); $tags = Tag::all(); return view('category', [ 'category' => $category, 'posts' => $posts, 'categories' => $categories, 'tags' => $tags ]); } . . . } ويكون عرض الفئة المقابل resources/views/category.blade.php كما يلي: @extends('layout') @section('title') <title>Category - {{ $category->name }}</title> @endsection @section('content') <div class="grid grid-cols-4 gap-4 py-10"> <div class="col-span-3 grid grid-cols-1">@include('vendor.list')</div> @include('vendor.sidebar') </div> @endsection صفحة الوسم Tag ضع ما يلي في ملف المتحكم app/Http/Controllers/TagController.php: <?php namespace App\Http\Controllers; use App\Models\Category; use App\Models\Tag; use Illuminate\Contracts\View\View; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Http\Response; class TagController extends Controller { /** * عرض قائمة المنشورات التي تنتمي إلى الوسم */ public function tag(string $id): View { $tag = Tag::find($id); $posts = $tag->posts()->where('is_published', true)->paginate(env('PAGINATE_NUM')); $categories = Category::all(); $tags = Tag::all(); return view('tag', [ 'tag' => $tag, 'posts' => $posts, 'categories' => $categories, 'tags' => $tags ]); } . . . } ويكون عرض الوسم المقابل resources/views/tag.blade.php كما يلي: @extends('layout') @section('title') <title>Tag - {{ $tag->name }}</title> @endsection @section('content') <div class="grid grid-cols-4 gap-4 py-10"> <div class="col-span-3 grid grid-cols-1">@include('vendor.list')</div> @include('vendor.sidebar') </div> @endsection صفحة المنشور ضع ما يلي في ملف المتحكم app/Http/Controllers/PostController.php: <?php namespace App\Http\Controllers; use App\Models\Category; use App\Models\Post; use App\Models\Tag; use Illuminate\Contracts\View\View; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Http\Response; use Illuminate\Support\Facades\Auth; use Illuminate\Database\Eloquent\Builder; class PostController extends Controller { . . . /** * عرض المنشور المطلوب */ public function post(string $id): View { $post = Post::find($id); $categories = Category::all(); $tags = Tag::all(); $related_posts = Post::where('is_published', true)->whereHas('tags', function (Builder $query) use ($post) { return $query->whereIn('name', $post->tags->pluck('name')); })->where('id', '!=', $post->id)->take(3)->get(); return view('post', [ 'post' => $post, 'categories' => $categories, 'tags' => $tags, 'related_posts' => $related_posts ]); } . . . } لاحظ في السطور من 27 إلى 29 أن هذه هي الطريقة التي يمكننا من خلالها استرداد المنشورات ذات الصلة، حيث نريد الحصول على المنشورات التي يكون لها الوسوم نفسها. قد تبدو سلسلة هذه التوابع مخيفة نوعًا ما، لكن لا تقلق بشأن ذلك، حيث سنوضّحها واحدًا تلو الآخر كما يلي: يعيد التابع الأول where('is_published', true)‎ جميع المنشورات التي نشرناها. تتعقد الأمور بعض الشيء عند التابع whereHas()‎، حيث إذا أردنا فهم هذا التابع، فيجب أن نتحدث أولًا عن التابع has()‎، وهو تابع Laravel Eloquent الذي يسمح لنا بالتحقق من وجود علاقة كما في المثال التالي: $posts = Post::has('comments', '>', 3)->get(); ستسترد الشيفرة البرمجية السابقة جميع المنشورات التي تحتوي على أكثر من 3 تعليقات، ولاحظ أنه لا يمكنك استخدام where()‎ لإنجاز ذلك، لأن comments ليس عمودًا في الجدول posts، بل هو جدول آخر له علاقة بالجدول posts. يعمل التابع whereHas()‎ مثل التابع has()‎ تمامًا، ولكنه يوفر مزيدًا من القوة، فالمعامل الثاني هو دالة تسمح بفحص محتوى جدول آخر، وهو الجدول tags في حالتنا، ويمكننا الوصول إلى الجدول tags من خلال المتغير ‎$q. يأخذ التابع whereIn()‎ في السطر 28 معاملَين هما: الأول هو العمود المحدَّد، والثاني هو مصفوفة من القيم المقبولة. يعيد هذا التابع السجلات ذات القيم المقبولة فقط ويستبعد الباقي. يجب أن تكون بقية التوابع سهلة الفهم، حيث يستبعد التابع where('id', '!=', $post->id)‎ المنشور الحالي، ويأخذ التابع take(3)‎ السجلات الثلاثة الأولى. يكون عرض المنشور المقابل resources/views/post.blade.php كما يلي: @extends('layout') @section('title') <title>Page Title</title> @endsection @section('content') <div class="grid grid-cols-4 gap-4 py-10"> <div class="col-span-3"> <img class="rounded-md object-cover h-96 w-full" src="{{ Storage::url($post->cover) }}" alt="..." /> <h1 class="mt-5 mb-2 text-center text-2xl font-bold">{{ $post->title }}</h1> <p class="mb-5 text-center text-sm text-slate-500 italic">By {{ $post->user->name }} | {{ \Carbon\Carbon::parse($post->created_at)->format('M d, Y') }}</p> <div>{!! $post->content !!}</div> <div class="my-5"> @foreach ($post->tags as $tag) <a href="{{ route('tag', ['tag' => $tag->id]) }}" class="text-blue-500 hover:underline" mr-3">#{{ $tag->name }}</a> @endforeach </div> <hr> <!-- المنشورات ذات الصلة --> <div class="grid grid-cols-3 gap-4 my-5"> @foreach ($related_posts as $post) <!-- المنشور --> <div class="mb-4 ring-1 ring-slate-200 rounded-md h-fit hover:shadow-md"> <a href="{{ route('post', ['post' => $post->id]) }}"><img class="rounded-t-md object-cover h-60 w-full" src="{{ Storage::url($post->cover) }}" alt="..." /></a> <div class="m-4 grid gap-2"> <div class="text-sm text-gray-500"> {{ \Carbon\Carbon::parse($post->created_at)->format('M d, Y') }} </div> <h2 class="text-lg font-bold">{{ $post->title }}</h2> <p class="text-base"> {{ Str::limit(strip_tags($post->content), 150, '...') }} </p> <a class="bg-blue-500 hover:bg-blue-700 rounded-md p-2 text-white uppercase text-sm font-semibold font-sans w-fit focus:ring" href="{{ route('post', ['post' => $post->id]) }}">Read more →</a> </div> </div> @endforeach </div> </div> @include('vendor.sidebar') </div> @endsection يُعَد ‎{!! $post->content !!}‎ في السطر 15 هو الوضع الآمن في لارافيل، حيث يخبر لارافيل بعرض وسوم HTML بدلًا من عرضها كنص عادي. صفحة البحث ضع ما يلي في ملف المتحكم app/Http/Controllers/PostController.php: <?php namespace App\Http\Controllers; use App\Models\Category; use App\Models\Post; use App\Models\Tag; use Illuminate\Contracts\View\View; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Http\Response; use Illuminate\Support\Facades\Auth; use Illuminate\Database\Eloquent\Builder; class PostController extends Controller { . . . /** * عرض نتيجة البحث */ public function search(Request $request): View { $key = $request->input('q'); $posts = Post::where('title', 'like', "%{$key}%")->orderBy('id', 'desc')->paginate(env('PAGINATE_NUM')); $categories = Category::all(); $tags = Tag::all(); return view('search', [ 'key' => $key, 'posts' => $posts, 'categories' => $categories, 'tags' => $tags, ]); } . . . } يكون عرض البحث المقابل resources/views/search.blade.php كما يلي: @extends('layout') @section('title') <title>Search - {{ $key }}</title> @endsection @section('content') <div class="grid grid-cols-4 gap-4 py-10"> <div class="col-span-3 grid grid-cols-1">@include('vendor.list')</div> @include('vendor.sidebar') </div> @endsection ترجمة -وبتصرُّف- للمقال Laravel for Beginners #5 - Create the Frontend لصاحبه Eric Hu. اقرأ أيضًا المقال السابق: لارافيل للمبتدئين-الجزء الرابع: إنشاء لوحة تحكم لموقع مدونة بسيطة تعرف على إطار عمل تطوير الويب الشهير لارافيل Laravel كيف تستخدم منشئ الاستعلامات Query builder للتخاطب مع قاعدة البيانات في Laravel تجريد إعداد قواعد البيانات في لارافيل باستعمال عملية التهجير Migration والبذر Seeder
  3. سننشئ في هذا المقال تطبيق مدونة باستخدام لارافيل ونجعل تطبيقنا كامل الميزات ويحتوي على منشورات وفئات Categories ووسوم Tags، حيث ناقشنا في المقال السابق عمليات CRUD للمنشورات، وسنكرر اليوم هذه العمليات نفسها على كل من الفئات والوسوم، كما سنناقش كيفية التعامل مع العلاقات فيما بينها. تهيئة مشروع لارافيل جديد سنبدأ بمشروع لارافيل جديد، حيث سننشئ مجلد العمل وننتقل إليه، ولكن تأكّد من تشغيل دوكر Docker، ثم نفّذ الأمر التالي: curl -s https://laravel.build/<app_name> | bash انتقل إلى مجلد التطبيق وابدأ تشغيل الخادم كما يلي: cd <app_name> ./vendor/bin/sail up لننشئ اسمًا بديلًا للأداة sail لتسهيل الأمور، لذا شغّل الأمر التالي: alias sail='[ -f sail ] && sh sail || sh vendor/bin/sail' يمكنك من الآن فصاعدًا تشغيل الأداة Sail مباشرةً دون تحديد المسار بأكمله كما يلي: sail up استيثاق المستخدم User authentication يأتي إطار عمل لارافيل مع نظام بيئي كبير فهو يحتوي على العديد من الأدوات والموارد الإضافية التي تسهل على المطورين برمجة التطبيقات، وتُعَد حزمة Breeze من لارافيل جزءًا من هذا النظام، فهي توفر طريقة سريعة لإعداد استيثاق المستخدم وتسجيله في تطبيق لارافيل. تتضمّن حزمة Breeze عروضًا Views ومتحكّمات Controllers مبنية مسبقًا خاصة بالاستيثاق، بالإضافة إلى مجموعة من واجهات برمجة التطبيقات الخلفية للتعامل مع استيثاق المستخدم وتسجيله، وصُمِّمت هذه الحزمة لتكون سهلة التثبيت والضبط مع وجود الحد الأدنى من الإعداد المطلوب. استخدم الأوامر التالية لتثبيت حزمة Breeze من لارافيل: sail composer require laravel/breeze --dev sail artisan breeze:install sail artisan migrate sail npm install sail npm run dev ستولّد هذه العملية تلقائيًا كل من المتحكمات والبرمجيات الوسيطة والعروض المطلوبة اللازمة لإنشاء نظام استيثاق مستخدم أساسي في تطبيقك. يمكنك الوصول إلى صفحة التسجيل من العنوان http://127.0.0.1/register، ثم تسجّل حسابًا جديدًا وسيُعاد توجيهك إلى لوحة التحكم. لن نناقش في هذا المقال كيفية عمل نظام استيثاق المستخدم، لأنه يرتبط ببعض المفاهيم المتقدمة إلى حدٍ ما، ولكن يوصَى بشدة بإلقاء نظرة على الملفات التي تولّدت هنا، فهي توفر نظرة أعمق حول كيفية العمل في لارافيل. إعداد قاعدة البيانات يجب بعد ذلك أن يكون لدينا تصور حول كيفية ظهور تطبيق المدونة، حيث نحتاج أولًا إلى قاعدة بيانات يمكنها تخزين المنشورات والفئات والوسوم، وسيكون لكل جدول قاعدة بيانات البنيةُ التالية: جدول المنشورات Posts: المفتاح نوعه id عدد صحيح كبير BigInteger created_at timestamps يملأ تلقائيًا عند إنشاء سجل جديد updated_at timestamps يملأ تلقائيًا عند تحديث سجل جديد title سلسلة نصية String cover سلسلة نصية String content نص Text is_published قيمة منطقية Boolean جدول الفئات Categories: المفتاح نوعه id عدد صحيح كبير created_at timestamps يملأ تلقائيًا عند إنشاء سجل جديد updated_at timestamps يملأ تلقائيًا عند تحديث سجل جديد name سلسلة نصية String جدول الوسوم Tags: المفتاح نوعه id عدد صحيح كبير created_at timestamps يملأ تلقائيًا عند إنشاء سجل جديد updated_at timestamps يملأ تلقائيًا عند إنشاء سجل جديد name سلسلة نصية يجب أن يكون هناك أيضًا جدول للمستخدمين users، ولكن لاحاجة لأن ننشئه فقد ولّدته حزمة Breeze من لارافيل مسبقًا، لذا سنتخطى هذه الخطوة حاليًا. يكون لهذه الجداول علاقات مع بعضها البعض كما هو موضح فيما يلي: كل مستخدم لديه منشورات متعددة. كل فئة لديها العديد من المنشورات. كل وسم لديه العديد من المنشورات. يعودة كل منشور إلى مستخدم واحد. يعود كل منشور إلى فئة واحدة. كل منشور له العديد من الوسوم. يمكننا إنشاء هذه العلاقات، ولكن يجب تعديل جدول المنشورات كما يلي: جدول المنشورات مع العلاقات: المفتاح نوعه id عدد صحيح كبير created_at timestamps يملأ تلقائيًا عند إنشاء سجل جديد updated_at timestamps يملأ تلقائيًا عند تحديث سجل جديد title سلسلة نصية String cover سلسلة نصية String content نص Text is_published قيمة منطقية user_id عدد صحيح كبير category_id عدد صحيح كبير ونحتاج أيضًا إلى جدول منفصل لعلاقة المنشور مع الوسم كما يلي: جدول يمثل علاقة المنشور مع الوسم Post/Tag: المفتاح نوعه post_id عدد صحيح كبير tag_id عدد صحيح كبير تطبيق بنية قاعدة البيانات يمكن تطبيق التصميم السابق من خلال توليد النماذج Models وملفات التهجير Migration باستخدام الأوامر التالية: sail artisan make:model Post --migration sail artisan make:model Category --migration sail artisan make:model Tag --migration بالإضافة إلى توليد ملف تهجير منفصل للجدول post_tag باستخدام الأمر التالي: sail artisan make:migration create_post_tag_table وسينشأ ملف التهجير database/migrations/create_posts_table.php التالي: <?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * تشغيل عمليات التهجير */ public function up(): void { Schema::create('posts', function (Blueprint $table) { $table->id(); $table->timestamps(); $table->string('title'); $table->string('cover'); $table->text('content'); $table->boolean('is_published'); $table->bigInteger('user_id'); $table->bigInteger('category_id'); }); } /** * عكس عمليات التهجير */ public function down(): void { Schema::dropIfExists('posts'); } }; وسينشأ ملف التهجير database/migrations/create_categories_table.php التالي: <?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * تشغيل عمليات التهجير */ public function up(): void { Schema::create('categories', function (Blueprint $table) { $table->id(); $table->timestamps(); $table->string('name'); }); } /** * عكس عمليات التهجير */ public function down(): void { Schema::dropIfExists('categories'); } }; وسينشأ ملف التهجير database/migrations/create_tags_table.php التالي: <?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * تشغيل عمليات التهجير */ public function up(): void { Schema::create('tags', function (Blueprint $table) { $table->id(); $table->timestamps(); $table->string('name'); }); } /** * عكس عمليات التهجير */ public function down(): void { Schema::dropIfExists('tags'); } }; وسينشأ أيضُا ملف التهجير database/migrations/create_post_tag_table.php التالي: <?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * تشغيل عمليات التهجير */ public function up(): void { Schema::create('post_tag', function (Blueprint $table) { $table->id(); $table->timestamps(); $table->bigInteger('post_id'); $table->bigInteger('tag_id'); }); } /** * عكس عمليات التهجير */ public function down(): void { Schema::dropIfExists('post_tag'); } }; طبّق هذه التغييرات باستخدام الأمر التالي: sail artisan migrate يجب بعد ذلك تفعيل الإسناد الجماعي Mass Assignment لحقول محدَّدة بالنسبة للنماذج المقابلة حتى نتمكّن من استخدام تابعَي create أو update معها كما ناقشنا في المقال السابق، ويجب أيضًا تعريف العلاقات بين جداول قاعدة البيانات. سيكون ملف النموذج app/Models/Post.php كما يلي: <?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsToMany; class Post extends Model { use HasFactory; protected $fillable = [ "title", 'content', 'cover', 'is_published' ]; public function user(): BelongsTo { return $this->belongsTo(User::class); } public function category(): BelongsTo { return $this->belongsTo(Category::class); } public function tags(): BelongsToMany { return $this->belongsToMany(Tag::class); } } وسيكون ملف النموذج app/Models/Category.php كما يلي: <?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\HasMany; class Category extends Model { use HasFactory; protected $fillable = [ 'name', ]; public function posts(): HasMany { return $this->hasMany(Post::class); } } ويكون ملف النموذج app/Models/Tag.php كما يلي: <?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsToMany; class Tag extends Model { use HasFactory; protected $fillable = [ 'name', ]; public function posts(): BelongsToMany { return $this->belongsToMany(Post::class); } } ويكون ملف النموذج app/Models/User.php كما يلي: <?php namespace App\Models; // use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use Laravel\Sanctum\HasApiTokens; class User extends Authenticatable { use HasApiTokens, HasFactory, Notifiable; /** * السمات‫ Attributes التي نسندها إسنادًا جماعيًا * * @var array<int, string> */ protected $fillable = [ 'name', 'email', 'password', ]; /** * ‫السمات التي يجب أن تكون مخفية لعملية السَلسلة Serialization * * @var array<int, string> */ protected $hidden = [ 'password', 'remember_token', ]; /** * السمات التي يجب تغيير نوعها‫ Cast * * @var array<string, string> */ protected $casts = [ 'email_verified_at' => 'datetime', ]; public function posts(): HasMany { return $this->hasMany(Post::class); } } المتحكمات Controllers والوِجهات Routes نحتاج إلى إنشاء متحكم موارد واحد لكل مورد (منشور وفئة ووسم) باستخدام الأوامر التالية: php artisan make:controller PostController --resource php artisan make:controller CategoryController --resource php artisan make:controller TagController --resource ننشئ بعد ذلك وِجهات لكل من هذه المتحكمات في الملف routes/web.php كما يلي: <?php use App\Http\Controllers\CategoryController; use App\Http\Controllers\PostController; use App\Http\Controllers\ProfileController; use App\Http\Controllers\TagController; use Illuminate\Support\Facades\Route; // وِجهات لوحة التحكم Route::prefix('dashboard')->group(function () { // الصفحة الرئيسية للوحة التحكم Route::get('/', function () { return view('dashboard'); })->name('dashboard'); // مورد الفئة للوحة التحكم Route::resource('categories', CategoryController::class); // مورد الوسم للوحة التحكم Route::resource('tags', TagController::class); // مورد المنشور للوحة التحكم Route::resource('posts', PostController::class); })->middleware(['auth', 'verified']); Route::middleware('auth')->group(function () { Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit'); Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update'); Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy'); }); require __DIR__ . '/auth.php'; لاحظ تجميع جميع الوِجهات باستخدام البادئة ‎/dashboard، ويكون للمجموعة البرمجيةُ الوسيطة auth، مما يعني أنه يجب على المستخدم تسجيل الدخول للوصول إلى لوحة التحكم. متحكمات الفئة والوسم Category/Tag يُعََد المتحكمان CategoryController و TagController واضحَين إلى حدٍ ما، ويمكنك إعدادهما بالطريقة نفسها التي أنشأنا بها المتحكم PostController في المقال السابق. إذًا لننشئ أولًا متحكم الفئة app/Http/Controllers/CategoryController.php كما يلي: <?php namespace App\Http\Controllers; use App\Models\Category; use Illuminate\Contracts\View\View; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Http\Response; class CategoryController extends Controller { /** * عرض قائمة الموارد */ public function index(): View { $categories = Category::all(); return view('categories.index', [ 'categories' => $categories ]); } /** * عرض الاستمارة الخاصة بإنشاء مورد جديد */ public function create(): View { return view('categories.create'); } /** * تخزين المورد الذي أنشأناه حديثًا في وحدة التخزين */ public function store(Request $request): RedirectResponse { // الحصول على البيانات من الطلب $name = $request->input('name'); // إنشاء نسخة جديدة من المنشور‫ Post ووضع البيانات المطلوبة في العمود المقابل $category = new Category(); $category->name = $name; // حفظ البيانات $category->save(); return redirect()->route('categories.index'); } /** * عرض المورد المُحدَّد */ public function show(string $id): View { $category = Category::all()->find($id); $posts = $category->posts(); return view('categories.show', [ 'category' => $category, 'posts' => $posts ]); } /** * عرض الاستمارة الخاصة بتعديل المورد المُحدَّد */ public function edit(string $id): View { $category = Category::all()->find($id); return view('categories.edit', [ 'category' => $category ]); } /** * تحديث المورد المُحدَّد في وحدة التخزين */ public function update(Request $request, string $id): RedirectResponse { // الحصول على البيانات من الطلب $name = $request->input('name'); // البحث عن الفئة المطلوبة ووضع البيانات المطلوبة في العمود المقابل $category = Category::all()->find($id); $category->name = $name; // حفظ البيانات $category->save(); return redirect()->route('categories.index'); } /** * إزالة المورد المٌحدَّد من وحدة التخزين */ public function destroy(string $id): RedirectResponse { $category = Category::all()->find($id); $category->delete(); return redirect()->route('categories.index'); } } ولننشئ الآن متحكم الوسم app/Http/Controllers/TagController.php كما يلي: <?php namespace App\Http\Controllers; use App\Models\Tag; use Illuminate\Contracts\View\View; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Http\Response; class TagController extends Controller { /** * عرض قائمة الموارد */ public function index(): View { $tags = Tag::all(); return view('tags.index', [ 'tags' => $tags ]); } /** * عرض الاستمارة الخاصة بإنشاء مورد جديد */ public function create(): View { return view('tags.create'); } /** * تخزين المورد الذي أنشأناه حديثًا في وحدة التخزين */ public function store(Request $request): RedirectResponse { // الحصول على البيانات من الطلب $name = $request->input('name'); // إنشاء نسخة جديدة من المنشور‫ Post ووضع البيانات المطلوبة في العمود المقابل $tag = new Tag(); $tag->name = $name; // حفظ البيانات $tag->save(); return redirect()->route('tags.index'); } /** * عرض المورد المُحدَّد */ public function show(string $id): View { $tag = Tag::all()->find($id); $posts = $tag->posts(); return view('tags.show', [ 'tag' => $tag, 'posts' => $posts ]); } /** * عرض الاستمارة الخاصة بتعديل المورد المُحدَّد */ public function edit(string $id): View { $tag = Tag::all()->find($id); return view('tags.edit', [ 'tag' => $tag ]); } /** * تحديث المورد المُحدَّد في وحدة التخزين */ public function update(Request $request, string $id): RedirectResponse { // الحصول على البيانات من الطلب $name = $request->input('name'); // البحث عن الوسم المطلوب ووضع البيانات المطلوبة في العمود المقابل $tag = Tag::all()->find($id); $tag->name = $name; // حفظ البيانات $tag->save(); return redirect()->route('tags.index'); } /** * إزالة المورد المُحدَّد من وحدة التخزين */ public function destroy(string $id): RedirectResponse { $tag = Tag::all()->find($id); $tag->delete(); return redirect()->route('tags.index'); } } تذكّر أنه يمكنك التحقق من اسم الوِجهات باستخدام الأمر التالي: sail artisan route:list متحكم المنشور Post يُعَد متحكم المنشور PostController أكثر تعقيدًا بعض الشيء، إذ يجب عليك التعامل مع عمليات رفع الصور والعلاقات بين الجداول في قاعدة البيانات في التابع store()‎. إذًا لننشئ هذا المتحكم app/Http/Controllers/PostController.php كما يلي: <?php namespace App\Http\Controllers; use App\Models\Category; use App\Models\Post; use App\Models\Tag; use Illuminate\Contracts\View\View; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Http\Response; use Illuminate\Support\Facades\Auth; use Illuminate\Database\Eloquent\Builder; class PostController extends Controller { . . . /** * تخزين المورد الذي أنشأناه حديثًا في وحدة التخزين */ public function store(Request $request): RedirectResponse { // الحصول على البيانات من الطلب $title = $request->input('title'); $content = $request->input('content'); if ($request->input('is_published') == 'on') { $is_published = true; } else { $is_published = false; } // ‫إنشاء نسخة جديدة من المنشور Post ووضع البيانات المطلوبة في العمود المقابل $post = new Post(); $post->title = $title; $post->content = $content; $post->is_published = $is_published; // حفظ صورة الغلاف $path = $request->file('cover')->store('cover', 'public'); $post->cover = $path; // ضبط المستخدم $user = Auth::user(); $post->user()->associate($user); // ضبط الفئة $category = Category::find($request->input('category')); $post->category()->associate($category); // حفظ المنشور $post->save(); // ضبط الوسوم $tags = $request->input('tags'); foreach ($tags as $tag) { $post->tags()->attach($tag); } return redirect()->route('posts.index'); } . . . } هناك بعض الأشياء التي يجب ملاحظتها في التابع store()‎، حيث سنستخدم في الأسطر من 28 إلى 32 مربع اختيار HTML لتمثيل الحقل is_published، وتكون قيمته إما 'on' أو null، ولكن تُحفَظ قيمته في قاعدة البيانات بوصفها true أو false، لذلك يجب استخدام التعليمة if لحل هذه المشكلة. يمكننا استرداد الملفات في السطور من 41 إلى 42 من خلال استخدام التابع file()‎ بدلًا من التابع input()‎، ويُحفظ الملف في القرص public ضمن المجلد cover. نحصل في السطور من 45 إلى 46 على المستخدم الحالي باستخدام التابع Auth::user()‎، ونربط المنشور بالمستخدم باستخدام التابع associate()‎، وتفعَل السطور من 49 إلى 50 الشيء نفسه بالنسبة للفئة، وتذكّر أنه يمكنك تطبيق ذلك فقط مع المتغير ‎$post وليس ‎$user أو ‎$category، لأن العمودان user_id و category_id موجودان في الجدول posts. وأخيرًا، يجب حفظ المنشور الحالي في قاعدة البيانات، ثم استرداد قائمة الوسوم وإرفاق كل منها بالمنشور واحدًا تلوَ الآخر باستخدام التابع attach()‎ بالنسبة للوسوم كما هو موضح في السطور من 56 إلى 60. تجري الأمور بطريقة مشابهة بالنسبة إلى التابع update()‎، باستثناء أنه يجب إزالة جميع الوسوم الموجودة قبل أن تتمكّن من إرفاق الوسوم الجديدة كما يلي: $post->tags()->detach(); العروض Views تذكر دائمًا أن تكون منظّمًا عند إنشاء نظام عرض، حيث سنتّبع البنية التالية في مثالنا: resources/views ├── auth ├── categories │ ├── create.blade.php │ ├── edit.blade.php │ ├── index.blade.php │ └── show.blade.php ├── components ├── layouts ├── posts │ ├── create.blade.php │ ├── edit.blade.php │ ├── index.blade.php │ └── show.blade.php ├── profile ├── tags │ ├── create.blade.php │ ├── edit.blade.php │ ├── index.blade.php │ └── show.blade.php ├── dashboard.blade.php └── welcome.blade.php أنشأنا ثلاثة مجلدات هي: posts و categories و tags، ولكل منها أربعة قوالب هي: create و edit و index و show باستثناء المجلد posts، لأن وجود صفحة show للمنشورات في لوحة التحكم أمر غير ضروري. يؤدي تضمين جميع هذه العروض في مقال واحد إلى جعل هذا المقال طويلًا بلا داعٍ، لذا سنوضّح فقط صفحات إنشاء وتعديل وفهرس المنشورات، ولكن يمكنك الاطلاع على الشيفرة المصدرية الكاملة على Github. عرض إنشاء منشور لننشئ أولًا عرض إنشاء المنشور resources/views/posts/create.blade.php كما يلي: <x-app-layout> <x-slot name="header"> <div class="flex justify-between"> <h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight" > {{ __('Posts') }} </h2> <a href="{{ route('posts.create') }}"> <x-primary-button>{{ __('New') }}</x-primary-button> </a> </div> <script src="https://cdn.tiny.cloud/. . ./tinymce.min.js" referrerpolicy="origin" ></script> </x-slot> <div class="py-12"> <div class="max-w-7xl mx-auto sm:px-6 lg:px-8"> <div class="p-4 sm:p-8 bg-white dark:bg-gray-800 shadow sm:rounded-lg"> <div class=""> <form action="{{ route('posts.store') }}" method="POST" class="mt-6 space-y-3" enctype="multipart/form-data" > {{ csrf_field() }} <input type="checkbox" name="is_published" id="is_published" /> <x-input-label for="is_published" >Make this post public</x-input-label > <br /> <x-input-label for="title">{{ __('Title') }}</x-input-label> <x-text-input id="title" name="title" type="text" class="mt-1 block w-full" required autofocus autocomplete="name" /> <br /> <x-input-label for="content">{{ __('Content') }}</x-input-label> <textarea name="content" id="content" cols="30" rows="30" ></textarea> <br /> <x-input-label for="cover">{{ __('Cover Image') }}</x-input-label> <x-text-input id="cover" name="cover" type="file" class="mt-1 block w-full" required autofocus autocomplete="cover" /> <br /> <x-input-label for="category">{{ __('Category') }}</x-input-label> <select id="category" name="category"> @foreach($categories as $category) <option value="{{ $category->id }}">{{ $category->name }}</option> @endforeach </select> <br /> <x-input-label for="tags">{{ __('Tags') }}</x-input-label> <select id="tags" name="tags[]" multiple> @foreach($tags as $tag) <option value="{{ $tag->id }}">{{ $tag->name }}</option> @endforeach </select> <br /> <x-primary-button>{{ __('Save') }}</x-primary-button> </form> <script> tinymce.init({. . .}); </script> </div> </div> </div> </div> </x-app-layout> استخدمنا في مثالنا محرّر النصوص TinyMCE، ولكن يمكنك استخدام محرّر نصوص آخر عوضًا عنه، أو استخدم العنصر <textarea></textarea> إذا أدرتَ ذلك. يجب أن تحتوي الاستمارة الموجودة في السطر 24 على السمة التي هي enctype="multipart/form-data"‎ لأننا لا ننقل النصوص فحسب، بل توجد ملفات أيضًا. تذكر في السطر 59 استخدام السمة type="file"‎ لأننا نرفع صورة، وستُنقَل قيمة الخيار في السطور من 67 إلى 71 إلى الواجهة الخلفية. هناك شيئان يجب الانتباه إليهما في الأسطر من 74 إلى 78، فلاحظ أولًا السمة name="tags[]"‎، حيث تخبر هذه الأقواس [] لارافيل بنقل مصفوفة قابلة للتكرار بدلًا من النصوص. ثانيًا، تنشئ السمة multiple استمارة متعددة التحديد بدلًا من تحديد فردي مثل استمارة الفئات. عرض تعديل المنشور لننشئ الآن العرض resources/views/posts/edit.blade.php كما يلي: <x-app-layout> <x-slot name="header"> <div class="flex justify-between"> <h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight" > {{ __('Posts') }} </h2> <a href="{{ route('posts.create') }}"> <x-primary-button>{{ __('New') }}</x-primary-button> </a> </div> <script src="https://cdn.tiny.cloud/. . ./tinymce.min.js" referrerpolicy="origin" ></script> </x-slot> <div class="py-12"> <div class="max-w-7xl mx-auto sm:px-6 lg:px-8"> <div class="p-4 sm:p-8 bg-white dark:bg-gray-800 shadow sm:rounded-lg"> <div class=""> <form action="{{ route('posts.update', ['post' => $post->id]) }}" method="POST" class="mt-6 space-y-3" enctype="multipart/form-data" > {{ csrf_field() }} {{ method_field('PUT') }} <input type="checkbox" name="is_published" id="is_published" @checked($post- />is_published)/> <x-input-label for="is_published" >Make this post public</x-input-label > <br /> <x-input-label for="title">{{ __('Title') }}</x-input-label> <x-text-input id="title" name="title" type="text" class="mt-1 block w-full" required autofocus autocomplete="name" value="{{ $post->title }}" /> <br /> <x-input-label for="content">{{ __('Content') }}</x-input-label> <textarea name="content" id="content" cols="30" rows="30"> {{ $post->content }}</textarea > <br /> <x-input-label for="cover" >{{ __('Update Cover Image') }}</x-input-label > <img src="{{ Illuminate\Support\Facades\Storage::url($post->cover) }}" alt="cover image" width="200" /> <x-text-input id="cover" name="cover" type="file" class="mt-1 block w-full" autofocus autocomplete="cover" /> <br /> <x-input-label for="category">{{ __('Category') }}</x-input-label> <select id="category" name="category"> @foreach($categories as $category) <option value="{{ $category->id }}" @selected($post-> category->id == $category->id)>{{ $category->name }} </option> @endforeach </select> <br /> <x-input-label for="tags">{{ __('Tags') }}</x-input-label> <select id="tags" name="tags[]" multiple> @foreach($tags as $tag) <option value="{{ $tag->id }}" @selected($post-> tags->contains($tag))>{{ $tag->name }} </option> @endforeach </select> <br /> <x-primary-button>{{ __('Save') }}</x-primary-button> </form> <script> tinymce.init({. . .}); </script> </div> </div> </div> </div> </x-app-layout> لاحظ في السطور من 24 إلى 26 أن لغة HTML لا تدعم التابع PUT افتراضيًا، لذا نستخدم السمة method="POST"‎ ثم نخبر لارافيل باستخدام التابع PUT من خلال التعليمة ‎{{ method_field('PUT') }}‎. عرض فهرس المنشورات لننشئ الآن العرض resources/views/posts/index.blade.php كما يلي: <x-app-layout> <x-slot name="header"> <div class="flex justify-between"> <h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight" > {{ __('Posts') }} </h2> <a href="{{ route('posts.create') }}"> <x-primary-button>{{ __('New') }}</x-primary-button> </a> </div> </x-slot> <div class="py-12"> <div class="max-w-7xl mx-auto sm:px-6 lg:px-8"> @foreach($posts as $post) <div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg mb-4 px-4 h-20 flex justify-between items-center" > <div class="text-gray-900 dark:text-gray-100"> <p>{{ $post->title }}</p> </div> <div class="space-x-2"> <a href="{{ route('posts.edit', ['post' => $post->id]) }}"> <x-primary-button>{{ __('Edit') }}</x-primary-button></a > <form method="post" action="{{ route('posts.destroy', ['post' => $post->id]) }}" class="inline" > {{ csrf_field() }} {{ method_field('DELETE') }} <x-danger-button> {{ __('Delete') }} </x-danger-button> </form> </div> </div> @endforeach </div> </div> </x-app-layout> لاحظ أن زر الحذف ليس زرًا عاديًا، إذ يجب أن يكون استمارة مع تابع DELETE، لأن الرابط العادي يمتلك التابع GET فقط. يُفترَض الآن أن تكون قادرًا على إنشاء بقية نظام العرض بسهولة. لقطات الشاشة وأخيرًا، إليك بعض لقطات الشاشة للوحة التحكم التي أنشأناها: صفحة إدارة التصنيفات: ننتقل لصفحة الإدارة كما يلي: ثم نختار التصنيفات Categories من الصورة أعلاه لننتقل للصفحة التالية لإدارة التصنيفات: صفحة إنشاء تصنيف جديد: صفحة تحديث المنشور: تابع معنا المقال التالي والأخير من هذه السلسلة حيث سننشئ فيه كل ما يخص الواجهة الأمامية من التطبيق كي يتمكن المستخدمون من رؤية منشورات المدونة والتفاعل معها. ترجمة -وبتصرُّف- للمقال Laravel for Beginners #4 - Create a Dashboard لصاحبه Eric Hu. اقرأ أيضًا المقال السابق: لارافيل للمبتدئين-الجزء الثالث: استخدام عمليات CRUD لإنشاء مدونة بسيطة تجريد إعداد قواعد البيانات في لارافيل باستعمال عملية التهجير Migration والبذر Seeder كيف تستخدم منشئ الاستعلامات Query builder للتخاطب مع قاعدة البيانات في Laravel إنشاء استمارة اتصال في Laravel باستخدام ميزة Form Request
  4. سنستخدم في هذا المقال كل ما تعلّمناه في المقالين السابقين من سلسلة مقالات لارافيل للمبتدئين لإنشاء مشروع حقيقي، حيث سننشئ مدونة صغيرة تحتوي على منشورات فقط بدون فئات Categories أو وسوم Tags، وكل منشور له عنوان ومحتوى، إذ لن ننشئ تطبيق مدونة كامل المواصفات، وسنوضّح كيفية استرداد البيانات من قاعدة البيانات، وكيفية إنشاء أو تحديث معلومات جديدة، وكيفية حفظها في قاعدة البيانات، وكيفية حذفها، حيث يمثّل الاختصار CRUD عمليات الإنشاء ‎Create والقراءة ‎Read والتحديث ‎Update والحذف ‎Delete. تصميم بنية قاعدة البيانات سننشئ أولًا ملف تهجير Migration للجدول post، ويجب أن يحتوي هذا الجدول على العنوان والمحتوى، ويمكنك توليد ملف تهجير باستخدام الأمر التالي: php artisan make:migration create_posts_table وسيتضمن الملف ما يلي: <?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * تشغيل عمليات التهجير */ public function up(): void { Schema::create('posts', function (Blueprint $table) { $table->id(); $table->timestamps(); $table->string('title'); $table->text('content'); }); } /** * عكس عمليات التهجير */ public function down(): void { Schema::dropIfExists('posts'); } }; أنشأنا في هذا المثال خمسة أعمدة في الجدول post باستدعاء 5 توابع وهي: ينشئ التابع id()‎ عمود المعرّف id الذي يُستخدَم عادة للفهرسة. ينشئ التابع timestamps()‎ عمودين هما: created_at و uptated_at، وسيُحدَّث هذان العمودان تلقائيًا عند إنشاء السجل وتحديثه. ينشئ التابع string('title')‎ العمود title الذي نوعه VARCHAR ويبلغ طوله الافتراضي 255 بايتًا. ينشئ التابع string('content')‎ عمود المحتوى content. يمكنك تطبيق التغييرات من خلال تشغيل الأمر التالي: php artisan migrate ويجب أن ينشأ جدول posts جديد كما يلي: يمكننا الآن إنشاء النموذج Model المقابل لهذا الجدول باستخدام الأمر التالي: php artisan make:model Post وسيتضمّن هذا النموذج app/Models/Post.php ما يلي: <?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class Post extends Model { use HasFactory; } لننشئ الآن متحكم الموارد Resource Controller المقابل باستخدام الأمر التالي: php artisan make:controller PostController --resource وأخيرًا، سجّل هذا المتحكم في الموجِّه Router كما يلي: use App\Http\Controllers\PostController; Route::resource('posts', PostController::class); يمكنك التحقق من الوِجهات Routes المُسجَّلة من خلال تشغيل الأمر التالي: php artisan route:list وسيظهر الخرج التالي: GET|HEAD posts ................................................................. posts.index › PostController@index POST posts ................................................................. posts.store › PostController@store GET|HEAD posts/create ........................................................ posts.create › PostController@create GET|HEAD posts/{post} ............................................................ posts.show › PostController@show PUT|PATCH posts/{post} ........................................................ posts.update › PostController@update DELETE posts/{post} ...................................................... posts.destroy › PostController@destroy GET|HEAD posts/{post}/edit ....................................................... posts.edit › PostController@edit يحتوي الخرج السابق على معلومات مثل توابع الطلبات Request Methods وتوابع المتحكّمات Controller Methods بالإضافة إلى أسماء الوِجهات، وتُعَد هذه المعلومات مهمة جدًا وسنحتاج إليها لاحقًا في هذا المقال. عمليات CRUD حان الوقت الآن لنتعمّق في التطبيق نفسه، فمن غير المحتمل أن ننشئ جميع المتحكمات أولًا ثم نصمّم القوالب ثم ننتقل إلى الموجّهات عند إنشاء تطبيقات واقعية، إذ يجب أن نفكّر من وجهة نظر المستخدم وفي الإجراءات التي قد يرغب المستخدم في اتخاذها. يجب أن يكون لدى المستخدم القدرة على إجراء أربع عمليات لكل مورد، والذي هو Post في حالتنا، وهذه العمليات هي: الإنشاء Create: يجب أن يكون المستخدم قادرًا على إنشاء موارد جديدة وحفظها في قاعدة البيانات. القراءة Read: يجب أن يكون المستخدم قادرًا على قراءة الموارد سواءً من خلال استرداد قائمة الموارد أو التحقق من تفاصيل مورد محدّد. التحديث Update: يجب أن يكون المستخدم قادرًا على تحديث الموارد الموجودة وتحديث سجل قاعدة البيانات المقابلة لها. الحذف Delete: يجب أن يكون المستخدم قادرًا على إزالة مورد من قاعدة البيانات. ويشار إلى هذه العمليات مع بعضها البعض باسم عمليات CRUD. إجراء الإنشاء Create لا تزال قاعدة بياناتنا فارغة حاليًا، لذا قد يرغب المستخدم في إنشاء منشورات جديدة، فلنبدأ بإجراء الإنشاء (C)، حيث يمكن إكمال هذا الإجراء من خلال استخدام التابعين التاليين: تابع المتحكم create()‎ الذي يعرض استمارة، مما يسمح للمستخدم بملء العنوان والمحتوى. تابع المتحكم store()‎ الذي يحفظ المنشور الذي أنشأناه حديثًا في قاعدة البيانات، ويعيد توجيه المستخدم إلى صفحة القائمة. يطابق التابع create()‎ نمط URL الذي هو ‎/posts/create (التابع GET)، ويطابق التابع store()‎ نمط URL الذي هو ‎/post (التابع POST). إليك مراجعةً مختصرة لتوابع HTTP في حال احتياجك إلى تجديد بعض المعلومات: تابع GET هو تابع طلبات HTTP الأكثر استخدامًا، ويُستخدَم لطلب البيانات والموارد من الخادم. يُستخدَم تابع POST لإرسال البيانات إلى الخادم، ويُستخدَم لإنشاء أو تحديث المورد. يعمل تابع HEAD مثل تابع GET تمامًا، باستثناء أن استجابة HTTP ستحتوي على الترويسة فقط بدون الجسم، ويستخدم المطورون هذا التابع لأغراض تنقيح الأخطاء Debugging. يُعَد تابع PUT مشابهًا لتابع POST، مع اختلاف واحد بسيط، حيث إذا أرسلتَ باستخدام التابع POST موردًا موجودًا مسبقًا على الخادم، فلن يسبّب هذا الإجراء أيّ فرق، ولكن سيكرّر تابع PUT هذا المورد في كل مرة نقدّم فيها الطلب. يزيل تابع DELETE موردًا من الخادم. لنبدأ بالتابع create()‎، حيث سيتضمّن ملف المتحكم app/Http/Controllers/PostController.php ما يلي: <?php namespace App\Http\Controllers; use App\Models\Post; use Illuminate\Contracts\View\View; . . . class PostController extends Controller { . . . /** * عرض الاستمارة الخاصة بإنشاء مورد جديد */ public function create(): View { return view('posts.create'); } . . . } سيُنفّذ هذا التابع عندما ترسل طلب GET إلى ‎/posts/create، وسيؤشّر إلى العرض views/posts/create.blade.php. لاحظ تغيير الاستجابة Response إلى عرض View في السطر 16، لأن هذه التوابع يجب أن تعيد عرضًا. يجب بعد ذلك أن ننشئ العرض المقابل، حيث سنبدأ بعرض تخطيط الصفحة layout، إذ سيتضمّن الملف views/layout.blade.php ما يلي: <!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <script src="https://cdn.tailwindcss.com"></script> @yield('title') </head> <body class="container mx-auto font-serif"> <div class="bg-white text-black font-serif"> <div id="nav"> <nav class="flex flex-row justify-between h-16 items-center shadow-md"> <div class="px-5 text-2xl"> <a href="{{ route('posts.index') }}"> My Blog </a> </div> <div class="hidden lg:flex content-between space-x-10 px-10 text-lg"> <a href="{{ route('posts.create') }}" class="hover:underline hover:underline-offset-1" >New Post</a > <a href="https://github.com/ericnanhu" class="hover:underline hover:underline-offset-1" >GitHub</a > </div> </nav> </div> @yield('content') <footer class="bg-gray-700 text-white"> <div class="flex justify-center items-center sm:justify-between flex-wrap lg:max-w-screen-2xl mx-auto px-4 sm:px-8 py-10" > <p class="font-serif text-center mb-3 sm:mb-0"> Copyright © <a href="https://www.ericsdevblog.com/" class="hover:underline" >Eric Hu</a > </p> <div class="flex justify-center space-x-4">. . .</div> </div> </footer> </div> </body> </html> لاحظ في السطرين 16 و 20 وجود الأقواس المزدوجة المتعرجة {{ }} التي تسمح بتنفيذ شيفرة PHP البرمجية ضمن القالب، وبالتالي سيعيد التابع route('posts.index')‎ الوِجهة التي اسمها posts.index، حيث يمكنك التحقق من أسماء الوِجهات من خلال الرجوع إلى خرج الأمر php artisan route:list. لننتقل بعد ذلك إلى العرض create، حيث يجب أن تكون منظمًا في هذا العرض. يُعَد العرض create مُخصّصًا للإجراءات المتعلقة بالمنشور، لذا أنشأنا المجلد post لتخزين هذا العرض. سيتضمن العرض views/posts/create.blade.php ما يلي: @extends('layout') @section('title') <title>Create</title> @endsection @section('content') <div class="w-96 mx-auto my-8"> <h2 class="text-2xl font-semibold underline mb-4">Create new post</h2> <form action="{{ route('posts.store') }}" method="POST"> {{ csrf_field() }} <label for="title">Title:</label><br /> <input type="text" id="title" name="title" class="p-2 w-full bg-gray-50 rounded border border-gray-300 focus:ring-3 focus:ring-blue-300" /><br /> <br /> <label for="content">Content:</label><br /> <textarea type="text" id="content" name="content" rows="15" class="p-2 w-full bg-gray-50 rounded border border-gray-300 focus:ring-3 focus:ring-blue-300" ></textarea ><br /> <br /> <button type="submit" class="font-sans text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-md text-sm w-full px-5 py-2.5 text-center" > Submit </button> </form> </div> @endsection توجد بعض الأشياء التي يجب ملاحظتها عند استخدام الاستمارات لنقل البيانات، وهي: السطر 6: تحدِّد سمة Attribute الإجراء action ما يحدث عند إرسال هذه الاستمارة، وتوجّه المتصفح في هذه الحالة لزيارة الوِجهة posts.store باستخدام تابع HTTP الذي هو POST. السطر 7 ‎{{ csrf_field() }}‎: يُعَد CSRF هجومًا ضارًا يستهدف تطبيقات الويب، وتوفر الدالة csrf_field()‎ الحماية من هذا النوع من الهجمات. اطلع على مقال تعرف على أمان مواقع الويب لمعرفة المزيد عن هجمات تزوير الطلبات عبر المواقع Cross-Site Request Forgery -أو CSRF اختصارًا. السمة name في السطر 12 و 20: يُربَط ما يدخله المستخدم بمتغير عند إرسال الاستمارة، وتحدّد السمة name اسم هذا المتغير، فمثلًا إذا كان name="title"‎، فسيُربَط دخل المستخدم بالمتغير title، ويمكن الوصول إلى قيمته باستخدام التعليمة ‎$request->input('title')‎، حيث سنرى كيفية عملها لاحقًا. السطر 27: يجب ضبط السمة type على القيمة submit حتى تعمل هذه الاستمارة. شغّل الآن خادم التطوير وانتقل إلى العنوان http://127.0.0.1:8000/posts/create حيث ستظهر الصفحة التالية: إذا نقرتَ على زر الإرسال "Submit"، فسيرسل المتصفح طلب POST إلى الخادم، وسيُنفَّذ التابع store()‎ هذه المرة، وسيحتوي طلب POST على دخل المستخدم، ويمكن الوصول إليه كما يلي في الملف PostController.php: <?php namespace App\Http\Controllers; . . . class PostController extends Controller { . . . /** * تخزين مورد أنشأناه حديثًا في وحدة التخزين */ public function store(Request $request): RedirectResponse { // الحصول على البيانات من الطلب $title = $request->input('title'); $content = $request->input('content'); // إنشاء نسخة جديدة من‫ المنشور Post ووضع البيانات المطلوبة في العمود المقابل $post = new Post; $post->title = $title; $post->content = $content; // حفظ البيانات $post->save(); return redirect()->route('posts.index'); } . . . } سيُعاد توجيهك إلى الوِجهة posts.index بعد تخزين البيانات. عُد إلى متصفحك واكتب عنوانًا ومحتوًى جديدًا، ثم انقر على زر الإرسال. لم ننشئ العرض index بعد، لذا ستُعاد رسالة خطأ، ولكن إذا تحققتَ من قاعدة البيانات، فيجب وجود سجل جديد مضاف كما يلي: إجراء القائمة List لننتقل الآن إلى عملية القراءة Read، حيث يوجد نوعان مختلفان من عمليات القراءة هما: الأول هو إجراء القائمة الذي يعيد قائمةً بجميع المنشورات إلى المستخدم، والإجراء الثاني سنوضّحه في الفقرة التالية. يقابل إجراء القائمة التابعَ index()‎ في الملف PostController.php كما يلي: <?php namespace App\Http\Controllers; use App\Models\Post; use Illuminate\Contracts\View\View; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Http\Response; class PostController extends Controller { /** * عرض قائمة الموارد */ public function index(): View { $posts = Post::all(); return view('posts.index', [ 'posts' => $posts, ]); } . . . } ويكون عرض index المقابل هو views/post/index.blade.php كما يلي: @extends('layout') @section('title') <title>Page Title</title> @endsection @section('content') <div class="max-w-screen-lg mx-auto my-8"> @foreach($posts as $post) <h2 class="text-2xl font-semibold underline mb-2"> <a href="{{ route('posts.show', ['post' => $post->id]) }}" >{{ $post->title }}</a > </h2> <p class="mb-4">{{ Illuminate\Support\Str::words($post->content, 100) }}</p> @endforeach </div> @endsection تتكرر حلقة foreach في السطر 5 على جميع المنشورات ‎$posts المُسترَدة، ثم تسنِد كل قيمة إلى المتغير ‎$post. لاحظ في السطر7 كيفية تمرير معرّف id المنشور إلى الوِجهة posts.show، ثم ستمرِّر هذه الوِجهة هذا المتغير إلى تابع المتحكم show()‎ الذي سنراه لاحقًا. يُعَد التابع Str::words()‎ في السطر 11 دالةً مساعدة في لغة PHP، وسيأخذ أول 100 كلمة من المحتوى content فقط. إجراء العرض Show عملية القراءة الثانية هي إجراء العرض الذي يعرض تفاصيل مورد معين، ويتحقّق هذا الإجراء باستخدام التابع show()‎ في الملف PostController.php كما يلي: <?php namespace App\Http\Controllers; use App\Models\Post; use Illuminate\Contracts\View\View; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Http\Response; class PostController extends Controller { . . . /** * عرض المورد المُحدَّد */ public function show(string $id): View { $post = Post::all()->find($id); return view('posts.show', [ 'post' => $post, ]); } . . . } ويكون عرض show المقابل هو views/post/show.blade.php كما يلي: @extends('layout') @section('title') <title>{{ $post->title }}</title> @endsection @section('content') <div class="max-w-screen-lg mx-auto my-8"> <h2 class="text-2xl font-semibold underline mb-2">{{ $post->title }}</h2> <p class="mb-4">{{ $post->content }}</p> <div class="grid grid-cols-2 gap-x-2"> <a href="{{ route('posts.edit', ['post' => $post->id]) }}" class="font-sans text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-md text-sm w-full px-5 py-2.5 text-center" >Update</a > <form action="{{ route('posts.destroy', ['post' => $post->id]) }}" method="POST" > {{ csrf_field() }} {{ method_field('DELETE') }} <button type="submit" class="font-sans text-white bg-red-700 hover:bg-red-800 focus:ring-4 focus:outline-none focus:ring-red-300 font-medium rounded-md text-sm w-full px-5 py-2.5 text-center" > Delete </button> </form> </div> </div> @endsection هناك بعض الأشياء التي يجب أن نلاحظها في المثال السابق. أولًا، لاحظ كيف أن زر التحديث "Update" هو رابط بسيط، ولكن زر الحذف "Delete" هو استمارة، لأن الرابط سيخبر المتصفح بإرسال طلب GET إلى الخادم، بينما نحتاج شيئًا آخر لإجراء الحذف، حيث إذا نظرتَ داخل الاستمارة، فستجد أنها تحتوي على السمة method="POST"‎، ولكننا نحتاج التابع DELETE لإجراء الحذف. تدعم لغة HTML التابعين GET و POST افتراضيًا، وإذا كنت بحاجة شيء آخر، فيجب أن تضبط السمة method="POST"‎، وأن تستخدم التابع method_field()‎ بدلًا من ذلك. إجراء التحديث Update تستخدم عملية التحديث التابع edit()‎ الذي يعرض استمارة HTML، والتابع update()‎ الذي يجري تغييرات على قاعدة البيانات، لذا ضع هذين التابعين في الملف PostController.php كما يلي: <?php namespace App\Http\Controllers; use App\Models\Post; use Illuminate\Contracts\View\View; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Http\Response; class PostController extends Controller { . . . /** * عرض الاستمارة الخاصة بتعديل المورد المُحدَّد */ public function edit(string $id): View { $post = Post::all()->find($id); return view('posts.edit', [ 'post' => $post, ]); } /** * تحديث المورد المحدَّد في وحدة التخزين */ public function update(Request $request, string $id): RedirectResponse { // الحصول على البيانات من الطلب $title = $request->input('title'); $content = $request->input('content'); // البحث عن المنشور المطلوب ووضع البيانات المطلوبة في العمود المقابل $post = Post::all()->find($id); $post->title = $title; $post->content = $content; // حفظ البيانات $post->save(); return redirect()->route('posts.show', ['post' => $id]); } . . . } ويكون عرض edit المقابل هو views/post/edit.blade.php كما يلي: @extends('layout') @section('title') <title>Edit</title> @endsection @section('content') <div class="w-96 mx-auto my-8"> <h2 class="text-2xl font-semibold underline mb-4">Edit post</h2> <form action="{{ route('posts.update', ['post' => $post->id]) }}" method="POST" > {{ csrf_field() }} {{ method_field('PUT') }} <label for="title">Title:</label><br /> <input type="text" id="title" name="title" value="{{ $post->title }}" class="p-2 w-full bg-gray-50 rounded border border-gray-300 focus:ring-3 focus:ring-blue-300" /><br /> <br /> <label for="content">Content:</label><br /> <textarea type="text" id="content" name="content" rows="15" class="p-2 w-full bg-gray-50 rounded border border-gray-300 focus:ring-3 focus:ring-blue-300" > {{ $post->content }}</textarea ><br /> <br /> <button type="submit" class="font-sans text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-md text-sm w-full px-5 py-2.5 text-center" > Submit </button> </form> </div> @endsection إجراء الحذف Delete وأخيرًا، يوجد التابع destroy()‎ للحذف، إذًا لنضعه في الملف PostController.php كما يلي: <?php namespace App\Http\Controllers; use App\Models\Post; use Illuminate\Contracts\View\View; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Http\Response; class PostController extends Controller { . . . /** * إزالة المورد المُحدَّد من وحدة التخزين */ public function destroy(string $id): RedirectResponse { $post = Post::all()->find($id); $post->delete(); return redirect()->route('posts.index'); } } لا يتطلب هذا الإجراء عرضًا، بما أنه يعيد توجيهك إلى posts.index بعد اكتمال الإجراء. ترجمة -وبتصرُّف- للمقال Laravel for Beginners #3 - The CRUD Operations لصاحبه Eric Hu. اقرأ أيضًا المقال السابق: لارافيل للمبتدئين-الجزء الثاني: استخدام بنية MVC لإنشاء مدونة بسيطة تعرف على إطار عمل تطوير الويب الشهير لارافيل Laravel المبادئ الأساسيّة لإطار العمل Eloquent ORM أساسيات التحقق من المدخلات في Laravel كيف تستخدم منشئ الاستعلامات Query builder للتخاطب مع قاعدة البيانات في Laravel
  5. سنتحدث في هذا المقال عن معمارية MVC الخاصة بإطار عمل لارافيل Laravel، فبنية MVC هي مبدأ تصميم الويب الذي يتكون من النموذج ‎Model المسؤول عن التواصل مع قاعدة البيانات، والمتحكِّم ‎Controller وهو المكان الذي نخزّن فيه شيفرة تطبيقنا البرمجية، والعرض ‎View وهو الواجهة الأمامية للتطبيق. يوجد مُوجّه Router في تطبيق الويب عادةً، وهذا ما ناقشناه في المقال السابق، حيث يؤشّر الموجّه إلى المتحكم، ويبحث المتحكم عن البيانات المطلوبة في قاعدة البيانات عبر النموذج، ثم يضع البيانات المُسترَّدة إلى الموقع المقابل في العرض، وأخيرًا يعيد هذا العرض إلى المستخدم. طبقة العرض لنبدأ أولًا بطبقة بالعرض، حيث عرّفنا في المقال السابق الوِجهة Route التالية: Route::get('/', function () { return view('welcome'); }); تؤشّر هذه الوِجهة إلى العرض welcome المُخزَّن في المجلد resources/views وله الامتداد ‎.blade.php، حيث يُعلِم هذا الامتداد لارافيل أننا نستخدم نظام قوالب Blade الذي سنتحدث عنه لاحقًا. يمكننا إنشاء عرض آخر من خلال إنشاء ملف آخر له الامتداد نفسه في المجلد views، إذًا لننشئ العرض greetings.blade.php التالي: <html> <body> <h1>Hello!</h1> </body> </html> تحدثنا أيضًا في المقال السابق عن إمكانية تمرير البيانات من الوِجهة إلى العرض كما يلي: Route::get('/', function () { return view('greeting', ['name' => 'James']); }); أسندنا في المثال السابق القيمة 'James' إلى المتغير name، ومرّرنا هذا المتغير إلى العرض greeting.blade.php الذي أنشأناه مسبقًا، ويمكننا استخدام الأقواس المزدوجة المتعرجة {{ }} لعرض هذه البيانات في العرض greetings.blade.php كما يلي: <html> <body> <h1>Hello, {{ $name }}</h1> </body> </html> من الشائع تنظيم ملفات العرض في مجلدات مختلفة بالنسبة لتطبيقات الويب الكبيرة، فمثلًا يمكن أن يحتوي تطبيقك على لوحة إدارة، وبالتالي ستخزّن العروض Views المقابلة لها في مجلد فرعي اسمه admin مثلًا، وتذكّر استخدام . بدلًا من / عندما تؤشّر إلى ملفات العرض في بنية متداخلة من الموجّه، حيث يؤشّر المثال التالي إلى الملف home.blade.php الموجود في المجلد views/admin/‎: Route::get('/admin', function () { return view('admin.home'); }); صيغة قوالب Blade تستخدم عروض لارافيل قالب Blade افتراضيًا، وهو مشابه لنظام المكونات في إطار عمل Vue.js. تُعَد العروض مستندات HTML، ويضيف قالب Blade ميزات برمجية إليها مثل نظام الوراثة والتحكم في التدفق والحلقات والمفاهيم البرمجية الأخرى، مما يؤدي إلى إنشاء صفحة ويب أكثر ديناميكية مع كتابة تعليمات برمجية أقل. التعليمة الشرطية if يمكننا إنشاء تعليمات if باستخدام التوجيهات Directives التي هي ‎@if و ‎@elseif و ‎@else و ‎@endif، حيث تعمل هذه التعليمات مثل تعليمات if التي نراها في لغات البرمجة الأخرى. @if ($num == 1) <p>The number is one.</p> @elseif ($num == 2) <p>The number is two.</p> @else <p>The number is three.</p> @endif يمكننا اختبار هذه الشيفرة البرمجية باستخدام الوِجهة كما يلي: Route::get('/number/{num}', function ($num) { return view('view', ['num' => $num]); }); نفترض هنا أن المتغير num يمكن أن يكون قيمته 1 أو 2 أو 3 فقط. التعليمة switch تكون بنية هذه التعليمة كما يلي: @switch($i) @case(1) First case. . . @break @case(2) Second case. . . @break @default Default case. . . @endswitch الحلقات يمكن أن نمثل الحلقات بحلقة for بسيطة: @for ($i = 0; $i < 10; $i++) The current value is {{ $i }} @endfor أو بحلقة foreach، حيث يساوي المتغير ‎$user العنصر التالي من ‎$users لكل تكرار في المثال التالي: @foreach ($users as $user) <p>This is user {{ $user->id }}</p> @endforeach يمكن أن نمثّل الحلقات بحلقة while أيضًا: @while (true) <p>I'm looping forever.</p> @endwhile تُعَد هذه التعليمات بسيطة جدًا، وتعمل تمامًا مثل نظيراتها في لغة PHP، ولكن يوجد شيء خاص بهذه الحلقات هو المتغير ‎$loop، فمثلًا ليكن لدينا شيء نريد عرضه مرة واحدة فقط، حيث يمكنك تطبيق ما يلي في التكرار الأول: @foreach ($users as $user) @if ($loop->first) <p>This is the first iteration.</p> @endif <p>This is user {{ $user->id }}</p> @endforeach سيُعرَض عنصر الفقرة <p>This is the first iteration.</p> مرة واحدة فقط في التكرار الأول، وتوجد العديد من الخاصيات الأخرى التي يمكنك الوصول إليها، لذا اطلع عليها في توثيق لارافيل الرسمي. لاحظ أنه لا يمكن الوصول إلى المتغير ‎$loop إلا ضمن الحلقة. الصنف الشرطي Conditional Class يُعَد الصنف Class الطريقة الأكثر استخدامًا لإسناد التنسيقات Styles إلى عناصر HTML المختلفة، ويمكننا بسهولة تغيير مظهر هذا العنصر من خلال تغيير صنفه، حيث يقدّم لارافيل طريقة لإسناد الأصناف ديناميكيًا بالاعتماد على المتغيرات كما يلي: <span @class([ 'p-4', 'font-bold' => $isActive, 'bg-red' => $hasError, ])></span> يعتمد الصنفان font-bold و bg-red في المثال السابق على قيمة المتغيرين ‎$isActive و ‎$hasError‎. بناء تخطيط الصفحة Layout توجد دائمًا بعض أجزاء الصفحة التي ستظهر في صفحات متعددة عندما نبني تطبيق ويب، حيث تُعَد كتابة الشيفرة البرمجية نفسها مرارًا وتكرارًا مضيعة للوقت والموارد، ولا يُعَد ذلك جيدًا للصيانة، لذا يجب فصل الجزء الذي سيظهر عدة مرات ووضعه في ملف آخر، وسنستورده ببساطة عندما نحتاج إليه. يقدم إطار عمل لارافيل طريقتين مختلفتين لذلك هما: وراثة القوالب ووراثة المكونات، حيث يُعَد فهم وراثة القوالب أسهل بكثير للمبتدئين، ولكن توفّر المكونات مزيدًا من الميزات، إذًا لنبدأ أولًا بوراثة القوالب. وراثة القوالب نحاول في المثال التالي تعريف صفحة رئيسية، وتكون الوِجهة كما يلي: Route::get('/', function () { return view('home'); }); يستورد العرض layout.blade.php ملفات CSS وجافاسكريبت JavaScript الضرورية، بالإضافة إلى شريط التنقل وتذييل الصفحة التي ستظهر في جميع الصفحات، وسنرث layout.blade.php في الملف home.blade.php. لنضع أولًا ما يلي في الملف layout.blade.php: <html> <head> @yield('title') </head> <body> <div class="container">@yield('content')</div> </body> </html> ولنضع الآن ما يلي في الملف home.blade.php: @extends('layout') @section('title') <title>Home Page</title> @endsection @section('content') <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. . .</p> @endsection لاحظ أن ‎@section('title')‎ يقابل ‎@yield('title')‎، وأن ‎@section('content')‎ يقابل ‎@yield('content')‎. سيرى لارافيل أولًا التوجيه ‎@extends('layout')‎ عند عرض الصفحة الرئيسية في مثالنا، وسيعرف أنه يجب عليه الانتقال إلى الملف layout.blade.php. سيحدد لارافيل بعد ذلك موقع ‎@yield('title')‎ ويضع القسم title مكانه، ثم يبحث عن التوجيه ‎@yield('content')‎ ويضع القسم content مكانه. سنحتاج في بعض الأحيان إلى تحميل عرض آخر من العرض الحالي، فمثلًا إذا أردنا إضافة شريط جانبي إلى صفحتنا الرئيسية، وهو شيء سنضمّنه في بعض الصفحات دون صفحات أخرى، فلن نضعه في الملف layout.blade.php، ولكن سيكون من الصعب جدًا صيانته إذا أنشأتَ شريطًا جانبيًا لكل صفحة تتطلب ذلك. يمكننا في مثالنا إنشاء الملف sidebar.blade.php، ثم استخدام التوجيه ‎@include لاستيراد هذا الملف في الصفحة الرئيسية كما يلي: @extends('layout') @section('title') <title>Home Page</title> @endsection @section('content') <p>. . .</p> @include('sidebar') @endsection المكونات Components يشبه نظام المكونات في لارافيل إلى حد كبير النظام الموجود في إطار عمل Vue.js. سنبني صفحة رئيسية مع تخطيطها، ولكننا سنعرّف التخطيط بوصفه مكونًا، لذا سننشئ المجلد components ضمن المجلد resources/views/‎، ثم ننشئ الملف layout.blade.php بداخله. لنضع أولًا ما يلي في الملف views/components/layout.blade.php: <html> <head> . . . </head> <body> <div class="container">{{ $slot }}</div> </body> </html> يمكن استخدام هذا المكون من خلال استخدام الوسم <x-layout>، وتذكّر دائمًا البادئة x-‎. <x-layout> <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum eu elit semper ex varius vehicula. </p> </x-layout> سيُسنَد المحتوى الموجود ضمن العنصر <x-layout> إلى المتغير ‎$slot تلقائيًا، ولكن إذا كان لدينا فتحات Slots متعددة كما في مثالنا الذي يحتوي على قسم title وقسم content، فيمكن حل هذه المشكلة من خلال تعريف عنصر <x-slot> كما يلي: <x-layout> <x-slot name="title"> <title>Home Page</title> </x-slot> <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum eu elit semper ex varius vehicula. </p> </x-layout> سيُسنَد محتوى <x-slot name="title"‎> إلى المتغير ‎$title وسيُسنَد المحتوى المتبقي إلى المتغير ‎$slot كما يلي: <html> <head> {{ $title }} </head> <body> <div class="container">{{ $slot }}</div> </body> </html> التعامل مع قواعد البيانات في لارافيل سنناقش أولًا كيفية التعامل مع قواعد البيانات في لارافيل قبل أن نتحدث بمزيد من التفصيل عن طبقة النموذج والمتحكم في بنية MVC. يمكن أن نستخدم ملف ‎.txt بسيط لتخزين المعلومات عند استخدام جافاسكريبت JavaScript و Node.js، ولكن لن ينجح ذلك في تطبيق الويب الواقعي، لذا سنحتاج لاستخدام قاعدة بيانات لتخزين ومعالجتها المعلومات بكفاءة. استخدمنا في مقالنا الحالي أداة Sail الخاصة بلارافيل لإنشاء هذا المشروع، لذا أعدت هذه الأداة قاعدة بيانات MySQL، إذ يجب أن توجد متغيرات البيئة التالية في ملف ‎.env: DB_CONNECTION=mysql DB_HOST=mysql DB_PORT=3306 DB_DATABASE=curl_demo DB_USERNAME=sail DB_PASSWORD=password عمليات تهجير Migrations قاعدة البيانات تكون قاعدة البيانات فارغة حاليًا، لذا سنضيف بعض جداول قاعدة البيانات لاستخدامها، حيث ستحتوي هذه الجداول على أعمدة، وستكون للأعمدة أسماء وبعض المتطلبات الخاصة، وتسمى عملية الإعداد هذه بتشغيل عمليات التهجير. تُخزَّن جميع ملفات التهجير في لارافيل ضمن المجلد database/migrations، ويمكننا إنشاء ملف تهجير جديد من خلال استخدام الأمر البسيط التالي، ولكن إذا استخدمتَ أداة Sail لإنشاء مشروع لارافيل، فضع ‎./vendor/bin/sail بدلًا من php لتشغيل PHP داخل حاوية دوكر Docker، وإن لم تستخدم أداة Sail، فتابع كالمعتاد: php artisan make:migration create_flights_table يمكن تطبيق ملف التهجير كما يلي: php artisan migrate وإذا أردتَ التراجع عن عملية التهجير السابقة، فاستخدم الأمر التالي: php artisan migrate:rollback أو إذا أردتَ إعادة ضبط عمليات التهجير بالكامل والتراجع عن جميع التغييرات المطبقة على قاعدة البيانات، فاستخدم الأمر التالي: php artisan migrate:reset إنشاء جدول لنلقِ نظرة على المثال التالي قبل إنشاء ملف التهجير. افتح ملف التهجير database/migrations/2014_10_12_000000_create_users_table.php التالي الذي يأتي مع لارافيل: <?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * تشغيل عمليات التهجير */ public function up(): void { Schema::create('users', function (Blueprint $table) { $table->id(); $table->string('name'); $table->string('email')->unique(); $table->timestamp('email_verified_at')->nullable(); $table->string('password'); $table->rememberToken(); $table->timestamps(); }); } /** * عكس عمليات التهجير */ public function down(): void { Schema::dropIfExists('users'); } }; يحتوي هذا الملف على صنف class له تابعان مختلفان، حيث يُستخدَم التابع ‎up()‎ لإنشاء جداول وأعمدة جديدة، بينما يمكن استخدام التابع ‎down()‎ لعكس العمليات التي يطبّقها التابع ‎up()‎. يستخدم لارافيل داخل التابع ‎up()‎ التابع create()‎ ضمن الصنف Schema لإنشاء جدول جديد بالاسم 'users'. وينشئ كل سطر من السطور 15 إلى 21 في الشيفرة البرمجية السابقة عمودًا مختلفًا مع اسم ونوع مختلفين، فمثلًا ينشئ السطر ‎$table->string('name');‎ عمودًا بالاسم name ضمن الجدول users، ويجب أن يخزّن هذا العمود سلاسلًا نصية. اطّلع على القائمة الكاملة لأنواع الأعمدة المتوفرة في لارافيل. لن نستعرض جميع هذه الأنواع، بل سنناقش أهمها وسبب اختيارنا لنوع العمود عندما نواجه مشكلات محددة مستقبلًا. لاحظ السطر 17 في الشيفرة البرمجية السابقة ‎$table->string('email')->unique();‎، حيث يوجد التابع unique()‎ بعد التصريح عن اسم العمود ونوعه، حيث يُسمَّى هذا التابع بمُعدِّل العمود Column Modifier، لأنه يضيف قيودًا إضافية إلى هذا العمود، ويتأكد في هذه الحالة من أن العمود لا يمكن أن يحتوي على قيم متكررة، ويجب أن يكون كل بريد إلكتروني يقدّمه المستخدم فريدًا. اطّلع على القائمة الكاملة لمُعدِّلي الأعمدة المتوفرة في لارافيل. إجراء تغييرات على الجدول يمكن أيضًا تحديث الجداول باستخدام التابع table()‎ كما يلي، حيث تضيف الشيفرة البرمجية التالية عمودًا جديدًا هو العمود age إلى الجدول users الموجود مسبقًا: Schema::table('users', function (Blueprint $table) { $table->integer('age'); }); كما يمكننا إعادة تسمية الجدول باستخدام التابع rename()‎: Schema::rename($from, $to); ويمكننا حذف جدول نهائيًا كما يلي: Schema::drop('users'); Schema::dropIfExists('users'); البذر أو توليد البيانات Seeding يُستخدَم مولّد البيانات Seeder لإنشاء بيانات وهمية لقاعدة بياناتك، وبالتالي يسهّل عليك إجراء الاختبارات، حيث يمكنك إنشاء مولّد بيانات من خلال تشغيل الأمر التالي: php artisan make:seeder UserSeeder يولّد الأمر السابق مولّد بيانات للجدول users، وسيُوضَع في المجلد database/seeders، حيث وضع ما يلي في الملف UserSeeder.php: <?php namespace Database\Seeders; use Illuminate\Database\Seeder; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Str; class UserSeeder extends Seeder { /** * تشغيل مولّدات بيانات قاعدة البيانات */ public function run(): void { DB::table('users')->insert([ 'name' => Str::random(10), 'email' => Str::random(10).'@gmail.com', 'password' => Hash::make('password'), ]); } } شغّل الأمر التالي لتنفيذ مولّد البيانات السابق وملء جداول قاعدة البيانات بالبيانات الافتراضية: php artisan db:seed UserSeeder باني الاستعلامات Query Builder باني الاستعلامات هو واجهة تسمح لنا بالتفاعل مع قاعدة البيانات، فمثلًا يمكننا استخدام التابع table()‎ الذي يوفره الصنف DB لاسترداد جميع الصفوف الموجودة في الجدول كما يلي: use Illuminate\Support\Facades\DB; $users = DB::table('users')->get(); يمكننا إضافة مزيدٍ من القيود من خلال إضافة التابع where()‎ إلى سلسلة التوابع كما يلي، حيث ستحصل الشيفرة البرمجية التالية على جميع الصفوف التي تكون فيها القيمة المخزنة في العمود age أكبر من أو تساوي 21: $users = DB::table('users')->where('age', '>=', 21)->get(); توجد الكثير من التوابع الأخرى إلى جانب التابعين where()‎ و get()‎، والتي سنتحدث عنها لاحقًا. طبقة النموذج Model يُعَد النموذج مفهومًا مهمًا جدًا في تصميم الويب الحديث، فهو مسؤول عن التفاعل مع قاعدة بيانات تطبيق الويب، وهو جزء من نظام رابط الكائنات بالعلاقات Object-Relational Mapper -أو ORM اختصارًا- في لارافيل الذي هو نظام Eloquent، حيث يمكن عَدّه باني استعلام مع بعض الميزات الإضافية. يمكننا استخدام الأمر make:model لإنشاء نموذج جديد كما يلي: php artisan make:model Post إذا أردتَ توليد ملف تهجير مقابل للنموذج، فاستخدم الخيار ‎--migration: php artisan make:model Post --migration وسيتضمّن ملف النموذج app/Models/Post.php ما يلي: <?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class Post extends Model { use HasFactory; . . . } يفترِض النموذج السابق وجود الجدول posts في قاعدة البيانات افتراضيًا، وإذا أردتَ تغيير هذا الإعداد، فاضبط الخاصية ‎$table كما يلي: class Post extends Model { /** * الجدول المرتبط بالنموذج * * @var string */ protected $table = 'my_posts'; } استرداد النموذج يُعَد نموذج Eloquent بانيَ استعلامات قويًا ويسمح بالتواصل مع قاعدة البيانات، فمثلًا يمكننا استخدام التابع all()‎ لاسترداد كافة السجلات في الجدول كما يلي، ولاحظ أننا استوردنا النموذج Post بدلًا من الصنف DB، حيث سيتصل النموذج Post تلقائيًا بالجدول posts: use App\Models\Post; $posts = Post::all(); بما أن النموذج هو باني استعلامات، لذا يمكن أن تصل النماذج إلى جميع توابع باني الاستعلامات كما يلي: $posts = Post::where('published', true) ->orderBy('title') ->take(10) ->get(); إذا استخدمتَ التابعين all()‎ أو get()‎ لاسترداد البيانات، فلن تكون القيمة المُعادة مصفوفةً أو كائنًا بسيطًا، ولكنها ستكون نسخة من الصنف Illuminate\Database\Eloquent\Collection، حيث يوفّر الصنف Collection عدة توابع أقوى من الكائنات البسيطة. يمكن استخدام التابع find()‎ مثلًا لتحديد موقع سجل باستخدام مفتاح رئيسي هو id عادة. $posts = Post::all(); $post = $posts->find(1); سيكون ‎$post في هذا المثال هو المنشور ذو المعرّف id==1. إدراج وتحديث النموذج يمكننا أيضًا إدراج السجلات أو تحديثها باستخدام النموذج، ولكن تأكد أولًا من أن النموذج المقابل له الخاصية ‎$fillable وتأكد من سرد جميع الأعمدة التي يجب أن تكون قابلة للملء. class Post extends Model { /** * السمات‫ Attributes التي يمكن إسنادها بطريقة جماعية * * @var array */ protected $fillable = ['title', 'content']; } يمكننا بعد ذلك استخدام التابع create()‎ لإنشاء سجل جديد كما يلي: $flight = Post::create([ 'title' => '. . .', 'content' => '. . .', ]); أو يمكننا تحديث السجلات الموجودة مسبقًا باستخدام التابع update()‎: Flight::where('published', true)->update(['published' => false]); حذف النموذج يوجد تابعان يسمحان بحذف السجلات، حيث إذا أردتَ حذف سجل واحد، فاستخدم التابع delete()‎: $posts = Post::all(); $post = $posts->find(1); $post->delete(); وإذا أردتَ حذف السجلات بطريقة جماعية، فاستخدم التابع truncate()‎: $posts = Post::where('published', true) ->orderBy('title') ->take(10) ->get(); $posts->truncate(); علاقات قاعدة البيانات لا تكون جداول قاعدة البيانات في تطبيقات الويب مستقلة بل تكون مترابطة بعلاقات فيما بينها، فمثلًا يمكن أن يكون لدينا مستخدم لديه منشورات ويمكن أن يكون لدينا منشوراتٌ تخص مستخدمًا ما، لذا يقدّم لارافيل طريقة لتعريف هذه العلاقات باستخدام نماذج Eloquent. يُحتمَل أن يكون القسم التالي صعبًا بعض الشيء بالنسبة للمبتدئين، ولكن لا تقلق بهذا الشأن، إذ سنعود إلى هذا الموضوع لاحقًا عندما نبدأ في إنشاء تطبيق المدونة، حيث سنوضّح فقط ما نحتاج إلى استخدامه لبناء نظام تدوين بسيط. علاقة واحد إلى واحد تُعَد علاقة واحد إلى واحد One to One العلاقة الأساسية، فمثلًا يرتبط كل مستخدم User بحيوان أليف Pet واحد. يمكن تعريف هذه العلاقة من خلال وضع التابع pet في النموذج User كما يلي: <?php namespace App\Models; use Illuminate\Database\Eloquent\Model; class User extends Model { /** * الحصول على الحيوان الأليف المرتبط بالمستخدم */ public function pet() { return $this->hasOne(Pet::class); } } إذا أردنا أن نطبّق العكس، حيث يكون معكوس العلاقة "يملك" has one هو "يعود إلى" belongs to one، مما يعني أن كل حيوان أليف Pet يعود إلى مستخدم User واحد، ويمكن تعريف معكوس العلاقة واحد إلى واحد من خلال وضع التابع user في النموذج Pet كما يلي: class Pet extends Model { /** * الحصول على المستخدم الذي يملك الحيوان الأليف */ public function user() { return $this->belongsTo(User::class); } } لكننا لم ننتهِ بعد، إذ يجب أيضًا إجراء بعض التغييرات على جداول قاعدة البيانات المتقابلة، إذ يفترض النموذج User وجود العمود pet_id ضمن الجدول users، ويخزّن معرّف id الحيوان الأليف الذي يملكه المستخدم. يجب أيضًا إجراء تعديلات على الجدول pets المقابل، إذ يجب وجود العمود user_id الذي يخزّن معرّف id المستخدم الذي يعود إليه هذا الحيوان الأليف. علاقة واحد إلى متعدد تُستخدم علاقة واحد إلى متعدد One-to-Many لتعريف العلاقات التي يمتلك فيها نموذج واحد نسخًا متعددة من نموذج آخر، فمثلًا يمكن أن تحتوي فئة Category واحدة على العديد من المنشورات posts، ويمكن تعريف هذه العلاقة من خلال وضع التابع posts في النموذج Category كما يلي: class Category extends Model { public function posts() { return $this->hasMany(Post::class); } } ولكن يجب في بعض الأحيان العثور على الفئة عبر المنشور، فمعكوس العلاقة "لديه العديد" has many هو "ينتمي إلى" belongs to، ويمكن تعريف معكوس العلاقة واحد إلى متعدد من خلال وضع التابع category في النموذج Post كما يلي: class Post extends Model { public function category() { return $this->belongsTo(Category::class); } } تفترض هذه العلاقة أن الجدول posts يحتوي على العمود category_id الذي يخزّن معرّف id الفئة التي ينتمي إليها هذا المنشور. علاقة متعدد إلى متعدد تُعَد العلاقة متعدد إلى متعدد Many-to-Many أصعب بعض الشيء، فمثلًا يمكن أن يكون لدينا مستخدم User لديه العديد من الأدوار، ويمكن أن يكون لدينا دور Role له العديد من المستخدمين كما يلي: class User extends Model { /** * الأدوار التي تخص المستخدم */ public function roles() { return $this->belongsToMany(Role::class); } } class Role extends Model { /** * المستخدمون الذين ينتمون إلى الدور */ public function users() { return $this->belongsToMany(User::class); } } تفترض هذه العلاقة وجود الجدول role_user في قاعدة البيانات واحتواء الجدول role_user على العمودين user_id و role_id، وبالتالي يمكننا مطابقة المستخدم مع دوره والعكس صحيح. لنفترض أن لدينا الجدول role_user كما يلي: user_id role_id 1 1 2 1 3 2 1 2 2 3 يمتلك المستخدم الذي له المعرّف id=1 دورَين لهما المعرّف id=1 والمعرّف id=2. إذا أردنا عكس الأمر للعثور على المستخدمين عبر الدور، فيمكننا أن نرى مستخدمَين لهما المعرّف id=3 والمعرّف id=1 بالنسبة للدور ذي المعرّف id=2. طبقة المتحكم Controller وضّحنا الوِجهات والعروض والنماذج وعلاقات قاعدة البيانات، وحان الوقت الآن لنتعرّف على الشيء الذي يربطها جميعًا مع بعضها البعض. تحدّثنا في المقال السابق عن الوِجهات، واستخدمنا المثال التالي: Route::get('/number/{num}', function ($num) { return view('view', ['num' => $num]); }); يبدو الأمر جيدًا في الأمثلة البسيطة، ولكن إذا كان لديك مشروع ضخم، فسيؤدي وضع كل الشيفرة البرمجية في ملف الوِجهة Route File إلى جعل الأمر فوضويًا جدًا، والحل الأفضل هو التأكّد من أن الوِجهة تؤشّر دائمًا إلى تابع في المتحكم مع وضع الشيفرة البرمجية ضمن هذا التابع. use App\Http\Controllers\UserController; Route::get('/users/{name}', [UserController::class, 'show']); يمكن إنشاء متحكم جديد من خلال استخدام الأمر التالي: php artisan make:controller UserController تُخزَّن ملفات متحكم لارافيل ضمن المجلد app/Http/Controllers/‎. المتحكم الأساسي لنلقِ نظرة على المثال التالي، حيث يتوقع التابع show()‎ المتغير ‎$id، ويستخدِم هذا المعرّف id لتحديد موقع المستخدم في قاعدة البيانات، ويعيد هذا المستخدم في العرض. <?php namespace App\Http\Controllers; use App\Models\User; use Illuminate\View\View; class UserController extends Controller { /** * عرض الملف الشخصي لمستخدمٍ معين */ public function show(string $name): View { return view('user.profile', [ 'user' => User::firstWhere('name', $name); ]); } } تحتاج في بعض الأحيان إلى تعريف متحكم له تابع واحد فقط، ويمكننا في هذه الحالة تبسيط الشيفرة البرمجية من خلال تغيير اسم التابع إلى ‎_invoke. class UserController extends Controller { public function __invoke(string $name): View { return view('user.profile', [ 'user' => User::firstWhere('name', $name); ]); } } لا حاجة الآن لتحديد اسم التابع في الوِجهة كما يلي: Route::get('/users/{id}', UserController::class); متحكم الموارد المورد Resource هو مفهوم حديث آخر لتصميم الويب، حيث نرى جميع نماذج Eloquent بوصفها مواردًا، مما يعني أننا سنجري مجموعة الإجراءات نفسها عليها جميعًا، وهذه الإجراءات هي الإنشاء ‎Create والقراءة ‎Read والتحديث ‎Update والحذف ‎Delete -أو CRUD اختصارًا. يمكن إنشاء متحكم موارد من خلال استخدام الخيار ‎--resource كما يلي: php artisan make:controller PostController --resource سيحتوي المتحكم الذي أنشأناه على التوابع التالية تلقائيًا: <?php namespace App\Http\Controllers; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Http\Response; class PostController extends Controller { /** * عرض قائمة الموارد */ public function index(): Response { // } /** * عرض الاستمارة الخاصة بإنشاء مورد جديد */ public function create(): Response { // } /** * تخزين المورد الذي أنشأناه حديثًا في وحدة التخزين */ public function store(Request $request): RedirectResponse { // } /** * عرض المورد المُحدَّد */ public function show(string $id): Response { // } /** * عرض الاستمارة الخاصة بتعديل المورد المُحدَّد */ public function edit(string $id): Response { // } /** * تحديث المورد المحدَّد في وحدة التخزين */ public function update(Request $request, string $id): RedirectResponse { // } /** * إزالة المورد المُحدَّد من وحدة التخزين */ public function destroy(string $id): RedirectResponse { // } } تشرح التعليقات في الشيفرة البرمجية السابقة وظيفة التوابع والغرض منها. يقدّم لارافيل أيضًا طريقة سهلة جدًا لتسجيل الوِجهات لكل من التوابع السابقة كما يلي: Route::resource('posts', PostController::class); ستَنشَأ الوِجهات التالية باستخدام توابع HTTP المختلفة تلقائيًا: تابع HTTP مسار URI الإجراء اسم الوِجهة GET ‏ ‎/posts ‏ index ‏ posts.index GET ‏ ‎/posts/create ‏ create ‏ posts.create POST ‏ ‎/posts ‏ store ‏ posts.store GET ‏ ‎/posts/{post}‎ ‏ show ‏ posts.show GET ‏ ‎/posts/{post}/edit ‏ edit ‏ posts.edit PUT/PATCH ‏ ‎/posts/{post}‎ ‏ update ‏ posts.update DELETE ‏ ‎/posts/{post}‎ ‏ destroy ‏ posts.destroy ترجمة -وبتصرُّف- للمقال Laravel for Beginners #2 - The MVC Structure لصاحبه Eric Hu. اقرأ أيضًا المقال السابق: لارافيل للمبتدئين-الجزء الأول: البدء في إنشاء مدونة بسيطة كيف تستخدم منشئ الاستعلامات Query builder للتخاطب مع قاعدة البيانات في Laravel تعرف على إطار عمل تطوير الويب لارافيل Laravel تجريد إعداد قواعد البيانات في لارافيل باستعمال عملية التهجير Migration والبذر Seeder علاقات Eloquent والتحميل الحثيث في Laravel 5
  6. يُعَد لارافيل Laravel إطار ويب يستخدم لغة PHP، وهو مجاني ومفتوح المصدر ويُستخدَم على نطاق واسع لتطوير تطبيقات الويب، كما أنه معروف بصيغته الأنيقة وأدواته المُستخدَمة للمهام الشائعة مثل التوجيه Routing والاستيثاق Authentication والتخزين المؤقت أو التخبئة Caching وقدرته على التعامل مع حركة الزوار العالية. يتبع لارافيل معمارية نموذج-عرض-متحكم Model-View-Controller (أو MVC اختصارًا)، ويتضمن دعمًا مُدمجًا لربط الكائنات بالعلاقات Object-Relational Mapping (أو ORM اختصارًا) وباني الاستعلامات، مما يسهّل التفاعل مع قواعد البيانات. يضم لارافيل مجتمعًا كبيرًا ونشطًا يوفّر مجموعةً كبيرة من البرامج التعليمية والحزم والموارد الأخرى التي يمكن للمطورين استخدامها، إذ يُعَد لارافيل أحد أطر عمل الويب الأكثر شعبية وقوة للغة PHP، وتثق به العديد من الشركات والمؤسسات لبناء تطبيقات ويب قوية وقابلة للتوسّع. سنشرح في هذا المقال الاستخدام الأساسي لإطار عمل لارافيل من خلال بناء نظام تدوين بسيط. ملاحظة: تعتمد هذه السلسلة من المقالات على الإصدار 11 من لارافيل. إنشاء مشروع لارافيل جديد لا يُعَد إعداد بيئة تطوير PHP مهمة سهلة، وخاصة إذا استخدمت أنظمة لينكس Linux، ولكن قدّم لارافيل منذ الإصدار 8.0 أداة جديدة اسمها Sail، والتي توفر حلًا سهلًا لتشغيل تطبيقات لارافيل من خلال استخدام أداة دوكر Docker بغض النظر عن نظام التشغيل الذي تستخدمه، طالما أن هذه الأداة مُثبَّتة لديك. أنشئ أولًا مجلد عمل جديد بالاسم laravel-tutorial مثلًا، ثم افتحه في محرّر الأكواد الذي تفضله وليكن VS Code، ونفّذ الأمر الآتي لإنشاء مشروع لارافيل جديد، وتأكّد من تشغيل دوكر قبل تنفيذ هذا الأمر، إذ قد تستغرق هذه العملية من 5 إلى 10 دقائق حتى الاكتمال: curl -s https://laravel.build/<app_name> | bash ملاحظة: إذا كنت تعمل على نظام ويندوز Windows، فتأكّد من تشغيل هذا الأمر ضمن نظام ويندوز الفرعي لنظام التشغيل لينكس WSL 2 الذي يسمح لك بتشغيل نواة Linux كاملة داخل ويندوز دون الحاجة إلى تثبيت نظام لينكس. ثانيًا، استخدم الأمر التالي للانتقال إلى مجلد التطبيق وشغّل الخادم: cd <app_name> سيؤدي الأمر التالي إلى بدء حاوية دوكر بالإضافة إلى خادم تطوير لارافيل: ./vendor/bin/sail up يمكنك الوصول إلى تطبيق لارافيل على العنوان http://localhost/‎، وإن لم يعمل هذا العنوان، فحاول الانتقال إلى العنوان http://127.0.0.1/‎ بدلًا من ذلك، ثم يجب أن تشاهد صفحة ترحيب لارافيل التالية: استكشاف بنية تطبيق لارافيل لنلقِ نظرة على المجلدات والملفات التي تنشأ ضمن مشروعنا قبل أن نبدأ ببرمجته، لذا إليك نظرة عامة على المجلد الجذر للمشروع: . ├── app │ ├── Console │ ├── Exceptions │ ├── Http │ │ ├── Controllers │ │ └── Middleware │ ├── Models │ └── Providers ├── bootstrap ├── config ├── database │ ├── factories │ ├── migrations │ └── seeders ├── public ├── resources │ ├── css │ ├── js │ └── views ├── routes ├── storage │ ├── app │ ├── framework │ └── logs ├── tests │ ├── Feature │ └── Unit └── vendor المجلد app: يُعَد هذا المجلد المكون الأساسي لمشروعنا، ويحتوي على مجلدات متعددة من أهمها مجلد المتحكمات controllers والبرمجيات الوسيطة middleware والنماذج models، حيث يعرّف المتحكم الشيفرة البرمجية الأساسية للتطبيق، وتعرّف البرمجيات الوسيطة الإجراءات التي يجب اتخاذها قبل استدعاء المتحكم، ويوفّر النموذج واجهة تسمح لنا بالتعامل مع قواعد البيانات، إذ سنتحدث عن كل منها بالتفصيل لاحقًا. المجلد bootstrap: يحتوي هذا المجلد على الملف app.php الذي يشغّل المشروع بأكمله، ولا حاجة لتعديل أيّ شيء في هذا المجلد. المجلد config: يحتوي على ملفات الضبط Configuration كما يدل اسمه، ولا حاجة للاهتمام بهذا الضبط في هذه السلسلة من المقالات. المجلد database: يحتوي على ملفات التهجير migrations والمصانع factories والبذور seeds، حيث تصف ملفات التهجير بنية قاعدة البيانات، وتُعَد ملفات المصانع والبذور طريقتين مختلفتين يمكننا من خلالهما ملء قاعدة البيانات ببيانات وهمية في لارافيل. المجلد public: يحتوي على الملف index.php الذي يُعَد نقطة الدخول إلى لتطبيق. المجلد resources: يحتوي على ملفات العرض views التي تمثّل جزء الواجهة الأمامية من تطبيق لارافيل. المجلد routes: يحتوي على جميع الموجّهات Routers لعناوين URL للمشروع فعندما ترسل طلب إلى عنوان URL محدد، سيتوجه الطلب إلى المُوجِّه المناسب بناءً على الوجهات التي تحددها الملفات ضمن هذا المجلد. المجلد storage: هو مساحة تخزين المشروع بأكمله، ويحتوي على السجلات logs والعروض المُصرَّفة Compiled Views بالإضافة إلى الملفات التي رفعها المستخدم. المجلد tests: يحتوي على ملفات الاختبار، حيث يُعَد الاختبار مفهومًا أكثر تقدمًا نسبيًا في لارافيل، لذلك لن نوضّحه في هذه السلسلة من المقالات، ولكن يمكنك الاطلاع على مقال كيفية استخدام PHPUnit لاختبار تطبيقات لارافيل لمزيد من التفاصيل حول الاختبارات. المجلد vendor: يتضمن جميع الاعتماديات Dependencies. متغيرات البيئة سنلقي نظرة أيضًا على متغيرات البيئة التي تُخزَّن في ملف ‎.env، حيث أُعِدت الكثير من عمليات الضبط افتراضيًا عندما أنشأنا مشروعنا باستخدام أداة Sail في لارافيل، ولكننا سنتحدث عن عملها فيما يلي. المتغير APP_URL يحدّد المتغير APP_URL عنوان URL للتطبيق، ويكون هذا العنوان هو http://localhost افتراضيًا، ولكن قد تحتاج إلى تغييره إلى العنوان http://127.0.0.1 عندما يعطي العنوانُ http://127.0.0.1 صفحةَ خادم Apache2 الافتراضية. APP_URL=http://127.0.0.1 أو: APP_URL=http://localhost قاعدة البيانات ستثبّت أداة Sail من لارافيل نظامَ إدارة قواعد البيانات MySQL بوصفه التطبيق الخاص بقاعدة البيانات، وتُعرَّف اتصالات قاعدة البيانات كما يلي: DB_CONNECTION=mysql DB_HOST=mysql DB_PORT=3306 DB_DATABASE=curl_demo DB_USERNAME=sail DB_PASSWORD=password يمكنك أيضًا تعريف متغيرات بيئة خاصة بك إن احتجت إليها كما يلي، ثم يمكنك الوصول إليها في أي مكان من المشروع: CUSTOM_VARIABLE=true ويمكن الوصول إلى هذا المتغير كما يلي: env('CUSTOM_VARIABLE', true) يمثّل المعامل الثاني القيمة الافتراضية لهذا المتغير، حيث إن لم يكن المتغير CUSTOM_VARIABLE موجودًا، فسيُعاد المعامل الثاني. أساسيات التوجيه Routing في لارافيل سنلقي نظرة في هذا القسم على وِجهات Route لارافيل وبرمجياتها الوسيطة، حيث تُعرَّف الوِجهات في المجلد routes. لاحظ وجود أربعة ملفات مختلفة في المجلد routes افتراضيًا، ولكن ما يهمنا الملفان api.php و web.php بالنسبة لمعظم المشاريع. إذا أردت استخدام لارافيل بطريقة صارمة مع الواجهة الخلفية (بدون العرض)، فيجب أن تحدّد الوِجهات في الملف api.php، ولكننا سنستخدم في هذا المقال لارافيل بوصفه إطار عمل متكامل full-stack، لذلك سنستخدم الملف web.php بدلًا من ذلك. الفرق بين هذين الملفين هو أن الملف api.php تغلِّفه مجموعة برمجيات api الوسيطة وأن الملف web.php موجود ضمن مجموعة برمجيات web الوسيطة، ويوفر هذان الملفان دوالًا مختلفة، وسيكون للوِجهات المحددة في الملف api.php بادئة عنوان URL التي هي /api/، وبالتالي يجب أن يكون عنوان URL مثل العنوان: http://example.com/api/somthing-else للوصول إلى وِجهة api. تقبل الوِجهة الأساسية في لارافيل عنوان URL ثم تعيد قيمة، حيث يمكن أن تكون هذه القيمة سلسلة نصية أو عرضًا أو متحكمًا. انتقل إلى الملف routes/web.php، وسترى أن هناك وِجهة مُعرَّفة مسبقًا هي: routes/web.php. يخبرنا الجزء التالي من الشيفرة البرمجية أنه إذا استقبل لارافيل الوِجهة "/"، فسيعيد عرضًا بالاسم "welcome"، والذي يقع في resources/views/welcome.blade.php: use Illuminate\Support\Facades\Route; Route::get('/', function () { return view('welcome'); }); افتح المتصفح، وانتقل إلى العنوان http://127.0.0.1:8000، وستحصل على الصفحة التالية: يمكن التحقق من أن welcome.blade.php هو العرض الظاهر من خلال إجراء بعض التغييرات على الملف، ثم تحديث متصفحك للتأكد من تغيير الصفحة. توابع الموجهات لنلقِ الآن نظرة على الموجِّه التالي في routes/web.php ونفهم كيفية عمله: use Illuminate\Support\Facades\Route; Route::get('/', function () { return view('welcome'); }); نستورد أولًا الصنف Route ونستدعي التابع get()‎، حيث يتطابق هذا التابع مع تابع HTTP الذي هو GET، وهناك توابع أخرى مُضمَّنة في الصنف Route، حيث تتطابق هذه التوابع مع توابع طلبات HTTP الأخرى كما يلي: Route::get($uri, $callback); Route::post($uri, $callback); Route::put($uri, $callback); Route::patch($uri, $callback); Route::delete($uri, $callback); Route::options($uri, $callback); إذا أردتَ أن تطابق الوِجهة بين توابع HTTP متعددة، فيمكنك استخدام تابع match أو any بدلًا من ذلك، حيث يتطلب التابع match()‎ تحديد مصفوفة توابع HTTP التي ترغب في مطابقتها، ويطابق التابع any()‎ ببساطة بين جميع طلبات HTTP. Route::match(['get', 'post'], '/', function () { . . . }); Route::any('/', function () { . . . }); تمرير البيانات إلى العرض يحتوي التابع get()‎ على معاملَين هما: المعامل الأول هو عنوان URL الذي يُفترَض أن يطابقه الموجِّه، والمعامل الثاني هو دالة رد النداء Callback التي تُنفَّذ عند نجاح المطابقة. تُعاد الدالة view()‎ المُضمَّنة ضمن دالة رد النداء، حيث ستبحث هذه الدالة عن ملف العرض المقابل بناءً على المعامل المُمرَّر إليها. يقدم لارافيل اختصارًا بسيطًا هو Route::view()‎ إذا أردتَ إعادة عرض فقط، حيث يسمح لك هذا التابع بتجنب كتابة وِجهة كاملة كما يلي: Route::view('/welcome', 'welcome'); الوسيط الأول لهذا التابع هو عنوان URL، والمعامل الثاني هو العرض المقابل، ويوجد أيضًا وسيط ثالث يسمح بتمرير بعض البيانات إلى هذا العرض كما يلي: Route::view('/welcome', 'welcome', ['name' => 'Taylor']); سنتحدث عن كيفية الوصول إلى البيانات عندما نصل إلى قوالب Blade. من الموجه إلى المتحكم يمكن أن نجعل الموجّه يؤشّر إلى المتحكم الذي يؤشّر بدوره إلى العرض، فالمتحكم هو أساسًا نسخة موسعة من دالة رد النداء، حيث سنتحدث عن المتحكمات بالتفصيل في المقال التالي. يخبرنا سطر الشيفرة البرمجية التالي أنه إذا استقبل الموجّه الوِجهة "‎/user"، فسيذهب لارافيل إلى الصنف UserController، ويستدعي التابع index: Route::get('/user', [UserController::class, 'index']); معاملات الوجهة تحتاج في بعض الأحيان إلى استخدام أجزاء من عنوان URL بوصفها معاملات، فمثلًا لنفترض أن لدينا مدونة مطورة بالكامل، ويوجد مستخدم يبحث عن منشور في المدونة له الاسم Slug الموجود في الرابط this-is-a-post، ويحاول العثور على هذا المنشور من خلال كتابة http://www.example.com/posts/this-is-a-post في متصفحه. يمكن التأكد من أن المستخدم قد عثر على المنشور الصحيح من خلال أخذ الجزء الموجود بعد posts/‎ كمعامل وإرساله إلى الواجهة الخلفية، ثم يمكن للمتحكم استخدام هذا المعامل للعثور على المنشور الصحيح وإعادته إلى المستخدم، حيث يمكنك تطبيق ذلك من خلال كتابة الشيفرة البرمجية التالية: Route::get('post/{slug}', [PostController::class, 'show']); سيتأكد السطر السابق من أن موجّه لارافيل يطابق الكلمة الموجودة بعد post/‎ كمعامل، ويسندها إلى المتغير slug عندما يرسلها لارافيل إلى الواجهة الخلفية. تُضمَّن معاملات الصنف Route دائمًا بين قوسين {} ويجب أن تتكون من أحرف أبجدية، ولا يجوز أن تحتوي على المحرف -. بما أننا لم نذكر المتحكمات في مثالنا، فيمكننا وضع دالة رد نداء بسيطة مكان المعامل الثاني واختبار الشيفرة البرمجية التالية: Route::get('/post/{slug}', function ($slug) { return $slug; }); افتح الآن متصفحك وانتقل إلى العنوان http://127.0.0.1:8000/posts/this-is-a-slug، وستظهر الصفحة التالية: يمكن أيضًا مطابقة معاملات متعددة في عنوان URL كما يلي: Route::get('category/{category}/post/{slug}', [PostController::class, 'show']); سيُسنَد الجزء الموجود بعد category/‎ إلى المتغير category في هذه الحالة، وسيُسنَد الجزء الموجود بعد post/‎ إلى المتغير slug. لا تعرف في بعض الأحيان ما إذا كان المعامل موجودًا في عنوان URL، وبالتالي يمكنك جعل هذا المعامل اختياريًا من خلال إلحاقه بإشارة استفهام (?) كما يلي: Route::get('post/{slug?}', [PostController::class, 'show']); وأخيرًا، يمكنك التحقق من صحة المعاملات باستخدام التعابير النمطية Regular Expressions، فمثلًا يمكنك التأكد من أن معرّف المستخدم هو عدد دائمًا كما يلي: Route::get('user/{id}', [UserController::class, 'show'])->where('id', '[0-9]+'); الموجهات المسماة تسمح الوِجهات المُسماة بإنشاء عناوين URL أو إعادة التوجيه بصورة مناسبة إلى وِجهات محددة، حيث يمكنك تحديد اسمٍ لوِجهة من خلال إضافة التابع name إلى سلسلة تعريف الوِجهة كما يلي: Route::get('user/profile', [UserController::class, 'show'])->name('profile'); وبالتالي إذا احتجت إلى الوصول إلى عنوان URL هذا، فيجب أن تستدعي الدالة route('profile')‎ فقط. تجميع الموجهات إذا أردت إنشاء موقع ويب كبير، فيجب أن يكون لديك عشرات أو حتى مئات من الموجّهات، وبالتالي من المنطقي أن تجمّعها مع بعضها بعضًا، فمثلًا يمكنك تجميعها بناءً على البرمجيات الوسيطة middleware كما يلي: Route::middleware(['auth'])->group(function () { Route::get('/user/profile', [UserController::class, 'show']); Route::get('/user/setting', [UserController::class, 'setting']); }); وستُسنَد الآن البرمجية الوسيطة auth إلى الموجّهَين. يمكنك أيضًا إسناد بادئات Prefixes إلى مجموعة من الموجّهات كما يلي: Route::prefix('admin')->group(function () { Route::get('/users', [UserController::class, 'show']); . . . }); سيكون لجميع الموجّهات المحدّدة في هذه المجموعة البادئة /admin/. البرمجيات الوسيطة Middleware البرمجية الوسيطة هي شيء يمكنه فحص وترشيح طلبات HTTP الواردة قبل أن تصل إلى تطبيقك، ويحدث بعد أن تطابق الوِجهة عنوان URL دون تنفيذ دالة رد النداء، وبالتالي هي شيء موجود في منطقة وسطى، ومن هنا جاءت تسميتها بالبرمجيات الوسيطة. يُعَد استيثاق المستخدم User Authentication من أمثلة البرمجيات الوسيطة، حيث إذا كان المستخدم مُستوثَقًا، فستأخذ الوِجهة هذا المستخدم إلى الهدف المفترض، وإن لم يكن الأمر كذلك، فستأخذ المستخدم إلى صفحة تسجيل الدخول أولًا. لن نكتب أيّ برمجيات وسيطة في هذه السلسلة من المقالات، ولكن البرمجية الوسيطة الوحيدة التي نحتاج إلى استخدامها هي البرمجية الوسيطة auth المُضمَّنة لأغراض الاستيثاق، والتي سنشرح أساسياتها. ننشئ برمجية وسيطة من خلال تشغيل الأمر التالي: php artisan make:middleware EnsureTokenIsValid مما سيؤدي إلى إنشاء صنف EnsureTokenIsValid جديد ضمن المجلد app/Http/Middleware. <?php namespace App\Http\Middleware; use Closure; class EnsureTokenIsValid { /** * معالجة الطلب الوارد * * @param \Illuminate\Http\Request $request * @param \Closure $next * @return mixed */ public function handle($request, Closure $next) { if ($request->input('token') !== 'my-secret-token') { return redirect('home'); } return $next($request); } } ستحصل هذه البرمجيات الوسيطة على قيمة المفتاح Token من الطلب، وتقارنها مع المفتاح السري المُخزَّن على موقعنا، حيث إذا كان مطابقًا، فانتقل إلى الخطوة التالية، وإذا لم يكن كذلك، فأعِد التوجيه إلى الصفحة الرئيسية. يمكنك استخدام هذه البرمجية الوسيطة من خلال تسجيلها في لارافيل، لذا انتقل إلى الملف app/Http/Kernel.php، وابحث عن الخاصية ‎$routeMiddleware، وأدرِج البرمجية الوسيطة التي أنشأناها. /** * البرمجيات الوسيطة لوِجهات التطبيق * * يمكن إسناد هذه البرمجيات الوسيطة إلى مجموعات أو استخدامها بصورة فردية * * @var array<string, class-string|string> **/ protected $routeMiddleware = [ 'auth' => \App\Http\Middleware\Authenticate::class, 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, 'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class, 'can' => \Illuminate\Auth\Middleware\Authorize::class, 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, 'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class, 'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class, 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, ]; وأخيرًا، تعرّفنا سابقًا على كيفية استخدام البرمجيات الوسيطة مع الموجّه كما يلي: Route::get('/profile', function () {. . .})->middleware('auth'); ترجمة -وبتصرُّف- للمقال Laravel for Beginners #1 - Getting Started لصاحبه Eric Hu. اقرأ أيضًا تعرف على إطار عمل تطوير الويب لارافيل Laravel مقارنة بين Django و Laravel و Rails تثبيت لارافيل وإعداده على Windows وUbuntu إنشاء مدونة باستخدام Laravel 5
×
×
  • أضف...