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

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

المحتوى عن 'mvc'.

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

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

نوع المحتوى


التصنيفات

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

التصنيفات

  • مقالات برمجة عامة
  • مقالات برمجة متقدمة
  • 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

ابحث في

ابحث عن


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

  • بداية

    نهاية


آخر تحديث

  • بداية

    نهاية


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

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

  • بداية

    نهاية


المجموعة


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

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

  1. إذا كنت مهتما بتعلم البرمجة، فعلى الأرجح أنك رأيت هذا الاقتباس من قبل: في الحقيقة لا أبالغ إن قلت أن البرمجة أصبحت من أهم المهارات في عصرنا الحالي، وذلك لأنها دخلت في جميع مجالات حياتنا سواءً كنا ندرك ذلك أم لا فانطلاقًا من الهواتف المحمولة ومرورًا بالمنازل والشاشات الذكية وانتهاءً بالسيارات ذاتية القيادة. كل شيء من حولنا لم نكن لنراه بهذا الشكل لولا البرمجة، فأنا على سبيل المثال أكتب وأعدل هذا الموضوع من خلال برنامج بُرمج عبر لغة برمجة وأنت تقرأه على الإنترنت باستخدام برنامج (المتصفح) وربما رأيته على مواقع التواصل الاجتماعي وكُلها برامج أيضًا. ومما لا شك فيه أن تعلم البرمجة أصبح ضرورة مُلحة في هذا العصر بل إن تعلمك لها سيزيد من فرصك بشكل كبير في الحصول على عمل. ولقد تحدثنا في مقالٍ سابق عن كيفية تعلم البرمجة والدخول إلى هذا المجال من أوسع أبوابه (إن كنت حديث عهدٍ في البرمجة أو تخطط للبدء بها، فننصحك بقراءته قبل إكمال هذا المقال) وسوف نتحدث في هذا المقال عن أكثر المهارات صعوبةً في احتراف البرمجة ألا وهي «حل المشاكل». حل المشكلات وارتباطها بالبرمجة لطالما وقفنا حائرين أمام مشكلةٍ ما ونسأل أنفسنا ماهي الطريقة الصحيحة للخروج من هذا المأزق؟ هل ستكون طريقة الحلّ التي أتبعها مشابهة للطريقة التي يتبعها المبرمجين المحترفين؟ كيف أستطيع أن أفكر مثلما يفكر المبرمجين المحترفين؟ في البداية وقبل الخوض في التفاصيل دعنا نُعرف البرمجة بحد ذاتها ولنذهب للمقال السابق ونلقي نظرة سريعة عليها: نلاحظ أن التعريف السابق صحيح ولكنه مُقْتضَب ولا يقدم لنا المعنى الكامل والدقيق للبرمجة لذا لابدّ لنا من تعريفٍ أكثر دقة وموضوعية يساعدنا في فهم هذا المقال. يعرف موقع HackerRank في تقريره عن مهارات المطورين بعام 2018: نستنتج مما سبق أن البرمجة تعتمد اعتمادًا أساسيًا على التفكير المنطقي والرياضي والقدرة على حلّ المشاكل وإن لغات البرمجة ما هي إلا وسيلة للتخاطب مع الحاسوب. إذن الأمر كله يتعلق بتطوير قدرتك في حلّ المشاكل وجعل هذه العملية سهلة وسلسة بنفس الوقت لذا فاسمح لي بأن آخذك معي برحلة صغيرة في هذا المقال نتعرف فيها على أهمية حلّ المشاكل وضرورتها في مشوارك البرمجي. دورة علوم الحاسوب دورة تدريبية متكاملة تضعك على بوابة الاحتراف في تعلم أساسيات البرمجة وعلوم الحاسوب اشترك الآن لماذا حلّ المشاكل مهم؟ تأتي أهمية حل المشاكل من كونها من أكثر المهارات المطلوبة عالميًا فوفقًا لتقرير أصدره موقع HackerRank جاء فيه: احتلت مهارة حلّ المشاكلالمرتبة الأولى بنسبة 94.9% لأكثر مهارة مطلوبة لأصحاب الشركات سواء الصغيرة منها أو الكبيرة. وعلى الصعيد العملي، إن عملية بناء أي شيء من الصفر سواء أكانت آلة معينة أو أي مُنتج جديد ستواجه الكثير من المشاكل في البداية، فعلى سبيل المثال بناء خدمات أمازون السحابية (AWS)، والّتي جاءت حلًا لمشكلة النفقات العالية للبنية التحتية لتجارتها الإلكترونية بالإضافة إلى الوقت الطويل الّتي تحتاجه في عملية البناء والذي شكل تحدي كبير في إمكانية توسع الشركة، إلى أن جاء آندي جاسي كبير مستشاري جيف بيزوس في ذلك الوقت والذي استطاع إيجاد حلّ لهذه المشكلة ولم يتوقف عند ذلك الحد وإنما أختار تحويل هذا الحلّ إلى خط أعمال جديد تحت أسم خدمات أمازون السحابية والّتي بلغت عائداتها السنوية لعام 2018 قيمة تناهز 25 مليار دولار. نجد من التجربة السابقة أنه حصلت مشكلة فأُوجِد لها حلًا وأثناء خلق حل للمشكلة ظهرت الكثير من المشاكل الأخرى لتقع في سلسلة من المشاكل لا تنتهي. وبعد حلّ جميع المشاكل سيصبح المنتج جاهزًا للعمل، أي أن دورة حياة أي منتج سواءً أكان برمجيًا أو ماديًا ستحتوي على المشاكل، وبناءً على ذلك تكون مهارة حل المشاكل اللَبِنَة الأساسية في بناء مشوارك البرمجي، والّتي يجب علينا الإهتمام بها عند الإقدام على تعلّم البرمجة. تكون طريقة تعاملنا مع المشاكل غير منظمة وعشوائية في أغلب الأحيان، فمعظمُ المبرمجين الجُدد يَبْدَؤُونَ بحلّ أي مشكلة تُواجهُهُم بالطريقة التالية: جرّب أي حلّ للمشكلة. إذا لم ينجح الحلّ الأول حاول أن تجرّب أي حلّ آخر. إذا لم يفلح الحلّ كرر الخطوة الثانية إلى أن تصل إلى الحلّ. لا يحصل هذا الأمر مع المبرمجين فقط، وإنما يحصل مع أي شخص يواجه مشكلة ولا يستعن على حلها بالتحليل والتفكير المنطقي. في الحقيقة، قد يحالفك الحظ أحيانًا ويتم حلّ المشكلة ولكن كن حذرًا! فهذه هي الطريقة الأسوأ لحلّ المشكلات بل وإنها ستهدر وقتك بشكل كبير لذا من الأفضل استخدام طريقة منظمة توصلنا للحلّ، واسمح لي بأن أشاركك طريقة شاملة توصلك إلى حلّ أي مشكلة قد تواجهك. بناء خطة حلّ شاملة إن خطة الحلّ الشاملة هي مجموعة من الخطوات الَّتي تتبعها لحلّ أي مشكلة تواجهك، حيث تكون هذه الخطة عامة وتخصص بحسب المشكلة، وهي ليست تعليمات ثابتة وإنما مبادئ ننطلق منها جميعًا، وكلٌ منا يطبقها بأسلوبه الخاص، وفي نهاية المطاف إذا انطلقنا جميعًا من نفس المبادئ، فسنصل حكمًا على نفس جودة الحلّ أيضًا. إن هذه الخطة مؤلفة من عدة أجزاء ويناقش كلّ جزء فيها جانبًا معينًا من المشكلة إلى أن نصل للحلّ، وهي على الشكل التالي: 1. فهم المشكلة إن فهمنا للمشكلة المطروحة هي الخطوة الأكثر صعوبة في طريقنا لحلّها، بل إن أكثر المشاكل تأتي صعوبتها من عدم تمكننا من فهم عميق لها. ولكن متى تعلم بأنك استطعت فهم المشكلة؟ إذا كنت قادرًا على شرحها بكلمات واضحة وسهلة بحيث يستطيع أي شخص فهمها عندها تكون بالفعل قد فهمت المشكلة. هل تذكر عندما كنت عالقًا في مشكلة وبدأت بشرحها فوجدت أخطاء منطقية كثيرة لم تكن قد انتبهت لها من قبل؟ أغلب المبرمجين قد مروا بهذه الحالة من قبل، لذلك من الأفضل دومًا أن تكتب المشكلة الَّتي تواجهك باستخدام مخططات الحالة (State diagram وهي نوع من المخططات الَّتي تستخدم لتوصيف سلوك نظام ما) أو بأن تخبر شخص آخر عن المشكلة سواءً أكان زميل لك في الشركة أو عضو في الفريق أو أي شخص أخر. شاع بين المبرمجين استخدام البطة المطاطية في شرح أجزاء المشكلة لها لفهمها وإيجاد حل لها. يعد أسلوب البطة المطاطية في تنقيح الأخطاء من أكثر الأساليب سهولة وبساطة، إذ يضع المبرمج بطةً بجوار حاسوبه، وعند مواجهته لأي مشكلة يشرع في شرح الشيفرة البرمجية للبطة وما هي النتيجة المرتقبة من الشيفرة وموازنتها مع النتيجة الحالية وغالبًا ما يجد الخطأ أثناء شرحه. وخلاصة لهذه الخطوة أنقل إليك مقولة ألبرت أينشتاين: 2. تحليل المشكلة إن تقسيم المشكلة يلعب دورًا مهمًا في طريقك لإيجاد الحلّ، لذا حاول أن تقسّمها إلى أجزاء صغيرة ثمّ حُلّ كلَّ جزء منها على حدة ويُنصح في البداية بحلّ أسهل جزء منها ومن ثمّ الأصعب فالأصعب وهكذا إلى أن يتمّ حلّ جميع أجزائها، وبعدها إجمع هذه الأجزاء مع بعضها للحصول على الحلّ النهائي للمشكلة الأصلية (الكبيرة). منذ فترة كنا بصدد برمجة إضافة لمتصفح غوغل كروم بهدف تسهيل مهمة لموقع ما، فقررنا تجربة هذه الطريقة وبدأنا بتقسّيم المهمة على أجزاء ثم عملنا على حلّ كل جزء منها، وبعدها جمعنا هذه الأجزاء مع بعضها بعضًا لبناء الإضافة، وبالفعل كان العمل بهذه الطريقة سهلًا جدًا ومريحًا كما لم نعهده من قبل. في بعض الأحيان عندما تواجه شيفرات برمجية كتبها مبرمجون لا يتبعون مبادئ SOLID (وهي مجموعة من العادات والمبادئ الَّتي يتبعها المبرمجون للحصول على شيفرة برمجية قابلة للصيانة وسهلة التعديل والتكيف مع متطلبات المشروع المتغيرة)، وغالبًا ما تكون شيفرات أولئك المبرمجين غيرمفهمومة ومتشابكة ويطلق عليها اسم Spaghetti code (تكون هذه الشيفرات ذات بنية معقدة ومتشابكة وصعبة القراءة وغير مرتبة أي تكون مثل المعكرونة ملتوية ومتشابكة) ولنفرض أنه طُلبَ منك تعديل هذه الشيفرة أو إضافة وظائف جديدة إليها،عندها حتمًا ستواجه العديد من المشاكل في عملية تقسيم المشكلة ومعرفة أي جزء من الشيفرة البرمجية المُسبب للخطأ لذا كان الحصول على شيفرات برمجية مرنة وقابلة للتعديل هي الأرضية المشتركة بين العديد من تقنيات تبسيط الشيفرات البرمجية مثل مبادئ SOLID أو مبدأ MVC والذي ينص على تقسيم الوحدات من حيث طبيعة مهمتها إلى ثلاثة أقسام (Model-View-Controller) والبرمجة كائنية التوجه OOP (Object-Oriented Programming)، إذ أن جميعهم يهدفون إلى فصل الأكواد إلى أقسام ليسهل تطويرها وتنقيحها وصيانتها. 3. إعداد خطة للحل بعد أن فهمت وحللت المشكلة، يأتي دور وضع الخطة المناسبة للحلّ بحيث تغطي كافة الجوانب والتفاصيل للمشكلة، ولا تشرع في الحلّ من دون خطة (على أمل أن تجد الحلّ بطريقة ما) لأن المفتاح الرئيسي للوصول للحلّ هي الخطة الواضحة والمنظمة والتي تضمن وصولنا للحلّ النهائي. أعط لنفسك وقتًا لتحلّيل المشكلة وربط المعلومات المدخلة إلى البرنامج ونوعها والمعلومات الَّتي ستظهر كخرج للبرنامج وفهم سياقها وللحصول على خطة جيدة يجب عليك الإجابة على السؤال التالي: إذا أُعطي للبرنامج الدخل س، ما هي الخطوات اللازمة للحصول على الخرج ع؟ إن إجابتك على هذا السؤال سوف يحدد ماهي الخطوات اللازمة لحلّ المشكلة ومن ثَمَّ تقوم بترتيبها في خطة واضحة ومنظمة من أجل الحصول على الخرج الذي تريده. 4. مواجهة حالة السكتة البرمجية ماذا لو فرضنا أنك لم تستطع حلّ أي جزء من المشكلة، ولا حتى الأجزاء السهلة منها (وهذا قد يحدث في بعض الأحيان)، إن كثير من المبرمجين يقعون في هذه الحالة فلا يستطيعون أن يُحرزوا أي تقدم يذكر في تطوير الشيفرة البرمجية وهذا أشبه ما يمكن بالسكتة الدماغية (حيث لا يستطيع المريض القيام بأي حركة)، في الحقيقة إن هذه الحالة طبيعية جدًا ومعظمنا قد تعرض لها في بداية مشواره والاختلاف الوحيد هو أن المبرمج المحترف لديه فضول أكثر حول المشكلة ومعرفة سبب حدوثها بدلًا من أن يكون منزعجًا أو غاضبًا منها. وفي هذه الحالة هنالك حلّين يمكنك تجربتهما للخروج من هذا المأزق: 1-4. تنقيح الأخطاء (Debug) ليس المقصود هنا الأخطاء الكتابية في صياغة اللغة (Syntax errors) مثل نسيان فاصلة منقوطة أو أي خطأ في استخدام المتغيرات أو الدوال أو ما شابه ذلك من أخطاء والَّتي تقوم باكتشافها أي بيئة تطوير متكاملة (IDE وهي عبارة عن محرر شيفرة برمجية مدمج مع نظام ذكي لإكمال الكود ومصحح أخطاء). وبالطبع ليست أيضًا الأخطاء الّتي تظهر أثناء التنفيذ (Runtime Errors) والّتي تكون عادة نتيجة لفشلٍ في فتح ملف ما أو محاولة القسمة على صفر أو مثل هذه الأخطاء، وإنما المقصود هنا هو أخطاء المنطق البرمجي (Logic errors) الّتي ينفِّذ فيها البرنامج أمرًا غير الَّذي بُرمج من أجله، لذا من الأفضل أن تحاول أن تفحص الشيفرة البرمجية سطرًا سطرًا لعلك تجد هذا الخطأ، أوإذا كنت تعمل على لغاتٍ مثل (C++, C) والّتي تدعم استخدام المُنقِّح Debugger (الذي يراقب عمل البرنامج ويتحكم في تنفيذه بطريقة تستطيع فيها إيقاف تنفيذ البرنامج أو حتى تغييره في أي موضع من الشيفرة وذلك من خلال مجموعة من الأدوات الّتي يقدمها المُنقِّح، مثل: GNU Debugger) فيمكنك استخدامه لإيجاد الخطأ ومن ثَمّ إصلاحه. ملاحظة : من المنهجيات البرمجية الجيدة هي كتابة تعليقات توضيحية قبل كلّ دالة (Function) أو صنف (Class) برمجي وخُصوصًا تلك الأجزاء المعقدة منها لأن ذلك سوف يساعدك كثيرًا في عملية مراجعة الشيفرة البرمجية وتنقيحها. 2-4. مراجعة وتقييم الحلّ في كثير من الأحيان عند مواجهتنا للمشاكل وخصوصًا للكبيرة منها قد نضيع في التفاصيل الصغيرة للمشكلة الَّتي نواجهها وننسى المشكلة الرئيسية ولذلك من الأفضل دومًا أن نسأل أنفسنا هل هذه هي الطريقة الأفضل للحلّ؟ هل هنالك حلًّا عموميًا أفضل من الموجود؟ هل الشيفرة البرمجية أمثلية؟ هل الحلّ يخرق معايير الجودة المطلوبة؟ ارجع خطوة إلى الوراء وحاول أن تراها من منظور مختلف، وحتمًا ستلاحظ العديد من المشاكل التي غفلت عنها أثناء انشغالك بالتفاصيل الصغيرة. ملاحظة : هنالك طريقة أخرى يتبعها بعض المبرمجين لإعادة تقييم الحلّ وهي حذف الشيفرة البرمجية الخاطئة بالكامل وإعادة كتابتها من جديد فكثير ما يأخذ ذلك وقتًا أقل من تتبع المشكلة ذاتها وحلها. 5. البحث عن حلول عبر الإنترنت إن أغلب المشاريع البرمجية تكون متشابهة بكثيرٍ من الوظائف والخصائص، ونادرًا ما نرى مشروع ذو أفكارٍ جديدة بالكامل لذا فإن أي مشكلة برمجية تواجهها قد واجهها عدد كبير من المبرمجين من قبلك وأوجدوا لها حلولًا وشاركوها مع غيرهم، وكل ما عليك فعله هو أن تتعلم كيف تبحث عن المشكلة. وبالطبع صديقنا stackoverflow والذي يعد من أشهر منصات مشاركة الحلول البرمجية الّذي يقدم لك الحلّ الَّذي أجمع عليه أغلب، المبرمجين، ويوجد العديد من المنصات الأخرى المشابهة مثل AskUbuntu وهو النسخة العربية من موقع stackoverflow والكثير غيرهم، وحتى لو أنك قد حللت المشكلة أنصحك بتصفح الحلول الموجودة لأنك سوف تتعلم طرقًا أُخرى لحلّها قد تكون أسهل وأفضل بكثير من الحلّ الَّذي وصلت اليه. إلى الآن نكون قد ناقشنا الخطوة الأولى من الطريقة الشاملة لحلّ المشاكل والآن لننتقل إلى الخطوة الثانية. التدرب على هذه الخطة إن أي مهارة جديدة تحتاج إلى تدريبٍ مُمنهجٍ ومستمرٍ حتى تتقنها. لذا لا بدّ لك من وضع خطة واضحة للتدريب وتخصيص زمن محدد وثابت تقضيه في صقل تلك المهارة.يمكنك البدء مثلًا بتخصيص ساعة للتدريب يوميًا لمدة شهر كامل، وبعدها توازن نفسك مع ما كنت عليه في السابق من سرعةٍ في تحديد المشكلة وجودةٍ الحل المطروح والوقت الإجمالي لحل المشكلة وبكل تأكيد ستلاحظ تحسنًا واضحًا في أدائك. وقد تساءل نفسك كيف يمكنني التدرب على حلّ المشاكل؟ في الحقيقة هنالك طريقة مفيدة جدًا أنصحك بها لتكوين قاعدة صلبة تنطلق منها، وهي على الشكل التالي. 1. التدرب على المسائل البرمجية تعد المسابقات البرمجية سواءً على مستوى الجامعة أو على مستوى القطر أو حتى على مستوى العالم مثل مسابقة ACM ICPC من أفضل الفرص للتدريب في مجال البرمجة وصقل هذه المهارة بل وتعتبر دفعة كبيرة لك في رحلتك كمبرمج ولمستقبلك المهني أيضًا، إذ تبادر العديد من الشركات العالمية لِضمّ أولئك المنافسين المتميزين إليها بعد أن أثبتوا بالفعل أنهم النخبة في مجالهم. فإن كنت تخطط للانضمام إلى هذه المسابقة فلا تتردد وبادر بالتحضير لها من الآن. ويقدم لنا الكاتب Steven Halim في كتابه Competitive Programming بعض الفوائد الَّتي نجنيها من التدرب على حلّ المشاكل البرمجية في المسابقات. السرعة في كتابة الشيفرة البرمجية كتابة الشيفرة البرمجية بسرعة في المسابقات يوفر لك الكثير من الوقت للتفكير في بقية المشاكل واختبارها لذلك نرى أغلب المبرمجين يتدربون على سرعة الكتابة على لوحة المفاتيح لينصب تركيزهم على حلّ المشكلة فقط، ولذلك من المنطقي أن تفضل الشركات الموظفين الَّذين سَبق إن شاركوا في المسابقات البرمجية لمعرفتها بسرعة إنتاجيتهم وقدرتهم على تطوير وإيصال منتج برمجي بسرعة عالية بِالْمُوازنة مع أقرانهم ممن لم يشاركوا في المسابقات. العصف الذهني وحصر الخوارزميات الممكنة لا بدّ من تحسين قدرتك على تحديد نوع المسألة بسرعة وأن تمتلك المعرفة الكافية بالخوارزميات لتتمكن من تطبيقها مباشرة على المسألة المطروحة، لذلك ينبغي على المبرمج الَّذي ينوي الانضمام إلى المسابقة أن يتمكن من جميع الخوارزميات الشائعة في المسائل البرمجية. العمل الجماعي وروح الفريق تعتمد المسابقة البرمجية على ترتيب الفرق بحسب عدد المسائل الَّتي أجابوا عليها، والوقت الَّذي استهلكوه لحلّ كُلَ مشكلة، وعدد الحلول الخاطئة الَّتي أُرسلت للحكم. وانطلاقًا من ذلك تكون فوائد العمل كفريق واحد متعددة منها إمكانية توزيع المسائل على كلّ عضو في الفريق بحيث يزداد العدد الكلي للمسائل المحلولة بتوزيعها بين بعضهم بعضًا، ومناقشة حلّ كلّ مسألة وبذلك ينقص عدد الحلول الخاطئة الَّتي قد تُرسل للحكم، وحُكمًا سينقص الوقت المستغرق لحلّ المسائل. وعلى الصعيد العملي أيضًا نجد أن طبيعة عمل المبرمجين سواءً في الشركات الصغيرة أو الكبيرة، يُطلب منهم العمل كفريق واحد، فعلى سبيل المثال منذ فترة طَلَبَ مني أحد العملاء بناء موقع ويب، وكنت عندها قد أنتهيت من بناء فريقي البرمجي. وبالفعل بدأنا العمل على الموقع و قسّمنا العمل على أجزاء فعكف كلُّ واحد منا على تطوير جزئه الخاص من الموقع، إذ استلمتُ جزء الواجهات الخلفية، وقام أحد أعضاء الفريق باستلام الواجهات الأمامية، والآخَر استلم بناء تطبيق للهواتف الّتي تعمل بنظام أندرويد وفي نهاية الأسبوع جمعنا كل الأجزاء مع بعضها بعضًا وأنهينا المشروع بوقتٍ قياسي لم نكن لنستطيع بلوغهُ لو أننا لم نعمل كفريقٍ واحد، ولذا فإن انضمامك إلى المسابقة البرمجية يجعلك تتدرب على العمل ضمن فريق قبل أن تواجهها في سوق العمل في حال أردت العمل مع أي شركة مستقبلًا. المرونة العصبية إن تدريبك المستمر على المسائل البرمجية سيؤدي إلى زيادة منطقة الحصين (المنطقة المسؤولة عن الذاكرة والتوجيهات) في الدماغ وهذا ما أثبتته الباحثة اليانور ماجواير من كلية لندن الجامعية عندما أجرت دراسة على سائقي الأجرة في مدينة لندن فقاموا بإجراء فحص بالرنين المغناطيسي الوظيفي fMRI لأدمغة السائقين الذين قضوا قرابة عامين من التدريب في سبيل تعلم كيفية التنقل في منعطفات المدينة وذلك من أجل الحصول على رخصة القيادة ومقارنتها بصور لأدمغة رجال أصحاء من نفس العمر ولا يعملون كسائقي أجرة فتبين أن منطقة الحصين أصبحت أكبر لدى السائقين، كما لاحظوا أنه كلما أمضى سائق الأجرة فترة أطول في التدريب، زاد حجم الحصين، وذلك استجابة إلى الخبرة الّتي يكتسبها السائق. والآن تخيل معي ما سيحدث في حال تدربك على حل المشاكل لمدة عام أو أكثر، سيصبح حلّك للمشاكل البرمجية عادة سهلة ومسلية خلاف ما كانت عليه من صعوبة وتعقيد. يوجد العديد من المواقع الَّتي تقدم المسائل البرمجية مثل HackerRank أو موقع TopCoder والكثير غيرهم. 2. التدرب باستخدام الألعاب نعم أنت لم تخطئ القراءة إنها الألعاب! تعد الألعاب أداة قوية جدًا لتنمية المهارات العقلية والقدرات الدماغية على التفكير والتذكر وربط المعلومات ببعضها بعضًا، حيث أن غالبية الألعاب تحتاج لتجميع المعلومات وتنظيمها بحيث تستطيع الاستفادة منها وجعلها مفتاحًا للحلّ، بل إن الركيزة الأساسية الَّتي تشترك بها كافة الألعاب هي حلّ المشاكل! بالتأكيد نحن لا نقصد هنا ألعاب الفيديو فقط وإنما ألعاب الذكاء أيضًا مثل : لعبة الشطرنج، والألغاز الرياضية، ولعبة سودوكو، ولعبة Go، والكثير من الألعاب الَّتي تندرج تحت هذا السياق تعد ألعاب مفيدة. فعند محاولتك حلّ الألغاز في Saw أنت فعليًا تقوم بحلّ المشاكل (ولكن بإطار مختلف). وتساعدنا أيضًا في انشاء سيناريوهات للحلّ وهذه المهارة مفيدة في عالم البرمجة في حال تعثرت في أسلوب حلّ معين فتغير سيناريو الحلّ وتبدأ من جديد. في الحقيقة إن الشيء المشترك بين جميع الناس الناجحين هي اكتسابهم لعادات يومية لحلّ المشاكل الصغيرة على سبيل المثال بيتر تيل (أحد مؤسسي شركة باي بال والمصنف كرابع أغنى شخص على مستوى العالم لعام 2014 بميزانية تفوق $2.2 بليون دولار) صرح بشكل رسمي أنه يلعب الشطرنج يوميًا بل وشارك في بطولات الشطرنج مرات عديدة، وإيلون ماسك (رائد الأعمال والرئيس التنفيذي لعدة شركات مثل: سبيس إكس لتصنيع مركبات الفضاء وتسلا لصناعة السيارات الكهربائية وغيرها)أكد بأنه يلعب ألعاب الفيديو والكثير غيرهم كرسوا جزءًا من وقتهم اليومي لتنمية مهارة حلّ المشاكل. ولكن يجب أن تكون حذرًا في إدارة وقت اللعب ويُفضل أن تختار الأوقات المناسبة للعب ووضع مدة زمنية مخصصة لها، وذلك لأن الهدف ليس إضاعة الوقت والتسلية فقط وإنما التدرب على حل المشاكل ولكي لا ينتهِ بك المطاف إلى قضاء اليوم بأكمله على الألعاب. الخلاصة اعتقد أنك قد كونت فكرة جيدة عن أهمية حلّ المشاكل وضرورتها في مشوارك البرمجي وكيفية بناء خطة حلّ شاملة انطلاقًا من فهم المشكلة وتحليلها ومرورًا بإعداد خطة مخصصة لكلّ مشكلة وتقسيم المشكلة إلى أجزاء ليسهل حلّها ومواجهة حالة السكتة البرمجية وكيفية التغلب عليها من خلال تنقيح الأخطاء أو مراجعة وتقييم الحلّ، وأهمية البحث عن حلول على الإنترنت لنفس المشكلة وانتهاءً بالأسلوب الصحيح للتدرب على حل المشاكل وما هي أفضل الوسائل لتحقيق ذلك سواءً بالانضمام إلى المسابقات البرمجية أو بالتدرب بإستخدام الألعاب. وختامًا، لا تتوقع أن تصبح مبرمجًا محترفًا في غضون أسبوعٍ أو شهر واحد فهذا ضرب من الخيال بل ستحتاج لحلّ الكثير من المشاكل لبناء قاعدة معرفية صلبة تمكنك من مواجهة أي مشكلة مهما كانت صعوبتها وعندها بالتأكيد سوف تستحق لقب مبرمج محترف. اقرأ أيضًا تعلم لغة PHP تعلم لغة بايثون
  2. يعتبر Laravel أشهر إطار ويب بلغة PHP، ولقد شهد إصداره الخامس العديد من التغييرات وتمت إضافة الكثير من المميزات الجديدة، ولذلك لتعلم أساسيات هذا الإطار سوف نقوم بإنشاء تطبيق Todo List، وسوف نتعلم من خلال هذا التطبيق الكثير من المفاهيم والطرق البرمجية حتى نتمكن من التعامل والتوسع مع إطار Laravel 5. ملاحظة: هذا الدرس طويل للغاية لذلك سوف يتم تقسيمه إلى أجزاء أصغر، ويمكنك الحصول على الكود المصدري لكل جزء من GitHub. في هذا الدرس سوف نتحدث عن تثبيت Laravel 5 وإعداده وعن بعض التقنيات الجديد كالتهجير migration والبذر seeding. قبل أن نبدأ أهم المصطلحات التي ستصادفنا في هذه الدروس: التهجير migration: نظام لإدارة قواعد البيانات، فهو يسمح للفريق بتعديل مخطط قواعد البيانات لجميع الأعضاء. البذر seeding: يقصد بها إدخال البيانات الأولية لتجربة التطبيق، أي أنه سيقوم بإدخال بيانات لمساعدتك على التطوير واكتشاف الأخطاء. هدف المشروع سوف يتكون تطبيقنا من مشروع أو أكثر، كل واحد لديه قائمة مهام خاصة به، وسوف تتمكن من إنشاء وعرض وتعديل وحذف المهام والمشاريع. في هذا الدرس سوف نقوم بـ: إعداد وتنصيب Laravel. تركيب حزم إضافية لتسهيل عملية التطوير. استخدام التهجير migration والبذر seeds. تعلم كيفية استخدام متحكِّمات الموارد resourceful controllers. تعلم كيفية استخدام العروض views. (بما في ذلك نظام blade ومخططات المحتوى) التعامل مع علاقات النماذج. التثبيت إن عملية تثبيت Laravel سهلة للغاية بفضل Composer، لذلك سوف نستخدمه في تثبيته. استخدام Composer والآن نقوم بتشغيل مثبت Laravel (سوف تحتاج إلى عمل هذا لمرة واحدة فقط): composer global require "laravel/installer=~1.1" والآن نقوم بعمل مشروعنا: laravel new l5todo الإعداد يستخدم Laravel 5 حزمة تدعى DotEnv تقوم بتخزين المعلومات الحساسة في ملفات ذات صيغة .env والتي يتم تحميلها كمتغيرات PHP عند التشغيل. قد يبدوا لك هذا معقدا لكن بكلمات أخرى، هذا يعني أن فقط الإعدادات الحسّاسة credentials يتم تخزينها في هذه الملفات وأما بقية إعداداتك فيتم تخزينها في ملفات الإعداد العادية. قاعدة البيانات سوف نحتاج إلى قاعدة بيانات، لذلك سنقوم بعمل واحدة ثم سنقوم بنسخ .env.example إلى .env وتحديث البيانات على الشكل التالي: DB_HOST=localhost DB_DATABASE=homestead DB_USERNAME=homestead DB_PASSWORD=secret في النهاية إذا لم تكن تستخدم MySQL فقُم بفتح config/database.php/ ثم قٌم بتغيير السطر الافتراضي: 'default' => 'mysql', تذكر أن تضيف ملفات بيئة عملك إلى .gitignore عن طريق إضافة سطر .env. خطواتنا الأولى كما ذكرنا في الأعلى، سوف يتكون تطبيق قائمة المهام على مشروع أو أكثر كل واحد مع قائمة المهام الخاصة به، ولذلك سوف نحتاج إلى مشروع، نماذج مهام، متحكِّمات، عروض views، التهجير migration، البذور seeds والطرق routes، ولقد قمنا بشرح بعض هذه المصطلحات في بداية الدرس. لنبدأ بالتعامل معهم كل واحد على حدة. التهجير migration أولا نريد أن نرى مخطط جدولنا، سوف يبدو مثل هذا: Projects +------------+------------------+------+-----+ | Field | Type | Null | Key | +------------+------------------+------+-----+ | id | int(10) unsigned | NO | PRI | | name | varchar(255) | NO | | | slug | varchar(255) | NO | | | created_at | timestamp | NO | | | updated_at | timestamp | NO | | +------------+------------------+------+-----+ Tasks +-------------+------------------+------+-----+ | Field | Type | Null | Key | +-------------+------------------+------+-----+ | id | int(10) unsigned | NO | PRI | | project_id | int(10) unsigned | NO | MUL | | name | varchar(255) | NO | | | slug | varchar(255) | NO | | | completed | tinyint(1) | NO | | | description | text | NO | | | created_at | timestamp | NO | | | updated_at | timestamp | NO | | +-------------+------------------+------+-----+ ثم سوف نقوم بعمل التهجير migration: php artisan make:migration create_projects_and_tasks_tables --create="projects" سوف نقوم بعمل كِلا الجدولين بتهجير migration واحد حتى نتمكن من حذفها بترتيب عكسي لتجنب سلامة خرق القيد integrity constraint violation، قُم بفتح: /database/migrations/createprojectsandtasks_tables.php وقُم بتحديث المعلومات حسب الآتي: <?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateProjectsAndTasksTables extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('projects', function(Blueprint $table) { $table->increments('id'); $table->string('name')->default(''); $table->string('slug')->default(''); $table->timestamps(); }); Schema::create('tasks', function(Blueprint $table) { $table->increments('id'); $table->integer('project_id')->unsigned()->default(0); $table->foreign('project_id')->references('id')->on('projects')->onDelete('cascade'); $table->string('name')->default(''); $table->string('slug')->default(''); $table->boolean('completed')->default(false); $table->text('description')->default(''); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::drop('tasks'); Schema::drop('projects'); } } قم بتنفيذ الهجرة migration: php artisan migrate إذا قمت الآن بمراجعة قاعدة البيانات، يجب أن تكون الجداول مكتملة. البذور Seeds سوف نحتاج إلى بذر المشاريع/المهام ليكون لديك شيء للعمل معه عندما نصل إلى المتصفح، لذلك قم بإنشاء: /database/seeds/ProjectsTableSeeder.php و TasksTableSeeder.php كالتالي: // /database/migrations/seeds/ProjectsTableSeeder.php <?php use Illuminate\Database\Seeder; class ProjectsTableSeeder extends Seeder { public function run() { // Uncomment the below to wipe the table clean before populating DB::table('projects')->delete(); $projects = array( ['id' => 1, 'name' => 'Project 1', 'slug' => 'project-1', 'created_at' => new DateTime, 'updated_at' => new DateTime], ['id' => 2, 'name' => 'Project 2', 'slug' => 'project-2', 'created_at' => new DateTime, 'updated_at' => new DateTime], ['id' => 3, 'name' => 'Project 3', 'slug' => 'project-3', 'created_at' => new DateTime, 'updated_at' => new DateTime], ); // Uncomment the below to run the seeder DB::table('projects')->insert($projects); } } // /database/migrations/seeds/TasksTableSeeder.php <?php use Illuminate\Database\Seeder; class TasksTableSeeder extends Seeder { public function run() { // Uncomment the below to wipe the table clean before populating DB::table('tasks')->delete(); $tasks = array( ['id' => 1, 'name' => 'Task 1', 'slug' => 'task-1', 'project_id' => 1, 'completed' => false, 'description' => 'My first task', 'created_at' => new DateTime, 'updated_at' => new DateTime], ['id' => 2, 'name' => 'Task 2', 'slug' => 'task-2', 'project_id' => 1, 'completed' => false, 'description' => 'My first task', 'created_at' => new DateTime, 'updated_at' => new DateTime], ['id' => 3, 'name' => 'Task 3', 'slug' => 'task-3', 'project_id' => 1, 'completed' => false, 'description' => 'My first task', 'created_at' => new DateTime, 'updated_at' => new DateTime], ['id' => 4, 'name' => 'Task 4', 'slug' => 'task-4', 'project_id' => 1, 'completed' => true, 'description' => 'My second task', 'created_at' => new DateTime, 'updated_at' => new DateTime], ['id' => 5, 'name' => 'Task 5', 'slug' => 'task-5', 'project_id' => 1, 'completed' => true, 'description' => 'My third task', 'created_at' => new DateTime, 'updated_at' => new DateTime], ['id' => 6, 'name' => 'Task 6', 'slug' => 'task-6', 'project_id' => 2, 'completed' => true, 'description' => 'My fourth task', 'created_at' => new DateTime, 'updated_at' => new DateTime], ['id' => 7, 'name' => 'Task 7', 'slug' => 'task-7', 'project_id' => 2, 'completed' => false, 'description' => 'My fifth task', 'created_at' => new DateTime, 'updated_at' => new DateTime], ); //// Uncomment the below to run the seeder DB::table('tasks')->insert($tasks); } } ولا تنسَ إضافة أصناف البذور(seed classes) إلى database/seeds/DatabaseSeeder.php/ : use Illuminate\Database\Seeder; use Illuminate\Database\Eloquent\Model; class DatabaseSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { Model::unguard(); $this->call('ProjectsTableSeeder'); $this->call('TasksTableSeeder'); } } الآن نقوم بالبذر: composer dump-autoload ثم: php artisan db:seed # أو php artisan migrate:refresh --seed يجب الآن أن تكون قاعدة البيانات قد تم بذرها. mysql> select * from projects; +----+-----------+-----------+---------------------+---------------------+ | id | name | slug | created_at | updated_at | +----+-----------+-----------+---------------------+---------------------+ | 1 | Project 1 | project-1 | 2015-02-05 01:25:43 | 2015-02-05 01:25:43 | | 2 | Project 2 | project-2 | 2015-02-05 01:25:43 | 2015-02-05 01:25:43 | | 3 | Project 3 | project-3 | 2015-02-05 01:25:43 | 2015-02-05 01:25:43 | +----+-----------+-----------+---------------------+---------------------+ mysql> select * from tasks; +----+------------+--------+--------+-----------+----------------+---------------------+---------------+ | id | project_id | name | slug | completed | description | created_at | updated_at | +----+------------+--------+--------+-----------+----------------+---------------------+---------------+ | 1 | 1 | Task 1 | task-1 | 0 | My first task | 2015-02-05 01:25:43 | 2015-02-05 01:25:43 | 2 | 1 | Task 2 | task-2 | 0 | My first task | 2015-02-05 01:25:43 | 2015-02-05 01:25:43 | 3 | 1 | Task 3 | task-3 | 0 | My first task | 2015-02-05 01:25:43 | 2015-02-05 01:25:43 | 4 | 1 | Task 4 | task-4 | 1 | My second task | 2015-02-05 01:25:43 | 2015-02-05 01:25:43 | 5 | 1 | Task 5 | task-5 | 1 | My third task | 2015-02-05 01:25:43 | 2015-02-05 01:25:43 | 6 | 2 | Task 6 | task-6 | 1 | My fourth task | 2015-02-05 01:25:43 | 2015-02-05 01:25:43 | 7 | 2 | Task 7 | task-7 | 0 | My fifth task | 2015-02-05 01:25:43 | 2015-02-05 01:25:43 +----+------------+--------+--------+-----------+----------------+---------------------+---------------+ النماذج Models للعمل مع جداول المشاريع والمهام نحتاج إلى نماذج مماثلة، سوف نقوم بإنشائها الآن: php artisan make:model Project php artisan make:model Task كان هذا سهلا استخدام Artisan – Tinker الآن لدينا المعلومات في قاعدة البيانات وسيكون من الرائع أن نتعلم حول واحدة من أهم ميزات artisan وهي Tinker الذي يوفر لك سطر أوامر للتعامل مع Laravel، وكمثال، لنستخدمه في معرفة عدد المشاريع في قاعدة البيانات الحالية: $ php artisan tinker > App\Project::count(); 3 > App\Task::count(); 7 وكما ترى يمكن أن يكون Tinker مفيدا للغاية، لذلك سوف نستخدمه عدة مرات في هذه السّلسلة. المُتحكِّمات Controllers الآن وصلنا إلى النقطة التي سوف نبدأ بعرض ما صنعناه على المتصفح، وسوف نحتاج إلى إنشاء بعض المتحكِّمات Controllers والطرق Routes. سنبدأ أولا بالمتحكِّمات: php artisan make:controller ProjectsController php artisan make:controller TasksController الموارد المُضمّنة نبدأ بإضافة موارد Project و Task إلى app/Http/routes.php/ : Route::get('/', 'WelcomeController@index'); //Route::get('home', 'HomeController@index'); // //Route::controllers([ // 'auth' => 'Auth\AuthController', // 'password' => 'Auth\PasswordController', //]); Route::resource('projects', 'ProjectsController');Route::resource('tasks', 'TasksController'); والآن دعونا نرى إحدى مميزات artisan والمُتمثّلة في route:list، في سطر الأوامر أكتب التالي: php artisan route:list لتكون النّتيجة +--------+----------+--------------------------+------------------+-------------------------------------------------+------------+ | Domain | Method | URI | Name | Action | Middleware | +--------+----------+--------------------------+------------------+-------------------------------------------------+------------+ | | GET|HEAD | / | | App\Http\Controllers\WelcomeController@index | | | | GET|HEAD | projects | projects.index | App\Http\Controllers\ProjectsController@index | | | | GET|HEAD | projects/create | projects.create | App\Http\Controllers\ProjectsController@create | | | | POST | projects | projects.store | App\Http\Controllers\ProjectsController@store | | | | GET|HEAD | projects/{projects} | projects.show | App\Http\Controllers\ProjectsController@show | | | | GET|HEAD | projects/{projects}/edit | projects.edit | App\Http\Controllers\ProjectsController@edit | | | | PUT | projects/{projects} | projects.update | App\Http\Controllers\ProjectsController@update | | | | PATCH | projects/{projects} | | App\Http\Controllers\ProjectsController@update | | | | DELETE | projects/{projects} | projects.destroy | App\Http\Controllers\ProjectsController@destroy | | | | GET|HEAD | tasks | tasks.index | App\Http\Controllers\TasksController@index | | | | GET|HEAD | tasks/create | tasks.create | App\Http\Controllers\TasksController@create | | | | POST | tasks | tasks.store | App\Http\Controllers\TasksController@store | | | | GET|HEAD | tasks/{tasks} | tasks.show | App\Http\Controllers\TasksController@show | | | | GET|HEAD | tasks/{tasks}/edit | tasks.edit | App\Http\Controllers\TasksController@edit | | | | PUT | tasks/{tasks} | tasks.update | App\Http\Controllers\TasksController@update | | | | PATCH | tasks/{tasks} | | App\Http\Controllers\TasksController@update | | | | DELETE | tasks/{tasks} | tasks.destroy | App\Http\Controllers\TasksController@destroy | | +--------+----------+--------------------------+------------------+-------------------------------------------------+------------+ ستلاحظ أن كل من projects و tasks هي روابط ذات مستوى عالي، وفي تطبيقنا، المهام تتبع المشاريع، لذلك سيكون من الأفضل للروابط أن تكون مُضمّنة مثل /projects/1/tasks/3 بدلا من /tasks/، وهذا يمكن فعله باستخدام شيء اسمه الموارد المُضمّنة nested resources، وكما هو الحال مع أغلب الأشياء في Laravel، التعديل المطلوب سريع وبسيط، فقط قٌم بفتح /app/Http/routes.php وقٌم بالتعديلات التالية: // Route::resource('tasks', 'TasksController'); Route::resource('projects.tasks', 'TasksController'); هذا كل شيء، قُم بكتابة php artisan route:list لنعلم ماذا لدينا لحد الآن: php artisan route:list +--------+----------+----------------------------------------+------------------------+-------------------------------------------------+------------+ | Domain | Method | URI | Name | Action | Middleware | +--------+----------+----------------------------------------+------------------------+-------------------------------------------------+------------+ | | GET|HEAD | / | | App\Http\Controllers\WelcomeController@index | | | | GET|HEAD | projects | projects.index | App\Http\Controllers\ProjectsController@index | | | | GET|HEAD | projects/create | projects.create | App\Http\Controllers\ProjectsController@create | | | | POST | projects | projects.store | App\Http\Controllers\ProjectsController@store | | | | GET|HEAD | projects/{projects} | projects.show | App\Http\Controllers\ProjectsController@show | | | | GET|HEAD | projects/{projects}/edit | projects.edit | App\Http\Controllers\ProjectsController@edit | | | | PUT | projects/{projects} | projects.update | App\Http\Controllers\ProjectsController@update | | | | PATCH | projects/{projects} | | App\Http\Controllers\ProjectsController@update | | | | DELETE | projects/{projects} | projects.destroy | App\Http\Controllers\ProjectsController@destroy | | | | GET|HEAD | projects/{projects}/tasks | projects.tasks.index | App\Http\Controllers\TasksController@index | | | | GET|HEAD | projects/{projects}/tasks/create | projects.tasks.create | App\Http\Controllers\TasksController@create | | | | POST | projects/{projects}/tasks | projects.tasks.store | App\Http\Controllers\TasksController@store | | | | GET|HEAD | projects/{projects}/tasks/{tasks} | projects.tasks.show | App\Http\Controllers\TasksController@show | | | | GET|HEAD | projects/{projects}/tasks/{tasks}/edit | projects.tasks.edit | App\Http\Controllers\TasksController@edit | | | | PUT | projects/{projects}/tasks/{tasks} | projects.tasks.update | App\Http\Controllers\TasksController@update | | | | PATCH | projects/{projects}/tasks/{tasks} | | App\Http\Controllers\TasksController@update | | | | DELETE | projects/{projects}/tasks/{tasks} | projects.tasks.destroy | App\Http\Controllers\TasksController@destroy | | +--------+----------+----------------------------------------+------------------------+-------------------------------------------------+------------+ جعل روابط ذات اسم لطيف الطرق routes الحالية ممتازة لكنها ذات عناوين يمكن تحسينها، مثل هذا العنوان /projects/1/tasks/2، فسيكون من الرائع للزوار أن يتم تغيير المعرف الرقمي ID بالاسم اللطيف للحقول، فمثلا سوف نحصل على /projects/my-first-project/tasks/buy-milk بدلا من /projects/1/tasks/2. قم بفتح ملف /app/Http/routes.php وضع به التالي: Route::bind('tasks', function($value, $route) { return App\Task::whereSlug($value)->first(); }); Route::bind('projects', function($value, $route) { return App\Project::whereSlug($value)->first(); }); هذه الشيفرة سوف تغير السلوك الافتراضي لـ tasks و projects في artisan routes وجعل الروابط ذات إسم لطيف slug. خاتمة اليوم لقد قمنا بعمل عدة أشياء وهي: تثبيت وإعداد Laravel. إضافة موردان اثنان resources. إنشاء التهجيرات migrations. إضافة بعض بيانات البذور seeds. تكوين هيكل الروابط. بكلمات أخرى، قمنا اليوم بوضع الأساس التي سوف نعتمد عليه في الدروس القادمة من خلال إنشاء جميع العناصر اللازمة حتى تعمل الواجهة الأمامية، ولدينا الآن بعض البيانات في قاعدة البيانات بالإضافة إلى الطرق، في الدرس القادم سوف نبدأ في الواجهة الأمامية للموقع. هنالك بضعة مواقع أخرى لتحصل على المزيد من المعلومات وآخر الأخبار: The Laravel Twitter feed: أخر الأخبار حول تطورات Laravel. Taylor Otwell’s Twitter feed: حساب تويتر لمُؤسّس Laravel. Laravel.io: لمتابعة آخر الأخبار و الدورات من مختلف مواقع الويب، بالإضافة إلى بودكاست أسبوعي يتحدث عن الأفكار والاهتمامات ذات علاقة إلى المنصة. Laravel Packages Registry: مكان جيد لإيجاد أفضل حزم Laravel. Code Bright: كتاب إلكتروني مجاني من المؤلف دايلي ريس Dayle Rees. ترجمة وبتصرف للمقال: Creating a Basic ToDo Application in Laravel 5 – Part 1. حقوق الصورة البارزة: Designed by Freepik.
  3. ASP.NET Core هي إطار عمل مفتوح المصدر وعالي الأداء لإنشاء تطبيقات ويب حديثة، ومن المفترض أن يكون النسخة الأفضل من إطار العمل ASP.NET لمايكروسوفت. تمَّ إصداره عام 2016، ويمكن تشغيله على العديد من أنظمة التشغيل مثل لينكس وmacOS، مما يتيح للمطورين استهداف نظام تشغيل معين للتطوير اعتمادًا على متطلبات التصميم. يستطيع المطور باستخدام ASP.NET Core بناء أي نوع من تطبيقات الويب أو الخدمات بغض النظر عن التعقيد والحجم. يمكن للمطورين أيضًا استخدام صفحات Razor لإنشاء تصميم يركز على الصفحة يعمل أعلى النمط التقليدي MVC (اختصارًا للعبارة Model-View-Controller). يوفر ASP.NET Core المرونة للتكامل مع أي إطار عمل للواجهة الأمامية للتعامل مع العمليات المنطقية من طرف العميل أو استخدام خدمة ويب. مثلًا، بإمكانك بناء واجهة برمجية RESTful مع ASP.NET Core واستخدامها ببساطة مع أطر عمل جافاسكربت مثل Angular، React و Vue.js. ستعدّ في هذا الدرس تطبيق ASP.NET Core وتنشره ليكون جاهزًا للإطلاق مع خادم MySQL على أوبنتو 18.04 باستخدام Nginx. ستنشر تطبيق ASP.NET Core تجريبي مماثل للتطبيق من توثيق مايكروسوفت والمُستضاف على GitHub. بمجرد أن يتم النشر، سيسمح لك التطبيق التجريبي بإنشاء قائمة أفلام وتخزينها في قاعدة البيانات. ستكون قادرًا على إنشاء، وقراءة، وتحديث، وحذف سجلات من قاعدة البيانات. يمكنك بدلًا من ذلك استخدام هذا الدرس لتنشر تطبيق ASP.NET Core خاص بك؛ من الممكن أن تحتاج إلى تنفيذ خطواتٍ إضافيةٍ تتضمن إنشاء ملف تهجير (migration) جديد إلى قاعدة البيانات الخاصة بك. المتطلبات الأساسية ستحتاج لما يلي لاتباع هذا الدرس: خادم أوبنتو 18.04 تم ضبطه باستخدام دليل التهيئة الأولية لخادم أوبنتو 18.04، متضمنًا مستخدمًا عاديًا مع وصول sudo وجدار حماية. خادم Nginx مثبَّت باتباع الخطوات كيفية تثبيت Nginx على أوبنتو 18.04. خادم ويب Nginx آمن. بإمكانك اتباع درس كيف تؤمّن خادم Nginx بشهادة Let's Encrypt في أوبنتو للقيام بذلك. ضبط سجلَّي الـ DNS لخادمك. يمكنك اتّباع هذه المقدمة إلى DigitalOcean DNS للتفاصيل حول كيفية إضافتها. سجل A مع إشارة اسم النطاق الخاص بك your-domain إلى عنوان IP العام للخادم. سجل A مع إشارة اسم النطاق الخاص بك مسبوقًا بـ www www.your-domain. إلى عنوان IP العام للخادم. نظام إدارة قواعد البيانات MySQL مثبَّت باتباع خطوات كيف تثبّت الإصدار الأخير من MySQL على أوبنتو 18.0. الخطوة 1: تثبيت مُشِّغل NET Core. الآني مُشِّغل NET Core. الآني (أي NET Core runtime.) مطلوب لتشغيل تطبيق NET Core.، لذا يجب البدء بتثبيته على جهازك. تحتاج أولًا لتسجيل مفتاح مايكروسوفت ومستودع المنتج. بعد ذلك ستثبّت الاعتماديات (dependencies) المطلوبة. أولًا، سجّل الدخول كمستخدم مُنشأ حديثًا، وتأكّد من أنَّك في المجلد الجذر: $ cd ~ ثمّ نفّذ الأمر التالي لتسجيل مفتاح مايكروسوفت ومستودع المنتج: $ wget -q https://packages.microsoft.com/config/ubuntu/18.04/packages-microsoft-prod.deb استخدم dpkg مع الراية i- لتثبيت الملف المحدد: $ sudo dpkg -i packages-microsoft-prod.deb لتسهيل تثبيت الحزم الأخرى الضرورية لتطبيقك، ستثبّت مستودع universe باستخدام الأمر التالي: $ sudo add-apt-repository universe ثمّ ثبّت حزمة apt-transport لتسمح باستخدام المستودعات التي يتم الوصول إليها عبر بروتوكول HTTP الآمن: $ sudo apt install apt-transport-https نفِّذ الأمر التالي لتنزيل قائمة الحزم من المستودعات وتحديثها للحصول على معلومات حول أحدث إصدارات الحزم واعتمادياتها: $ sudo apt update أخيرًا، يمكنك تثبيت SDK لمشغّل NET. الآني عبر الأمر: $ sudo apt install dotnet-sdk-2.2 ستُعرض تفاصيل ما سيجري تنزيله ويُطلب تأكيد ذلك عبر الضغط على Y أي yes للمتابعة. لقد انتهيت الآن من تثبيت SDK لمشغّل NET Core. الآني على الخادم، وأنت جاهز تقريبًا لتنزيل التطبيق التجريبي من Github لضبط إعدادات النشر. لكن أولًا يجب إنشاء قاعدة بيانات التطبيق. الخطوة 2: إنشاء مستخدم MySQL وقاعدة بيانات ستنشئ في هذا الجزء مستخدم على خادم MySQL، وقاعدة بيانات للتطبيق، وتمنح جميع الصلاحيات الضرورية للمستخدم الجديد ليتصل بقاعدة البيانات من تطبيقك. تحتاج لتبدأ إلى الوصول لـMySQL العميل باستخدام حساب MySQL الجذر كما هو موضح هنا: $ mysql -u root -p سيُطلب منك إدخال كلمة سر الحساب الجذر الذي تمّ إعداده ضمن درس المتطلبات الأساسية. ثم أنشئ قاعدة بيانات MySQL للتطبيق باستخدام الأمر: mysql> CREATE DATABASE MovieAppDb; ستشاهد الخرج التالي في سطر الأوامر: Output Query OK, 1 row affected (0.03 sec) لقد أنشأت الآن قاعدة البيانات بنجاح، بعد ذلك ستُنشئ مستخدم MySQL جديد، وتربطه بقاعدة البيانات التي أُنشئت حديثًا، وتمنحه جميع الصلاحيات. نفّذ الأمر التالي لتُنشئ مستخدم MySQL وكلمة مرور. تذكّر أن تغيّر اسم المستخدم وكلمة المرور ليصبحوا أكثر أمانًا: mysql> CREATE USER 'movie-admin'@'localhost' IDENTIFIED BY 'password'; ستشاهد الخرج التالي: Output Query OK, 0 rows affected (0.02 sec) يحتاج مستخدم MySQL إلى الإذن المناسب للوصول إلى قاعدة بيانات أو تنفيذ إجراء محدد عليها، حاليًا لا يملك المستخدم movie-admin الإذن المناسب على قاعدة بيانات التطبيق. سنغيّر ذلك بتنفيذ الأمر التالي لمنح حق الوصول للمستخدم movie-admin إلى قاعدة البيانات MovieAppDb: mysql> GRANT ALL PRIVILEGES ON MovieAppDb.* TO 'movie-admin'@'localhost'; ستشاهد الخرج التالي: Output Query OK, 0 rows affected (0.01 sec) يمكنك الآن إعادة تحميل جداول الصلاحيات بتشغيل الأمر التالي لتطبيق التغييرات التي قمت بها باستخدام عبارة التدفق: mysql> FLUSH PRIVILEGES; ستشاهد الخرج التالي: Output Query OK, 0 rows affected (0.00 sec) لقد أنشأت مستخدم جديد ومنحه الصلاحيات. لتختبر فيما إذا تمّ الأمر بشكلٍ صحيحٍ، أغلق MySQL العميل: mysql> quit; ثمّ سجّل الدخول مجددًا باستخدام بيانات الاعتماد لمستخدم MySQL الذي أنشأته وأدخل كلمة المرور المناسبة عندما تُطلب منك: $ mysql -u movie-admin -p للتأكد من أن المستخدم movie-admin يمكنه الوصول إلى قاعدة البيانات المُنشأة، تحقق باستخدام الأمر: mysql> SHOW DATABASES; الآن ستشاهد جدول MovieAppDb ضمن قائمة الخرج: Output +--------------------+ | Database | +--------------------+ | MovieAppDb | | information_schema | +--------------------+ 2 rows in set (0.01 sec) الآن، أغلق MySQL العميل: mysql> quit; لقد أنشأت قاعدة بيانات، ومستخدم MySQL جديد للتطبيق التجريبي، ومنحت المستخدم الجديد الصلاحيات الصحيحة للوصول إلى قاعدة البيانات. في الجزء التالي، ستبدأ بضبط التطبيق التجريبي. الخطوة 3: ضبط التطبيق التجريبي وبيانات اعتماد قاعدة البيانات كما ذُكر سابقًا، ستنشر تطبيق ASP.NET Core موجود. تم بناء هذا التطبيق لإنشاء قائمة أفلام باستخدام نمط التصميم (Model-View-Controller) لضمان وجود الهيكل المناسب والفصل بين الاهتمامات. لإنشاء أو إضافة فيلم جديد إلى القائمة، سيملأ المستخدم حقول النموذج بالتفاصيل المناسبة ويضغط على زر إنشاء لإرسال التفاصيل إلى المتحكم (controller). عند ذلك يستقبل المتحكم طلب POST HTTP مع التفاصيل المرسلة وتبقى البيانات في قاعدة البيانات من خلال النموذج (model). ستستخدم Git لسحب (pull) الشيفرة المصدرية لهذا التطبيق التجريبي من GitHub وحفظه في مجلد جديد. يمكنك أيضًا تحميل تطبيق بديل إذا كنت ستنشر تطبيقًا مختلفًا. للبدء، أنشئ مجلدًا جديدًا باسم movie-app من الطرفية (terminal) باستخدام الأمر التالي: $ sudo mkdir -p /var/www/movie-app سيكون هذا المجلد بمثابة المجلد الجذر لتطبيقك. بعد ذلك، عدّل مالك ومجموعة المجلد للسماح لحساب المستخدم العادي بالعمل على ملفات المشروع: $ sudo chown sammy:sammy /var/www/movie-app استبدل sammy باسم المستخدم العادي الذي يملك صلاحيات sudo. يمكنك الآن الانتقال إلى المجلد الأب ونسخ (clone) التطبيق من Github: $ cd /var/www $ git clone https://github.com/do-community/movie-app-list.git movie-app ستشاهد الخرج التالي: Output Cloning into 'movie-app'… remote: Enumerating objects: 91, done. remote: Counting objects: 100% (91/91), done. remote: Compressing objects: 100% (73/73), done. remote: Total 91 (delta 13), reused 91 (delta 13), pack-reused 0 Unpacking objects: 100% (91/91), done. لقد نسخت التطبيق التجريبي بنجاح من GitHub، لذا ستكون الخطوة التالية هي إنشاء اتصال ناجح بقاعدة بيانات التطبيق. ستقوم بذلك عن طريق تعديل خاصية ConnectionStrings ضمن ملف appsettings.json وإضافة تفاصيل قاعدة البيانات. غيّر المجلد ضمن التطبيق: $ cd movie-app ثم افتح الملف للتعديل: $ sudo nano appsettings.json أضف بيانات اعتماد قاعدة البيانات في ملف appsettings.json: { "Logging": { "LogLevel": { "Default": "Warning" } }, "AllowedHosts": "*", "ConnectionStrings": { "MovieContext": "Server=localhost;User Id=movie-admin;Password=password;Database=MovieAppDb" } } بوضع هذه البيانات بطريقةٍ صحيحة فقد أنشأت اتصالًا ناجحًا بقاعدة البيانات. الآن اضغط CTRL+X لحفظ تغييرات الملف واضغط Y للتأكيد. ثم اضغط ENTER لإغلاق الصفحة. تستخدم تطبيقات ASP.NET Core مكتبة NET. قياسية تسمّى Entity Framework (EF) Core لإدارة التفاعل مع قاعدة البيانات. Entity Framework Core هي إصدار خفيف عابر للمنصات (cross-platform) من التقنية الشائعة للوصول إلى بيانات Entity Framework. إنّها تقنية ربط الكائنات بالعلاقات (ORM) التي تتيح لمطوري NET. العمل مع قاعدة البيانات باستخدام أي مزود قاعدة بيانات، مثل MySQL. يمكنك الآن تحديث قاعدة بياناتك مع الجداول من التطبيق التجريبي المنسوخ. نفّذ الأمر التالي لهذا الغرض: $ dotnet ef database update سيحدّث هذا الأمر قاعدة البيانات ويُنشئ المخططات المناسبة. الآن، لتبني المشروع وكلّ اعتمادياته، نفّذ الأمر التالي: $ dotnet build ستشاهد خرجًا مشابهًا للتالي: Output Microsoft (R) Build Engine version 16.1.76+g14b0a930a7 for .NET Core Copyright (C) Microsoft Corporation. All rights reserved. Restore completed in 95.09 ms for /var/www/movie-app/MvcMovie.csproj. MvcMovie -> /var/www/movie-app/bin/Debug/netcoreapp2.2/MvcMovie.dll MvcMovie -> /var/www/movie-app/bin/Debug/netcoreapp2.2/MvcMovie.Views.dll Build succeeded. 0 Warning(s) 0 Error(s) Time Elapsed 00:00:01.91 سيؤدي هذا إلى بناء المشروع وتثبيت أيّة اعتماديات خارجية (third-party dependencies) مذكورة في ملف project.assets.json، ولكن لن يكون التطبيق جاهزًا للنشر بعد. نفّذ الأمر التالي ليصبح التطبيق جاهزًا للنشر: $ dotnet publish ستشاهد التالي: Output Microsoft (R) Build Engine version 16.1.76+g14b0a930a7 for .NET Core Copyright (C) Microsoft Corporation. All rights reserved. Restore completed in 89.62 ms for /var/www/movie-app/MvcMovie.csproj. MvcMovie -> /var/www/movie-app/bin/Debug/netcoreapp2.2/MvcMovie.dll MvcMovie -> /var/www/movie-app/bin/Debug/netcoreapp2.2/MvcMovie.Views.dll MvcMovie -> /var/www/movie-app/bin/Debug/netcoreapp2.2/publish/ هذا سيُحزّم التطبيق ويترجمه، ويقرأ اعتمادياته، وينشر مجموعة الملفات الناتجة في مجلد للنشر، ويُنتج ملف dll. عابر للمنصات يستخدم مشغّل NET Core. الآني المثبّت لتشغيل التطبيق. بتثبيت الاعتماديات، وإنشاء اتصال بقاعدة البيانات، وتحديث قاعدة البيانات بالجداول الضرورية، ونشرها للإنتاج، تكون قد أكملت ضبط التطبيق التجريبي. في الخطوة التالية ستضبط إعدادات خادم الويب لجعل التطبيق متاحًا وآمنًا على نطاقك. الخطوة 4: ضبط إعدادات خادم الويب الآن وبعد اتّباع درس كيف تؤمّن خادم Nginx بشهادة Let's Encrypt ستحصل على كتلة الخادم لنطاقك في ‎/etc/nginx/sites-available/your_domain مع توجيه server_name الذي تمّ تعيينه بالفعل بشكلٍ مناسب. في هذه الخطوة ستعدّل كتلة الخادم هذه لتقوم بضبط Nginx كوكيل (proxy) عكسي لتطبيقك. الوكيل العكسي هو خادم يقع أمام خوادم الويب ويعيد توجيه كل طلب من مستعرض الويب إلى خوادم الويب هذه. إنّه يستقبل كل الطلبات من الشبكة ويعيد توجيهها إلى خادم ويب مختلف. في حالة تطبيق ASP.NET Core، يعدّ خادم الويب Kestrel هو الخادم المفضّل المضمّن افتراضيًا، وهذا ممتاز لتقديم المحتوى الديناميكي من تطبيق ASP.NET Core لكونه يوفر أداءً أفضل لمعالجة الطلبات وقد صُمم لجعل ASP.NET أسرع قدر الإمكان. ومع ذلك لا يعد Kestrel خادم ويب كامل المواصفات لأنه لا يمكنه إدارة الأمان وتقديم الملفات الثابتة، ولهذا يُستحسن دائمًا تشغيله خلف خادم ويب. للبدء، تأكد من أنّك داخل المجلد الجذر للخادم: $ cd ~ افتح كتلة الخادم للتعديل بهذا الأمر: $ sudo nano /etc/nginx/sites-available/your_domain كما هو مفصّل في الخطوة 4 من درس كيف تؤمّن خادم Nginx بشهادة Let's Encrypt، إذا اخترت الخيار 2، فإنَّ برنامج Certbot سيضبط تلقائيًا كتلة الخادم هذه لإعادة توجيه حركة مرور HTTP إلى HTTPS مع بعض التعديلات فقط. تابع الإعدادات بضبط أول كتلتين في الملف لتصل إلى ما يلي في ملف ‎/etc/nginx/sites-available/your-domain: server { server_name your-domain www.your-domain; location / { proxy_pass http://localhost:5000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection keep-alive; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } listen [::]:443 ssl ipv6only=on; # managed by Certbot listen 443 ssl; # managed by Certbot ssl_certificate /etc/letsencrypt/live/your-domain/fullchain.pem; # managed by Certbot ssl_certificate_key /etc/letsencrypt/live/your-domain/privkey.pem; # managed by Certbot include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot } … هذه الإعدادات في كتلة الخادم ستطلب من خادم Nginx التنصّت على المنفذ 443، الذي هو المنفذ القياسي للمواقع التي تستخدم SSL. بالإضافة إلى ذلك سيقبل Nginx حركة المرور العامة على المنفذ 443 ويعيد توجيه كل طلب مطابق إلى خادم Kestrel المدمج http://localhost:5000. أخيرًا، بعد كتلة الخادم التي عدلتها في الملف، تأكّد من أنّ كتلة الخادم الثانية في الملف ‎/etc/nginx/sites-available/your-domain تبدو كالتالي: … server { if ($host = www.your-domain) { return 301 https://$host$request_uri; } # managed by Certbot if ($host = your-domain) { return 301 https://$host$request_uri; } # managed by Certbot listen 80; listen [::]:80; server_name your-domain www.your-domain; return 404; # managed by Certbot } ستعيد كتلة الخادم توجيه كل الطلبات إلى https://your-domain وhttps://www.your-domain لوصول HTTPS آمن. بعد ذلك، دع Nginx يعكس التغييرات التي قمت بها على كتلة الخادم بتنفيذ الأمر: $ sudo nginx -s reload بعد إكمال إعداد خادم Nginx، تمّ ضبط الخادم بالكامل لإعادة توجيه جميع طلبات HTTPS الموجهة إلى https://your-domain على تطبيق ASP.NET Core الذي يعمل على الخادم Kestrel إلى http://localhost:5000. ولكن لم يتم ضبط Nginx ليدير إجراء الخادم Kestrel. ستستخدم وظائف systemd لمعالجة هذا والتأكّد من أنّ عملية Kestrel تعمل في الخلفية. ستسمح لك ملفات Systemd بأن تدير إجراءًا من خلال توفير وظائف البدء، والإيقاف، وإعادة التشغيل، والدخول، حالما تنشئ إجراء عمل يدعى وحدة. انتقل ضمن مجلد systemd: $ cd /etc/systemd/systems أنشئ ملفًا جديدًا للتعديل: $ sudo nano movie.service وأضف المحتوى التالي له: [Unit] Description=Movie app [Service] WorkingDirectory=/var/www/movie-app ExecStart=/usr/bin/dotnet /var/www/movie-app/bin/Debug/netcoreapp2.2/publish/MvcMovie.dll Restart=always RestartSec=10 SyslogIdentifier=movie User=sammy Environment=ASPNETCORE_ENVIRONMENT=Production Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false [Install] WantedBy=multi-user.target يحدد ملف الإعداد موقع مجلد المشروع مع WorkingDirectory وأمر التنفيذ في بداية العملية في ExecStart. بالإضافة إلى ذلك، لقد اعتدت على استخدام توجيه RestartSec لوصف متى سيتم إعادة تشغيل خدمة systemd إذا تعطلت خدمة مشغّل NET. الآني. الآن، احفظ الملف وفعّل خدمة movie المنشأة حديثًا مع الأمر: $ sudo systemctl enable movie.service بعد ذلك، تابع لتشغيل الخدمة والتحقق من أنّها تعمل عند التشغيل: $ sudo systemctl start movie.service ثمّ تفحّص حالتها: $ sudo systemctl status movie.service ستشاهد الخرج التالي: Output movie.service - Movie app Loaded: loaded (/etc/systemd/system/movie.service; enabled; vendor preset: enabled) Active: active (running) since Sun 2019-06-23 04:51:28 UTC; 11s ago Main PID: 6038 (dotnet) Tasks: 16 (limit: 1152) CGroup: /system.slice/movie.service └─6038 /usr/bin/dotnet /var/www/movie-app/bin/Debug/netcoreapp2.2/publish/MvcMovie.dll يعطيك هذا الخرج نظرةً عامةً عن الحالة الراهنة لخدمة movie.service المنشأة لتحافظ على التطبيق شغّالًا. تشير إلى أنّ الخدمة شغّالة وفعّالة حاليًا. انتقل إلى https://your-domain من متصفحك لتشغيل التطبيق واختباره. ستشاهد الصفحة الرئيسية للتطبيق التجريبي- تطبيق قائمة الأفلام. مع الخادم الوكيل العكسي وKestrel الذي تتم إدارته باستخدام systemd، تم ضبط تطبيق الويب بالكامل ويمكن الوصول إليه من المتصفح. خاتمة نشرت في هذا الدرس تطبيق ASP.NET Core على خادم أوبنتو. وقمت بتثبيت خادم MySQL واستخدمته لاستمرار البيانات وإدارتها، واستخدمت خادم الويب Nginx كوكيل عكسي لخدمة تطبيقك. بعد هذا الدرس، إذا كنت مهتمًا ببناء تطبيق ويب تفاعلي باستخدام #C بدلًا من جافاسكربت، يمكنك تجربة إطار العمل Blazor لواجهة المستخدم للويب من مايكروسوفت. إنّه واجهة المستخدم للويب تعتمد على المكونات ومقادة بالأحداث لتحقيق المنطق من جهة العميل في تطبيق ASP.NET Core. إذا كنت ترغب بنشر تطبيقك، ستحتاج إلى أخذ الإجراءات الأخرى بالحسبان لنشر تطبيقك. الشيفرة المصدرية الكاملة لهذا التطبيق التجريبي تجدها هنا على GitHub. ترجمة -وبتصرف- للمقال How To Deploy an ASP.NET Core Application with MySQL Server Using Nginx on Ubuntu 18.04 لصاحبه Oluyemi Olususi
  4. تجهيز قاعدة البيانات بما أنّنا سنستخدم قاعدة البيانات PostgreSQL لهذا التّطبيق، عليك أولا أن تقوم بتنصيبها، إن كنت تستخدم Ubuntu فيُمكنك مُراجعة درس كيفيّة تنصيب PostgreSQL على Ubuntu، أمّا إن كنت على نظام Windows أو نظام Mac OS فتستطيع تحميلها من الموقع الرّسمي وتنصيبها، يُمكنك كذلك استخدام برنامج pgAdmin لإدارة قواعد بياناتك عن طريق برنامج رسومي. يُمكنك استعمال البرنامج الرّسومي لإنشاء قاعدة البيانات إن أردت ذلك، لكنّنا سنستعمل أداة psql لتنفيذ أوامر PostgreSQL واستعمال لغة SQL مباشرة من سطر الأوامر، لذا تأكّد من أنّك تستطيع الوصول إلى أداة psql. إنشاء قاعدة البيانات لإنشاء قاعدة البيانات، يكفي أن ننفّذ الأمر التّالي على سطر أوامر psql : CREATE DATABASE kalima; بعد تنفيذ الأمر، سيكون المُخرج كما يلي: CREATE DATABASE كلمة المرور للاتّصال بقاعدة البيانات، سنحتاج إلى كلمة مرور، إن سبق لك وأن خصّصت كلمة مرور أثناء التّنصيب، تستطيع تخطي هذه الخطوة، أمّا إن لم تمتلك كلمة مرور أو أنّك نسيتها، فيُمكنك ببساطة تغييرها في سطر أوامر psql بالأمر التّالي: \password بعد تنفيذ الأمر سيُطلب منك كتابة كلمة مرور جديدة وإعادة كتابتها للتّأكد من أنّك لم تُخطئ. كلمة المرور هذه مُهمّة جدا للاتّصال بقاعدة البيانات من تطبيق فلاسك باستخدام أداة SQLAlchemy لذا تأكّد من أنّها مُتاحة. ما معنى ORM؟ ORM اختصار Object-Relational Mapper أي رابط الكائنات بالعلاقات (الجداول)، الهدف منه ببساطة التّعامل مع قواعد بيانات SQL باستعمال الكائنات Objects والأصناف Classes عوضا عن تعليمات SQL، فعوضا عن إنشاء البيانات وجلبها وتعديلها وحذفها عن طريق تعليمات SQL التّي ستتكرّر كثيرا في الشّفرة يُمكن ببساطة استخدام لغة بايثون للوصول إلى نفس الهدف، كما أنّ استعمالها مع لغة برمجة يخلق شيئا من عدم التّناسق. لو تابعت سلسلة دروس Flask للمُبتدئين، للاحظت بأنّنا استعملنا ملفّا باسم manage_db للتّعامل مع قواعد البيانات عن طريق الدّوال عوضا عن كتابة تعليمات SQL عند الرّغبة في إجراء كل عمليّة، ولو استعملنا SQL الخام لكان تكرار الشّفرة كثيرا. بالإضافة إلى ميّزة تعريف الأصناف التّي تُمثّل الجداول والتّعامل معها عن طريق التّوابع (الدوال) لتجنّب تكرار الشّفرة، يقوم الـORM بالكثير من الأشياء المُملّة خلف الكواليس، فعوضا عن الاهتمام بالتّفاصيل الصّغيرة بأنفسنا، يقوم الـORM بالاهتمام بها من أجلنا لنهتمّ بالأمور الأكثر أهميّة، وفي النّهاية، مُعظمنا لا يتقن لغة SQL كثيرا لذا من المُفضّل استعمال مكتبة للتّعامل مع قواعد البيانات باللغة التّي نُجيدها. وبما أنّ استعمال الـORM يُتيح لنا عزل الشّفرة المسؤولة عن التّعامل مع قاعدة البيانات في معزل عن التّطبيق فهذا يُتيح لنا تعديل الخصائص وإضافة الميّزات للتّطبيقات دون التّأثير على تطبيق معيّن بحد ذاته، ومن الشّائع جمع الشّفرة المسؤولة عن التّعامل مع قاعدة البيانات في ملف باسم models.py في مجلّد المشروع. يُساعدنا الـORM كذلك على تحقيق مبدأ MVC أو Model-View-Controller أي “نموذج، عرض، مُتحكّم ” وهو مبدأ يقوم على عزل جزء العرض، المُتحكّمات، والنّماذج في تطوير التّطبيقات لمرونة أكثر في التّطوير، أي أنّ كلّا من العرض (الذي يُمثّل القوالب في تطبيقنا) والمُتحكّم (الذي يُمثّل دوال الموجّهات في ملفّات views.py) والنّموذج (أي الجزء الذي يتعامل مع البيانات) أجزاء منفصلة حسب الوظيفة متّصلة في التّطبيق ويُمكن أن تنتقل البيانات بينها ببساطة، ورغم أنّ إطار Flask يمنحك الحريّة الكاملة في طريقة تسميّة أجزاء تطبيقك، إلّا أن أطر عمل أخرى مثل Django و Rails تفرض عليك تسميّات محدّدة. ما هو SQLAlchemy؟ بعد أن عرّفنا معنى الـORM بقي تعريف مكتبة SQLAlchemy، وهي مكتبة تُسهّل علينا التّعامل مع قواعد البيانات المُختلفة، كما أنّها تحتوي على ORM قوي ومُعتمد عليه من طرف المشاريع الكبيرة. تحتوي SQLAlchemy على الكثير من الدّوال المُساعدة والكائنات التّي تُسهّل عليك كتابة SQL بلغة بايثون، والORM المبني فيه يُستعمل لربط الكائنات بالبيانات في قاعدة البيانات. سنستعمل في غالب هذا المشروع الـORM الخاص بـSQLAlchemy ولن نتطرّق إلى بقيّة ميّزات المكتبة، لذا إن أردت معرفة المزيد عن المكتبة فعليك بالتّوثيق الرّسمي. إضافات Flask Flask Extensions أو إضافات فلاسك عبارة عن مكتبات وأطر عمل Frameworks مبنيّة لتُسهّل على المُطوّر القيام بالأشياء المُفصّلة وتجنّب التّعامل مع كل شيء بأنفسنا، فمثلا، يُمكننا أن نستعمل مكتبة SQLAlchemy مع تطبيق Flask مُباشرة، إلّا أن في هذه الطّريقة الكثير من الأمور غير المُهمّة والكثير من الإعدادات، وبما أنّنا في مسيرة تعلّم إطار فلاسك لا كيفيّة استعمال SQLAlchemy، ولتسهيل استعمال SQLAlchemy مع المشاريع المبنيّة بإطار العمل فلاسك، قام كاتب الإطار Armin Ronacher بتطوير إضافة للإطار باسم Flask-SQLAlchemy التّي أصبحت أحد أشهر الإضافات على موقع Github. بالإضافة إلى Flask-SQLAlchemy هناك الكثير من الإضافات التّي يُمكننا استعمالها لتطوير تطبيقات فلاسك، وإليك بعضا من هذه الإضافات: Flask-Login إضافة لتسهيل التّعامل مع جلسات المُستخدمين Sessions، تُتيح إمكانيّة تسجيل دخول مُستخدم وتسجيل خروجه، لكنّها لا تقوم بالتّحقق من صحّة البيانات (اسم المُستخدم وكلمة المرور مثالا) وتترك المُطوّر للاهتمام بالأمر. Flask-Bcrypt إضافة لتشفير كلمات المرور باستعمال خوارزميّة Bcrypt قبل حفظها بقاعدة البيانات لحماية أكثر، هذا لمنع المُخترق من سرقة كلمات المرور حتى بعد وصوله إلى قاعدة البيانات، وكلّما كانت خوارزميّة التّشفير أعقد كلّما كان من الصّعب فك التّشفير عن كلمة المرور ما يُوفّر حماية أكثر. Flask-WTF بما أنّ مُعظم التّطبيقات تعتمد على نماذج HTML (حقول النّصوص، كلمات المرور و مساحة النّص TextArea) وبما أنّها تُسبّب الكثير من الثّغرات الأمنيّة في التّطبيقات والتّي يُمكن أن يُساء استغلالها، فلا بد من توفير طريقة آمنة وبسيطة للتأكّد من أنّ ما يُدخله المُستخدم في هذه الحقول آمن. يُمكنك بالطّبع التّأكد من مُدخلات المُستخدم بنفسك، لكنّ الأمر يأخذ وقتا، كما أنّه ممل في الحقيقة، إذ عليك تكرار نفس الشّيء كلّ مرّة. عوضا عن الاعتماد على مهاراتك البرمجيّة للتأكّد من سلامة المُدخلات، يُمكنك أن تترك إضافة Flask-WTF للاهتمام بالأمر في تطبيقاتك المبنيّة بإطار فلاسك، الإضافة مبنيّة على مكتبة WTForms التّي يُمكنك استعمالها مع أطر عمل أخرى، وأنصحك بالاطلاع على توثيق المكتبة الرّسمي للمزيد من المعلومات عن كيفيّة مُعالجة النّماذج. Flask-Migrate ببساطة، Flask-Migrate إضافة تُتيح لك تهجير البيانات Migrations عند تطوير تطبيقك. هذه الإضافة مهمّة جدّا إن كنت تطوّر تطبيقا سبق وأن نشرته على نحو مُتواصل، إذ تُتيح لك تهجير البيانات في كل مرّة تُضيف جدولا جديدا أو علاقة بين الجداول إلى قاعدة البيانات. تخيّل أنّك تمتلك تطبيقا يحتوي جدول المُستخدمين فيه على أكثر من ألف مُدخل، إن أردت إتاحة إمكانيّة إضافة تاريخ ازدياد للمُستخدمين فستضطر إلى حذف كلّ شيء في هذا الجدول وإضافة العمود الجديد ثمّ إعادة البيانات من جديد، الأمر مُعقّد بعض الشّيء وفقدان البيانات أمر غير مرغوب فيه بتاتا. مُجدّدا عوضا عن القيام بكل شيء بنفسك، دع إضافة Flask-Migrate لتهتمّ بكل هذا التّعقيد من أجلك، فبسطر أو سطرين تستطيع تطبيق الميّزة الجديدة لتطبيقك وتهجير البيانات القديمة بكل بساطة. Flask-Testing إضافة لتسهيل إجراء اختبارات وحدة Unit tests لتطبيقك، إذ أنّ اختبار التّطبيق مُهمّ جدّا للتأكّد من أنّ كلّ شيء بخير وأنّ المُستخدمين لا يواجهون أية مشاكل، خاصّة إذا كان مشروعك كبيرا، فاختبار كل وظيفة يدويّا قد يأخذ منك وقتا طويلا، وإن لم تختبره آليا فنسبة الخطأ كبيرة جدّا. إذا أضفت خاصيّة جديدة لتطبيقك فأنت بذلك تُعرّض أجزاء أخرى من التّطبيق إلى الخطأ، وستُسهّل عليك الاختبارات معرفة ما إذا كان التّطبيق لا يزال يعمل كما ينبغي له أو لا، وبما أنّ اختبارات الوحدة تختبر كلّ وظيفة على نحو مُستقل، فسيسهل عليك معرفة أي جزء يجب عليك الاهتمام به لإصلاح التّطبيق. Flask-Gravatar هذه الإضافة تُسهّل لنا استعمال خدمة Gravatar لتمكين المُستخدمين من إضافة صورة شخصيّة لهم، إذ تكون الصّورة الشّخصيّة مُرتبطة بالبريد الإلكتروني للمُستخدم وتُستعمل هذه الخدمة في الكثير من التّطبيقات في وقتنا الحالي. هناك مئات الإضافات الخاصّة بإطار Flask لذا لا يُمكنني ذكرها كلّها، إن أردت الاطّلاع على إضافات أخرى، تستطيع البحث عنها عن طريق أداة pip كما يلي: pip search flask سنتعرّف على معظم هذه الإضافات في قادم الدّروس مع التّقدم في المشروع، وهذا الهدف الأساسي من هذه الإضافات، كلّما تقدّمت أكثر وكبر المشروع كلّما احتجت إلى التّعامل مع الكثير من المهام المُملّة كالتّأكد من أنّ ما يرسله المُستخدم إلى قاعدة بياناتك آمن، وكالتّعامل مع الجلسات لتوفير نظام تسجيل دخول آمن دون الاضطرار للقيام بكل التّفاصيل؛ كما أنّ الإضافات تُقلّل من حجم الخطأ في التّطوير، فالاعتماد على إضافة لتسجيل دخول المُستخدمين بشكل آمن أفضل من مُحاولة إنشاء طريقة خاصّة بك قد تكون مليئة بالثّغرات الأمنيّة. وبما أنّ فلاسك إطار عمل مُصغّر، فبإضافاته يُمكنك أن تستخدمه كما تُستخدم أطر العمل الضّخمة كـDjango و Rails و Laravel وغيرها من الأطر المشهورة. خاتمة بعد أن أنشأنا قاعدة البيانات وتعرّفنا على بعض المفاهيم المُهمّة في تطوير تطبيقات الويب مع إطار فلاسك، حان الوقت لإنشاء الجداول الأساسيّة في قاعدة البيانات لنتمكّن من التّعامل معها عن طريق إضافة Flask-SQLAlchemy وهذا بالضّبط ما سنقوم به في الدّرس القادم.
  5. يقدّم هذا الدرس كيفية إنشاء نموذج Model قاعدي في Laravel ومن ثم استخدامه. النماذج هي الجزء من بنية MVC الذي تُعالَج فيه البيانات وتُنفَّذ عليها قواعد التطبيق. يتطلّب الدرس تثبيت Laravel وإعداده مع قاعدة البيانات. نبدأ بإنشاء النموذج باستخدام أمر artisan على النحو التالي: php artisan make:model Widget -m سمّينا النموذج بـWidget وأضفنا خيار m- لإنشاء التهجير في نفس الوقت. يختزل الأمر بهذه الطريقة الكثير من الوقت ويجعلنا نركّز على الأهم: عمل النموذج. ستجد بعد اكتمال تنفيذ الأمر ملفا باسم Widget.php في مجلّد app المتفرّع عن مجلّد المشروع، وملفًّا للتهجير في المجلّد database/migrations. يظهر اسما الملفّيْن في مخرجات تنفيذ الأمر السّابق. نفتح ملفّ Widget.php للنظر في محتواه: <?php namespace App; use Illuminate\Database\Eloquent\Model; class Widget extends Model { // } هذا كلّ ما يوجد الملف! هيكل نموذج يمكننا الاستفادة منه لإنشاء ما نريد. بالانتقال إلى مجلّد database/migrations نجد ملفًّا يشبه التالي: 2016_03_19_163722_create_widgets_table.php يظهر في بداية اسم الملف ختم زمني بتاريخ إنشائه. يفترض Laravel أن اسم النموذج كلمة مفردة (Widget مثلا) تبدأ بحرف كبير Uppercase، في حين يتوقّع أن يكون اسم الجدول Table جمعًا (widgets) يبدأ بحرف صغير. يمكن تفسير الأمر بأن النموذج يُرجِع نظيرا واحدا لتسجيلات الجدول. إذا نظرنا إلى ملفّ التهجيرات فسنجد أن لدينا قاعدة يمكننا البناء عليها لأمور أكثر تقدّما: use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateWidgetsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('widgets', function (Blueprint $table) { $table->increments('id'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::drop('widgets'); } } ينشئ Laravel تلقائيا حقل المعرّف في قاعدة البيانات id ويجعله يتقدّم تلقائيا فور إدراج تسجيلة جديدة في الجدول (autoincrement) وذلك باستخدام الدّالة increments. يضيف Laravel كذلك ختمين زمنيّين لتاريخَيْ إنشاء التسجيلة وتحديثها timestamps. سنضيف عمودا Column جديدا إلى قاعدة البيانات؛ لذا نضيف التعليمة التالية إلى دالّة up ضمن ملفّ التهجير: $table->string('widget_name')->unique(); يضيف السّطر أعلاه عمودا جديدا للجدول باسم widget_name من نوع string ويقيّده بـunique لكي لا توجد تسجيلتان في الجدول بنفس الاسم. تصبح دالّة up في ملف التهجير بعد إضافة العمود على النحو التالي: public function up() { Schema::create('widgets', function (Blueprint $table) { $table->increments('id'); $table->string('widget_name')->unique(); $table->timestamps(); }); } ثم ننفّذ التهجير: php artisan migrate ستلاحظ بعد تنفيذ الأمر إنشاءَ جدول جديد في قاعدة البيانات لديك. بما أننا نخطّط لإدراج تسجيلات إلى قاعدة البيانات بالتطبيق فيجب أن نضيف الخاصيّة التالية إلى النموذج Widget: /** * The attributes that are mass assignable. * * @var array */ protected $fillable = ['widget_name']; تخبر هذه التعليمة Laravel أن العمود widget_name يدعم الإسناد الشّامل Mass assignment (تحديد قيم معطياتٍ عدّةٍ مرة واحدة). إن لم نضف هذه التعليمة فلن يمكننا باستخدام التطبيق إدراجُ تسجيلات جديدة في الجدول. سنستخدم في الدرس التالي النموذج الذي أنشأناه أعلاه مع معمل النماذج Model factory في Laravel لملْء تسجيلات في جدول widgets. ترجمة -وبتصرّف- للمقال How to Make a Model in Laravel 5.1 لصاحبه Bill Keck.
  6. أقدّم بين أيديكم هذه السلسلة التي استلهمت فكرتها من كتب برمجة صغيرة مجّانيّة ومحبّبة، مثل كتاب The Little Book on CoffeeScript لمؤلّفه Alex MacCaw. لقد ألّفت هذه السلسلة لتعليم Angular بالطّريقة التي تمنّيت أن أتعلمها بها، ولذلك فهذه السلسلة معينك للانطلاق السريع مع Angular واستخدام هذه المكتبة الرائعة في عملك كمطوّر ويب. أسلوب هذه السلسلة مستلهم من مبدأ باريتو Pareto فهي تعلّمك جزءًا كبيرًا من نقاط قوّة Angular دون أن تثقل كاهلك بالكثير من تعقيداتها، وستتمكّن بعد إنهائك لهذه السلسلة من كتابة تطبيقات واجهة front-end قويّة، وستدهشك سهولة ذلك، بالرّغم من أنّ السلسلة لا تقدّم إلا جزءًا محدودًا ممّا عليك تعلّمه لاحتراف Angular بشكل كامل، لكنها ستمنحك الثّقة الكافية للقيام بذلك لأنّ خبرتك ستكون مبنيّة على التّجربة العمليّة أثناء قراءتك لدروسها، فهي بحدّ ذاتها صفحات ويب ديناميكيّة تحوي آخر نسخة من Angular وتستخدمها لتنفيذ جميع الأمثلة بشكل مباشر، وجميع الشّيفرات البرمجيّة تتيح التّعديل المباشر عليها ممّا يغيّر المخرجات مباشرة، وأتمنّى لو تقوم بكلّ التّجارب والتّعديلات التي تخطر على بالك عند كل مثال. إذا كنت ترغب في معرفة سبب كتابتي لهذه السلسلة يمكنك قراءة هذه المقدّمة إلى نهايتها فهي تتضمّن بعضًا من ذكرياتي مع Angular، ثم سنناقش أفضليّة استخدام Angular في مشاريعك. هل هي صعبة أم سهلة؟لقد عملت من قبل على تطبيقات معتمدة على Web MVC لما يزيد عن عشر سنوات، واستعملت الكثير من الأدوات بدءًا من Struts وانتهاءً بـSpring MVC مرورًا بـRuby on Rails وBackbone، حتّى أنّني قمت بكتابة كتاب Backbone and CoffeeScript لذا كان من الطّبيعي بالنّسبة لي أن أفترض أنّ تعلّم ِAngular سيكون بسيطًا بالنسبة لي، إلّا أنّ تقدّمي في تعلّمها واجه سدًّا كبيرًا من المصطلحات غير المألوفة بعد غوصي في توثيق هذه المكتبة، مصطلحاتٌ مثل transclusion، توجيه directive والمجال المعزول isolate scope، وكلّما قرأت أكثر في التّوثيق الرّسمي للّغة ودروسها تأكّدت أكثر بأنّني كنت أتوهّم عندما ظننت بأنّ Angular أداةٌ سهلة، وأتذكّر بشكل خاصّ مروري على العبارة التّالية: لن تجد العبارة السابقة هذه الأيام داخل توثيق اللّغة، والشّكر يعود للجهود الجادّة التي بذلها فريق ِAngluar لتحسين التّوثيق، وعلى أيّ حال فالاقتباس السّابق جعلني أشعر بالجهل وبدأت الشّكوك تراودني والقلق يساورني: هل هذه المكتبة مجال جديد كلّيّا عليّ؟ كانت الإجابة تأتيني من كلّ الجهات تقريبًا: إنّ ِAngular تقنيّة معقدّة، ولا مجال للعبث معها، ففي ملتقيات المبرمجين على Stack Overflow ومجموعة AngularJS على Google وفي كلّ مكان آخر، كانت تفاصيل هذه المكتبة محلّ النّقاشات المطوّلة، مع توثيقات مرعبة للمشاكل والأفخاخ والحيل فيها، فشمّرت عن ساعد الجد كأي محترف يحترم نفسه وقبلت هذا التّحدّي، ومع مرور الوقت أصبحت أكثر ألفة مع هذه المفاهيم والمصطلحات، وتقبّلت أنّ تعلّمي هذه الأداة سيكون بطيئًا، إلى أن جاء ذلك اليوم الذي شاهدت فيه مقابلة مع Miško Hevery مخترع Angular، وبعد ذلك اكتشفت حقيقة بسيطةً إلّا أنّها كانت شديدة الأهمّيّة: الهدف الأصليّ من Angular هو سهولة الاستخدام. لقد بيّن Hevery بأنّه أراد أن تكون Angular أداة ليستخدمها غير المبرمجين، ليتمكّنوا من بناء صفحات ويب ديناميكيّة باستخدامهم لنصوص تصريحية بسيطة، لقد اكتشفت بأنّني قد تعمّقت في الكثير من تفاصيل Angular دون أن أجني منها فائدة مكافئة للوقت والجهد المبذول، ورغم أنّ مشروع Angular ومجتمع Angular أصبحا معتمدين على بعضهما بشكل وثيق حيث تقدّم المكتبة التّحديات والتّعقيدات التي تشبع شغف هؤلاء، إلّا أنّه كان هناك طريق أفضل لاستخدام المكتبة كما أراد مخترعها، بعيدا عن مبدأ عملها المصنوع بعناية، وقد كان الصّواب بالنسبة إليّ الابتعاد عن هذه الأمور الدّاخليّة الدّقيقة وتركها آمنة لتقود كلّ شيء دون المساس بها، وبعد أن اعتمدت هذا المبدأ في التعامل معها وبالرّغم من وجود العديد من الأدوات الأخرى إلّا أنّ شمس Angular صارت تشرق لي عند مواجهة العديد من الحالات. هل تناسبك؟تعتمد إجابة هذا السّؤال على معرفتك لمشروعك بدقّة، فعليك معرفة فيما إن كان يحتاج بالفعل لمعالجة البيانات وإخراج HTML في طرف الزّبون، أم أنّك تخدع نفسك ببعض المتطلّبات الإضافيّة لتعطيها دافعًا لتعلّم أداة جميلة وجذّابة، فقد لا تلزمك Angular إن كان بإمكانك الاعتماد على بعض المعالجة من طرف الخادوم مع "رشّةٍ" من التّفاعل المعتمد على jQuery. ضع في حسابك أنّ Angular واسعة وتدعو للتشبّث بها، فبالرّغم من أنّنا ندعوها "مكتبة" إلا أنّ دعمها الذّاتي للـوحدات modules ولـحقن التّبعيّة dependency injection سيفرض على المطوّر طريقة إدارة مشروعه، وقد يُفضّل حلًّا آخر على Angular، ربّما سيختار أداة أقدم منها ومتميزةً بكونها الأفضل في وقتها، لكنّه قد يجد أنّه من الصّعب أو المستحيل أن تحلّ محلّ Angular، أضف إلى ذلك أنّك لن تحتاج إلى الدّعم الفنّي من فريق تطوير Angular إن لم تكن تنوي أن يصبح مشروعك كبيرًا بالقدر الّذي يعتبره مهندسو Google مثاليًّا. هناك مشكلة أخرى تظهر عندما تحاول دمج شيفرات Angular مع شيفرات غير Angular وتريدها أن تعمل إلى جانب تغليف Angular للبيانات أو آلية إخراج الصفحة فيها، سيكون عليك عندها الغوص في تفاصيل غامضة في Angular غوصًا عميقا، قارن ذلك مع Backbone التي تعد طبقة رقيقة فوق jQuery فهي أصغر وأقرب للفهم، وإن كان تطبيقك معتمدًا بشكل كبير على ملحقات jQuery فسيكون عليك استخدام Backbone لتزيد من التّحسين في تصميم تطبيقك. أخيرًا، إنّ لطريقة Angular الأساسيّة للرّبط ثنائي الاتجاه بين عناصر واجهة المستخدم وكائنات النمذجة حدودًا تعتمد على مدى تعقيد التّطبيق، وقد تشارك المطوّرون في Facebook خيبة أملهم بربط البيانات ثنائيّ الاتجاه فقالوا: وبالرّغم من كون مكتبة React الخاصة بـFacebook بالكاد تهتمّ بالإخراج ومن ثمّ فهي تعدّ حلّا جزئيًّا بالمقارنة مع Angular، إلّا أنّ تطبيقات طرف الزّبون التي تعتمد عليها تستحقّ التقدير حقًّا. أسباب هامة تدفعك لاستخدام Angularلا شك أنّ Angular هي أشهر مكتبات JavaScript المختصّة بحلول النّمذجة والعرض عالميًّا في هذه الأيام، فقد حصلت على أكثر من 33000 نجمة على GitHub، وربّما ازدادت أكثر من ذلك بكثير منذ وقت كتابة هذه السلسلة إلى اليوم، فقد تربّعت على قمّة قائمة الحلول المطروحة التي تمّت دراستها ومقارنتها بواسطة مشروع TodoMVC وسنناقش الآن بعض أسباب نجاح Angular. الإنتاجية الآنيةإذا كان مشروعك بحاجة إلى واجهة مستخدم معقّدة إلى حدّ ما لتقوم بعمليات إدارة البيانات CRUD على البيانات من طرف الزّبون، فإنّ Angular ستفي بوعدها لك بتحقيق إنتاجيّة شبه آنيّة، فبإضافتك لرشّةٍ من بعض الخصائص المميّزة غلى نصّ HTML الأصلي، والقليل من شيفرات Javascript، ستتمكّن من جعل صفحتك تتفاعل مع المستخدم، في حين كنت ستحتاج إلى الكثير من المهارات وبذل الجهود لتقوم بذلك باستخدام مكتبة أدنى مستوى. الألفةتعتمد Angular على كتابة شيفرات JavaScript السّهلة ونصوص مطابقة تقريبًا لتعليمات HTML، لقد قلت "تقريبًا" لأنّ Angular تقدّم عناصر وخصائص جديدة وبعض الشيفرات المستغربة، إلّا أنها بالمقارنة مع أنظمة القوالب الأخرى تبقى قريبةً جدًّا إلى HTML النّقيّة، وهذا يجعلها سهلة الفهم لأغلبية مطوري الويب. المرونةتتبنّى Angular التّوجّه الحالي نحو التكيف مع واجهات المستخدم في أطر عمل JavaScript، دون الكثير من التّضحية بإنتاجيّتها، فإن كنت تحبّ العمل مع أحد أطر العمل الشهيرة المختصة بواجهة المستخدم مثل Bootstrap فستتمكّن من الاستفادة من الإضافة المقدّمة من مشاريع طرف ثالث مثل AngularUI لتقوم بتكامل سهل. لذا سواء كنت تريد زخرفة صفحة ويب تقليديّة ببعض التّطبيقات التفاعلية هنا وهناك، أو كنت تريد تطوير تطبيقٍ كاملٍ وحيد الصّفحة فإن Angular ستكون الأفضل للعمل ضمن شروطك ومحدداتك. المعايير المستقبليةلا أدري أيّهما أكثر صحّة، إن كانت Angular متبصّرة بالمستقبل أو أنّ Google والدةَ Angular ستصنع المستقبل، إلا أنّه من الواضح أنّ استخدامك لـAngular سيكون طريقة ناجحة لتألف المعايير المقترحة مثل Web Components وأضف إلى ذلك أنّني أتوقّع بقاء Angular واستمرارها لأنّها تواكب ميزات JavaScript المرتقبة مثل Object.observe. مجتمع Angularلابدّ أنّ أحد أقوى الأسباب لاختيار Angular بدلًا من منافساتها هو أنّها الأكثر شيوعًا، فهي تُستخدم الآن في عدد غير محدود من مواقع الويب كثيرة الزيارة، ولكن تذكّر بأنّ الجري وراء الأغلبية ليس الصّواب دومًا وليس لكلّ النّاس ولا في كلّ الأحيان، وعليك أن تدرس متطلّبات مشروعك الحقيقيّة بعناية. عن هذه السلسلةلقد جاءتني فكرة الكتب التفاعلية منذ عدّة سنوات أثناء قراءتي لكتاب إلكترونيّ عن البرمجة باستخدام JavaScript على حاسوبي المحمول، فقد توجّب عليّ لتجربة أحد أمثلة الكتاب أن أقوم بتحميله من موقع النّاشر، ثم البحث عن موضع الملفّ الذي تم تحميله وإيجاد مكان لفكّ ضغطه، ثم الإبحار عبر الكثير من الملفّات إلى أن أعثر على الشّيفرة المطلوبة، وأفتحها أخيرًا في أحد المحرّرات، ثمّ أكتشف بأنّ عليّ إنشاء ملف HTML ليُشغّل السكربت، وبعد كلّ هذا لا تعمل الشيفرة وأكون قد ضيّعت وقتي في مطاردة أماكن الملفّات والارتباطات. لم كلّ هذا؟ إن كنت أقرأ كتابًا عن تقنية front-end على الحاسوب فهل عليّ أن أعاني كلّ تلك المعاناة لأشغّل المثال؟ لقد اكتشفت بعد مدّة قصيرة من ذلك النّسخةَ الأولى لكتاب Marijn Haverbeke المسمّى Eloquent JavaScript واكتشفت بأنّه بإمكاني استخدام مشروعه CodeMirror لأنشر رؤيتي الخاصّة: كتاب إلكترونيّ رائع المظهر، أنيق الحروف وجميل التّصميم، ولكن مع أمثلة حية قابلة للتّعديل والتّشغيل مباشرة من الدّرس ذاته، ولهذا ستجد جميع أمثلة السلسلة داخل محرّرات حيّة تفاعليّة، وستجد مخرجات هذه الشيفرات مباشرة تحت المثال داخل صندوق iframe. <div ng-app=""> <strong>The lucky number {{11+12}}</strong> </div> See the Pen angular-intro by Hsoub Academy (@HsoubAcademy) on CodePen. هيّا حاول تغيير المثال أعلاه الآن، رغم أنك لم تتعلم شيئًا بعد عن Angular، قم ببعض التغييرات على المثال وراقب التّغييرات. ماذا بعد؟سيأخذك الفصل الأوّل من هذه السلسلة في رحلة لطيفة للتّعرف على مبادئ Angular في القولبة templating من طرف الزّبون وفي الرّبط ثنائيّ الاتّجاه، وقد أقرّ العديد من المطوّرين المحترفين في ِAngular بأنهم استفادوا من هذا الفصل رغم أنّ جميع أمثلته بسيطة ويمكن فهمها بلمحة سريعة. ترجمة وبتصرّف للجزء الأول من كتاب: Angular Basics لصاحبه: Chris Smith.
  7. أنشأنا في الدرس السابق مسارات تكتفي بكتابة عبارات في صفحة الويب. سننشئ في هذا الدرس عروضا لتقديمها إلى الزوار حسب الرابط الذي يختارونه؛ نستخدم لهذا الغرض قوالب Blade. هذا الدرس جزء من سلسلة تعلم Laravel والتي تنتهج مبدأ "أفضل وسيلة للتعلم هي الممارسة"، حيث ستكون ممارستنا عبارة عن إنشاء تطبيق ويب للتسوق مع ميزة سلة المشتريات. يتكون فهرس السلسلة من التالي: مدخل إلى Laravel 5.تثبيت Laravel وإعداده على كلّ من Windows وUbuntu.أساسيات بناء تطبيق باستخدام Laravel.إنشاء روابط محسنة لمحركات البحث (SEO) في إطار عمل Laravel.نظام Blade للقوالب. (هذا الدرس)تهجير قواعد البيانات في Laravel.استخدام Eloquent ORM لإدخال البيانات في قاعدة البيانات، تحديثها أو حذفها.إنشاء سلة مشتريات في Laravel.الاستيثاق في Laravel.إنشاء واجهة لبرمجة التطبيقات API في Laravel.إنشاء مدوّنة باستخدام Laravel.استخدام AngularJS واجهةً أمامية Front end لتطبيق Laravel.الدوّال المساعدة المخصّصة في Laravel.استخدام مكتبة Faker في تطبيق Laravel لتوليد بيانات وهمية قصدَ الاختبار. تعرِّف القوالب Templates إطارا عامًّا للعرض، يحدّد أجزاءه والعناصر التي تكوّنه. Blade هو نظام إدارة القوالب والتعامل معها المضمَّن في Laravel. نحصُل بنهاية هذا الدرس على الواجهة التالية. يشمل الدرس المواضيع التالية: توريث القالب.المخطّط الرئيس Master layout.تمديد المخطّط الرئيس.عرض المتغيرات.التعبيرات الشرطية Conditional statements في Blade.الحلقات التكراراية Loops في Blade.تنفيذ تعليمات PHP في Blade,تنتهي جميع ملفات العروض في Blade بالامتداد blade.php. توريث القوالبيمكن تعريف توريث القوالب بأنه تلك الخاصيّة التي تسمح بجمع عناصر مشتركة بين عدة عروض ضمن عرض تعيد استخدامه عند الحاجة مع إضافة العناصر الخاصة بالصفحة؛ مما يجنب تكرار العناصر المشتركة في كلّ مرة. المخطط الرئيستُجمع العناصر المشتركة في عرض يُطلَق عليه المخطّط الرئيس تعيد العروض الأخرى استخدامه وتضيف إليه العناصر الخاصة بها. سننشئ في ما يلي مخطّطا رئيسا نضمنه عناصر مشتركة بين العروض.ننشئ مجلدا جديدا باسم layouts على المسار resources/views/. ثم ننشئ ملفا باسم master.blade.php ضمن المجلد layouts.نضيف الشفرة التالية إلى الملف master.blade.php:<html> <head> <title>@yield('title')</title> </head> <body dir="rtl"> @section('sidebar') هنا يوجد محتوى المقطع sidebar في المخطط الرئيس. @show <div class="container"> @yield('content') </div> </body> </html>قالب Blade عبارة عن وسوم HTML مع تعليمات خاصة بنظام القوالب. يستخدم القالب أعلاه ثلاث تعليمات هي section ،@yield@ و show@. تُستخدم التعليمة section@ لتعريف مقطع (عنصر) من القالب.بينما تستخدم التعليمة yield@ لعرض محتوى مقطع معطى (مقطع باسم title في أول استخدام للتعليمة yield@ في المخطّط السابق).أما التعليمة show@ فتستخدم لعرض محتوى مقطع.تمديد المخطط الرئيسننشئ الآن قالبا يمدّد القالب الرئيس السابق. أنشئ ملفا باسم page.blade.php في المسار resources/views وأضف إليه الشفرة التالية: @extends('layouts.master') @section('title', 'عنوان الصفحة') @section('sidebar') @parent <p>هذا المحتوى ملحق بمحتوى المقطع sidebar الموجود في المخطط الرئيس</p> @endsection @section('content') <p>جسم الصفحة.</p> @endsectionتشير التعليمة ('extends('layouts.master إلى أننا في صدد تمديد المخطّط الرئيس master.blade.php الموجود ضمن المجلد layouts.تعيّن التعليمة ('section('title', 'Page Title قيمة المقطع title المعرّف في المخطّط الرئيس. ثم نعرّف مقطع جديدا باسم sidebar ضمن العرض page. داخل المقطع sidebar نطبع محتوى المقطع الذي يحمل نفس الاسم في المخطّط الرئيس باستخدام التعليمة parent@، نضيف فقرة HTML باستخدام الوسم <p>...</p> ثم ننهي المقطع sidebar بالتعليمة endsection@.تعرّف التعليمة ('section('content محتوى المقطع content وهو في هذا المثال الفقرة <p>جسم الصفحة.</p>.في المخطط الرئيس توجد ثلاثة مقاطع وهي، حسب ترتيب الاستخدام، sidebar ،title و content. بالنسبة للمقطعين title و content فقد اكتفينا بتحديد أماكنهما (التعليمة yield@)؛ أما المقطع sidebar فقد عرفنا محتواه وعرضناه (التعليمتان section@ و show@). في القالب page أعدنا استخدام المخطّط الرئيس ثم أعطينا قيمتي المقطعين title و content المعرّفين سابقا. بالنسبة للمقطع sidebar فقد أعدنا تعريفه مع إعادة استخدام محتوى المقطع المماثل له في الاسم ضمن القالب الرئيس (تعليمة parent@) وإضافة فقرة نصية أخرى. سنضيف مسارا خاصًّا لتجربة عمل نظام القوالب. افتح ملف المسارات routes.php وأضف المسار التالي: Route::get('blade', function () { return view('page'); });احفظ التعديلات. الآن أدخل الرابط في المتصفح: http://larashop.dev/bladeستحصل على الصفحة التالية: استخدام متغيرات في قوالب bladeنعرّف متغيرا باسم name ونمرره إلى قالب blade. افتح ملف المسارات وغيره بحيث يصبح على النحو التالي: Route::get('blade', function () { return view('page',array('name' => 'حسوب')); });احفظ التعديلات. نحدّث القالب page.blade.php لإظهار المتغير الذي يمرره المسار: @extends('layouts.master') @section('title', 'عنوان الصفحة') @section('sidebar') @parent <p>هذا المحتوى ملحق بمحتوى المقطع sidebar الموجود في المخطط الرئيس</p> @endsection @section('content') <h2>{{$name}}</h2> <p>جسم الصفحة</p> @endsectionتستخدم التعليمة {{name$}} لطباعة قيمة المتغير name$. احفظ التعديلات ثم أعد تحميل الصفحة http://larashop.dev/blade. ستلاحظ وجود عنوان h2 جديد هو حسوب التي هي قيمة المتغير الممرًّر لقالب page.blade.php. التعبيرات الشرطية في Bladeيدعم نظام Blade التعبيرات الشرطية التي تحدّد ما يُعرض حسب شرط معرَّف مسبقا. سنمرر متغيرا إلى القالب ثم نختبر قيمته (الشرط) وحسب نتيجة الاختبار يكون العرض. افتح ملف routes.php وعدل المسار بحيث يصبح ما يلي: Route::get('blade', function () { return view('page',array('name' => 'حسوب','day' => 'Friday')); });أضفنا هنا متغيرا جديد باسم day وأعطيناه القيمة Friday. احفظ التعديلات. افتح ملف القالب وعدله على النحو التالي: @extends('layouts.master') @section('title', 'عنوان الصفحة') @section('sidebar') @parent <p>هذا المحتوى ملحق بمحتوى المقطع sidebar الموجود في المخطط الرئيس</p> @endsection @section('content') <h2>{{$name}}</h2> <p>جسم الصفحة</p> <h2>تعبير if الشرطي</h2> @if ($day == 'Friday') <p>إنه يوم الجمعة</p> @else <p>اليوم ليس يوم الجمعة</p> @endif @endsectionإذا كانت قيمة المتغير day هي Friday نعرض الجملة <p>إنه يوم الجمعة</p> وإلا فإن الفقرة المعروضة تصبح <p>اليوم ليس يوم الجمعة</p>. احفظ التعديلات ثم أعد تنزيل الصفحة ولاحظ الفرق. الحلقات التكرارية في Bladeيدعم Blade كل الحلقات التكرارية التي يدعمها PHP. سنرى مثالا لاستخدام الحلقة التكرارية foreach لعرض محتويات مصفوفة Array في قالب Blade. عدل ملف المسارات routes.php على النحو التالي: Route::get('blade', function () { $foods = array('Barbecue','Couscous','Fish'); return view('page',array('name' => 'حسوب', 'day' => 'Friday', 'foods' => $foods)); });عرّفنا مصفوفة تحوي ثلاثة عناصر ومررناها إلى قالب Blade. احفظ التعديلات ثم افتح ملف القالب وعدّله على النحو التالي: @extends('layouts.master') @section('title', 'عنوان الصفحة') @section('sidebar') @parent <p>هذا المحتوى ملحق بمحتوى المقطع sidebar الموجود في المخطط الرئيس</p> @endsection @section('content') <h2>{{$name}}</h2> <p>جسم الصفحة</p> <h2>تعبير if الشرطي</h2> @if ($day == 'Friday') <p>إنه يوم الجمعة</p> @else <p>اليوم ليس يوم الجمعة</p> @endif <h2>حلقة foreach التكرارية</h2> @foreach ($foods as $food) {{$food}} <br> @endforeach @endsectionاحفظ التعديلات ثم أعد تحميل الصفحة ولاحظ طباعة محتوى المصفوفة. تضمين شفرة PHP في قالب Bladeافتح ملف القالب وعدّله بإضافة سطر يستخدم إحدى دوالّ PHP (اخترنا الدالة date). يصبح ملف القالب كما يلي: @extends('layouts.master') @section('title', 'عنوان الصفحة') @section('sidebar') @parent <p>هذا المحتوى ملحق بمحتوى المقطع sidebar الموجود في المخطط الرئيس</p> @endsection @section('content') <h2>{{$name}}</h2> <p>جسم الصفحة</p> <h2>تعبير if الشرطي</h2> @if ($day == 'Friday') <p>إنه يوم الجمعة</p> @else <p>اليوم ليس يوم الجمعة</p> @endif <h2>حلقة Loop التكرارية</h2> @foreach ($foods as $food) {{$food}} <br> @endforeach <h2>شفرة PHP</h2> <p>تاريخ اليوم {{date(' D M, Y')}}</p> @endsectionوضعنا دالة PHP داخل معكوفين مزدوجين لتنفيذها. أعد تنزيل الصفحة http://laravel.dev/blade بعد حفظ التعديلات. العروض المكونة لمشروع Larashopذكرنا في بداية السلسلة أننا نهدف إلى إنشاء موقع للتسوق الإلكتروني. سنستخدم ما تعلمناه في هذا الدرس والدروس السابقة لبناء اللبنة الأولى من هذا المشروع وهي الجانب المرئي. لإعطاء وجه لائق لمشروعنا بحثنا عن تصميم HTML جاهز ووجدنا أن قالب Eshopper من موقع shapebootstrap.net مناسب لما نريد. الخطوة التالية للحصول على تصميم HTML هي تحويله إلى قالب Blade يعمل مع Laravel. سنبدأ أولا بمحتوى الملف المرفق وكيفية استخدامه للحصول على واجهة الموقع المعروضة في أول الدرس، ثم نشرح بعد ذلك تعليمات Blade التي لم نتطرق إليها حتى الآن. محتوى الملف المرفقيحوي الملف المرفق: عروض المشروع larashop.ملف المتحكم Front الذي حدثناه ليحمّل عروض المشروع.ملف المسارات.الموارد المستخدمة في القالب: ملفات CSS، الصور، ملفات Js والخطوط.العروضيحوي المشروع العروض التالية: layout: وهو المخطط الرئيس ويوجد في المجلد الفرعي layouts.sidebar: وهو عرض مشترك بين صفحاتٍ عدة، يوجد في مجلد فرعي باسم shared.blog: صفحة المدونة.blog_post: صفحة منشور على المدونة.cart: صفحة سلة المشتريات.checkout: صفحة الدفع.contact_us: صفحة الاتصال.home: الصفحة الرئيسية.login: صفحة تسجيل الدخول.products: صفحة المنتجات.product_details: تفاصيل منتج.يجب أن توضع العروض في المجلد resources/views. تبدو بنية هذا المجلد كالتالي بعد إضافة العروض إليها. المتحكم Front وملف المساراتأنشأنا في الدرس السابق متحكم Front لكنه يكتفي بعرض نص في المتصفح. سنحتاج لتعديله ليحمِّل العروض. عدّلنا قليلا على ملف المسارات routes.php لتعريف مسار لكل تصنيف أو علامة تجارية. مجلد publicيحوي الموارد (صور، css، js) التي تحتاجها واجهة الموقع للعمل. يجب وضع هذه الموارد في مجلد public في المشروع. ملحوظة: تأكد بعد نقل الملفات من المرفق إلى أماكنها في المشروع من أن الأذون ما زالت على الحال التي ضبطناها بها في درس الإعداد (لينكس). الآن وبعد التأكد من نقل الملفات من المرفق إلى أماكنها الصحيحة، اذهب إلى الصفحة الرئيسية http://larashop.dev وستظهر واجهة الموقع. نأخذ وقتا لشرح المشروع. تحويل صفحات HTML إلى قالب Bladeقوالب Blade هي، كما رأينا، وسوم HTML مع تعليمات خاصّة بنظام القوالب؛ لذا نحافظ على البنية العاملة لملفات HTML المكوِّنة للموقع مع إجراء تغييرين أساسيين: جمع العناصر المشتركة بين مختلف الصفحات ضمن صفحة خاصة.إبدال الروابط في صفحة HTML بتعليمات Blade.يُترجَم تحويل صفحات HTML إلى قوالب Blade بالخطوات التالية: إنشاء مخطّط رئيس تُمدده جميع الصفحات. يحوي المخطط الرئيس العناصر المشتركة بين الصفحات.إنشاء صفحة (عرض) لكل مسار من المسارات المعرَّفة في الدرس السابق.تحديث المتحكم Front.php لتحميل العروض.إضافة الموارد (الصور، ملفات css وjs) إلى مجلد public في تطبيق Laravel.يحوي الملف المرفق بهذا المقال عروض Blade الناتجة عن تحويل صفحات HTML إلى قوالب Blade، إضافة إلى ملف المسارات الخاص بمشروع Larahop، المتحكم Front.php والموارد مثل الصور وملفات css (مجلد public). المخطط الرئيسأنشأنا ملفا لمخطط رئيس باسم layout.blade.php على المسار resources/views/layouts. في هذا الملف توجد وسوم HTML مع تعليمات Blade؛ بعضها لم نتطرق له بعد: تعليمة {{url}}:{{url('products')}}تنشئ التعليمة {{url}} رابط URL بالنسبة للمجلدpublic. في المثال أعلاه فإن الرابط الذي ترجعه التعليمة هو http://larashop.dev/products؛ وهو الرابط الذي تتعامل معه دالة المسار products/. لإنشاء رابط إلى الصفحة الرئيسية فالتعليمة تكون: {{url('')}}نأخذ أيضا المثال التالي الموجود في المخطط الرئيس: <li><a href="{{url('products')}}" {{$page == 'products' ? 'class=active' : ''}}>Products</a></li>نلاحظ استخدام التعليمة url لإعطاء قيمة الرابط ضمن وسم <a>. في نفس السطر توجد تعليمة تشبه تعليمةً مألوفة في PHP وهي {{$page == 'products' ? 'class=active' : ''}}هنا نتحقق من قيمة المتغير page فإذا كانت تساوي products نضيف صنف CSS يسمّى active إلى الوسم. تعليمة {{asset}}:{{asset('css/bootstrap.min.css')}}تُرجع الدالة asset مسار مجلد public (أي عنوان الموقع) وتلحق به المعطى الممرَّر إليها. في السطر أعلاه ترجِع الدالةُ القيمةَ http://larashop.dev/css/bootstrap.min.css. تُستخدم هذه الدالة كثيرا لإضافة ملفات CSS، الصور وملفات JavaScript كما في الأمثلة التالية المأخدوذة من المخطط الرئيس. <!-- CSS --> <link href="{{asset('css/bootstrap.min.css')}}" rel="stylesheet"> <!-- صورة --> <img src="{{asset('images/home/iframe1.png')}}" alt="" /> <!-- JavaScript --> <script src="{{asset('js/jquery.js')}}"></script>يمكن من خلال المعطيات الممرَّرة إلى الدالة asset في الأمثلة أعلاه اسنتاجُ أننا نستخدم مجلدًّا فرعيا في public لكل نوع من الموارد (CSS، الصور، JS إضافة للخطوط fonts). إن نظرت جيدا في ملف المخطط الرئيس فستميز ثلاثة أجزاء فارقة: الجزء الأول ويحوي الوسوم المؤسِّسة للجزء الأعلى المشترك بين مختلف صفحات الموقع، ويحوي ترويسة الموقع وقائمته الرئيسية.الجزء الثاني تمثله التعليمة ('yield('content@ وتلعب دور ماسك مكان Place holder. ستحدّد العروض الأخرى الممدّدة للمخطط الرئيس محتوى ماسك المكان.الجزء الثالث وبه التذييل المشترك بين صفحات الموقع.إن فهمت جيدا آلية عمل نظام القواعد فستعرف أن الجزأين الأول والثالث لا يتغيران بتغير الصفحة التي يطلبها الزائر. ما يتغير هو فقط الجزء الثاني الذي يعبئه نظام القوالب بمحتوى مختلف حسب الصفحة. الصفحات المشتقةننتقل الآن للعروض الخاصة بكل صفحة. يمدّد كل عرض المخطّط الرئيس. في ما يلي المنحى العام للعروض: @extends('layouts.layout') @section('content') <!--وسوم HTML --> @include('shared.sidebar') <!--وسوم HTML --> @endsectionيمدد العرض المخطط الرئيس بتنفيذ التعليمة التالية:@extends('layouts.layout')يعرّف مقطعا باسم content:@section('content')يُحمَّل محتوى المقطع content في المكان المعرّف بنفس الاسم في المخطّط الرئيس. ينتهي المقطع عند التعليمة endsection@. نضمِّن محتوى العرض sidebar الموجود في المجلد resources/views/shared:@include('shared.sidebar')تستخدم التعليمة include لتحميل محتوى عرض ضمن آخر. عرض sidebar الذي يمثل شريطا جانبيا للموقع موجود في أغلب الصفحات. نضيف وسوم HTML المأخوذة من قالب E-Shopper لتكوين الصفحة.تحديث المتحكم Frontنحدّث المتحكم Front.php ليحمل العروض بدلا من الاكتفاء بطباعة نص في الصفحة. يصبح محتوى المتحكم بعد التحديث على النحو التالي. تحمل كل دالّة العرض الموافق لها حسب جدول الدرس السابق. <?php namespace App\Http\Controllers; use App\Http\Controllers\Controller; class Front extends Controller { public function index() { return view('home', array('page' => 'home')); } public function products() { return view('products', array('page' => 'products')); } public function product_details($id) { return view('product_details', array('page' => 'products')); } public function product_categories($name) { return view('products', array('page' => 'products')); } public function product_brands($name, $category = null) { return view('products', array('page' => 'products')); } public function blog() { return view('blog', array('page' => 'blog')); } public function blog_post($id) { return view('blog_post', array('page' => 'blog')); } public function contact_us() { return view('contact_us', array('page' => 'contact_us')); } public function login() { return view('login', array('page' => 'home')); } public function logout() { return view('login', array('page' => 'home')); } public function cart() { return view('cart', array('page' => 'home')); } public function checkout() { return view('checkout', array('page' => 'home')); } public function search($query) { return view('products', array('page' => 'products')); } } جرب تصفح الموقع والتنقل بين صفحاته. إذا اتبعت خطوات الدرس على النحو المشروع فستكون قادرا على التنقل بين صفحات الموقع دون مشاكل. يمكنك أيضا فتح الشفرة المصدرية لصفحات موقع الويب ومقارنتها بما كتبناه في قوالب Blade. خاتمةنحصل بنهاية الدرس على تصميم أنيق لصفحات الموقع، استخدمنا لإنشائه Blade الذي يوفر نظاما فعّالا لكتابة قوالب العروض في Laravel مما يتيح تجنب تكرار العناصر المشتركة. بانتهاء هذا الدرس نكون قد أنهينا أساس جزء العرض من بنية MVC. الدروس التالية من السلسلة تتناول بقية الأجزاء. ترجمة -وبتصرّف- لمقال Laravel 5 Blade Template لصاحبه Rodrick Kazembe.
  8. بايثون (Python) لغة ممتازة لبرمجة الويب نظرا لمرونتها وأدائها العالي. أطُرُ الويب يمكن أن تجعل برمجة تطبيقات الويب أبسط بكثير لأنها توصل العديد من المكونات الضرورية مع بعضها للحصول على تطبيق قوي. في حين تهدف بعض أطر الويب إلى توفير كل شيء يمكن أن يرغب به المستخدم لتطوير تطبيق ما، هناك أطر أخرى تحاول البقاء بعيدا والاهتمام بالأهم، Bottle إطار للغة بايثون يندرج ضمن النوع الثاني. إنه بالفعل خفيف ومصغر، لكنه يجعل تطوير التطبيقات سهلا وسريعا. في هذا المقال، سنعرض طريقة ضبط واستعمال إطار العمل المصغّر Bottle لإنشاء تطبيقات ويب بسيطة وباستعمال لغة Python. كيفية تثبيت Bottleتثبيت Pythonإطار Bottle مبني على لغة Python وهي مثبتة مسبقا على أنظمة لينكس وOS X، أما بالنسبة لنظام Windows فيمكن تنصيب Python 2 عبر تحميلها من موقعها الرسمي. الجدير بالذّكر أن هذا الدّرس خاصّ بالإصدار الثاني من بايثون (Python 2.x). تثبيت وتفعيل بيئة وهميةسنثبت حزمة virtualenv لعزل مشروع بايثون الخاص بنا من بيئة بايثون الخاصة بالنظام. يمكننا فعل هذا بسهولة عبر تنصيب أداة إدارة الحزم الخاصة ببايثون واسمها pip. لتنصيب pip على Ubuntu يكفي تنصيب virtualenv مباشرة، فهي إحدى اعتمادياته: sudo apt-get update sudo apt-get install python-virtualenv بالنسبة لأنظمة OS X و Windows يمكن تنصيبه بتنصيب pip ثم تنصيب virtualenv عبرها: أو احفظ هذا الملف على جهازك، ثم نفذ الأمر التالي على نافذة الأوامر بالنسبة لـ Windows أو Terminal بالنسبة لـِ Mac OS X، في كلا النظامين، ستحتاج أن تنفذ الأمر التالي بصلاحيات المدير: python get-pip.pyبرنامج virtualenv عبارة عن بيئة وهمية لبايثون لتنصيب مكتبات بايثون في معزل عن بيئة بايثون الحقيقية على النظام، هذا مهم جدا لعزم اعتماديات مشروع الويب الخاص بك وما يحتاجه من حزم عن الحزم العامة المنصبة على كافة النظام، تحتوي بيئة مشاريع بايثون الخاصة بنا على حزمنا فقط، لكي لا تؤثر على نظام التشغيل ككل. سننشئ مجلد المشاريع projects في مجلد Home أو ماشابه، ثم نقوم بإنشاء بيئة وهمية في هذا المجلد، بالنسبة لـ Windows يمكن تنفيذ هذه الأوامر باستخدام طرفية PowerShell: mkdir ~/projects cd ~/projects virtualenv --no-site-packages venv هذا سينشئ مجلدا باسم venv داخل مجلد المشاريع. لقد تُبِّتت بعض أدوات بايثون داخل هذا المجلد وأنشِئَت بنية مجلد لتثبيت أدوات إضافية. يجب علينا تفعيل البيئة الوهمية قبل بداية العمل على مشروعنا : source venv/bin/activateالأمر المُخرَج سيتغير لعكس حقيقة أننا نقوم بعمليات داخل بيئة وهمية الآن. وسيكون كالتالي: (venv)user@Hostname:~/projects$إذا أردت الخروج من البيئة الوهمية، يمكنك في أي وقت كتابة الأمر التالي : deactivateملاحظة: لا تخرج من البيئة الوهمية حاليا. تثبيت Bottleأداة pip تُمكنك من تثبيت حزم بايثون بسهولة من دليل حزم بايثون Python package index، وهو مستودع مُفهرس لمكتبات بايثون. إذا أردنا البحث عن حزم بايثون المتعلقة بـِ Bottle، يمكن تنفيذ: pip search bottleسنبدأ بتثبيت حزمة Bottle فقط: pip install bottleبعد إنتهاء العملية، سنتمكن من استخدام إطار Bottle داخل تطبيقاتنا. إنشاء أول تطبيق باستخدام Bottleكمعظم الأطر، Bottle يطبق نسخة من النمط البرمجي MVC .MVC اختصار لـِ Model, view, controller نموذج،عرض،هيكل؛ وهو أمر للفصل بين عوامل مختلفة بين واجهة المستخدم والمنطق البرمجي. النموذج model تمثيل لمجموعة من البيانات وهو مسؤول عن تخزين، استعلام، وتحديث البيانات.العرض view مسؤول عن كيفية تقديم المعلومة للمستخدم. ويُستخدم لتشكيل وضبط عرض البيانات.الهيكل controller هو مركز العمليات الرئيسي للتطبيق، والذي يقرر كيفية الإجابة لطلبات المستخدم.تطبيقات bottle يمكنها أن تكون بسيطة للغاية. في شكلها البسيط يمكنها تنفيذ جميع المكونات في ملف واحد. سنقوم بإنشاء تطبيق "مرحبا بالعالم" لعرض كيفية العمل. باستعمال محررك المفضل (في هذه الحالة سنستعمل محرر سطر الأوامر nano، بالنسبة لنظام Windows يمكنك استعمال محرر مرئي)، أنشئ تطبيق بايثون باسم hello.py: nano hello.pyفي هذا الملف، سنقوم أولا باستدعاء بعض الوظائف من حزمة Bottle. الأمر الذي سيمكننا من استخدام أدوات الإطار داخل تطبيقنا: from bottle import route, runهذا السطر يخبر برنامجنا بأننا نحتاج إلى استيراد المسار route وتشغيل النماذج modules من حزمة Bottle. نموذج run الذي قمنا باستيراده يمكن أن يُستعمل لتشغيل التطبيق في خادوم التطوير، الشيء الذي يعتبر جيداً لرؤية نتائج برنامجك بسرعة. نموذج route مسؤول عن إخبار التطبيق بالتعامل مع أي من طلباتURL باستخدام أي من دوال بايثون.تطبيقات Bottle تنفّذ التوجيه routing باستدعاء دالّة بايثون واحدة لكلّ طلب من طلبات URL. وتقوم بعد ذلك بإرجاع نتائج الدالة للمُستخدم. نستطيع إضافة توجيه سيُوافق رابط URL /hello from bottle import route, run @route('/hello')هذا التوجيه سيوافق رابط URL /hello عندما يتمّ طلب هذا المسار على الخادوم. ستُنفّذ الدّالة التّابعة مباشرة: # -*- coding: utf-8 -*- from bottle import route, run @route('/hello') def hello(): return u"<h1>مرحباً بالعالم</h1>"ملاحظات: السطر # -*- coding: utf-8 -*- مسؤولٌ عن دعم اليونيكود وبالتالي اللغة العربية، فهو يخبر مفسر بايثون أن هذا الملف به أحرف unicode غير الأحرف الانجليزية.حرف u (اختصارًا لـ unicode) قبل النص العربي مهم أيضا لإخبار بايثون أن هذه السلسلة النصية بالضبط تحتوي على أحرف يونيكود وبالتالي يعامل السلسلة النصية بطريقة خاصة.هذه الدّالة بسيطة جدّاً، لكنّها كافية لإكمال المتطلّب الوحيد لدالّة توجيه: تقوم بإرجاع قيمة يُمكن عرضها على المتصفّح. في هذه الحالة، القيمة عبارة عن نص HTML. يمكننا حذف وسم h1 وستُعرض نفس القيمة بشكل غير منسّق. أخيراً، نحتاج إلى تشغيل تطبيقنا باستعمال خادوم التّطوير development server: # -*- coding: utf-8 -*- from bottle import route, run @route('/hello') def hello(): return u"<h1>مرحباً بالعالم</h1>" run(host='0.0.0.0', port=8080)هذا السطر سيُشغل الخادوم. بتمرير المعامل 'host='0.0.0.0 الأمر سيقوم بإرجاع المحتوى لأي حاسوب، وليس فقط الحاسوب المحليّ. هذا مهم بما أن تطبيقنا مستضاف عن بعد. معامل port يقوم بتحديد المنفذ الذي سنستخدمه. يُمكننا تشغيل التطبيق بتنفيذ الأمر: python hello.pyيمكنك زيارة التطبيق بمتصفح الويب عن طريق الذهاب إلى عنوان IP الخاصّ بك (إن كنت تعمل على خادوم) أو localhost إن كنت تعمل محليا، متبوعاً برقم المنفذ الذي قمنا باختياره (8080)، متبوعا بالتوجيه الذي أنشأناه (hello/): http://localhost:8080/helloسيظهر لك التالي: يُمكن إيقاف الخادوم في أي وقت بالضغط على CTRL-C في نافذة الطرفيّة أو سطر الأوامر. تطبيق مبدأ MVCلقد نفّذنا الآن تطبيقنا الأول. لقد كان بسيطاً بالتأكيد، ولكنّه لا ينفّذ مبادئ MVC، أو لا يقوم بشيء مثير للاهتمام. لنحاول جعله تطبيقاً أكثر تعقيداً هذه المرّة. إنشاء النموذجلنبدأ بنموذجنا، هذا هو الجزء الخاص بالتّعامل مع تخزين البيانات في برنامجنا. يمكن لـ Bottle بسهولة استخدام مجموعة من الواجهات الخلفية backends للبيانات باستعمال الإضافات. سنستخدم ملف SQLite لقاعدة بيانات التطبيق. هذه قاعدة بيانات بسيطة للغاية مُصمّمة للعمليّات الخفيفة التي يُمكن أن يقوم بها برنامجنا. ثبّت أولا SQLite، على Ubuntu يمكن ذلك عبر الأمر التالي: sudo apt-get install sqliteبالنسبة لـ Windows و OS X يمكن تثبيتها من على موقعها الرسمي. نحتاج كذلك إلى تحميل وتثبيت إضافة Bottle التي تُخوّلنا لاستعمال قواعد البيانات هذه: pip install bottle-sqliteالآن نحن نمتلك المكوّنات الأساسية، نستطيع أن ننشئ قاعدة بيانات بسيطة لتخزين بياناتنا فيها. سنقوم بإنشاء ملفّ بايثون لتوليد قاعدة بيانات SQLite مع بعض البيانات بداخلها عندما ننفّذ الملفّ. يُمكننا فعل الأمر على مترجم بايثون، لكنّ هذه الطريقة تجعل الأمر أسهل للتكرار. سننشئ ملفا باسم picnic_data.py. nano picnic_data.pyهنا نقوم باستيراد حزمة SQLite. بعد ذلك، يمكننا تنفيذ أمر يُنشئ جدولا ويدخل بيانات فيه. وفي الأخير، ننّفذ التغييرات: # -*- coding: utf-8 -*- import sqlite3 db = sqlite3.connect('picnic.db') db.execute("CREATE TABLE picnic (id INTEGER PRIMARY KEY, item CHAR(100) NOT NULL, quant INTEGER NOT NULL)") db.execute("INSERT INTO picnic (item,quant) VALUES ('خبز' , 4')") db.execute("INSERT INTO picnic (item,quant) VALUES ('جبن', 2)") db.execute("INSERT INTO picnic (item,quant) VALUES ('عنب', 30)") db.execute("INSERT INTO picnic (item,quant) VALUES ('كعك', 1)") db.execute("INSERT INTO picnic (item,quant) VALUES ('مشروبات', 4)") db.commit()احفظ الملفّ وأغلقه يُمكننا تنفيذ الملفّ، الشيء الذي سينشئ ملفّ قاعدة بيانات باسم نزهة.db داخل المُجلّد: python picnic_data.py نموذج تطبيقنا الآن مكتمل. يمكننا الآن استنتاج أن النموذج يملي على التطبيق كيفية تعامل جزء التحكم مع البيانات. إنشاء هيكل التّطبيقبعد أن أنشأنا قاعدة بيانات التّطبيق، يمكننا البدء في تطوير تطبيقنا الرئيسي. هذه العمليّة ستكون وظيفة الهيكل بشكل أساسي. وسيكون الملفّ الأكثر شبها بتطبيقنا الأول. أنشئ ملفّا باسم picnic.py لحفظ تطبيقنا الرئيسي: nano picnic.pyداخل هذا الملفّ، نحتاج إلى استيراد بعض الأشياء من حزمة Bottle، تماما كما سبق. نحتاج إلى بعض النّماذج الإضافية التي لم يسبق لنا أن استخدمناها. إضافة إلى ذلك، نحتاج إلى استيراد وظيفة SQLite: # -*- coding: utf-8 -*- import sqlite3 from bottle import route, run, templateتالياً، سنقوم بتعريف توجيه يوافق مسار URL على الشكل التالي picnic/: # -*- coding: utf-8 -*- import sqlite3 from bottle import route, run, template @route('/picnic')سنضيف دالّة للاتصال بقاعدة البيانات، لتحضر البيانات من الجدول، والاتصال مع العرض View لتقديم الصفحة. وفي الأخير، ستُرجع المخرج إلى المُستخدم. # -*- coding: utf-8 -*- import sqlite3 from bottle import route, run, template @route('/picnic') def show_picnic(): db = sqlite3.connect('picnic.db') c = db.cursor() c.execute("SELECT item,quant FROM picnic") data = c.fetchall() c.close() output = template('bring_to_picnic', rows=data) return outputبعد ذلك سنضيف أمر run لكي نقوم بتشغيل التطبيق: # -*- coding: utf-8 -*- import sqlite3 from bottle import route, run, template @route('/picnic') def show_picnic(): db = sqlite3.connect('picnic.db') c = db.cursor() c.execute("SELECT item,quant FROM picnic") data = c.fetchall() c.close() output = template('bring_to_picnic', rows=data) return output run(host='0.0.0.0', port=8080)احفظ وأغلق الملفّ. نقوم بالاتصال مع قاعدة البيانات باستعمال: db = sqlite3.connect('picnic.db')نقوم باستعلام قاعدة البيانات وجلب جميع القيم بالأسطر الأربعة الموالية. السطر الذي نقوم فيه بالإتصال إلى "العرض" لتشكيل البيانات هو: output = template('bring_to_picnic', rows=data)السطر يقوم بالاتصال بقالب (عرض) باسم bringtopicnic.tpl لتشكيل البيانات وتقديمها. حيث يقوم بتمرير المتغير data لمتغير القالب rows. سنقوم بإنشاء ملفّ القالب هذا في المرحلة التالية. إنشاء العرضالآن نحن نمتلك كلّا من النموذج والهيكل، الشيء الوحيد المتبقّي هو إنشاء العرض. يُمكن القيام بهذا بسهولة بالاستعانة بمحرك القوالب المُدمج مع Bottle. سيبحث التطبيق عن قالب موافق للاسم الذي عرّفناه في الدّالة السابقة، الملفّ يجب أن ينتهي ب .tpl يُمكن للملفّ أن يكون إمّا في مجلّد المشروع، أو داخل مجلّد باسم "view”. أنشئ ملفّا باسم يوافق الاسم الذي عرّفناه في دالّة القالب: nano bring_to_picnic.tplفي هذا الملفّ، يُمكننا دمج HTML والبرمجة معاً. سيكون ملفّنا بسيطا جدّاً. سنقوم باستعمال حلقة تكرار لإنشاء جدول يقوم بعرض بيانات النّموذج: <html dir="rtl" style="font:droid arabic naskh"> <body> <h1>أشياء لإحضارها إلى النّزهة</h1> <table> <tr><th>المكوّن</th><th>الكميّة</th></tr> %for row in rows: <tr> %for col in row: <td>{{col}}</td> %end </tr> %end </table> </body> </html>هذه الأسطر ستقوم بتقديم صفحتنا بصيغة HTML. لغة القالب التي رأينها هي ببساطة لغة بايثون. متغيّر row الذي قمنا بتمريره إلى القالب مُتاح للاستخدام عند تصميم المُخرجات. يُمكننا كتابة أسطر بايثون بتقديم علامة "%". يُمكننا الوصول إلى المُتغيّرات بداخل HTML باستخدام "{{var}}". احفظ الملفّ وأغلقه. مشاهدة النتائجتطبيقنا مكتملٌ الآن ويمكننا تشغيل الملفّ الرئيسي: python picnic.pyيمكننا رؤية النتائج بزيارة عنوان IP متبوعا برقم المنفذ، متبوعاً بالتوجيه picnic/ http://localhost:8080/picnic ختاماًإلى هذه النقطة، يجب أن تكون قادراً على رؤية كيف يُمكنك بناء تطبيقات معقّدة باستخدام الإطار المُصغر والبسيط Bottle. رغم أنّ أمثلتنا بسيطة، إلا أنّك الآن تستطيع بسهولة أن تطوّر تطبيقات إلى القيام بوظائف متقدّمة. نظام إضافات Bottle يعدّ أيضاً أصلاً مهمّاً. الإضافات مُشارَكةُ بنشاط في المجتمع وإضافة وظائف أكثر تعقيداً لتطبيقك يُمكن أن يُنفّذ بسهولة باستخدام الإضافات. هناك طريقة سهلة للبحث عن الإضافات وهي عن طريق استعمال الأمر: pip search bottleهذا سيُعطيك فكرة عن بعض الإضافات الأكثر شعبية. ترجمة -وبتصرّف- للمقال: How To Use the Bottle Micro Framework to Develop Python Web Apps.
  9. بعد أن انتهينا من إعداد قواعد البيانات والطرق (routes)، سوف نتحدث في هذا الجزء عن المتحكِّمات (Controllers)، النماذج (مع العلاقات)، والعروض views. (بما في ذلك نظام blade ومخططات المحتوى). مساعدات النماذج Form Helpers في Laravelبعد كل هذه الإعدادات، إذا ذهبنا الآن (في المتصفح) إلى projects/ فسوف نحصل على صفحة فارغة فما هو السبب؟ حسنا، لنكتشف ذلك، قم بتنفيذ الأمر php artisan route:list على سطر الأوامر مرة أخرى وأنظر إلى هذا السطر: +--------+----------+----------------------------------------+------------------------+-------------------------------------------------+------------+ | Domain | Method | URI | Name | Action | Middleware | +--------+----------+----------------------------------------+------------------------+-------------------------------------------------+------------+ | | GET|HEAD | projects | projects.index | App\Http\Controllers\ProjectsController@index | | +--------+----------+----------------------------------------+------------------------+-------------------------------------------------+------------+يبدو أن رابط projects/ يقوم بتحميل الوظيفة Index الخاصة بـ ProjectsController، لذلك سنقوم بفتح app/Http/controllers/ProjectsController.php/ وسنقوم بتحديث التابع (method) لنقوم بربطه إلى عرض سوف نقوم بإنشائه. قم بإضافة السطر ;('return view('projects.index إلى الوظيفة ()index كالتالي: public function index() { return view('projects.index'); }بما أننا نستخدم في هذا الدرس نظام Blade للقَوْلَبة سنقوم بإنشاء ملف resources/views/projects/index.blade.php/ وسنكتب نص قصير داخله، ثم سنقوم مرة أخرى بالذهاب إلى projects/ في المتصفح، إذا كان كل شيء يعمل جيدا فسوف تجد النص الذي كتبته بالأعلى. والآن قُم بنفس الشيء لتابع المتحكِّم create عن طريق إضافة السطر ;('return view('projects.create إلى التابع ()create كالتالي: public function create() { return view('projects.create'); }إن إظهار محتويات العرض view هو أمر رائع، لكن ماذا لو كنا نملك أكثر من صفحة واحدة في موقعنا ؟ في هذه الحالة، سوف نحتاج إلى قالب ثابت لجميع الصفحات، أي أننا نحتاج إلى عرض محتويات العرض view داخل قالب HTML بسيط، وسنقوم بهذا عن طريق مخططات المتحكِّمات. من أجل عمل مخططات المتحكِّمات سنقوم بالخطوات التالية: إنشاء مخطط العرض، وبما أن Laravel توفر لنا واحدة جيدة تدعى app.blade.php لذلك سنوفر الوقت وسنقوم بإستخدامها. لاحظ أن قرب أسفل المخطط هنالك سطر ('@yield('content وهذه دالة ستقوم بتحميل محتوانا الحالي.الإشارة إلى مخططك في عرضك view باستخدام ('extends('app@ ولفها عن طريق كتلة ('section('content@، مثل التالي: @extends('app') @section('content') This is my /resources/views/projects/index.blade.php file! @endsection سنقوم الآن بتنفيذ هذه الخطوات بإنشاء عروض show.blade.php و create.blade.php و index.blade.php في مجلد resources/views/projects/ مع الشيفرات (markup) المذكورة أعلاه وقُم بتغيير اسم الملف إذا كان الأمر ضروريا، ثم قُم بعمل تحديث للصفحة projects/ في متصفحك، وسوف ترى هيكل app.blade.php حول محتويات عرضك (view). الربط بين الطريق (route) والنموذجسيوفر Laravel بشكل افتراضي معرف رقمي ID للعديد من توابع متحكِّمات الموارد مثل ()show و ()edit و ()update و ()destroy، وهذا الأمر سيضيف العديد من الشيفرات الجاهزة (boilerplate) التي نحتاج إلى كتابتها، مثل الحصول على مثيل النموذج والتأكد من وجودها وغيرها. ولهذا سوف نستخدم إحدى مميزات Laravel والتي تدعى الربط بين الطريق و النموذج route model binding، فبدل من توفير متغير id$، سوف نقوم بإعطاء الوظيفة (method) كائن project$ أو task$. قُم بفتح app/Http/routes.php/ وأضف هذين السطرين (أول سطرين بعد التعليق): // Provide controller methods with object instead of ID Route::model('tasks', 'Task'); Route::model('projects', 'Project'); // Use slugs rather than IDs in URLs Route::bind('tasks', function($value, $route) { return App\Task::whereSlug($value)->first(); }); Route::bind('projects', function($value, $route) { return App\Project::whereSlug($value)->first(); }); Route::resource('projects', 'ProjectsController'); Route::resource('projects.tasks', 'TasksController'); وفي TasksController و ProjectsController (ملفات app/Http/Controllers/ProjectsController.php/ و app/Http/Controllers/TasksController.php/) قُم باستبدال كل وظيفة (method) مُعرف بمرجع id$ بـ task$ أو project$ مثل التالي: // public function edit($id) public function edit(Project $project)لا تنسَ إضافة use App\Task و use App\Project في أعلى المتحكِّمات حتى نشير إلى هذه النماذج. لا تقلق إذا لم تفهم هذه التغييرات، فسنقوم بعرض الشيفرة البرمجة الكاملة لـ TasksController و ProjectsController لاحقا. وفي هذه المرحلة يمكنك تمرير الكائن (object) إلى عرضه في كل متحكِّم لوظيفة (method) الإظهار والتعديل والتحديث حتى نستطيع استخدامهم لاحقا: public function edit(Project $project) { return view('projects.show', compact('project')); }سوف يحتاج TasksController إلى بضعة تعديلات طفيفة أخرى، وبما أننا نستخدم الموارد المضمّنة nested resources، سيخبرنا php artisan route:list أن جميع طرق task تحتوي على قناع {projects} بالإضافة إلى أن بعضها يستقبل قناع {tasks} أيضا. و كنتيجة لذلك، سيتم تمرير مثيل Project كمُعامل أول لتوابع المتحكِّمات (controller methods)، لذلك قُم بتحديثها وقٌم بتمرير متغير project$ الجديد. إذا لم تفهم خطوات الإضافة لـ TasksController و ProjectsController فهذه هي الشيفرة الكاملة للملفين بعد كل التعديلات: // /app/Http/Controllers/ProjectsController.php <?php namespace App\Http\Controllers; use App\Project; use App\Http\Requests; use App\Http\Controllers\Controller; use Illuminate\Http\Request; class ProjectsController extends Controller { /** * Display a listing of the resource. * * @return Response */ public function index() { $projects = Project::all(); return view('projects.index', compact('projects')); } /** * Show the form for creating a new resource. * * @return Response */ public function create() { return view('projects.create'); } /** * Store a newly created resource in storage. * * @return Response */ public function store() { // } /** * Display the specified resource. * * @param \App\Project $project * @return Response */ public function show(Project $project) { return view('projects.show', compact('project')); } /** * Show the form for editing the specified resource. * * @param \App\Project $project * @return Response */ public function edit(Project $project) { return view('projects.edit', compact('project')); } /** * Update the specified resource in storage. * * @param \App\Project $project * @return Response */ public function update(Project $project) { // } /** * Remove the specified resource from storage. * * @param \App\Project $project * @return Response */ public function destroy(Project $project) { // } } // /app/Http/Controllers/TasksController.php <?php namespace App\Http\Controllers; use App\Project; use App\Task; use App\Http\Requests; use App\Http\Controllers\Controller; use Illuminate\Http\Request; class TasksController extends Controller { /** * Display a listing of the resource. * * @param \App\Project $project * @return Response */ public function index(Project $project) { return view('tasks.index', compact('project')); } /** * Show the form for creating a new resource. * * @param \App\Project $project * @return Response */ public function create(Project $project) { return view('tasks.create', compact('project')); } /** * Store a newly created resource in storage. * * @param \App\Project $project * @return Response */ public function store(Project $project) { // } /** * Display the specified resource. * * @param \App\Project $project * @param \App\Task $task * @return Response */ public function show(Project $project, Task $task) { return view('tasks.show', compact('project', 'task')); } /** * Show the form for editing the specified resource. * * @param \App\Project $project * @param \App\Task $task * @return Response */ public function edit(Project $project, Task $task) { return view('tasks.edit', compact('project', 'task')); } /** * Update the specified resource in storage. * * @param \App\Project $project * @param \App\Task $task * @return Response */ public function update(Project $project, Task $task) { // } /** * Remove the specified resource from storage. * * @param \App\Project $project * @param \App\Task $task * @return Response */ public function destroy(Project $project, Task $task) { // } }لاحظ أنه إذا قمت بتحديث صفحة projects/project-1/ سيبقى كل شيء يعمل. عرض نماذجناصفحة عرض قائمة المشاريع، حان الآن وقت البدء بعرض المشاريع والمهام، قٌم بفتح projects/ في متصفحك. وبالاعتماد على php artisan route:list، ستكون هذه الصفحة هي صفحة عرض المشاريع، لذلك قُم بفتح resources/views/projects/index.blade.php/ وأضف التالي: @extends('app') @section('content') <h2>Projects</h2> @if ( !$projects->count() ) You have no projects @else <ul> @foreach( $projects as $project ) <li><a href="{{ route('projects.show', $project->slug) }}">{{ $project->name }}</a></li> @endforeach </ul> @endif @endsectionسوف أشرح بعض النقاط في هذه الشفرة البرمجية: استخدمنا في هذه الشفرة لغة Blade للقَوْلَبة، لذلك فإن if و foreach تتحكم في تدفق flow الدوال بإضافة إلى أنها دالة طباعة (الأقواس المعقوفة المزدوجة).تأكدنا إن كان هنالك أي مشروع لعرضه، إن كان هنالك أية مشاريع فسيقوم بعرضها وإن لم يكن تظهر رسالة تخبرك بذلك.استدعينا مساعد ()route مع طريق ذا اسم (يمكنك رؤية قائمة الطرق ذات الاسم عن طريق php artisan route:list) لربط كل مشروع مع صفحة تفاصيله.سوف تحتاج أيضا إلى تمرير متغير projects$ لهذا العرض view وإلا ستحصل على خطأ متغير غير معرف undefined variable error. قُم بفتح app/Http/controllers/ProjectsController.php/ وقُم بتحديث الوظيفة ()index إلى: public function index() { $projects = Project::all(); return view('projects.index', compact('projects')); }قُم بتحديث الصفحة (Refresh) وستجد قائمة من مشاريعك. علاقات النموذج - صفحة تفاصيل المشروعفي صفحة تفاصيل المشروع نحتاج إلى عرض قائمة من مهام المشاريع، وللقيام بذلك نحتاج إلى تعريف علاقة "واحد إلى الكثير one-to-many" في نموذج Project للسماح له بالحصول على المهام tasks. قُم بفتح app/Project.php/ وأضف التابع ()tasks كالتالي: public function tasks() { return $this->hasMany('App\Task'); }بشكل عكسي، يمكننا إضافة علاقة "الكثير إلى واحد many-to-one" لنموذج المهام Task: public function project() { return $this->belongsTo('App\Project'); }للتأكد من أنها تعمل اكتب الأمر: $ php artisan tinkerوستكون النّتيجة كالتي حسب المدخلات: >App\Project::whereSlug('project-1')->first()->tasks->count(); 5 >App\Project::whereSlug('project-2')->first()->tasks->count(); 2 >App\Task::first()->project->name; Project 1ممتاز، يمكننا الآن تحديث عرض resources/views/projects/show.blade.php/: @extends('app') @section('content') <h2>{{ $project->name }}</h2> @if ( !$project->tasks->count() ) Your project has no tasks. @else <ul> @foreach( $project->tasks as $task ) <li><a href="{{ route('projects.tasks.show', [$project->slug, $task->slug]) }}">{{ $task->name }}</a></li> @endforeach </ul> @endif @endsectionاضغط على أي مشروع في صفحة عرض قائمة المشاريع في متصفحك وسوف يتم عرض المشروع مع جميع المهام المرتبطة به. وأخير تَبقى لدينا صفحة عرض المهام (resources/views/tasks/show.blade.php/)، وهذه سهلة للغاية: @extends('app') @section('content') <h2> {!! link_to_route('projects.show', $project->name, [$project->slug]) !!} - {{ $task->name }} </h2> {{ $task->description }} @endsectionملاحظة: كُن حذرا عندما تستخدم علاقات النماذج (models)، فإنه من السهل إنشاء عدد كبير من استعلامات SQL، وتسمى هذه المشكلة بـ "مشكلة N+1". الخاتمةاليوم تعلمنا الكثير من الأشياء الجديدة مثل: الربط بين الطريق (route) و النموذجالنماذج (مع علاقة واحد إلى الكثير one-to-many)المتحكِّمات (مع الربط بين الطريق و النموذج)العروض (مع لغة blade للقَوْلَبة والمخططات)أصبحت الصفحة تعرض لنا قائمة من المشاريع والمهام، وفي الدرس القادم سوف نركز على تعديل وإضافة وحذف المشاريع والمهام. ترجمة -وبتصرّف- للمقال Creating a Basic ToDo Application in Laravel 5 – Part 2. حقوق الصورة البارزة: Designed by Freepik.
  10. حتى الآن تعلمنا كيفية تثبيت وإعداد Laravel، بالإضافة إلى إعداد بعض المشاريع والمهام والموارد المضمّنة و عرضهم على المستخدم، كما أنشأنا وظائف الإنشاء والتعديل والحذف، وفي هذا الفصل سوف نختم الدرس عن طريق إضافة استمارة التحقق. استمارة التحقق من جانب الخادمعلى الرغم من أن استمارات الإنشاء والتعديل تعمل إلا أننا لم نقم بالتحقق من ما يتم إدخاله، وهذا ما سنقوم بعمله اليوم. هنالك طرق متعددة للتعامل مع استمارة التحقق، بعضها أفضل من الآخر، ولهذا التطبيق الصغير نقترح أن نستخدم وظيفة المتحكِّمات ()validate مع كائن Illuminate\Http\Request مثل هذا، في ملف app/Http/Controllers/ProjectsController.php/: // /app/Http/Controllers/ProjectsController.php use Illuminate\Http\Request; class ProjectsController extends Controller { protected $rules = [ 'name' => ['required', 'min:3'], 'slug' => ['required'], ]; /** * Store a newly created resource in storage. * * @param \Illuminate\Http\Request $request * @return Response */ public function store(Request $request) { $this->validate($request, $this->rules); $input = Input::all(); Project::create( $input ); return Redirect::route('projects.index')->with('message', 'Project created'); } /** * Update the specified resource in storage. * * @param \App\Project $project * @param \Illuminate\Http\Request $request * @return Response */ public function update(Project $project, Request $request) { $this->validate($request, $this->rules); $input = array_except(Input::all(), '_method'); $project->update($input); return Redirect::route('projects.show', $project->slug)->with('message', 'Project updated.'); }وأما في ملف app/Http/Controllers/TasksController.php/: // /app/Http/Controllers/TasksController.php use Illuminate\Http\Request; class TasksController extends Controller { protected $rules = ['name' => ['required', 'min:3'], 'slug' => ['required'], 'description' => ['required'], ]; /** * Store a newly created resource in storage. * * @param \App\Project $project * @param \Illuminate\Http\Request $request * @return Response */ public function store(Project $project, Request $request) { $this->validate($request, $this->rules); $input = Input::all(); $input['project_id'] = $project->id; Task::create( $input ); return Redirect::route('projects.show', $project->slug)->with('Task created.'); } /** * Update the specified resource in storage. * * @param \App\Project $project * @param \App\Task $task * @param \Illuminate\Http\Request $request * @return Response */ public function update(Project $project, Task $task, Request $request) { $this->validate($request, $this->rules); $input = array_except(Input::all(), '_method'); $task->update($input); return Redirect::route('projects.tasks.show', [$project->slug, $task->slug])->with('message', 'Task updated.'); }سوف نحتاج أيضا إلى مكان لعرض أية أخطاء، لذلك قُم بفتح resources/views/app.blade.php/ وأضف السطور التالية أسفل ('yield('content@: <div class="content"> @if (Session::has('message')) <div class="flash alert-info"> <p>{{ Session::get('message') }}</p> </div> @endif @if ($errors->any()) <div class='flash alert-danger'> @foreach ( $errors->all() as $error ) <p>{{ $error }}</p> @endforeach </div> @endif @yield('content') </div>إذا أردت قائمة كاملة من قواعد التحقق، أنصحك بزيارة التوثيق الرسمي. من المفترض أن يعمل كل شيء الآن، وإذا كان عكس ذلك، فسوف تقوم ()this->validate$ بتوجيهك إلى الصفحة الحالية مع الأخطاء التي سوف تظهر على الصفحة. خاتمةلقد قمنا في هذه الدورة بتعلم الكثير من الأشياء حول Laravel، مثل كيفية تثبيت وإعداد Laravel 5 بالإضافة إلى بعض المفاهيم المتقدمة مثل الربط بين الطريق والنموذج route model binding والحماية من CSRF، وعلى الرغم من بساطة التطبيق الذي قمنا به إلا أنه بداية جيدة لكل من يريد احتراف Laravel. ترجمة -وبتصرّف- للمقال Creating a Basic ToDo Application in Laravel 5 – Part 4. حقوق الصورة البارزة: Designed by Freepik.
  11. حتى الآن تعلمنا كيفية تثبيت وإعداد Laravel، بالإضافة إلى أننا قمنا بـإعداد بعض موارد المشاريع والمهام وعرضهم للمستخدم. في هذا الدرس، سنتعلم كيفية إنشاء وتعديل وحذف الصفحات والإجراءات. إضافة روابط التصفحقبل أن نفعل أي شيء، سنقوم بإضافة روابط create/edit/delete/back إلى صفحات المشاريع والمهام: <!-- /resources/views/projects/index.blade.php --> @extends('app') @section('content') <h2>Projects</h2> @if ( !$projects->count() ) You have no projects @else <ul> @foreach( $projects as $project ) <li> {!! Form::open(array('class' => 'form-inline', 'method' => 'DELETE', 'route' => array('projects.destroy', $project->slug))) !!} <a href="{{ route('projects.show', $project->slug) }}">{{ $project->name }}</a> ( {!! link_to_route('projects.edit', 'Edit', array($project->slug), array('class' => 'btn btn-info')) !!}, {!! Form::submit('Delete', array('class' => 'btn btn-danger')) !!} ) {!! Form::close() !!} </li> @endforeach </ul> @endif <p> {!! link_to_route('projects.create', 'Create Project') !!} </p> @endsection <!-- /resources/views/projects/show.blade.php --> @extends('app') @section('content') <h2>{{ $project->name }}</h2> @if ( !$project->tasks->count() ) Your project has no tasks. @else <ul> @foreach( $project->tasks as $task ) <li> {!! Form::open(array('class' => 'form-inline', 'method' => 'DELETE', 'route' => array('projects.tasks.destroy', $project->slug, $task->slug))) !!} <a href="{{ route('projects.tasks.show', [$project->slug, $task->slug]) }}">{{ $task->name }}</a> ( {!! link_to_route('projects.tasks.edit', 'Edit', array($project->slug, $task->slug), array('class' => 'btn btn-info')) !!}, {!! Form::submit('Delete', array('class' => 'btn btn-danger')) !!} ) {!! Form::close() !!} </li> @endforeach </ul> @endif <p> {!! link_to_route('projects.index', 'Back to Projects') !!} | {!! link_to_route('projects.tasks.create', 'Create Task', $project->slug) !!} </p> @endsectionهذه الشفرة البرمجية مفهومة، الصعوبة تكمن في رابط الحذف، متحكِّمات الموارد تتطلب إرسال وظيفة HTTP DELETE، وهذا لا يمكن أن يتم مع رابط عادي، لذلك يتطلب نموذج يتم تقديمه إلى طريق route محدد. للمزيد من المعلومات قُم بزيارة إجراءات يتم التحكم بها عن طريق متحكِّمات الموارد. إنشاء صفحات الإضافة والتعديلبعد الانتهاء من صفحات عرض قائمة المشاريع، نحتاج إلى إمكانية إضافة وحذف المشاريع والمهام. استمارات الإضافة والحذف ستكون متشابهة كثيرا، لذلك بدلا من تكرارهم، سنقوم بوراثتهم من نموذج واحد. سوف نبدأ مع عروض create/edit للمشروع: <!-- /resources/views/projects/create.blade.php --> @extends('app') @section('content') <h2>Create Project</h2> {!! Form::model(new App\Project, ['route' => ['projects.store']]) !!} @include('projects/partials/_form', ['submit_text' => 'Create Project']) {!! Form::close() !!} @endsection <!-- /resources/views/projects/edit.blade.php --> @extends('app') @section('content') <h2>Edit Project</h2> {!! Form::model($project, ['method' => 'PATCH', 'route' => ['projects.update', $project->slug]]) !!} @include('projects/partials/_form', ['submit_text' => 'Edit Project']) {!! Form::close() !!} @endsectionقُم بعمل نفس الشيء مع المهام لكن لا تنسَ تحديث الطرق (routes): <!-- /resources/views/tasks/create.blade.php --> @extends('app') @section('content') <h2>Create Task for Project "{{ $project->name }}"</h2> {!! Form::model(new App\Task, ['route' => ['projects.tasks.store', $project->slug], 'class'=>'']) !!} @include('tasks/partials/_form', ['submit_text' => 'Create Task']) {!! Form::close() !!} @endsection <!-- /resources/views/tasks/edit.blade.php --> @extends('app') @section('content') <h2>Edit Task "{{ $task->name }}"</h2> {!! Form::model($task, ['method' => 'PATCH', 'route' => ['projects.tasks.update', $project->slug, $task->slug]]) !!} @include('tasks/partials/_form', ['submit_text' => 'Edit Task']) {!! Form::close() !!} @endsectionالآن توجد عدة مفاهيم جديدة: إضافة جزئيةالاستمارة تتطلب عدة وسوم، وبدلا من ذلك سنقوم بتقسيمها من جزئية _form ونضعها مباشرة في العرض، استمارة Add هي من نوع POST ترسل البيانات إلى طريق projects.store واستمارة Edit هي من نوع PATCH وترسل البيانات إلى projects.update. قد يبدوا هذا معقدا ومبهم لكن هذه هي الطريقة التي تعمل بها المتحكِّمات. لاحظ أننا استخدمنا ()Form::model، وتسمى هذه نموذج الاستمارة الملزمة و ليس لدى هذه أهمية كبيرة الآن، فهي تُستخدم للتعبئة التلقائية وسوف نحتاجها في وقت لاحقا عندما نضيف الحقول باستخدام ()Form::input.الحماية من CSRF:مساعدي الاستمارة (Form helpers) توفر العديد من الوظائف مجانا، فإذا ذهبت إلى projects/create/ ورأيت مصدر الصفحة ستجد شيئا مشابها لهذا: <form method="POST" action="http://l4todo.localhost.com/projects" accept-charset="UTF-8"> <input name="_token" type="hidden" value="Y8uOo7SeD5tQZExezDf5a7UwiYR4P6qIHEUKJNxI"> </form>هل ترى حقل token_ ؟ هذا هو الرمز المميز لـ CSRF مُنشئ تِلقائيا بواسطة استدعاء {{ ()Form::model }} الذي يمنع ثغرة CSRF. يكفي أن نقول أن هذا شيئ مفيد وأننا لم نقم بأي شيئ للحصول عليه. إنشاء استمارة التعديلنحن بحاجة إلى ترميز (markup) الاستمارة للمشاريع والمهام، وبفضل نموذج الاستمارة الملزمة يمكننا فقط استخدام مساعدي الاستمارة في Laravel لإخراج كافة الحقول التي نحتاجها. <!-- /resources/views/projects/partials/_form.blade.php --> <div class="form-group"> {!! Form::label('name', 'Name:') !!} {!! Form::text('name') !!} </div> <div class="form-group"> {!! Form::label('slug', 'Slug:') !!} {!! Form::text('slug') !!} </div> <div class="form-group"> {!! Form::submit($submit_text, ['class'=>'btn primary']) !!} </div> <!-- /resources/views/tasks/partials/_form.blade.php --> <div class="form-group"> {!! Form::label('name', 'Name:') !!} {!! Form::text('name') !!} </div> <div class="form-group"> {!! Form::label('slug', 'Slug:') !!} {!! Form::text('slug') !!} </div> <div class="form-group"> {!! Form::label('completed', 'Completed:') !!} {!! Form::checkbox('completed') !!} </div> <div class="form-group"> {!! Form::label('description', 'Description:') !!} {!! Form::textarea('description') !!} </div> <div class="form-group"> {!! Form::submit($submit_text) !!} </div>هذا كل شيئ، سوف تستطيع الأن التصفح في صفحات الإضافة والتعديل، الأمر كان سهلا للغاية. جعل الاستمارات تعمللدينا استمارات الإضافة والتعديل للمشروع و المهام، بالإضافة إلى استمارة مُضللة لحذفهم، الآن يجب علينا أن نجعلهم يعملون لأداء مهامهم. أولا، أضف هذا في أعلى المتحكِّمات: use Input; use Redirect;والآن لوظائف store و update و destroy: // ProjectsController public function store() { $input = Input::all(); Project::create( $input ); return Redirect::route('projects.index') ->with('message', 'Project created'); } public function update(Project $project) { $input = array_except(Input::all(), '_method'); $project->update($input); return Redirect::route('projects.show', $project->slug) ->with('message', 'Project updated.'); } public function destroy(Project $project) { $project->delete(); return Redirect::route('projects.index')->with('message', 'Project deleted.'); } // TasksController public function store(Project $project) { $input = Input::all(); $input['project_id'] = $project->id; Task::create( $input ); return Redirect::route('projects.show', $project->slug) ->with('message', 'Task created.'); } public function update(Project $project, Task $task) { $input = array_except(Input::all(), '_method'); $task->update($input); return Redirect::route('projects.tasks.show', [$project->slug, $task->slug]) ->with('message', 'Task updated.'); } public function destroy(Project $project, Task $task) { $task->delete(); return Redirect::route('projects.show', $project->slug)->with('message', 'Task deleted.'); }الشفرة البرمجية في الأعلى مفهومة ولا داعي لشرحها. إذا قمت الآن بتقديم إحدى الاستمارات فسوف تحصل على خطأ MassAssignmentException: _token، فالإحالة الكتلية تحدث عندما تقوم بتمرير مصفوفة بيانات إلى ()Model::create أو ()Model::update من المتحكِّمات بدل من وضع حقل واحد في نفس الوقت، ولإصلاح هذا، قم ببساطة بإضافة خاصية guarded إلى كل نموذج. class Project extends Model { protected $guarded = []; class Task extends Model { protected $guarded = [];رسائل الفلاشسوف تلاحظ من الشفرة السابقة أننا استخدمنا دالة ()with، وهذه الدالة تقوم بتمرير متغير فلاش (يستخدم لمرة واحدة) للجلسة session والتي يمكن قراءتها عند تحميل الصفحة التالية. والآن نحتاج إلى التأكد من تلك الرسالة وعرضها على المستخدم. قُم بفتح resources/views/app.blade.php/ وأضف التالي: ... <div class="content"> @if (Session::has('message')) <div class="flash alert-info"> <p>{{ Session::get('message') }}</p> </div> @endif @yield('content') </div> ...حاول إنشاء مشروع، هذه الرسالة ستظهر مهما حدّثت الصفحة وستذهب بعد فترة. خاتمةاليوم قمنا بأشياء مثيرة للغاية، فلقد أضفنا استمارات الإضافة والتعديل والحذف وتعلمنا حول CSRF و نموذج الاستمارة الملزمة بالإضافة إلى تعلمنا المزيد حول blade و الدوال. كل شيئ الآن يعمل، يمكن تجربة التطبيق وإنشاء وتعديل وحذف المهام والمشاريع، وفي أي وقت يمكنك كتابة الأمر: php artisan migrate:refresh –seed وسوف يتم إعادة تعيين قواعد البيانات. في المرحلة القادمة والتي ستكون النهائية سوف نقوم بالتحقق من الاستمارات. ترجمة -وبتصرّف- للمقال Creating a Basic ToDo Application in Laravel 5 – Part 3. حقوق الصورة البارزة: Designed by Freepik.
×
×
  • أضف...