البحث في الموقع
المحتوى عن 'تهجير'.
-
تجهيز قاعدة البيانات بما أنّنا سنستخدم قاعدة البيانات 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 وهذا بالضّبط ما سنقوم به في الدّرس القادم.
-
يمكن لنقل مدونتك إلى خادوم جديد أو خدمة استضافة ووردبريس أخرى أن يستهلك الكثير من الوقت وقد يتحول الأمر إلى كابوس حقيقي ما لم تملك الأدوات المُناسبة لذلك. لحسن حظّك لن تراودك أية كوابيس بفضل مُلحق Snapshot. لا يعلم كثير من مستخدمي snapshot أنه يحتوي على خاصية التهجير. ولهذا رغبت في أن أتطرّق لها في هذا الدّرس. هذا الدّرس موجّه لك سواء كنت تستخدم نسخة ووردبريس العادية (موقع واحد) أو نسخة متعددة المواقع. يُمكنك تنفيذ خطوات هذا الدّرس حتى ولو رغبت من نقل مدونتك من نسخة ووردبريس عادية إلى تلك التي تدعم مواقع عديدة والعكس. إن أردت أن تغير اسم نطاق موقعك، سأعمل على إعطاء بعض التوجيهات في ما يخص هذا الأمر أيضا. لم تعد وظائف ملحق Snapshot تقتصر على عمل نسخ احتياطية (backing up) واسترجاعها (restoring) فحسب بل أصبحت تتعدى ذلك بكثير، لنبدأ دون أي تأخير في التعرف على كيفية تهجير موقعك. يبدأ الأمر بإعداد نسخة احتياطية (Backup) فلنبدأ بالأساسيات: إن أردت تهجير موقعك باستخدام Snapshot عليك أولا بعمل نسخة احتياطية، لكن قبل القيام بذلك يجب أن تنصب وتفعل Snapshot سواء من خلال استخدام ملحق WPMU DEV Dashboard في خلفية (backend) موقعك أو عن طريق تحميل Snapshot مباشرة. قبل أن نواصل، من الحري بنا أن نشير إلى إمكانية مواجهة Snapshot بعض المشاكل في التشغيل إن لم تكن تمتلك حد ذاكرة أقصى كافيا على استضافة ووردبريس الخاصة بك. لتجاوز هذا المشكل اطلع على مقال: كيفية الزيادة في القيمة القصوى للرفع وفي الحد الأقصى لذاكرة PHP على ووردبريس، يعتبر القيام بهذه العملية التي لا تستغرق أكثر من بضع دقائق أمرا في غاية الأهمية، من الوارد جدا أن تواجه مستقبلا مشاكل أخرى تتعلق بالحد الأقصى للذاكرة إن لم يكن قد حدث ذلك فعلا. إليك كيفية عمل نسخة احتياطية (backup): اذهب إلى: Snapshots > Add New في لوحة التحكم، (أو في لوحة تحكم الشبكة (super admin) في حالة نسخة ووردبريس متعددة المواقع). إن لم يتم عرض عنوان URL الخاص بموقعك أسفل Blog to backup، اضغط على زر Change لتحديد موقع آخر في حال استخدامك نسخة ووردبريس متعددة المواقع، لن تواجه هذا الأمر في حال استخدام النسخة العادية (موقع واحد). حدد الموقع الذي تريد تهجيره إن لم يكن محددا مسبقا قم بتحديد include selected files في قسم What Files to Archive، ثم ضع علامة في كل الخانات الظاهرة أمام الملفات الموافقة لها. بغرض نقل موقعك بنجاح، عليك بحفظ كل شيء. إن اخترت أيا من الخيارات الأخرى، لن يتم نسخ التغييرات المخصصة التي قمت بها. إن كنت تعتزم عمل نسختك الاحتياطية على Dropbox يمكنك أن تترك خيار Archive الافتراضي عوض خيار Mirror/Sync. يجب أن يتم حفظ كل الملفات من أجل عملية تهجير ناجحة في القسم التالي، اضغط على زر الاختيار Include selected database tables in this Snapshot. لا تنس تحديد الجداول (tables) عندما تظهر قائمة جداول قاعدة البيانات، اضغط على Select all links لتحديد كل الخانات تلقائيا، يضمن القيام بهذه الخطوة تخزين نسخة احتياطية لكل الجداول حتى يظهر موقعك بنفس الشكل بعد تهجيره. يمكن تحديد زر الاختيار الأوسط لتضمين كل الجداول تلقائيا دونما الحاجة إلى تحديدها كلها، يضمن تحديد كل الجداول يدويا التأكد من تخزين نسخة احتياطية منها كلها. يبقى القرار الأخير لك حول أي الخيارين ستعتمد. أسفل Backup Interval، اترك خيار Run Immediate محددا واكتب رقما مناسبا في خانة Maximum number of local archives، عادة ما أكتب رقم 2، حتى يتم تخزين نسختين فقط دفعة واحدة فيما يخص هذا الجانب بالتحديد. إن قمت بكتابة عدد كبير أو 0 للحفاظ على كل النسخ الاحتياطية، عليك أن تتأكد من توفرك على مساحة التخزين الكافية لهذا الغرض. حدد مكان حفظ نسختك الاحتياطية من القائمة المنسدلة Backup Destination من خلال تحديد واحدة من الخيارات التي أعددتها مسبقا. إن لم تكن تتوفر على أي وجهة تخزين محفوظة ولا تريد عمل نسختك الاحتياطية على خادومك، تجاوز هذا القسم حاليا. اختر وجهة حفظ نسختك الاحتياطية بعد الاختيار، اضغط على زر Create Snapshot المتواجد أسفل الصفحة. يمكنك أن ترى في الصفحة التالية نسخة snapshot الخاصة بك في طور الإنشاء. لا تقلق إن استغرق الأمر بضع ثوان، خصوصا بالنسبة للمواقع الكبيرة فهذا عادي جدا. لن تأخذ العملية فترة طويلة. عندما ترى رسالة أعلى الشاشة تخبرك بنجاح العملية فإن ذلك علامة على انتهاء إنشاء نسختك الاحتياطية، اذهب إلى: Snapshots > All Snapshots لتراها في القائمة. أخيرا، تفحص حالة نسختك أسفل خانة Archives، يجب أن يتم إدراج حالة النسخة الاحتياطية في وجهة الحفظ (destination) التي اخترتها. تأكد من إرسال نسختك الاحتياطية إلى الوجهة المحددة إن رأيت جملة Destination: 1 Pending فمعنى ذلك أنه يتم رفع نسختك إلى وجهة الحفظ، في حالتي يعني هذا أن عملية المزامنة مع حسابي في Dropbox في طور الإنجاز. بمجرد مزامنة نسختك الاحتياطية مع وجهة التخزين، فأنت على أتم استعداد لبدأ تهجير موقعك. تهجير موقعك بعد تجهيز نسخة snapshot الخاصة بك، عليك بتنصيب نسخة جديدة من ووردبريس على خادومك الجديد. يتضمن ذلك كل الاحتمالات الممكنة لتهجير موقعك سواء ضمن شبكة ما أو إلى شبكة ما. على استضافتك الجديدة، قم بتثبيت وتفعيل نفس القوالب والملحقات المتواجدة على الموقع الذي تود تهجيره. بعد ذلك، قم بتثبيت وتفعيل القالب الذي تود استخدامه في موقعك الجديد بعد التهجير، يجب أن يتم تفعيله قبل التهجير حتى وإن كان متضمنا في نسخة snapshot. تضمن هذه الخطوة جاهزية موقعك مباشرة بعد إتمام العملية. نصِّب وفعِّل Snapshot على موقعك الجديد إن لم تكن قمت بذلك مسبقا. بعد ذلك، اذهب إلى: Snapshots > Import وأدخل عنوان URL عامًا (public URL) أو مسار ملف إلى ملحق snapshot الخاص بك. ما يعني أنك إن أرسلت نسختك الاحتياطية إلى Dropbox ،Amazon S3 أو وجهة أخرى، سيكون عليك سواء أن تجعل الملف عاما (public)، مشاركته برابط أو حفظه في مكان آخر. استيراد نسخة snapshot إن كانت نسخة snapshot الخاصة بك متواجدة على خادومك، ما عليك إلا أن تكتب مسار الملف الذي يؤدي إليها. عند إدخال نسخة snapshot إلى خانة الاستيراد (import field)، اضغط على زر Scan/Import Snapshots. من المفترض أن يتم عرض رسالة " success" أسفل الصفحة. إن أدخلت مسار ملف قد تظهر أكثر من رسالة واحدة. حان الوقت الآن لإتمام تهجير الموقع من خلال الذهاب إلى: Snapshots > All Snapshots تم استيراد نسخة snapshot بنجاح اعثر على نسخة snapshot الخاصة بك مدرجة في القائمة، ضع مؤشر فأرة الحاسوب فوق العنوان ثم حدد رابط restore. إن لم تكن متأكدا من ماهية كل عنصر، حاول مقارنة اسم ملف نسخة snapshot مع اسم الملف في وجهة الحفظ. من المفترض أن يتم بعد ذلك توجيهك إلى صفحة أرشيف كل النسخ الاحتياطية. اضغط على restore لإنهاء عملية التهجير شخصيا، أمتلك نسخة snapshot واحدة في أرشيفي، يمكن أن يكون لديك أكثر من ذلك. اختر تلك التي تريد استخدامها، ثم ضع الفأرة فوق عنوانها. اختر استرجاع (restore) نسخة snapshot التي تريد اضغط على رابط restore الذي يظهر. في الصفحة الموالية، يمكنك مراجعة الخيارات وما تستطيع أن تقوم بتهجيره. الانتهاء من التهجير وتغيير النطاق انزل نحو قسم Restore Blog Options، ثم اضغط على زر Change قرب الوسم Site URL.تستطيع أن تغير عنوان URL الخاص بملفات نسختك الاحتياطية حتى يتم استخدام موقعك القديم مع نطاقك الجديد. يمكن تهجير موقعك إلى عنوان URL مختلف في الخانة الظاهرة، تستطيع إدخال رقم تعريف الموقع (site ID) أو مسار المدونة الخاص بالموقع الجديد الذي أنشأته ليتم استبداله بنسخة snapshot. اضغط على زر Lookup ليتم عرض الموقع الذي قمت بتحديده. في القسم التالي المسمى ?What Tables to Restore اضغط على الخيار Restore selected database tables. لا تختر الخيار الأول لاسترجاع كل الجداول نظرا لأن بعضها تم استثناؤه اضغط على رابط Select All قرب وسم WordPress Global Tables. من المفترض أن يتم تحديد الجداول الأخرى تلقائيا، إن لم يحدث ذلك، اضغط على روابط Select All. اختر خيار Include selected files في القسم التالي وتأكد من وضع علامة في كل الخانات. إن لم تكن تقوم بتهجير موقعك الأساسي قم بتحديد خيار Restore all files. في آخر جزء من الصفحة، اختر من القائمة القالب الذي تود استخدامه من بين تلك المتوفرة في استضافتك الحالية. تأكد من تنصيب وتفعيل القالب الذي تود استخدامه في موقعك الجديد بعد التهجير تأكد أيضا من عدم وضع علامة في خانة Turn off all plugins وإلا قد لا يتم عرض موقعك بشكل جيد بعد التهجير. أخيرا، اضغط زر Restore Snapshot أسفل الصفحة. من المفترض أن ترى رسالة مشابهة بمجرد انتهاء عملية التهجير باستخدام snapshot في الصفحة التالية، ستظهر قائمة تحتوي على الجداول والملفات إضافة إلى عمود يعرض حالة تقدم عملية الرفع. بعد انتهاء عملية التهجير بنجاح تظهر رسالة أعلى الصفحة تشير إلى ذلك. عملية تهجير بسيطة باستخدام Snapshot موقعك الجديد بعد التهجير جاهز الآن كما أنه يبدوا تماما مثل سابق عهده فقط على خادوم ونطاق جديدين. لقد قمت بعد تطبيق هذا الموضوع بتهجير موقعك بسهولة باستخدام Snapshot موفرا عليك عناء الساعات التي كنت ستقضيها للقيام بهذه العملية يدويا. ترجمة -وبتصرف- للمقال: How to Move a WordPress Site Without Hassle with Snapshot لكاتبته: JENNI MCKINNON.
-
يقدّم هذا الدرس كيفية إنشاء نموذج 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.
-
يوفر تهجير قواعد البيانات Database migration في Laravel آليات لإنشاء الجداول Tablesوالتعديل عليها بغض النظر نظام إدارة قواعد البيانات المستخدم. يعني هذا أنك لن تضطر للاهتمام بالاختلافات بين نظم إدارة قواعد البيانات في صياغة أوامر SQL. يمكّن التهجير أيضا من التراجع والعودة إلى ما كانت عليه قاعدة البيانات قبل آخر التعديلات. هذا الدرس جزء من سلسلة تعلم 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 لتوليد بيانات وهمية قصدَ الاختبار. يمكن النظر إلى تهجير قواعد البيانات كما لو كان نظام إدارة نسخ خاص بقواعد البيانات، إذ يتيح لفريق العمل سهولة تغيير مخطّط Schema البيانات وتشاركه. نكمل في هذا الدرس اعتمادا على ما أنشأناه في الدروس السابقة من السلسلة. يغطي الدرس المواضيع التالية: متطلبات التهجير.أمر Artisan لتهجير قواعد البيانات.بنية التهجير.إنشاء جدول بآلية التهجير.استخدام آلية التهجير للتراجع Rollback عن التعديلات.بذر قواعد البيانات Database seeding.بنية قاعدة البيانات الخاصة بمشروع Larashop.ملفات التهجير لقاعدة بيانات Larashop.متطلبات التهجيريجب أولا إنشاء قاعدة بيانات في نظام إدارة قواعد البيانات المستخدم (MySQL في حالتنا) وإعداد معطيات الاتصال بها في Laravel ولدى أداة سطر الأوامر Artisan. إنشاء قاعدة بياناتنفذ الأمر التالي في سطر أوامر MySQL أو استخدم التطبيق المفضّل لديك (PHPMyAdmin مثلا) لإنشاء قاعدة بيانات larashop: CREATE DATABASE `larashop`;إعداد Laravel للاتصال بقاعدة البياناتأعددنا Laravel في الدرس الأول من هذه السلسلة للاتصال بقاعدة بيانات باسم larashop. في ما يلي تذكير بخطوات الإعداد. افتح الملف config/database.php واعثر على الأسطُر التالية: 'mysql' => [ 'driver' => 'mysql', 'host' => env('DB_HOST', 'localhost'), 'database' => env('DB_DATABASE', 'forge'), 'username' => env('DB_USERNAME', 'forge'), 'password' => env('DB_PASSWORD', ''), 'charset' => 'utf8', 'collation' => 'utf8_unicode_ci', 'prefix' => '', 'strict' => false, ],حدّث القيم التالية لتوافق إعدادات MySQL لديك: 'database' => env('DB_DATABASE', 'larashop'), 'username' => env('DB_USERNAME', 'root'), 'password' => env('DB_PASSWORD', 'melody'), إعداد معطيات اتصال Artisan بقاعدة البياناتيواجه الكثير من المطورين رسالة الخطأ التالية عند العمل على تهجير قواعد البيانات باستخدام أداة Artisan: Access denied for user 'homestead'@' localhost' (using password: YES)ستظهر الرسالة أعلاه حتى ولو كانت معطيات الاتصال في الملف configuration/database.php صحيحة. يعود السبب في ذلك إلى أن Artisan يستخدم المعطيات الموجودة في الملف env.. الحل هو إذن تحرير الملف env. الواقع في مجلد التطبيق، ستجد ما يلي: APP_ENV=local APP_DEBUG=true APP_KEY=aqk5XHULL8TZ8t6pXE43o7MBSFchfgy2 DB_HOST=localhost DB_DATABASE=homestead DB_USERNAME=homestead DB_PASSWORD=secret CACHE_DRIVER=file SESSION_DRIVER=file QUEUE_DRIVER=sync MAIL_DRIVER=smtp MAIL_HOST=mailtrap.io MAIL_PORT=2525 MAIL_USERNAME=null MAIL_PASSWORD=null MAIL_ENCRYPTION=null حدث المتغيرات التالية: DB_HOST=localhost DB_DATABASE=homestead DB_USERNAME=homestead DB_PASSWORD=secretلتصبح: DB_HOST=localhost DB_DATABASE=larashop DB_USERNAME=root DB_PASSWORD=melody احرص على موافقة اسم قاعدة البيانات، اسم المستخدم وكلمة مروره للمعطيات لديك. احفظ التعديلات. تهجير قواعد البيانات بأداة Artisanينشئ أمر artisan ملفا على المسار database/migrations لكل عملية تهجير. يمكن تغيير المسار الخاص بحفظ ملفات التهجير إن أردت ولكننا هنا سنكتفي بالمسار المبدئي. ننفذ الأمر التالي لإنشاء أول ملف تهجير: php artisan make:migration create_drinks_tableتظهر رسالة باسم ملف التهجير الجديد. اخترنا اسم create_drinks_table للدلالة على أن التهجير ينشئ جدولا باسم drinks في قاعدة البيانات. نتيجة الأمر هي إنشاء ملف للتهجير بنفس الاسم الذي أعطيناه مع إضافة ختم زمني قبله، مثلا: 2015_12_21_215845_create_drinks_table.phpبنية ملف التهجيرندرس الآن محتوى ملف التهجير الذي أنشأناه للتو. افتح الملف التالي لرؤية محتواه (انتبه إلى أن اسم الملف يبدأ بختم زمني للحظة إنشائه): database/migrations/2015_12_21_215845_create_drinks_table.phpنجد ما يلي: <?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateDrinksTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { // } /** * Reverse the migrations. * * @return void */ public function down() { // } }يعرف ملف التهجير صنفا جديدا باسم CreateDrinksTable يمدد الصنف Migration: CreateDrinksTable extends Migrationداخل الصنف CreateDrinksTable توجد دالة باسم up. تنفّذ تعليمات الدالة up عند تشغيل التهجير. توجد أيضا دالة باسم down في الصنف CreateDrinksTable. تنفذ تعليمات الدالة down عند التراجع عن تغييراتِ تهجير.ملف تهجير لإنشاء جدول بقاعدة البياناتليمكن إنشاء جدول في قاعدة البيانات فجيب تعريف حقوله في ملف التهجير. نعيد فتح ملف التهجير السابق ونعدله ليصبح محتواه التالي : <?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateDrinksTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('drinks', function (Blueprint $table) { $table->increments('id'); $table->string('name',75)->unique(); $table->text('comments')->nullable(); $table->integer('rating'); $table->date('juice_date'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('drinks'); } }في الدالة up: نستدعي الدالة create المعرَّفة في الصنف Schema ونمرر لها معطيين، الأول اسم الجدول الذي نريد إنشاءه drinks، والمعطى الثاني دالة غير محدّدة الاسم تعرّف حقول الجدول. نستخدم كائنا من صنف Blueprint لتعريف الجدول.نعرف أول حقل من الجدول وهو الحقل id. تعرف الدالة increments التابعة للصنف Blueprint عددا طبيعيا (عدد صحيح إشارته موجبة) يزداد تلقائيّا مع كل إدخال للبيانات في الجدول.الحقل الثاني هو حقل الاسم name، الذي نعرفه بالدالة string. تنشئ الدالة stringحقلا من سلسلة محارف مع تحديد طول السلسلة (75 في المثال). نعلّم الحقل name بالدالة unique \لجعله وحيدا وهو ما يعني أنه لا يمكن لتسجيلتين في الجدول أن تحويا نفس القيمة بالنسبة لهذا الحقل.الحقل الثالث comments نصي، وتستخدم الدالة text لتعريفه. نتيح إمكانية ألا يحوي الحقل بيانات باستخدام الدالة nullable.الحقل الرابع rating للتقيمات. نستخدم الدالة integer للإشارة إلى أنه عدد صحيح.ثم نضيف حقلا لتخزين تاريخ المشروب juice_date ونستخدم الدالة date لهذا الغرض.تُستخدم الدالة timestamps لإضافة حقلين هما created_at وupdated_at في الجدول تلقائيا. الحقلان عبارة عن ختم زمني ل، على التوالي، تاريخ إضافة التسجيلة إلى قاعدة البيانات وتاريخ آخر تحديث عليها.في الدالة down نحذف الجدول drinks من قاعدة البيانات في حالة وجوده.ننفذ بعد حفظ ملف التهجير الأمر التالي: php artisan migrateستظهر مخرجات في سطر الأوامر على النحو التالي: Migration table created successfully. Migrated: 2014_10_12_000000_create_users_table Migrated: 2014_10_12_100000_create_password_resets_table Migrated: 2015_12_21_215845_create_drinks_tableإن نظرت في جداول قاعدة البيانات الآن فستجد التالي: ستلاحظ وجود أربعة جداول من بينها جدول drinks. الجداول الأخرى أنشأها Laravel لأن ملفات تهجيرها تأتي مبدئيا مع Laravel. التراجع عن التعديلاتيوفر التهجير إمكانية التراجع عن تعديلاته والعودة إلى حالة قاعدة البيانات قبل تنفيذه. أنشأنا في الفقرة السابقة جداول في قاعدة البيانات، ننفذ الأمر التالي للتراجع عن ذلك: php artisan migrate:rollbackتظهر الرسائل التالية: Rolled back: 2015_12_21_215845_create_drinks_table Rolled back: 2014_10_12_100000_create_password_resets_table Rolled back: 2014_10_12_000000_create_users_tableإن أعدت التحقق في MySQL سترى أن الجدول drinks لم يعد موجودا. نعيد إنشاء الجدول بتنفيذ التهجير مرة أخرى: php artisan migrateتمكن ملاحظة أن تنفيذ التهجير يكون بالتسلسل الزمني التصاعدي لتاريخ إنشاء ملفات التهجير (من الأقدم إلى الأحدث)، في ما يكون التراجع بتنفيذ ملفات التهجير حسب التسلسل الزمني التنازلي (من الأحدث إلى الأقدم). إدارة جداول البيانات في Laravel باستخدام التهجيرسنرى في هذه الفقرة كيفية استخدام التهجير للقيام بأشغال شائعة على جداول قواعد البيانات. إدراج بياناتسنرى الآن كيفية استخدام التهجير لإدراج بيانات في جدول أثناء إنشائه. ننشئ جدولا بالموظفين employees وندرج فيه 33 تسجيلة بالاعتماد على مكتبة Faker (سنخصص درسا لتفصيل استخدام Faker). نفذ الأمر التالي لإنشاء ملف تهجير لجدول employees: php artisan make:migration employeesنفتح الملف المنشأ للتو ونضيف الشفرة التالية: <?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class Employees extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('employees', function (Blueprint $table) { $table->increments('id'); $table->string('name'); $table->string('email')->unique(); $table->string('contact_number'); $table->timestamps(); }); $faker = Faker\Factory::create(); $limit = 33; for ($i = 0; $i < $limit; $i++) { DB::table('employees')->insert([ //, 'name' => $faker->name, 'email' => $faker->unique()->email, 'contact_number' => $faker->phoneNumber, ]); } } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('employees'); } } ننشئ كائنا من صنف Faker بالتعليمة $faker = Faker\Factory::create();نحدد عدد التسجيلات التي نود إدراجها: limit$. نستخدم حلقة for التكرارية لإضافة التسجيلات إلى الجدول. تولد التعليمة faker->name$ اسما وهميّا، faker->unique()->email$ اسم بريد وحيد وfaker->phoneNumber$ رقم هاتف وهميا.الأمر التالي ينفذ التهجير: php artisan migrateتظهر الرسالة التالية دلالة على تهجير الجدول employees: Migrated: 2015_12_21_225233_employees إن بحثت الآن عن محتوى الجدول employees، مثلا بتنفيذ الاستعلام التالي في سطر أوامر MySQL: SELECT * FROM employees;ستحصُل على أسماء الموظفين، عناوينهم البريدية وأرقام هواتفهم. نتراجع عن إنشاء الجدول employees بتنفيذ الأمر: php artisan migrate:rollbackتظهر رسالة دلالة على التراجع عن إنشاء الجدول. نفتح ملف التهجير للتعديل عليه ثم نضع الشفرة الخاصة بإدراج بيانات وهمية بين علامتي تعليق، هكذا: /* $faker = Faker\Factory::create(); $limit = 33; for ($i = 0; $i < $limit; $i++) { DB::table('employees')->insert([ //, 'name' => $faker->name, 'email' => $faker->unique()->email, 'contact_number' => $faker->phoneNumber, ]); } */احفظ ملف التهجير ثم نفذ الأمر: php artisan migrateسيُنشأ جدول employees من جديد ولكن هذه المرة دون إدراج تسجيلات في الجدول. إضافة عمود إلى جدول أو حذفه منهنفرض أننا نود إضافة عمود جديد gender لتخزين جنس الموظّف، مباشرة بعد العمود contact_number. ننفذ الأمر التالي لإنشاء ملف تهجير باسم add_gender_to_employees مع تحديد الجدول الذي نريد العمل عليه وهو employees: php artisan make:migration add_gender_to_employees --table=employeesتشير التعليمة table=employees-- إلى أننا نريد العمل على الجدول employees الموجود في قاعدة البيانات. افتح ملف التهجير المنشأ بعد الأمر السابق، وعدله ليصبح على النحو التالي: <?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class AddGenderToEmployees extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::table('employees', function (Blueprint $table) { $table->string('gender')->after('contact_number'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('employees', function (Blueprint $table) { $table->dropColumn('gender'); }); } } في الدالة up أضفنا حقلا جديدا من نوع string (سلسلة محارف) وحددنا مكانه بأنه بعد العمود contact_number.في الدالة down نحذف الحقل gender.الآن عند تنفيذ أمر التهجير php artisan migrate ستلاحظ إضافة عمود جديد باسم gender بعد عمود contact_number. تغيير نوع عموديحتاج تغيير نوع العمود لتثبيت حزمة Doctrine Database Abstract Layer, DBAL. تُستخدم هذه الحزمة لتهجيرات التعديل على الجداول Alter table. سنستخدم أداة إدارة الاعتماديات Composer لتثبيت الحزمة. افتح ملف composer.json الذي يوجد في مجلد التطبيق. ابحث عن مقطع require: "require": { "php": ">=5.5.9", "laravel/framework": "5.2.*" },توجد في هذا المقطع حزم المكتبات التي يحتاجها تطبيقنا. حتى الآن توجد حزمتان فقط هما php وlaravel. يشير الجزء الأول (قبل النقطتين) إلى اسم الحزمة، في ما يشير الثاني لإصدارها. نضيف حزمة dbal` إلى هذه الاعتماديات، وذلك على النجو التالي: "require": { "php": ">=5.5.9", "laravel/framework": "5.2.*", "doctrine/dbal": "v2.4.2" }, لاحظ الفاصلة اللاتينية التي أضفناها بعد حزمة Laravel. نفذ الأمر التالي لتحديث المشروع: composer updateعند إنشاء العمود gender لم نحدد طول الحقل، أي أنه سيأخذ الطول المبدئي للحقول من نوع string وهو255 محرفا. ننشئ ملف تهجير جديدا لتعديل طول الحقل ليصبح 5 كحد أقصى. نعدل الملف على النحو التالي: php artisan make:migration modify_gender_in_employees --table=employeesقبول فراغ الحقول في الجدوليفترض Laravel عند إنشاء الحقول أنها لا تقبل فراغ القيمة، أي أنه يجب ذكر قيمة للحقل عند إدراج تسجيلات في الجدول. يمكننا تغيير هذا الإعداد المبدئي وجعل قيمة حقل ما اختيارية. سنأخذ الحقل gender للتمثيل به. ننشئ ملفا للتهجير: php artisan make:migration make_gender_null_in_employees --table=employeesثم نعدله على النحو التالي: <?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class MakeGenderNullInEmployees extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::table('employees', function (Blueprint $table) { $table->string('gender', 5)->nullable()->change(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('employees', function (Blueprint $table) { $table->string('gender', 5)->change(); }); } } تجعل الدالة nullable الحقل gender يقبل قيما فارغة.php artisan migrate إضافة مفتاح خارجي Foreign keyنصنف موظفينا حسب القسم الذي يعملون فيه. ننشئ جدولا للأقسام depts ثم نضيف مفتاحا خاريجا في جدول الموظفين employees. الأمر أدناه ينشئ ملف تهجير لجدول الأقسام: php artisan make:migration deptsعدل ملف التهجير: <?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class Depts extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('depts', function (Blueprint $table) { $table->increments('id'); $table->string('name'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('depts'); } }ثم ننفذ أمر التهجير لإنشاء الجدول: php artisan migrateيُشترط لتصح علاقة عبر مفتاح خارجي بين جدولين أن يكون المفتاح الخارجي والمفتاح الرئيس Primary key متطابقين في النوع. استخدمنا في تعريف المفتاح الرئيس idضمن الجدول depts دالة increments التي تعطي النوع عددا طبيعيا من عشرة أرقام ;(unsigned integer INT(10 وهو ما يعني أننا سنعطي نفس النوع للمفتاح الخارجي الذي سننشئه في الجدول employees. الفرق أن المفتاح الخارجي لا يزداد تلقائيا لذا سنستخدم الدالة unsignedInteger التي لها نفس مفعول increments من حيث نوع الحقل وطوله، مع فرق أنها لا تضيف الازدياد التلقائي. ملحوظة: حتى تمكن إضافة مفتاح خارجي في الجدول employees يجب أن يكون الجدول فارغا (بدون تسجيلات). لهذا السبب علقنا في فقرة ماضية الشفرة الخاصة بـFaker. نفذ الأمر التالي لإنشاء ملف تهجير لإضافة حقل المفتاح الخارجي dept_id إلى الجدول employees: php artisan make:migration add_dept_id_in_employees --table=employeesثم نعدل ملف التهجير: <?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class AddDeptIdInEmployees extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::table('employees', function (Blueprint $table) { $table-> unsignedInteger ('dept_id')->after('gender'); $table->foreign('dept_id') ->references('id')->on('depts') ->onDelete('cascade'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('employees', function (Blueprint $table) { $table->dropColumn('dept_id'); }); } }ثم ننفذ التهجير: php artisan migrate بذر قواعد البياناتيشير مصطلح البذر Seeding إلى عملية إضافة بيانات وهمية لأغراض الاختبار في قواعد البيانات. نطبق هذا الإجراء على جدول drinks الذي أنشأناه في أول الدرس. نفذ الأمر التالي لإنشاء ملف للبذر: php artisan make:seeder DrinksTableSeederينشئ الأمر ملفا باسم DrinksTableSeeder.php على المسار database/seeds. افتح الملف: <?php use Illuminate\Database\Seeder; class DrinksTableSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { // } } يمدد الصنف DrinksTableSeeder الصنف Seeder ويعرّف الدالة run التي تُنفّذ عند تشغيل أمر البذر في Artisan. عدل الملف ليصبح محتواه التالي: <?php use Illuminate\Database\Seeder; class DrinksTableSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { DB::table('drinks')->insert([ 'name' => 'Orange Juice', 'comments' => 'Rich in C vitamin', 'rating' => 9, 'juice_date' => '2015-12-20', ]); } أضفنا في الدالة run أمر إدراج في جدول البيانات drinks ومررنا مصفوفة توافق عناصرها حقول الجدول مع تحديد قيم عناصر المصفوفة. ننفذ الأمر أمر البذر لإضافة التسجيلة أعلاه إلى الجدول: php artisan db:seed --class=DrinksTableSeederنمرر لأمر البذر php artisan db::seed اسم الملف المراد تنفيذه. الآن عند التحقق نجد في جدول قاعدة البيانات التسجيلة التالية: قاعدة البيانات الخاصة بمشروع Larashopتعرفنا في الفقرات الماضية على أساسيات التهجير في Laravel. سنجعل هذه المعرفة موضع التطبيق لإنشاء قاعدة بيانات لمشروع Larashop. ستشنرك جميع الجداول في الحقول التالية التي أنشأناها لأغراض الفحص والتدقيق. التسلسلالحقلنوع البياناتالوصف1created_at Timestamp ختم زمني لتاريخ إدراج التسجيلة2updated_at Timestamp ختم زمني لتاريخ تحديث التسجيلة3created_at_ip (Varchar(45 عنوان IP المستخدم لإدراج التسجيلة4updated_at_ip (Varchar(45 عنوان IP المستخدم لتحديث التسجيلةجدول منشورات المدونةالتسلسلالحقلنوع البياناتالوصف1id INT مفتاح رئيس (AUTOINCREMENT) عدد طبيعي يزداد تلقائيا2url (Varchar(255 رابط الصفحة3title (Varchar(140 عنوان الصفحة4description (Varchar(170 وصف يظهر في محركات البحث5content Text محتوى الصفحة أو المنشور 6conblogtent (Tinyint(1 يحدد ما إذا كان المنشور صفحةجدول التصنيفاتالتسلسلالحقلنوع البياناتالوصف1id INT مفتاح رئيس (AUTOINCREMENT) عدد طبيعي يزداد تلقائيا2name (Varchar(255 اسم التصنيفجدول العلامات التجاريةالتسلسلالحقلنوع البياناتالوصف1id INT مفتاح رئيس (AUTOINCREMENT) عدد طبيعي يزداد تلقائيا2name (Varchar(255 اسم العلامة التجاريةجدول المنتجاتلكل منتج تصنيف وعلامة تجارية وحيدين. التسلسلالحقلنوع البياناتالوصف1id INT مفتاح رئيس (AUTOINCREMENT) عدد طبيعي يزداد تلقائيا2name (Varchar(255 اسم المنتج3title (Varchar(140 عنوان المنتج4description (Varchar(500 عنوان المنتج5price int ثمن المنتج6category_id int معرف تصنيف المنتج7brand_id int معرف العلامة التجارية للمنتجملفات التهجير لجداول قاعدة بيانات المشروعسننشئ في هذه الفقرة ملفات تهجير لجداول البيانات المذكورة أعلاه؛ سنضيف أيضا بعض البيانات الوهمية إلى الجداول باستخدام آلية البذر التي تعرفنا عليها سابقا. توليد ملفات التهجيرافتح سطر الأوامر ونفذ الأوامر التالية لتوليد ملفات الجداول: جدول منشورات المدونة: php artisan make:migration create_posts_table جدول تصنيفات المنتجات php artisan make:migration create_categories_tableجدول العلامات التجارية للمنتجات php artisan make:migration create_brands_tableجدول المنتجات php artisan make:migration create_products_tableتحرير ملفات التهجيرننتقل لتحرير كل ملف من ملفات التهجير. جدول منشورات المدونة <?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreatePostsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('posts', function (Blueprint $table) { $table->increments('id'); $table->string('url', 255)->unique(); $table->string('title', 140); $table->string('description', 170); $table->text('content'); $table->boolean('blog'); $table->timestamps(); $table->string('created_at_ip'); $table->string('updated_at_ip'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::drop('posts'); } } جدول التصنيفات <?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateCategoriesTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('categories', function (Blueprint $table) { $table->increments('id'); $table->string('name', 255)->unique(); $table->timestamps(); $table->string('created_at_ip'); $table->string('updated_at_ip'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::drop('categories'); } } جدول العلامات التجارية <?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateBrandsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('brands', function (Blueprint $table) { $table->increments('id'); $table->string('name', 255)->unique(); $table->timestamps(); $table->string('created_at_ip'); $table->string('updated_at_ip'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::drop('brands'); } } جدول المنتجات <?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateProductsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('products', function (Blueprint $table) { $table->increments('id'); $table->string('name', 255)->unique(); $table->string('title', 140); $table->string('description', 500); $table->integer('price'); $table->unsignedInteger('category_id'); $table->unsignedInteger('brand_id'); $table->timestamps(); $table->string('created_at_ip'); $table->string('updated_at_ip'); // مفتاح خارجي على جدول التصنيفات $table->foreign('category_id') ->references('id')->on('categories') ->onDelete('cascade'); // مفتاح خارجي على جدول العلامات التجارية $table->foreign('brand_id') ->references('id')->on('brands') ->onDelete('cascade'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('products'); } }بذر قاعدة بيانات المشروعندرج بيانات وهمية في جداول قاعدة البيانات قصدَ الاختبار. أنشئ ملفات البذر بتنفيذ الأوامر أدناه على التوالي: php artisan make:seeder CategoriesTableSeeder php artisan make:seeder BrandsTableSeeder php artisan make:seeder ProductsTableSeederيحوي جدول المنتجات مفتاحين خارجيين لجدولي التصنيف والعلامة التجارية. لذا يجب البدء بهما (لا يصح إدراج مفتاح خارجي لتسجيلة غير موجودة في الجدول الذي مثل المفتاح الخارجي مرجعا إليه). بذر جدول التصنيفات <?php use Illuminate\Database\Seeder; class CategoriesTableSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { DB::table('categories')->insert(['name' => 'MENS']); DB::table('categories')->insert(['name' => 'WOMENS']); DB::table('categories')->insert(['name' => 'KIDS']); DB::table('categories')->insert(['name' => 'FASHION']); DB::table('categories')->insert(['name' => 'CLOTHING']); } } بذر جدول العلامات التجارية<?php use Illuminate\Database\Seeder; class BrandsTableSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { DB::table('brands')->insert(['name' => 'ACNE']); DB::table('brands')->insert(['name' => 'RONHILL']); DB::table('brands')->insert(['name' => 'ALBIRO']); DB::table('brands')->insert(['name' => 'ODDMOLLY']); } } بذر جدول المنتجات<?php use Illuminate\Database\Seeder; class ProductsTableSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { DB::table('products')->insert(['name' => 'Mini skirt black edition', 'title' => 'Mini skirt black edition','description' => 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna','price' => 35,'category_id' => 1,'brand_id' => 1,]); DB::table('products')->insert(['name' => 'T-shirt blue edition', 'title' => 'T-shirt blue edition','description' => 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna','price' => 64,'category_id' => 2,'brand_id' => 3,]); DB::table('products')->insert(['name' => 'Sleeveless Colorblock Scuba', 'title' => 'Sleeveless Colorblock Scuba','description' => 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna','price' => 13,'category_id' => 3,'brand_id' => 2,]); } } ثم ننفذ أوامر البذر لكل جدول. خاتمةتعرفنا في هذا الدرس على تهجير قواعد البيانات وبذرها في Laravel. كما أننا حددنا هيكلة قاعدة بيانات المشروع الذي نعمل عليه. في الدروس القادمة سنتعرف على إطار عمل Eloquent الذي سنعتمد عليه للتخاطب مع قاعدة البيانات وعرض محتوياتها عند الاقتضاء. ترجمة -وبتصرّف- لمقال Laravel 5 Migrations لصاحبه Rodrick Kazembe.
-
من المرغوب عادةً عند الانتقال من خادوم إلى آخر أن تُنقَل قواعد الجدار الناري iptables كجزءٍ من العملية؛ سيشرح هذا الدرس لك كيف تنسخ بسهولة القواعد المُفعّلة لجدار iptables من خادوم إلى آخر. المتطلبات المسبقةيتطلب هذا الدرس وجود خادومَين؛ سنشير إلى الخادوم المصدر (الذي توجد فيه قواعد iptables) بالخادوم A، والخادوم الوجهة (الذي ستُنقَل إليه القواعد) بالخادوم B. ستحتاج إلى امتيازات الجذر على كلي الخادومين. عرض قواعد Iptables الموجودةقبل نقل قواعد iptables، لننظر إليها أولًا؛ يمكنك فعل ذلك عبر تطبيق هذا الأمر على الخادوم A: sudo iptables -Sمثالٌ عن ناتج الأمر السابق: -P INPUT ACCEPT -P FORWARD ACCEPT -P OUTPUT ACCEPT -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -A INPUT -p tcp -m tcp --dport 22 -j ACCEPT -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT -A INPUT -s 15.15.15.51/32 -j DROPستُستخدَم القواعد الناتجة في المثال السابق لشرح عملية نقل الجدار الناري. تصدير قواعد Iptablesيكتب الأمر iptables-save قواعد iptables الحالية إلى stdout (مجرى الخرج القياسي)؛ مما يمنحنا طريقةً سهلةً لتصدير قواعد الجدار الناري إلى ملف، وذلك عبر إعادة توجيه stdout إلى ملف. استخدم الأمر iptables-save على الخادوم A -الذي تريد نقل القواعد منه- لتصدير القواعد الحالية إلى ملف باسم «iptables-export» كما يلي: cd ~ sudo iptables-save > iptables-exportهذا سيُنشئ الملف iptables-export في مجلد المنزل (home) الخاص بمستخدمك؛ يمكن أن يُستخدَم هذا الملف على خادومٍ آخر لتحميل قواعد الجدار الناري إلى iptables. عرض محتويات الملف (خطوة اختيارية)لنلقِ نظرةً سريعةً على محتويات الملف؛ سنستخدم الأمر cat لإظهار الملف على الطرفية: cat iptables-exportسيكون ناتج الأمر السابق شبيهًا بما يلي: # Generated by iptables-save v1.4.21 on Tue Sep 1 17:32:29 2015 *filter :INPUT ACCEPT [135:10578] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [8364:1557108] -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -A INPUT -p tcp -m tcp --dport 22 -j ACCEPT -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT -A INPUT -s 15.15.15.51/32 -j DROP COMMIT # Completed on Tue Sep 1 17:32:29 2015كما لاحظت، يحتوي الملف على ضبط قواعد iptables المُفعّلة؛ نحن جاهزون الآن لنسخ هذا الملف إلى الوجهة، التي هي الخادوم B. نسخ القواعد المصدرة إلى الخادوم الوجهةسنحتاج إلى نسخ ملف القواعد إلى الخادوم الوجهة (الخادوم B)؛ أسهل طريقة لفعل ذلك هي استخدام scp أو نسخ محتويات الملف ثم لصقها في ملفٍ جديد على الخادوم B. سنشرح كيفية استخدام الأمر scp لنسخ الملف عبر الشبكة إلى مجلد /tmp. نفِّذ أمر scp الآتي على الخادوم A؛ تأكد أنك قد استبدلت الأجزاء المعلّمة ببيانات دخولك إلى الخادوم وعنوان IP له: scp iptables-export *user*@*server_b_ip_address*:/tmpسيُنسَخ الملف إلى مجلد /tmp في الخادوم B بعد الاستيثاق (authentication)؛ الحظ أن محتويات المجلد /tmp ستُحذَف عند إعادة الإقلاع، لذا يمكنك نسخ ذاك الملف إلى مكانٍ آخر إن أردت الاحتفاظ به. استيراد قواعد Iptablesبعد نسخ القواعد المُصدَّرة إلى الخادوم الوجهة، فيمكننا الآن تحميلها إلى iptables؛ لكن -وبالاعتماد على بيئتك- ربما تحتاج إلى تحديث القواعد في الملف ووضع عناوين ومجالات IP جديدة، وربما تريد أيضًا أن تعدِّل أسماء البطاقات الشبكيّة؛ إذا أردت أن تعدِّل القواعد قبل تحميلها، فعليك تعديل الملف /tmp/iptables-export الآن. بعد أن تكون جاهزًا لتحميل القواعد من ملف iptables-export إلى iptables، فاستخدم الأمر iptables-restore لفعل ذلك. على الخادوم الوجهة (الخادوم B)، نفِّذ هذا الأمر لتحميل قواعد الجدار الناري: sudo iptables-restore < /tmp/iptables-exportسيحمِّل الأمر السابق القواعد إلى ipables، يمكنك التحقق من تحميلها بوساطة الأمر sudo iptables -S. حفظ القواعدتكون قواعد Iptables مؤقتةً، لذلك يجب أخذ الحيطة عند التعامل معها للحفاظ عليها حتى بعد إعادة الإقلاع؛ سيكون عليك تنفيذ هذه الخطوة على الخادوم B. سنشرح كيفية حفظ القواعد على توزيعتَي أوبنتو و CentOS. توزيعة أوبنتوإن أسهل طريقة لحفظ قواعد Iptables في أوبنتو لكي تبقى بعد إعادة الإقلاع هي استخدام حزمة iptables-persistent؛ يمكنك تثبيتها باستخدام الأمر apt-get: sudo apt-get install iptables-persistentستُسأل أثناء التثبيت إذا ما كنتَ تريد حفظ قواعد الجدار الناري الحالية؛ اختر yes إذا أردت حفظها. إذا حدَّثتَ قواعد جدارك الناري في المستقبل، وأردت حفظ التعديلات، فنفِّذ هذا الأمر: sudo invoke-rc.d iptables-persistent saveتوزيعة CentOS 6 وما قبلهاعلى توزيعة CentOS 6 وما قبلها (حيث تستخدم CentOS 7 افتراضيًا جدار FirewallD الناري)، يمكنك استخدام سكربت init الخاص بتطبيق iptables لحفظ القواعد: sudo service iptables saveسيحفظ الأمر السابق قواعد iptables الحالية إلى الملف /etc/sysconfig/iptables، الذي يحمَّل بوساطة iptables عند الإقلاع. الخلاصةتهانينا، لقد أتممت نقل قواعد جدارك الناري من خادومك الأصلي إلى خادومك الجديد. ترجمة -وبتصرف- للمقال: How To Migrate Iptables Firewall Rules to a New Server لصاحبه: Mitchell Anicas.