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

عبد اللطيف ايمش

الأعضاء
  • المساهمات

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

  • تاريخ آخر زيارة

  • عدد الأيام التي تصدر بها

    63

كل منشورات العضو عبد اللطيف ايمش

  1. لا يجب أن تبدأ دائمًا بالتطوير باستعمال إطار عمل JavaScript، لكن هنالك حالات يكون فيها استعمال إطار العمل أمرًا منطقيًا. منذ فترةٍ كتبتُ مقالةً بعنوان «لماذا يدفعنا التطوير بلغة JavaScript إلى حافة الجنون!» والتي زارها الكثيرون، وظهر التساؤلان الآتيان ردًا عليها: - متى يجب أن نستخدم إطار عمل؟ - إذا لم نستعمل إطار عمل، فكيف نبدأ؟ سأحاول في هذه المقالة الإجابة على التساؤل الأول، أما سؤال كيفية البدء إذا لم نستعمل إطار عمل فهو سؤالٌ أكبر، ويحتاج تفصيلًا أكثر. بدأتُ العمل منذ عدِّة سنوات في شركة (لن أسميها هنا)، والتي تستطيع أن تطلب منها إنشاء أي شيء غريب يمكن تخيله، وستصنعه لك خلال فترةٍ وجيزة. كان للشركة هامش ربحٍ كبير، وتجني أموالًا طائلة، وتعلمتُ الكثير عن إدارة الأعمال منها، لكنني تعلمتُ أيضًا بعض الأمور عن الأنظمة. كانت واجهة موقع الشركة الإلكتروني معقدةً! ولهذا السبب بدأ موقع الشركة في بدايات 2000 بالتحول إلى تطبيقٍ ذي صفحةٍ واحدة، حتى قبل انتشار فكرة التطبيقات ذات الصفحة الواحدة (single page application). كانت شيفرات JavaScript التي تشغل موقعهم معقدةً جدًا، وكانوا نادرًا ما يوظفون مبرمجين، واختاروا إطار عمل YUI بدلًا من jQuery لأنه كان شائعًا في تلك الفترة، ثم اشتقوا (fork) شيفرة YUI مما جعل التعامل معها وصيانتها أمرًا صعبًا جدًا. ولسببٍ أجهله، كانوا يكتبون شيفرات JavaScript بشكلٍ شبيهٍ بشيفرات Visual Basic. كانوا أيضًا يستعملون نظامًا لإدارة الإصدارات (version control system) من الثمانينات الذي كانت آلية حماية الملفات من التعديل فيه هي قفلها (file locks) والتي تستطيع بسهولة تجاوزها. سكربت البناء الذي كانوا يستعملونه هو شخصٌ اسمه Ed، فلتحويل الشيفرة إلى الخادوم الإنتاجي سيكون عليك أن تطبع قائمةً بالملفات التي عدَّلتَها وتسلمها لذاك الشخص، ثم سيبحث عن تلك الملفات وينسخها يدويًا إلى كل خادوم على حدة. كان السند الخلفي (backend) للموقع هو حاسوبٌ قديمٌ يُشغِّل برمجيات مكتوبة في سبعينيات القرن الماضي، وكانت تُحدَّث بيئة التطوير المشتركة يدويًا من قِبل المطورين، متى أرادوا ذلك. إذا كنتَ تضحك الآن على حالهم وتظن أن هؤلاء الأشخاص أغبياء، فأظن أنَّ الوقتَ مناسبٌ لأشير إلى أنهم لم يعانوا من انقطاعاتٍ في الخدمة، وكانوا يُصدرون ميزاتٍ جديدة دوريًّا، ويجنون أموالًا طائلة تزداد عامًا بعد عامًا. لذا أظن أنهم يضحكون الآن خلال طريقهم إلى البنك ليستلموا أرباحهم. هل تتلقى أجرًا لقاء كتابتك للشيفرات؟ إذًا أنت تعمل لدى شركة. وعندما تقرر الشركة كيف تُنفِق ميزانية المطورين، فلن يكون منطقيًا أن تنفق مالًا لكتابة البرمجيات «بالطريقة الصحيحة». الفكرة التي أحاول إيصالها هنا هي أنَّ حال الشركة السابقة قد يبدو بائسًا بالنسبة إلى مطوري البرمجيات، لكنه منطقيٌ جدًا وجيد بالنسبة إلى أصحاب تلك الشركة الذين يهتمون بالربح. استخدام إطار عمل JavaScript قد لا يكون خيارًا استراتيجيًا للشركات حتى لو كان حالها سيئًا كالشركة التي نتحدث عنها. إذًا، كيف يجب أن تتابع الشركة السابقة بتطوير برمجياتها؟ كيف ستقرر الشركة أنها بحاجة إلى استخدام إطار عمل؟ شرحتُ سابقًا في مقالة «لماذا يدفعنا التطوير بلغة JavaScript إلى حافة الجنون!» أنَّ من غير المنطقي البدء بتطوير البرمجيات انطلاقًا من إطار عمل. لنفترض وجود شيءٍ شبيهٍ بإطار Angular عند نشأة الشركة السابقة؛ فهل كان عليهم استخدام إطار عمل؟ هل يجب عليهم استخدام إطار عمل في هذه الفترة؟ عندما تختار الشركة إطار عمل لتستعمله لتطوير برمجياتها، فهنالك كلف ومخاطر: - ماذا لو اختفى دعم إطار العمل خلال خمس سنوات؟ هل يمكن أن تتحمل الشركة عبء صيانة إطار العمل بنفسها؟ هل يتوافر في فريق العمل أشخاصٌ لهم خبرات في هذا المجال؟ الجواب هو النفي لأغلبية الشركات، ولا نُغفِل أنَّ الكثير من أطر العمل Frameworks تختفي فجأةً. عليك أن تجري مع التيار وتخمِّن إن كانت الزيادة في الإنتاجية الآتية من اختيار إطار عمل تستحق الوقت والكلفة اللازمة للتحويل إلى إطارٍ آخر لاحقًا، أو أنَّها تستحق كلفة التعامل مع برمجيات غير مدعومة لسنوات في نفس الوقت الذي تحاول فيه توفير ميزانية كافية للتحويل إلى إطارٍ جديد. إذا كانت شيفرات برمجيتك قليلة نسبيًا وكنت تعمل في شركة وتحذف كميةً لا بأس بها من الشيفرات وتُعيد كتابتها (كثير منا يفعل ذلك لكنهم لا يعترفون)، فأرى أنَّ من المناسب استعمال إطار عمل. أما لو كانت شيفرات برمجيتك كثيرة جدًا، وكنت تنظر إلى خطتك التي وضعتها للسنوات الخمسة القادمة ورأيتها مليئةً بالميزات الجديدة التي ستكوِّن جزءًا أساسيًا من شركتك، فإن اختيار إطار عمل الذي سينتهي دعمه خلال خمسة سنوات هو رهانٌ خاسر، والطريقة الوحيدة التي ستجعلك تختار واحدًا هي التفكير بأجوبةٍ للأسئلة المتبقية … - كم ستخسر من إنتاجية مطوريك حتى يتعلموا إطار العمل الجديد؟ إذا كنت تنتقل إلى إطارٍ وليكن Angular (وما لم توظِّف مطورين يعرفون Angular من قبل ويلمّون بأحدث المعلومات) فستنفق مالًا لتدريب فريق التطوير وسيضيّع المطورون الأشهر القادمة يسألون «كيف أفعل ذلك في Angular؟» حتى لو كانوا يفعلون نفس الأمر تمامًا باستخدام jQuery وشيفرات JavaScript البسيطة دون أدنى تفكير. - هل يمكننا بناء هذه الميزة التي تجعلنا نجني مالًا أكثر بنفس مقدار الوقت اللازم لتعلم إطار العمل؟ الجواب لأغلبية الشركات الربحية هو «نعم». - هل سيسمح لنا إطار العمل بجني المزيد من المال بجعلنا نُنشِئ الميزات الجديدة بوتيرة أسرع في المستقبل؟ نظريًا: هذه هي الفائدة المرجوة من إطار العمل. إذ ستحصل على فائدة أكبر من إطار العمل عندما يكون فريقك كبيرًا، وشيفرات مشروعك كثيرة، وتريد تسريع وتيرة تطوير الميزات الجديدة. لكن لاحظ أنَّك تستطيع تطوير بعض البرمجيات بسهولة ولن تستفيد فيها من التحول إلى إطار عمل. حسنًا، ربما تظن أنَّ هذا قرارٌ صعبٌ، إلا أنه في الواقع عكس ذلك لأغلبية الشركات. - الشركات الصغيرة وسريعة التغيرات تقع في الزاوية العليا اليسرى من المخطط السابق، فإذا كانت شيفرات برنامجك تتغير بسرعة فلن يكون من الخطر التغيير إلى إطار عمل آخر. فلو كنت تضيف الميزات يمنةً ويسرةً لأنك لم تعلم أيها يجني مالًا بعد، فلن تهتم إن «مات» إطار العمل الذي تستخدمه، لأنك ستحذف تلك الميزات والأجزاء من الشيفرات على أيّة حال. إذا كانت إنتاجيتك أكبر عندما تستعمل إطار عمل، وكنت تعمل لدى شركةٍ ناشئةٍ فسأقول أنَّ من الصواب استعمال إطار عمل، مع الأخذ بالحسبان أنَّ أغلبية الأشخاص تقل إنتاجيتهم عندما يستعملون إطار عمل لكنهم لا يعترفون بذلك. - تتواجد الشركات الصغيرة وذات معدل التغيير البطيء في الطرف السفلي اليساري من المخطط، وتكون إما شركات صغيرة تقليدية التي لا تُمثِّل البرمجيات مكوِّنًا أساسيًا من طريقة عملها، أو شركات البرمجيات الصغيرة. إذا كنت مبرمجًا تعمل لدى شركة صغيرة، فلا تستعمل إطار Angular أو React أو غيرهما. وإنما استخدم المكتبات المستقرة والثابتة والتي تعمل على جميع المتصفحات. فلو كنت تعمل في شركةٍ صغيرةٍ، فأرى أنَّ مخاطر استعمال إطار عمل تفوق ميزاته. - الشركات الكبيرة وذات معدل التغيير السريع تتواجد في القسم العلوي الأيمن من المخطط، وهي الشركات التي تكون البرمجيات مكوِّنًا أساسيًا فيها، أي أنهم يضيفون الميزات كثيرًا ويحذفون أجزاءً كبيرةً من الشيفرات ويعيدون كتابتها (حتى لو لم يعترفوا بذلك)، ولديهم فرق برمجية كبيرة، والميزات الجديدة التي يُضيفونها ستدر عليهم مالًا. لذا من المنطقي أن يدرِّبوا فِرَق البرمجة عندهم لاستعمال إطار عمل، لكي يبدأ الجميع من نفس المكان ولتُكتَب الميزات بوتيرة أسرع. إذا كانت شركتك ذات دخلٍ كبير، فستقل مخاطر فقدان دعم إطار العمل مع مرور الزمن، لأنك عندما كنت تختار ما هو إطار العمل الذي ستعمل عليه فستأخذ بالحسبان الدعم الطويل له. أما لو كنت تعمل في شركةٍ كبيرةٍ وتجني أموالًا طائلة وكانت البرمجيات من أساس عملك، فمن المنطقي أن تكتب إطار عمل خاص بك مثل Facebook و Google … - الشركات الكبيرة وذات معدل التغيير البطيء تقع في الركن السفلي اليميني من المخطط. وتنتمي الشركة التي تحدثنا عنها في بداية المقالة إلى هذا النوع، ولن يكون الخيار الصائب واضحًا هنا. فليس مجال عمل الشركة هو البرمجيات، لكن البرمجيات هي جزءٌ أساسيٌ نظام عمل الشركة. لن تتغير الشركة بسرعة، لكن الميزات الجديدة ستساعد الزبائن بشراء المزيد من المنتجات. مثاليًا، يمكنك أن تبني على إطار عمل الذي سيبقى حوالي 50 سنة، وهذا أمرٌ معقولٌ بالنسبة إلى شركةٍ تستعمل برمجيات وشيفرات عمرها 40 سنة. لكن هذا ليس موجودًا، لذا قد تفكر ببناء إطار عمل خاص بك، لأنك ستتأكد أنك لن تقع في فخ أطر العمل الميتة بعد 10 سنوات. لذا يجب أن تنشئ كل شيء من الصفر، وسترى أنَّ نفقات إنشاء ذلك قليلةٌ مقارنةً بالربح خلال 50 سنة … إذًا، هل يجب أن تنتقل الشركة إلى إطار عمل؟ لا يوجد جواب سهل! فعندما كنتُ مطورًا أعمل عندهم وكنت أفكر عن البرمجيات فقط، فكان الجواب الجلي بالنسبة لي هو: بالطبع يجب أن ينتقلوا لاستخدام Angular! لكن عندما بدأتُ شركتي الخاصة، فأصبحتُ أرى البرمجيات من زاويةٍ أخرى، لذا لن يكون الخيار سهلًا. توقف برهةً وفكِّر بعمق قبل الالتزام بإطار عمل، وإذا كنتَ صاحب قرار التحول إلى إطار عمل في حال استعملتك الشركة كمستشار، فأسدِ خدمةٍ إلى الشركة، وانزع عنك نظارات المبرمج، وضع نظارات مدير الأعمال. ترجمة -وبتصرّف- للمقال Should You Ever Use a JS Framework?‎ لصاحبه Sean Fioritto
  2. قائمة التنقل هي جزءٌ مهمٌ من موقع ووردبريس وتساعدك زوار موقعك ليتنقلوا بسهولة في موقعك وتسهِّل إظهار البُنية الهيكلية لموقعك؛ وهنالك مكانٌ مخصصٌ لها في مكانٍ ما في أعلى الصفحات في أغلبية القوالب. لكن هل استعمال قوائم التنقل مقتصرٌ على ترويسة الموقع؟ الحقيقة هي أنَّك تستطيع القيام بالكثير معها. سأريك في هذا الدرس بعض الخيارات التي بمقدورك استعمالها لتوظيف قوائم التنقل بشكلٍ أفضل. وسأريك كيف يمكنك إنشاء عدِّة قوائم تنقل لموقعك. سأريك أيضًا طريقتين لوضع القوائم الإضافية في الواجهة الأمامية لموقعك، إحداها باستخدام ودجت والأخرى عبر كتابة شيفرات. استخدام ودجت هو الطريقة الأسهل والمناسبة إذا لم تشأ كتابة الشيفرات، لكنها تعتمد على وجود منطقة ودجات في القالب الذي تستخدمه، فلو لم يكن يحتوي القالب المُفعَّل على منطقة لإضافة ودجات في المكان الذي تريد وضع القائمة فيه، فسأريك كيفية كتابة شيفرة لإضافة القائمة في قالب. لنبدأ بإنشاء بضع قوائم. إنشاء عدِّة قوائم للتنقل يملك موقعي قائمة تنقل رئيسية أنشأتُها مسبقًا، يمكنك أن ترى من الصورة الآتية وجود عدِّة مستويات فيها، حيث توجد العناصر الفرعية داخل قائمة «Services». لقد فعّلتُ خيار «Primary Menu» (القائمة الأساسية) وهذا يعني أنَّ القالب الذي أستعمله (وهو Twenty Sixteen) سيُعرِض القائمة في المكان الرئيسي لها: لكنني لستُ محدودًا بهذه القائمة فقط، وأستطيع إنشاء واحدة أخرى، وسأرجع إلى صفحة «Menus» (قوائم) في لوحة التحكم وأضغط على رابط «create a new menu» (أنشئ قائمة جديدة): هنا يمكنك إضافة قائمةٍ جديدة، القائمة التي سأُنشِئها ستكون لصفحات الخدمات، لذا سأُطلِق عليها اسم «Services» ثم سأضغط على زر «Create Menu» (إنشاء قائمة) لإنشائها، ثم سأضيف تلك الصفحات إليها: بعد إضافة الصفحات إلى قائمتك، فاضغط على زر «Save Menu» (حفظ القائمة) لحفظها. ملاحظة: لا تُفعِّل خيار «Primary Menu» (القائمة الأساسية)، لأنَّك إذا فعلتَ ذلك، فستوضع هذه القائمة بدلًا من القائمة التي أنشأتها ووضعتها في ترويسة الموقع؛ لكننا لا نريد فعل ذلك، بل نريد إضافة هذه القائمة إلى مكانٍ آخر في الموقع. يمكنك فعل كل ذلك عبر صفحة تخصيص القالب، وذلك بالضغط على الخيار «Menus» (قوائم) على جانب الشاشة (الأيسر لو كانت لوحة التحكم عندك بالإنكليزية، والأيمن لو كانت بالعربية) وسترى كل القوائم التي أنشأتها معروضةً: من هنا يمكنك إضافة قائمة جديدة باستخدام زر «Add a Menu» (أضف قائمة)، أو يمكنك اختيار قائمة موجودة مسبقًا لتعدلها. إضافة القوائم باستخدام ودجت بعد أن ضبطتَ القائمة الجديدة، فحان الوقت لإضافتها إلى موقعك. من المفيد أن تُضيف القائمة إلى تذييل الموقع، وهذا يعني أنَّه عندما يُمرِّر الزوار إلى أسفل الصفحة، فليسوا بحاجةٍ إلى التمرير إلى أعلى الصفحة مرةً أخرى لكي يستطيعوا الانتقال إلى صفحةٍ أخرى في الموقع، وهذا مفيدٌ جدًا خصوصًا في الشاشات الصغيرة للهواتف المحمولة. لا يملك القالب الذي أستخدمه منطقة ودجات في التذييل، لكنه يملك منطقة ودجات باسم «Content Bottom» (أسفل المحتوى) الموجودة تحت المحتوى، وسأستخدمها. يمكنك استخدام أيّة منطقة ودجات موجودة في القالب تشاء، وذلك لملائمة احتياجاتك. لإضافة القائمة إلى ودجت، فيمكنك إما أن تستخدم صفحة التخصيص أو صفحة الودجات في لوحة التحكم من «Appearance > Widgets» (مظهر > ودجات)، لكن دعنا نستخدم صفحة التخصيص هذه المرّة. افتح صفحة التخصيص ثم اضغط على خيار «Widgets» (ودجات) الموجود في القائمة الظاهرة على طرف الشاشة: اختر منطقة الودجات التي تريد إضافة القائمة إليها، واضغط على زر «Add a Widget» (أضف ودجت)، وهنا سيُتاح لك الخيار لإضافة الودجات التي تريد: اختر منها «Custom Menu» (قائمة مخصصة)، ثم اختر القائمة التي تريد إضافتها، وأعطها عنوانًا: بعد أن تنتهي من فعل ذلك، فاضغط على زر «Save and Publish» (حفظ ونشر) لتحفظ تعديلاتك، ثم تحقق من ظهور القائمة في واجهة موقعك: إذا أردتَ إضافة قوائم أخرى –سواءً إلى نفس منطقة الودجات أو إلى منطقةٍ أخرى– فاتبع نفس الخطوات السابقة لإنشاء أي عدد من القوائم تحتاج له، ثم أضفها إلى منطقة الودجات التي تشاء. إضافة قائمة إلى قالبك باستخدام الشيفرات إذا لم يملك قالبك منطقةً ودجات مناسبةً لإضافة القائمة التي أنشأتها إليها، فيمكنك كتابة شيفرة لوضع القائمة أينما تشاء. فبدلًا من استخدام منطقة الودجات المسماة «Below Content» (أسفل المحتوى) فسأكتب شيفرةً لإضافة قائمة الخدمات إلى تذييل القالب يدويًا. لاحظ أنني أستعمل قالب Twenty Sixteen، وسأُنشِئ «قالب ابن» (child theme) جديد لفعل ذلك، لأنَّ أيّة تعديلات سأجريها على القالب الأصلي ستختفي عندما أُحدِّث القالب في المستقبل؛ أما إذا كنت تعمل على قالبٍ أنشأتَه أنت (وهو خاصٌ بموقعك) فيمكنك تعديل ملفات القالب مباشرةً. ملاحظة: يمكنك أن تجد الملفات المصدرية لهذا الدرس على GitHub. سأنُشِئ القالب الجديد في مجلد wp-content/themes وفيه ملفين، أحدهما باسم style.css الذي يُخبر ووردبريس أنَّ هذا القالب هو «قالب ابن»، وملف التذييل footer.php الذي سينوب عن الملف الأصلي الموجود في القالب الرئيسي. وستستعمل ووردبريس الملفات الأخرى من مجلد القالب الأب. إنشاء قالب ابن أولًا، عليك إضافة ما يلي إلى ملف style.css لتخبر ووردبريس أنَّ هذا القالب هو قالب ابن: /* Theme Name: WPMU DEV Navigation Menus for Power Users Theme URI: https://github.com/rachelmccollin/wpmudev-nav-menus Description: Theme to support WPMU DEV post on navigation menus Author: Rachel McCollin Author URI: http://rachelmccollin.co.uk/ Template: twentysixteen Version: 1.0 */ @import url("../twentysixteen/style.css"); احفظ الملف. إضافة القائمة إلى التذييل عليك –لإضافة القائمة إلى التذييل– تعديل الملف footer.php؛ وإذا كنتَ تعمل على قالب ابن، فعليك نسخ ملف footer.php من القالب الأساسي إلى مجلد القالب الابن، ثم عليك تعديل الملف المنسوخ. افتح ملف التذييل وأضف ما يلي بعد وسم بداية العنصر <footer>: <aside class="footer-menu widget"> <h3>Services</h3> <nav class="footer-navigation" role="navigation" aria-label="<?php esc_attr_e( 'Footer Menu', 'wpmudev' ); ?>"> <?php wp_nav_menu( array( 'menu' => 'services', 'menu_class' => 'footer-menu' ) ); ?> </nav> </aside> لاحظ أنني أضفت اسم القائمة (services) كأحد المعاملات التي مررتها إلى الدالة wp_nav_menu()‎، لذا لو سمّيتَ قائمتك باسمٍ مختلف، فاحرص على استعمال ذاك الاسم هنا. أضفتُ أيضًا الفئة widget لتنسيق القائمة، وهذا يعني أنَّ القائمة ستبدو شبيهةً بالودجات في واجهة الموقع: حسنًا، مظهرها ليس جميلًا أبدًا، فهي تقع ملتصقةً بحقوق النشر، لذا لنضف بعض التنسيق عليها. تنسيق القائمة احفظ ملف footer.php وافتح ملف style.css مرةً أخرى، ثم أضف إليه الشيفرة الآتية: .footer-menu { width: 100%; } الشيفرة السابقة تحلّ مشكلة العرض، وستظهر القائمة على يسار الشاشة مع وجود فراغ كبير حولها: لنحل هذه المشكلة بجعل عناصر القائمة تظهر بسطرٍ وحيد، وذلك عبر إضافة الشيفرة الآتية إلى ملف الأنماط: .footer-menu ul { margin-left: 0; } .footer-menu li { float: left; list-style-type: none; margin-left: 0; margin-right: 20px; font-size: 1.5em; } هذا أفضل. يجب أن تبدو القائمة كما هو ظاهر في الصورة الآتية في الشاشات الكبيرة: لكنها لا تظهر بشكلٍ جميلٍ في الشاشات الصغيرة: لنضف سطرًا لكي نفصل بين القائمة والعنصر الذي يليها، وذلك بإضافة الشيفرة الآتية إلى صفحة الأنماط: .site-info { clear: both; } كيف تبدو الآن؟ حسنًا، أصبحت أجمل. أصبح لدينا الآن قائمة الخدمات في تذييل الموقع، ونستطيع إضافة ما نشاء من روابط إليها دون الحاجة إلى تعديل ملفات القوالب مجددًا، إذ كل ما عليك فعله هو تعديل القائمة في لوحة التحكم. أليس هذا بسيطًا؟ الخلاصة يمكن الاستفادة من قوائم التنقل في أمكان كثيرة بخلاف ترويسة الصفحة. يمكنك إنشاء قوائم جديدة وإضافتها إلى مختلف الأماكن مثل التذييل. يمكنك أيضًا إنشاء قائمة للشريط الجانبي وأخرى للتذييل (أو أي مكانٍ آخر في قالبك)، أو يمكنك إضافة عدِّة قوائم في نفس منطقة الودجات؛ فالخيار عائدٌ إليك تمامًا! ترجمة -وبتصرّف- للمقال The Power Users Guide to WordPress Navigation Menus لصاحبته Rachel McCollin.
  3. حسنًا، لقد تعرفتَ على ووردبريس حديثًا وأصبحتَ جاهزًا لنقل موقعك الساكن (static) القديم المبني باستخدام HTML فقط إلى نظام إدارة محتوى حديث ومتطور. لكن انتظر برهةً، كيف ستفعل ذلك دون أن تفقد موقعك القديم أثناء بنائك للموقع الجديد؟ وكيف تضمن أنَّ الزوار لن يكتشفوا موقعك الجديد ببحثهم في Google قبل أن تنتهي من ضبطه كاملًا؟ سأريك في هذا الدرس طريقةً حلّ هذه الإشكالية، وسننظر إلى كيفية العمل على الموقع الجديد خلف الستار، مبقيًا موقعك القديم مكانه كي يبقى متاحًا للزوار أثناء تطوير موقعك الجديد. الأمر أبسط بكثير مما تتخيل، لذا لنبدأ! ما الذي ستحتاج إليه لكي تُكمِل معنا في هذا الدرس، فستحتاج إلى: موقعٍ ساكن (static) موجود مسبقًا في نفس المكان الذي تريد وضع موقعك الجديد فيه. المقدرة على تثبيت ووردبريس على خادومك، إما يدويًا أو تلقائيًا عبر سكربت. وصول عبر FTP أو لوحة تحكم cPanel إلى الخادوم. إذا لم تكن متأكدًا من أحد البنود السابقة، فاسأل مزود الاستضافة، وأخبرهم أنَّك ستحتاج إلى تعديل ونقل بضعة ملفات، ويجب أن يعطوك طريقةً للوصول إلى تلك الملفات، وإن لم يعطوك ذلك فأنصحك بالبحث عن مزود استضافة آخر! أين يجب تطوير الموقع الجديد؟ إذا كنتَ مطوِّر ووردبريس خبير، فلا أظن أنَّه توجد مشكلة في نقل مواقع ووردبريس، إذ أنَّ من المرجح أنَّك ستطوِّر الموقع على حاسوبك المحلي أو على خادوم تخزين صغير خاص بك، ثم ستنقل الموقع النهائي إلى الخادوم الإنتاجي. أما لو كنتَ جديدًا على ووردبريس، فقد تَحسَبُ أنَّ المهمة شاقة، فكيف ستُثبِّت ووردبريس محليًا؟ ناهيك عن الرعب الناجم عن التفكير بطريقة نقل موقعك إلى الخادوم الإنتاجي بعد انتهاء العمل عليه! لا حاجة لكي تقلق من الأمور السابقة، إذ يمكنك تثبيت ووردبريس على خادومك الإنتاجي وتعمل عليها دون معرفة الزوار؛ وفي نفس الوقت يكون موقعك الثابت القديم في مكانه. وبعد أن ينتهي ضبط ونقل المحتوى إلى الموقع الجديد، فليس عليك نقل الملفات أو تصدير قاعدة البيانات، وإنما كل ما عليك فعله هو نسخ وتعديل ملف أو ملفين، وتغيير بعض إعدادات الضبط. يبدو أنَّ ذلك سهلٌ جدًا ويمكنك –حتى لو كنتَ مبتدئًا باستعمال ووردبريس– أن تفعله. رائع! الخطوة الأولى: تثبيت ووردبريس في مجلدٍ فرعي أوّل خطوة هي تثبيت ووردبريس، لكن بدلًا من تثبيتها في المجلد الجذر لموقعك، فعليك تثبيتها في مجلدٍ فرعي، فلو كان موقعك موجودًا على الرابط http://mysite.com فعليك تثبيتها في مجلد باسم http://mysite.com/wordpress. لكن دعني أحذرك بادئ الأمر أنني لا أثبت ووردبريس في مجلدٍ باسم wordpress، إذ عليك إعطاء المجلد اسمًا أكثر غموضًا وأصعب تخمينًا، لكي لا يعثر الزوار (أو المخترقون) عليها صدفةً. باختصار: يمكنك أن تسمِّ المجلد بأيِّ اسمٍ تشاء لطالما كان يختلف عن أسماء الملفات أو المجلدات الموجودة في الموقع القديم، وإلا فقد يصدف وأن يزور أحدهم تلك الصفحة في موقعك القديم مما يأخذهم إلى الموقع الجديد. لذا إذا كنتَ تريد تثبيت ووردبريس، فثبِّتها في مجلدٍ جديد. وإذا كنتَ تستعمل سكربتًا توفره شركة الاستضافة (مثل Softaculous أو Fantastico) فيمكنك تحديد ذلك أثناء التثبيت. أو إذا كنتَ تثبت ووردبريس يدويًا، فعليك وضع الملفات في مجلدٍ فرعي. ملاحظة: إذا كان موقعك الجديد سيُشِّكل شبكةً متعددة المواقع، فما يزال بإمكانك فعل ما سبق ذكره، لكنك لن تستطيع استخدام النطاقات الفرعية (subdomains) كبنيةٍ لشبكتك، فلن تستطيع استخدام سوى المجلدات الفرعية (subdirectories) فقط. الخطوة الثانية: إخفاء الموقع من المؤكد أنَّك لن ترغب أن يعثر الناس على موقعك الجديد أثناء إنشائه في محركات البحث، لذا ستحتاج إلى إخفائه. تُسهِّل ووردبريس من ذلك كثيرًا، فكل ما عليك فعله هو الذهاب إلى صفحة «Settings > Reading» (الإعدادات > قراءة) في لوحة التحكم التابعة للموقع الجديد، وستجد مربع اختيار لإخفاء موقعك عن محركات البحث: اختر حقل «Discourage search engines from indexing this site» (منع محركات البحث من أرشفة هذا الموقع) ثم اضغط على زر «Save Changes» (حفظ التغييرات)، وهذا سيُنشِئ ملف robots.txt في موقعك الذي سيطلب من محركات البحث ألّا تُفهِرس الموقع الجديد. لا تقلق بخصوص هذا الملف، ستعتني به ووردبريس وتتكفل بأمره. الخطوة الثالثة: إعداد موقعك الجديد هذا هو الجزء الذي يأتي فيه المرح! أمضِ بعض الوقت في موقعك الجديد، واختر قالبًا، وثبِّت الإضافات، وأنشِئ بعض المحتوى، وارفع الصور والوسائط الأخرى… الخطوة الرابعة: تحضّر لنقل الموقع الجديد ليصبح موقعك الرئيسي عندما يصبح موقعك الجديد جاهزًا، فحان الوقت لتحضير نقله ليصبح موقعك الرئيسي. ستحتاج إلى نسخ بضعة ملفات وتعديل أحدها، ثم تغيير بعض الإعدادات. لنشرح طريقة فعل ذلك خطوةً بخطوة. نسخ موقعك القديم احتياطيًا ثم حذفه خذ أولًا نسخةً احتياطيةً من ملفات موقعك القديم بنسخها إلى جهازك المحلي، وذلك عبر عميل FTP أو عبر خاصية إدارة الملفات File Manager في لوحة cPanel. بعد تحميل كامل ملفات موقعك القديم، فاحذفها. نسخ وتعديل ملفين باستخدام FTP أو File Manager، انسخ (ولا تنقل) ملفين من المجلد الفرعي الذي ثبتت فيه ووردبريس إلى المجلد الجذر (أي مجلد public_html): index.php ‎htaccess إذا لم تكن قادرًا على رؤية ملف ‎.htaccess فعليك تفعيل إظهار الملفات المخفية في cPanel أو في عميل FTP. يمكنك فعل ذلك في cPanel عبر العودة إلى الصفحة الرئيسية والضغط على File Manager ثم تفعيل الخيار «Show Hidden Files». أما عملاء FTP فكلٌ له طريقته لإظهار الملفات المخفية، لذا انظر في توثيق البرنامج. افتح الآن الملف index.php الموجود في المجلد الجذر لموقعك (والذي أُنشِئ عندما نسخته من المجلد الفرعي) وابحث عن هذا السطر: require('./wp-blog-header.php') وعدِّله ليصبح كما يلي: require('./subdirectory/wp-blog-header.php') ضع اسم المجلد الفرعي الذي ثبتت فيه ووردبريس بدلًا من subdirectory في الشيفرة السابقة. فلو ثبتتُ ووردبريس في http://rachelmccollin.co.uk/new-site/‎ فستبدو الشيفرة كما يلي: require('./new-site/wp-blog-header.php') احفظ الملف الآن واخرج منه. الخطوة الخامسة: تعديل ضبط الموقع اذهب إلى صفحة «Settings > General» (الإعدادات > عام) في لوحة التحكم، وأضبط عنوان موقعك الجديد، واسمح لمحركات البحث بفهرسة موقعك. تعديل رابط URL للموقع اذهب إلى صفحة «Settings > General» (الإعدادات > عام) في لوحة التحكم، وسترى حقلين يمكنك إدخال رابط URL فيهما، واحدٌ لرابط ووردبريس، والآخر لرابط الموقع. يُشير رابط ووردبريس إلى مكان تثبيت ووردبريس، بينما يجب أن يُشير رابط الموقع إلى الرابط الذي سيعثر الناس على موقعك فيه. هذا هو الضبط الخاص بي قبل التعديل: عدِّل حقل «Site Address» (عنوان الموقع) واحذف المسار الذي يُشير إلى المجلد الفرعي، فبدلًا من أن يكون http://rachelmccollin.co.uk/new-site يجب أن يصبح http://rachelmccollin.co.uk. احفظ تعديلاتك بالضغط على زر «Save Changes» (حفظ التغييرات). السماح لمحركات البحث بالوصول إلى موقعك هل تذكر عندما كنتَ تضبط موقعك الجديد أنَّك أخفيته عن محركات البحث؟ يجب ألّا تغفل عن التراجع عن هذه الخطوة. اذهب إلى صفحة «Settings > Reading» (الإعدادات > قراءة) وألغِ تفعيل الخيار «Discourage search engines from indexing this site box» (منع محركات البحث من أرشفة هذا الموقع) ثم اضغط على زر «Save Changes» (حفظ التغييرات). يجب أن تكون محركات البحث قادرةً على فهرسة موقعك الآن. ملاحظة: لتسريع هذه العملية، فأنصحك بتسجيل موقعك في أدوات «Google webmaster tools» وتخبر Google عن موقعك. وسترى أيضًا تواتر فهرسة Google لموقعك ويمكنك أن تختار إخفاء أجزاءٍ منه. مشاكل؟ أحيانًا بعد فعلك لمثل هذه العمليات فقد تحاول زيارة الموقع ولا تجده يعمل، فقد تتم إعادة توجيهك عدِّة مرات، أو ربما تحصل على رسالة خطأ 404. إذا حدث ذلك، فاحذف الملفات المؤقتة في متصفحك؛ فلربما كان يتذكر متصفحك موقعك القديم ويُظهِر رسالة الخطأ. بعدا أن تفعل ذلك فيجب أن يعمل الموقع عملًا سليمًا. ويمكنك أيضًا أن تجرِّب متصفحًا أو جهازًا آخر. الخلاصة ربما افترضتَ أنَّ عليك حذف موقعك الثابت في أثناء إنشائك وإعدادك لموقع ووربريس، لكن ذلك ليس صحيحًا؛ ولو اتبعتَ ما ذكرناه في هذا الدرس فستتمكن من الإبقاء على موقعك القديم أثناء تطويرك للجديد في الخفاء، والذي لن يظهر للزوار أو لمحركات البحث إلا بعد أن يجهز تمامًا. ترجمة -وبتصرّف- للمقال How to Keep Your Static Site Running While Building a New WordPress Site لصاحبته Rachel McCollin.
  4. سنستعرض في هذا الدرس أمثلةً عن مختلف حالات استعمال flexbox في CSS3، سنجعل الشرح مختصرًا قدر الإمكان وستُوضَّح الفكرة عبر قراءة الشيفرة؛ لذا تمعّن فيها كثيرًا، وانظر إلى ناتجها (لا تغفل أهميّة تجربتها عندك). لمزيدٍ من المعلومات حول flexbox، فانظر درس «تعرف على CSS Flexbox وأساسيات استعماله لهيكلة صفحات الويب». إنشاء حاوية flex أول خطوة لإنشاء تخطيط صفحة يعتمد على flexbox هو إنشاء حاوية flex، وذلك بضبط الخاصية display إلى flex؛ يجدر بالذكر أنَّك ما زلت تحتاج إلى استخدام السابقة ‎-webkit في متصفح Safari: .flexcontainer { display: -webkit-flex; display: flex; } ترتيب عناصر flex في صف عناصر flex هي العناصر الأبناء لحاوية flex، والتي يمكن وضعها على المحور الرئيسي والمحور العمودي عليه. افتراضيًا، المحور الرئيسي هو المحور الأفقي، لذا ستوضع العناصر في صف (row)، يمكنك قلب المحور الرئيسي بضبط الخاصية flex-direction إلى column، حيث أنها مضبوطةٌ افتراضيًا إلى row: /* ضبط الحاوية */ .flexcontainer { display: -webkit-flex; display: flex; -webkit-flex-direction: row; flex-direction: row; } ترتيب عناصر flex على شكل عمود يمكننا ترتيب عناصر flex على شكل عمود بضبط الخاصية flex-dirextion إلى column. .flexcontainer { display: -webkit-flex; display: flex; -webkit-flex-direction: column; flex-direction: column; } تحريك عناصر flex إلى الأعلى كيفية نقل لعناصر flex إلى الأعلى (top) يعتمد على اتجاه المحور الرئيسي. فإن كان رأسيًا (vertical) فيمكنك ضبط justify-content وإذا كان أفقيًا فاضبط align-items. .flexcontainer { -webkit-flex-direction: column; flex-direction: column; -webkit-justify-content: flex-start; justify-content: flex-start; } أما الشيفرة الآتية: .flexcontainer { display: -webkit-flex; display: flex; -webkit-flex-direction: row; flex-direction: row; -webkit-align-items: flex-start; align-items: flex-start; } فتُنتِج: تحريك عناصر flex إلى بداية الحاوية تحريك العناصر إلى يمين أو يسار الحاوية يعتمد على أمرين، أولهما هو المحور الرئيسي، فلو كانت الخاصية flex-direction مضبوطةً إلى row فاستعمل حينها الخاصية justify-content، وإن كانت مضبوطةٌ إلى column فاستعمل align-items؛ وثانيهما هو اتجاه الصفحة، فلو استعملتَ القيمة flex-start (كما في أمثلتنا) وكان اتجاه صفحتك من اليسار إلى اليمين فستتحرك العناصر إلى اليسار، أما لو كان اتجاه صفحتك من اليمين إلى اليسار فستتحرك العناصر إلى اليمين. .flexcontainer { display: -webkit-flex; display: flex; -webkit-flex-direction: row; flex-direction: row; -webkit-justify-content: flex-start; justify-content: flex-start; } أما الشيفرة: .flexcontainer { display: -webkit-flex; display: flex; -webkit-flex-direction: column; flex-direction: column; -webkit-align-items: flex-start; align-items: flex-start; } فتنتج: تحريك عناصر flex إلى نهاية الحاوية وكما ذكرنا في القسم السابق، ستتحرك العناصر إلى اليمين عند استخدام القيمة flex-end في حال كانت الصفحة من اليسار إلى اليمين، وستتحرك إلى اليسار عندما تكون الصفحة من اليمين إلى اليسار. .flexcontainer { display: -webkit-flex; display: flex; -webkit-flex-direction: row; flex-direction: row; -webkit-justify-content: flex-end; justify-content: flex-end; } أما الشيفرة: .flexcontainer { display: -webkit-flex; display: flex; -webkit-flex-direction: column; flex-direction: column; -webkit-align-items: flex-end; align-items: flex-end; } فتنتج: توسيط العناصر التوسيط الأفقي والرأسي للعناصر الموجودة في حاوية flex هو أمرٌ هيّن، فكل ما علينا فعله هو ضبط القيمة center إلى الخاصية justify-content و align-items. لا اختلاف فيما لو كانت الخاصية flex-direction مضبوطةً إلى row أو column. .flexcontainer { display: -webkit-flex; display: flex; -webkit-flex-direction: row /* works with row or column */ flex-direction: row; -webkit-align-items: center; align-items: center; -webkit-justify-content: center; justify-content: center; } والعناصر المرتبة عموديًا: تكبير عنصر flex عددًا معيّنًا من المرات يمكنك أن تُحدِّد كيف يكبر أو يصغر حجم عنصر flex نسبةً إلى عناصر flex الأخرى الموجودة في الحاوية. يمكنك فعل ذلك بضبط الخاصية flex لكل عنصر تريد تكبيره أو تصغيره: .bigitem { /* This will be twice as big as the small item. */ -webkit-flex: 2 0 0; flex: 2 0 0; } .smallitem { -webkit-flex: 1 0 0; flex: 1 0 0; } التفاف عناصر flex لتصبح عدِّة أسطر دعم المتصفحات لالتفاف (wrapping) عناصر flex ما يزال محدودًا إلى متصفحات webkit و IE11، فللأسف لا يدعم firefox هذه الميزة بعد. /* On the flex container */ .flexcontainer { display: -webkit-flex; display: flex; -webkit-align-items: center; align-items: center; -webkit-justify-content: center; justify-content: center; /* You can set flex-wrap and flex-direction individually */ -webkit-flex-direction: row; flex-direction: row; -webkit-flex-wrap: wrap; flex-wrap: wrap; /* Or do it all in one line with flex flow */ -webkit-flex-flow: row wrap; flex-flow: row wrap; /* tweak the where items line up on the row */ /* valid values are: flex-start, flex-end, space-between, space-around, stretch */ -webkit-align-content: flex-end; align-content: flex-end; } التفاف عناصر flex لتُشكِّل عدة أعمدة لاحظ إسناد القيمة column wrap إلى الخاصية flex-flow في الشيفرة الآتية: .flexcontainer { display: -webkit-flex; display: flex; -webkit-align-items: center; align-items: center; -webkit-justify-content: center; justify-content: center; -webkit-flex-flow: column wrap; flex-flow: column wrap; -webkit-align-content: stretch; align-content: stretch; } إزالة المسافة بين عناصر flex التي تشكل أسطرًا وأعمدةً تسمَح لك الخاصية align-content بضبط قيم للتحكم بكيفية توزيع المسافة بين الأسطر أو الأعمدة. الخيارات المتاحة لك هي flex-start و flex-end و space-between و space-around و stretch. فلإزالة المسافة بين الأعمدة الملتفة اضبط الخاصية align-content إلى center: .flexcontainer { display: -webkit-flex; display: flex; -webkit-align-items: center; align-items: center; -webkit-justify-content: center; justify-content: center; -webkit-flex-flow: column wrap; flex-flow: column wrap; -webkit-align-content: center; align-content: center; } تثبيت أحد العناصر إلى أحد أطراف حاوية flex يمكنك التحكم بقيمة align-items لمختلف العناصر الموجودة في حاوية flex عبر الخاصية align-self. يمكنك أيضًا ضبط هوامش (margins) بقيم معيّنة لتحريك العناصر إلى الأعلى أو الأسفل أو إلى اليمين أو اليسار؛ فلو كانت عندك حاوية تُرتَّب فيها العناصر على شكل عمود، وأردتَ نقل أحد العناصر إلى يسار الحاوية، فاضبط الخاصية margin-right إلى auto: /* On the flex item to pin */ .left { -webkit-align-self: flex-start; align-self: flex-start; } .right { margin-left: auto; } ترجمة -وبتصرّف- للمقال The Ultimate Flexbox Cheat Sheet لصاحبه Sean Fioritto
  5. هل وجدتَ موقعك الذي يعتمد على ووردبريس معطلًا وكل ما يمكنك قوله هو «لم أفعل شيئًا! صدقًا!» ومع ذلك سترى رسائل الخطأ التي تقول أنَّ ملفاتك ناقصة أو قاعدة بياناتك تالفة… لا تقلق في حال اختفت المنشورات والتصنيفات فجأةً، أو ظهرت رسائل خطأ مثل: “Warning: require_once(path/to/file.php) [function.require-once]: failed to open stream: No such file or directory in…” أو “Cannot establish database connection.” فسأشرح كيف تحدث مختلف أنواع رسائل الخطأ السابقة، وكيفية حل تلك المشاكل؛ وسأبيّن لك كيف أنَّه من غير المرجّح أن تكون أنت سبب المشكلة. حسنًا، ما هي الفكرة الرئيسية هنا؟ يصعب عادةً معرفة المُسبِّب الرئيسي لأيٍّ من الأخطاء السابقة لوجود عدد كبير (نسبيًا) من المسببات. هذه هي الأسباب الرئيسية التي تؤدي إلى تلف في قاعدة البيانات، أو فقدانها: لم توضَع معلومات الدخول بشكلٍ صحيحٍ في ملف wp-confing.php. حدثت مشكلة في نظام التشغيل. تعطلت إحدى القطع العتادية في الخادوم. حدثت علّة في خادوم MySQL أو مفسر PHP أو في القوالب والإضافات التي تستعملها. استهلاكك للموارد أصبح كبيرًا مما أدى إلى إيقاف محاولة الوصول إلى قاعدة البيانات (مثل PHP memory limit). ربما تعرضتَ إلى هجومٍ إلكتروني، وتم اختراق موقعك. هنالك قائمةٌ أطول للأسباب المحتملة لفقدان ملفاتك: أذونات الملفات غير مضبوطة ضبطًا صحيحًا. الجدار الناري (أو برمجيات الحماية الأخرى) لخادومك يمنع الوصول إلى بعض أجزاء موقعك. أدخلتَ رابط URL للموقع بشكلٍ خاطئ في صفحة «Settings > General» (الإعدادات > عام). الإضافات الخارجية حذفت أحد الملفات أو سببت مشكلةً في المسارات. ثبّتت شهادة SSL ونسيت استبدال روابط الصور. الروابط الدائمة غير مضبوطة ضبطًا صحيحًا. هنالك أخطاء في ملف ‎.htaccess. رُفعِتَ الصور في مجلدٍ خاطئ. تمت مقاطعة عملية رفع المحتوى. هنالك علّة في إضافة أو سكربت أو قالب تستعمله في موقعك. ربما حدثت مشكلة في الخادوم. ربما تعرضتَ إلى هجومٍ إلكتروني، وتم اختراق موقعك. بغض النظر عن المسبب الرئيسي، فهنالك بضع طرائق لإصلاح مشاكل الملفات وقاعدة البيانات. النسخ الاحتياطي والاستعادة منه أسهل طريقة لحل مشكلة تتعلق بقواعد البيانات أو فقدان الملفات هي استعادة نسخة احتياطية من موقعك؛ أما لو لم يكن لديك نسخةٌ احتياطيةٌ لتستعيدها، فهنالك حلولٌ أخرى يمكنك تجربتها. لكن قبل أن تجرِّب الحلول الأخرى، فأنصحك بأخذ نسخة احتياطية لكامل موقعك، حتى لو لم يكن يعمل، لأنَّه قد تسوء بعض الأمور ومن الأفضل أن تكون لديك نسخةٌ أساسيةٌ (حتى لو كانت معطلةً) عوضًا أن تكون خالي الوفاض. ألق نظرة على هذا المقال للمزيد من التفاصيل عن النسخ الاحتياطي: الحماية وإضافات النسخ الاحتياطي في ووردبريس وبعد أن تأخذ نسخةً احتياطيةً من موقعك لتستعملها في حال وقوع كارثة، فأنت جاهزٌ لتبدأ بإصلاح الموقع. إصلاح مشكلة في قاعدة البيانات هنالك عدِّة طرائق يمكنك فيها إصلاح قاعدة البيانات. يمكنك أن تجرب الطريقة الآتية التي تحل أغلبية المشاكل التي تواجه قواعد البيانات. يمكنك إضافة السطر الآتي إلى ملف wp-config.php والموجود في المجلد الجذر لموقعك: define( 'WP_ALLOW_REPAIR', true ); يمكنك إضافة السطر السابق في أي مكان في الملف لكن لا تضفه داخل دالة (على سبيل المثال). أنصحك بوضعه قبل السطر الآتي مباشرةً: /* That's all, stop editing! Happy blogging. */ لتفاصيلٍ إضافيةٍ حول طريقة تعديل ملف wp-config.php فراجع درس «كيف تستخدم FTP لنقل ملفات مدونة ووردبريس الخاص بك؟». بعد أن أضفتَ السطر السابق، فيمكنك زيادة صفحة إصلاح قاعدة البيانات من الرابط الآتي: http://your-site.com/wp-admin/maint/repair.php أو عليك زيارة https://your-site.com/wp-admin/maint/repair.php إذا كانت عندك شهادة SSL. لا تنسَ وضع اسم نطاق موقعك بدلًا من your-site.com. ابقِ في ذهنك أنَّه ليس من الضروري أن تسجِّل دخولك لرؤية هذه الصفحة، لذا بعد انتهاءك من إصلاح قاعدة البيانات، فاحرص على حذف السطر السابق، وإلا فسيستطيع أيُّ شخصٍ الوصولَ إلى تلك الصفحة. عندما تزور الصفحة السابقة، تستطيع أن تضغط على زر «Repair Database» لإصلاح قاعدة البيانات، أو يمكنك إصلاح قاعدة البيانات وتحسينها عبر الضغط على زر «Repair and Optimize Database». بعد إنهاء العملية السابقة، يجب أن يكون موقعك قد أُصلِحَ وأصبحَ جاهزًا للاستعمال. لكن إذا لم تفلح محاولتنا السابقة، فيمكننا إصلاح قاعدة البيانات عبر phpMyAdmin. بعد تسجيل الدخول إلى phpMyAdmin، فاضغط على اسم قاعدة البيانات الموجود في القائمة على يسار الشاشة، ثم بعد ظهور جداول قاعدة البيانات في الصفحة، فمرِّر إلى الأسفل ثم اضغط على مربع الاختيار المُعَنوَن Check All، ثم اختر «Repair table» من القائمة المنسدلة الموجودة بجوار مربع الاختيار. سيتم إصلاح جداول قاعدة البيانات تلقائيًا ويجب أن يعمل موقعك مجددًا. إصلاح الملفات التالفة أو المفقودة لكن ماذا لو كانت ملفاتك هي الناقصة أو التالفة؟ يمكنك إصلاحها أيضًا. إذا ثبّتتَ شهادة SSL في الآونة الأخيرة في موقعك، فقد يبدو أنَّ الصور أمست مفقودةً، ولحلّ هذه المشكلة عليك استبدال روابط الصور لكي تُضمِّن فيها السابقة https. لتفاصيل عن كيفية فعل ذلك، فراجع الدرس «استبدال روابط الصور على مواقع ووردبريس بعد تثبيت شهادة SSL». أما للغالبية العظمى من المشاكل الأخرى، فوضع نسخة جديدة من الملف المعطوب يجب أن يحل المشكلة. يمكنك استخدام FTP للوصول إلى موقعك واستبدال الملفات المعطوبة ووضع أخرى جديدة بدلًا منها، التي يمكنك أن تعثر عليها بتنزيل نسخة من برمجية ووردبريس من موقع WordPress.org. بعد فك ضغط ملف ZIP، يمكنك أن تنقل الملفات الجديدة إلى خادومك، لكن احرص على ترك المجلد ‎/wp-content/‎ دون تعديل، أو قد تعرِّض نفسك لخسارة جميع ملفات الوسائط والصور التي رفعتها إلى موقعك. يمكنك تطبيق ما سبق على الإضافات والقوالب، لكن الفرق الوحيد هو أنَّ عليك تنزيل نسخة حديثة من الإضافات أو القوالب بدلًا من ووردبريس نفسها. إذا فشل كل ما سبق… إذا جرّبتَ كل ما سبق، لكن لم يحالفك الحظ في تشغيل موقعك، فيمكنك أن تجرّب تفعيل نمط التنقيح في ووردبريس، لتفاصيل عن كيفية فعل ذلك، فارجع إلى درس «Debugging WordPress: How to Use WP_DEBUG». ربما تنظر أيضًا في سجل الأخطاء فلربما استطعت التعرف على المشكلة من هناك، ومن ثم ترسلها إلى شركة الاستضافة لتحليل الخطأ. ابحث عن ملفٍ باسم debug.log داخل مجلد ‎/wp-content/‎. إذا لم يحّل أيٌّ مما سبق مشكلتك، فحان الوقت لكي تراسل شركة الاستضافة، فقد تكون المشكلة من ضبط الخادوم، أو ربما أصابته مشكلة. والحل الوحيد لهذه المشكلة هو التواصل مع شركة الاستضافة وإعلامها بالأمر. حسنًا، هنالك حلٌّ أخير لا أحبذه: إذا لم تكن لديك نسخةٌ احتياطيةٌ وليست لديك مشكلة مع فقدان بيانات موقعك بالكامل، فيمكنك حذف الموقع وإعادة تثبيت ووردبريس. الخلاصة إذا طبّقتَ ما ذكرناه في هذا الدرس وحالفك الحظ فيجب أن يعمل موقعك عملًا سليمًا دون أن ترى أيّة رسائل خطأ تُشير إلى ملف ناقص أو قاعدة بيانات معطوبة. ولتجنب الصداع الناجم عن محاولة إصلاح الموقع (إذا صَدَفَ وتعطّل) فأنصحك بأخذ نسخ احتياطية دوريًّا. ترجمة -وبتصرّف- للمقال Repairing Corrupted, Broken or Missing Files and Databases in WordPress لصاحبته Jenni McKinnon.
  6. تمهيد عندما تُنشِئ حاوية Docker فسيُسنَد إليها مُعرِّف عالمي فريد (UUID) وهو ضروريٌ لتفادي التضاربات في الأسماء ولتسهيل أتمتة إنشاء الحاويات دون تدخل البشر. وهو مفيدٌ لكي يتعرف الحاسوب المضيف (والشبكة) على المضيف. لكن يصعب التفريق بين الحاويات بالنسبة للبشر، سواءً كنّا نقصد الاسم الطويل (64 محرف) أو المُعرِّف القصير (12 محرف) الذي يبدو كالآتي 285c9f0f9d3d. توفِّر Docker أسماءً مولّدةً تلقائيًا للحاويات تأتي من جمع كلمتين عشوائيتين بشرطة سفلية، مثلًا: evil_ptolemy، مما يُسهِّل التفريق بين الحاويات، لكن الأسماء العشوائية لا تعطينا أيّة فكرة عن وظيفة الحاوية مَثَلُهَا كمثل مُعرِّف UUID. سأخبرك بثلاث نصائح التي ستُسهِّل من التعرف على الحاويات. أولًا: سمِّ الحاوية عندما تُنشِئها وذلك بإضافة الخيار ‎--name=meaningful_name إلى الأمر docker run، وبهذا سيصبح اسم الحاوية ذا معنى عند استخدام الجلسات التفاعلية وفي ناتج بعض الأوامر مثل docker ps. لكن هنالك بعض المحدوديات، لأن أسماء الحاويات يجب أن تكون فريدةً، لهذا لا يمكن استخدام نفس الاسم لأكثر من حاوية. ضع ما يلي في سطر الأوامر أو في ملف Docker: docker run –name=meaningful_name على سبيل المثال، إذا أردنا إنشاء حاوية مبنيةً على صورة nginx فسنُنفِّذ الأمر: docker run --name nginx -d nginx وسيظهر الاسم في قائمة الحاويات التي تعمل حاليًا: docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 08f333ef7216 nginx "nginx -g 'daemon off" 15 seconds ago Up 14 seconds 80/tcp, 443/tcp nginx صحيحٌ أنَّ الاسم سيظهر في ناتج الأمر docker ps ويمكن استخدامه لإدارة الحاوية، لكنه لن يظهر في مِحَث (prompt) سطر الأوامر إذا فتحتَه، أو في السجلات. ولفعل ذلك عليك أن تُسنِد «اسم مضيف» (hostname) إلى الحاوية. ثانيًا: إسناد اسم مضيف إلى الحاوية القيمة المُمرَّرة إلى الخيار ‎--hostname ستُضبِط اسم المضيف داخل الحاوية في الملفين ‎/etc/hostname و ‎/etc/hosts. وبناءً على ذلك، سيظهر اسم المضيف في مِحَث سطر الأوامر، وسيلعب اسم المضيف دورًا في ضبط خدمة DNS للحاوية وقد يكون مفيدًا عند تعلم التعامل مع أكثر من حاوية. لكنه ليس ضروريًا للوصول إلى الحاوية من خارجها، إلا أنَّه سيظهر في ملفات السجل التابعة للحاوية، وعندما تُكتَب تلك الملفات إلى جهاز تخزين مستقل عن الحاسوب المضيف، فسيسهل التعرف على الحاوية بناءً على اسم المضيف الخاص بها. ضع ما يلي في سطر الأوامر أو في ملف Docker: docker run –hostname=value أو docker run -h value وصحيحٌ أنَّ ‎--name و ‎--hostname مفيدان للتعرف على الحاويات، لكن الأمر ليس متعلقًا بإطلاق اسم على الحاوية فحسب، وإنما ستستفيد منه (في السجلات مثلًا) إذا ضبطتَ الحاوية لكي تُحذَف تلقائيًا بعد انتهاءك منها. ثالثًا: حذف الحاويات تلقائيًا بعد انتهاء تنفيذها عندما تجرّب مع حاويات Docker، فقد تستفيد في بعض الأحيان من الحفاظ على الحاوية بعد انتهاء تنفيذها. يمكنك حفظ البيانات مثل ملفات السجلات وتتفحص آخر حالة للحاوية. لكن في بعض الأحيان لا ترغب في بقاء الحاوية بعد انتهاء التجربة معها، وفي هذه الحالة يمكنك استخدام الخيار ‎--rm لحذفها عند انتهاء تنفيذها. وهذا سيُسهِّل من «تنظيف» بيئة التجارب. لكن انتبه! إذا كنتَ تستخدم حجوم Docker ‏(Docker volumes) فالخيار ‎--rm سيؤدي إلى حذف أيّة حجوم مرتبطة بالحاوية. ضع ما يلي في سطر الأوامر أو في ملف Docker: docker run –rm ستستفيد من هذا الخيار كثيرًا عند إنشائك لصورة Docker وتحتاج إلى الوصول إلى حاوية للتجربة عليها، لكنك لا تريد أن تملأ كامل قرصك بحاويات لن تستعملها مرةً أخرى. الخلاصة هنالك ثلاثة خيارات للأمر docker run: الخيار ‎--name و ‎--hostname و ‎--rm التي تُسهِّل من تعاملك مع حاويات Docker عندما تحاول تعلم التعامل معها. يمكنك العثور على مزيدٍ من المعلومات حول الأمر docker run في درس «التعامل مع حاويات Docker». ترجمة -وبتصرّف- للمقال Naming Docker Containers: 3 Tips for Beginners لصاحبته Melissa Anderson
  7. قبل البدء بتوضيح كيفية استخدام Codeception مع شيفرات PHP، فيجب أن نغطي الأساسيات عبر شرحنا لماذا نحتاج إلى اختبار تطبيقاتنا. ربما يمكننا إكمال المشروع الذي نعمل عليه دون تضييع الوقت بكتابة الاختبارات، أليس ذلك؟ بلى، إذ لن تحتاج إلى كتابة اختبارات لكل شيء (كإنشاء صفحة رئيسية لموقعك). لن تحتاج غالبًا إلى كتابة اختبارات عندما يحتوي مشروعك على صفحات ثابتة مرتبطة بصفحة توجيه وحيدة. لكنك ستحتاج إلى إجراء اختبارات عندما: يستخدم فريقك أسلوب «التطوير الموجّه بالاختبارات» (TDD) أو «التطوير الموجّه بالسلوك» (BDD). تُجرى عدِّة عمليات إيداع في مستودع Git التابع لمشروعك يوميًا. أنت مطوِّرٌ محترف، وتعمل على مشروع جاد. يمكنك أن تعذر نفسك بقولك أنَّ هنالك فريق كامل مخصص للاختبارات، وهم مجموعةٌ من الأشخاص الذين يجرون اختبارات ويكتبونها عند الحاجة. لكن لك أن تتخيل كم سيستغرق حلّ العلّة من الوقت بعد أن تضيف وظائف جديدة إلى مشروعك. ما المشاكل التي تحلها الاختبارات؟ لنتعرّف أولًا على المشاكل التي يمكن حلها عبر الاختبارات. لن تتمكن من التخلص من جميع الأخطاء الظاهرة عند الاختبار، لكنك ستستطيع توصيف السلوك المتوقع في حالات الاختبار (test cases). انتبه إلى أنَّه قد توجد أخطاء ضمن حالات الاختبار التي تكتبها. لكن كن على يقين أنَّ شيفرتك ستتغير فيما بعد (إما بتصحيح العلل، أو إضافة ميزات جديدة، حتى تصبح شيفرتك خاليةً من الأخطاء الموصوفة في حالة الاختبار). يمكن -إضافةً إلى ما سبق- استعمال حالات الاختبار المكتوبة بشكلٍ جيد في التوثيق لأنك سترى من خلالها ما هو السلوك المتوقع للبرنامج في حالات معيّنة. خلاصة القول أنَّ كتابة الاختبارات هي استثمارٌ مهمٌ تستفيد منه في المستقبل. إذًا، ما هي الاختبارات التي يمكننا استعمالها في مشروعنا؟ اختبارات الوحدات (unit tests): هي اختباراتٌ منخفضة المستوى (low-level) التي تتحقق من سلامة أجزاء صغيرة من شيفرتك، والتي تكون عادةً الدوال الموجودة في الأصناف (classes) الموجودة في مشروعك والمعزولة عن بعضها. اختبارات التكامل (integration tests): تتحقق اختبارات التكامل من سلامة أداء جزءٍ من برنامجك والذي قد يحتوي على عدِّة أصناف أو دوال، إلا أنها تؤدي جميعًا غرضًا وحيدًا ألا وهو توفير ميزة معيّنة. يجب أن تتحقق هذه الاختبارات من كيفية تفاعل الأصناف مع بعضها. الاختبارات الوظيفية (function tests): اختبار استجابة التطبيق لمختلف الطلبيات، كالتغيرات الحاصلة في قاعدة البيانات ...إلخ. اختبارات القبول (acceptance tests): الغرض من هذه الاختبارات في أغلبية الحالات هو معرفة إن كان التطبيق يلبّي جميع متطلبات العميل. للتوضيح، لنحاول شرح هذه العملية بتمثيلها على شيءٍ ملموس مثل المباني. تتألف بعض أنواع المباني من لبنات صغيرة تُشكِّل جدرانًا، ويجب أن تتحقق اشتراطاتٌ معيّنةٌ فيها، إذ يجب أن تستطيع أن تقاوم الحِمل المُطبَّق عليها، ويجب أن تكون لها أبعاد وحجم معيّن وهلم جرًا… تلك الأمور تسمى «اختبارات الوحدات» (unit tests). أما الفكرة من اختبارات الاندماج هي معرفة متانة وقوة الارتباط بين اللّبنات، وكيف تُشكِّل مع بعضها عنصرًا من عناصر المبنى (مثل الجدران). أما الاختبارات الوظيفية فيمكن ربطها بالاختبارات التي تُجرى على جدارٍ من جدران المبنى، مثل اختبار جودة العزل الحراري، ومعرفة إن كان بالإمكان مرور أشعة الشمس من النوافذ الموجودة في هذا الجدار. وأخيرًا، تتضمن اختبارات القبول إجراء فحص لكامل المبنى: كفتح الباب، والدخول إلى الغرفة، وإغلاق الباب، وتشغيل الضوء، والصعود إلى الطابق الثاني والنظر إلى الحديقة عبر النافذة… تعرّف على Codeception التقسيم السابق مشروطٌ بعدِّة عوامل، ويصعب مقاومة النزعة إلى خلط عدِّة أنواع من الاختبارات. يستعمل الكثير من المبرمجين اختبارات الوحدات ويدّعون أنها كافية؛ وكنتُ منهم، إلا أنني وجدتُ أنَّ استخدام عدِّة أنظمة لمختلف أنواع الاختبارات هو أمرٌ مرهقٌ ويستهلك وقتًا طويلًا. لذا قررتُ منذ فترة أن أعثر على شيءٍ أكثر فائدةً من مكتبة PHPUnit؛ أردتُ أن يتطور استخدامي لاختبارات الشيفرات، إلا أنني لم أرغب بقراءة وتعلم الكثير من صفحات التوثيق وأن أبحث عن الحلول الالتفافية للمشاكل التي تواجهني. ومن ثم اكتشفت وجود Codeception. كنتُ بادئ الأمر متشككًا ومرتابًا منه، وهذه طبيعتنا عندما نرى شيئًا جديدًا (الحق يقال: هذا المشروع موجودٌ منذ خمس سنوات، لذا لا يمكننا اعتباره «جديدًا»)، لكن بعد تجربته لعدة أيام، خَلِصتُ إلى أنَّ Codeception هو نظامٌ مفيدٌ جدًا. قد تتساءل كيف تستطيع تثبيت Codeception؟ الأمر أبسط مما تتخيل: $ composer require "codeception/codeception" $ php vendor/bin/codecept bootstrap بعد التثبيت ستجد مجلدًا جديدًا باسم test في مشروعك، وستجد داخله مجلداتٍ فرعيةً باسم acceptance و functional و unit، يبدو أننا نستطيع البدء بكتابة اختباراتنا، إذًا ما هي الخطوة القادمة؟ لنحاول الآن إضافة اختبار قبول لمثالٍ بسيط: $ php vendor/bin/codecept generate:cept acceptance HelloWorld يمكننا الآن فتح ملف اختبار القبول ذي المسار tests/acceptance/HelloWorldCept.php ونضع فيه المحتوى الآتي: <?php $I = new AcceptanceTester($scenario); $I->wantTo('perform actions and see result'); المتغير الافتراضي المسمى ‎$I ليس مجرد حرف، وإنما هو ضمير متحدث يُشير إلى «الشخص» الذي يجري الاختبار: الذي يفتح صفحة تطبيقك أو أحد أصنافه ويفعل شيئًا ما ويريك النتيجة النهائية لأفعاله. إذ سترى ماذا فعل وما هو الخطأ الذي حدث. هذا هو سبب تسمية ذاك الكائن بالاسم ‎$I ولماذا تدعى الدوال الموجودة فيه wantTo()‎ و see()‎ و amOnPage()‎. لنفكر كشخصٍ يريد إجراء اختبارات للتحقق من عمل إحدى الصفحات. أوّل ما يخطر ببالنا هو فتح الصفحة والبحث عن عبارة معينة فيها، مما يعني أنَّ الصفحة متوافرة للزوار. يسهل كثيرًا فعل ذلك: <?php $I->amOnPage('/'); $I->see('Welcome'); ثم نستعمل الأمر الآتي لتشغيل اختبارات Codeception: $ php vendor/bin/codecept run سنلاحظ مباشرةً أنَّ شيئًا ما ليس صحيحًا، وسنرى من أوّل وهلة أنَّ رسالة الخطأ طويلة وغير واضحة، لكن عندما ننظر إليها عن قرب، فسنجد أنَّ معناها أصبح جليًا. لدينا اختبارٌ وحيدٌ وقد وَجَدَ خطأً: Acceptance Tests (1) Perform actions and see result (HelloWorldCept) Error ---------- 1) Failed to perform actions and see result in HelloWorldCept (tests/acceptance/HelloWorldCept .php) [GuzzleHttp\Exception\ConnectException] cURL error 6: Could not resolve host: localhost (see http://curl.haxx.se/libcurl/c/libcurl-errors.html) خلاصة الرسالة السابقة هي أنَّ الموقع في localhost غير متوافر. انظر إلى أوّل سطر من ملف الاختبار: 1. $I->amOnPage("/") لنفتح ملف tests/acceptance.suite.yml ونعدّل قيمة url: http://localhost/‎ إلى رابطٍ آخر تتوافر عليه الصفحة. يتوافر على جهازي المحلي خادومٌ يحتوي على الصفحة التي نريد اختبارها url: https://local.codeception-article.com/‎. شغِّل الاختبار مرةً أخرى، ويجب أن تظهر عندك النتيجة الآتية: Acceptance Tests (1) --------------------------------------------------------------------------------------- Perform actions and result (HelloWorldCept) Ok تهانينا! لقد نجح اختبارنا. تتوافر العديد من الدوال للاختبار بجانب الدالة amOnPage()‎، لكنني عرضتُها لسهولة استعمالها. يمكن تقسيم دوال الاختبار الموجودة في Codeception إلى المجموعات الآتية: دوال التفاعل مع الصفحة: fillField()‎ و selectOption()‎ و submitForm()‎ و click()‎. دوال التحقق من وجود أشياء معيّنة في الصفحة: see()‎ و dontSee()‎ و seeElement()‎ و seeInCurrentUrl()‎ و seeCheckboxIsChecked()‎ و seeInField()‎ و seeLink()‎. يمكنك إضافة لاحقة (suffix) إلى الدوال السابقة عندما تحتاج استعمالها دون أن توقف الاختبار إن لم يُعثَر على العنصر الذي تختبر وجوده. دوال التعامل مع الكعكات (cookies): setCookie()‎ و grabCookie()‎ و seeCookie()‎. التعليقات وشرح حالة الاختبار: amGoingTo()‎ و wantTo()‎ و expect()‎، استخدم هذه الدوال لكي تشرح ما الذي يفعله الاختبار، مما يذكرك بالهدف منه. فلو أردنا مثلًا أن نختبر صفحةً لاستعادة كلمة المرور عبر البريد الإلكتروني، فسنكتب ملفًا شبيهًا بالملف الآتي: <?php $I = new AcceptanceTester($scenario); $I->wantTo('Test forgotten password functionality'); $I->amOnPage('/forgotten') $I->see('Enter email'); $I->fillField('email', 'incorrect@email.com'); $I->click('Continue'); $I->expect('Reset password link not sent for incorrect email'); $I->see('Email is incorrect, try again'); $I->amGoingTo('Fill correct email and get link'); $I->see('Enter email'); $I->fillField('email', 'correct@email.com'); $I->click('Continue'); $I->expect('Reset password link sent for correct email'); $I->see('Please check your email for next instructions'); يبدو أنَّ الملف السابق يكفي لاختبار الصفحة، لكن ماذا لو كانت هنالك بعض الأقسام في الصفحة التي تُحمَّل عبر Ajax؟ كيف نستطيع أن نختبر تلك الصفحة؟ أجيبك أنَّ Codeception يستعمل مكتبة PhpBrowser المبنية على Symfony BrowserKit و Guzzle. أي لا تقلق، فالأمر سهلٌ وبسيط، ولن تحتاج إلا إلى استعمال curl لحل هذه المشكلة. يمكنك أيضًا استخدام Selenium واختبار الصفحات عبر المتصفحات الحقيقية. وصحيحٌ أنَّ ذلك أبطأ، إلا أنك ستتمكن من اختبار سكربتات JavaScript أيضًا. لكن أولًا عليك تثبيت إضافة Selenium، وتعديل ملف acceptance.suite.yml وإعادة بناء الصنف AcceptanceTester، ويمكنك بعد ذلك استخدام الدوال wait()‎ و waitForElement()‎؛ وستتمكن أيضًا من الحفاظ على وقتك ومواردك باستخدام الدالتين saveSessionSnapshot()‎ و loadSessionSnapshot()‎ اللتين تسمحان لك بحفظ حالة الجلسة الراهنة، وإجراء اختبارات جديدة على جلسات محفوظة مسبقًا، وقد تستفيد من هذا في بعض الحالات مثل اختبار عملية الاستيثاق من المستخدم. الخلاصة هي أننا نستطيع اختبار العديد من الوظائف بسهولة وسرعة وكفاءة. الاختبار الوظيفي لننتقل الآن إلى إجراء اختبارات وظيفية على تطبيقنا: $ php vendor/bin/codecept generate:cept functional HelloWorld ومحتوى الملف: <?php $I = new FunctionalTester($scenario); $I->amOnPage('/'); $I->see('Welcome'); انتظر قليلًا، ماذا يحدث! لا يوجد خطأ في الشيفرة السابقة، حيث تُكتَب الاختبارات الوظيفية بنفس طريقة كتابة اختبارات التكامل، الفرق بينهما هو أنَّ الاختبارات الوظيفية تتفاعل مباشرةً مع تطبيقك، مما يعني أنَّك لن تحتاج إلى خادوم ويب لتشغيل اختبار وظيفي، وستستطيع اختبار أجزاء مختلفة من تطبيقك. وصحيحٌ أنَّه لا يتوافر دعمٌ لجميع إطارات العمل، لكن قائمة الإطارات المدعومة كبيرة (Symfony، و Silex، و Phalcon، و Yii، و Zend Framework، و Lumen، و Laravel) ويجب أن تكون كافيةً لأغلبية الحالات ولمعظم المطورين. رجاءً راجع توثيق Codeception للحصول على قائمة بجميع الدوال المتوافرة وكيفية تفعيلها في ملف functional.suite.yml. قبل أن ننتقل إلى اختبارات الوحدات، فدعني أنحرف قليلًا عن الموضوع وأحدثك قليلًا عن شيءٍ ربما لاحظتَه عند إنشاء الاختبارات: $ php vendor/bin/codecept generate: cept acceptance HelloWorld لاحظ الكلمة cept، واعلم أنَّ ما سبق ليس الطريقة الوحيدة لإنشاء الاختبارات، فهنالك أيضًا «اختبارات cest»، والفرق بينهما هو أنَّك تستطيع هيكلة عدِّة حالات وسيناروهات في نفس الصنف: $ php vendor/bin/codecept generate:cest acceptance HelloWorld <?php class HelloWorldCest { public function _before(AcceptanceTester $I) { $I->amOnPage('/forgotten') } public function _after(AcceptanceTester $I) { } // tests public function testEmailField(AcceptanceTester $I) { $I->see('Enter email'); } public function testIncorrectEmail(AcceptanceTester $I) { $I->fillField('email', 'incorrect@email.com'); $I->click('Continue'); $I->see('Email is incorrect, try again'); } public function testCorrectEmail(AcceptanceTester $I) { $I->fillField('email', 'correct@email.com'); $I->click('Continue'); $I->see('Please check your email for next instructions'); } } ستُشغَّل الدالتان ‎_before()‎ و ‎_after()‎ قبل وبعد كل اختبار. وستُمرَّر نسخةٌ من الصنف AcceptanceTester إلى كل اختبار، لذا يمكنك استخدامه بنفس الآلية المتبعة في اختبارات cest. قد يكون هذا النوع من الاختبارات مفيدًا في بعض الأحيان، لذا من المفيد تعلمه ومعرفة وجوده. اختبارات الوحدات أخيرًا: اختبارات الوحدات. بُني Codeception على PHPUnit، أي أنَّك تستطيع استخدام الاختبارات المكتوبة لمكتبة PHPUnit، وذلك باستخدام الأمر الآتي: $ php vendor/bin/codecept generate:phpunit unit HelloWorld أو يمكنك ببساطة أن «ترث» (inherit) الاختبارات في ‎\PHPUnit_Framework_TestCase. لكن إن أردتَ شيئًا إضافيًا، فيمكنك تجربة اختبارات الوحدات التي يوفرها Codeception: $ php vendor/bin/codecept generate:test unit HelloWorld <?php class HelloWorldTest extends \Codeception\TestCase\Test { /** * @var \UnitTester */ protected $tester; protected function _before() { } protected function _after() { } // tests public function testUserSave() { $user = User::find(1); $user->setEmail('correct@email.com'); $user->save(); $user = User::find(1); $this->assertEquals('correct@email.com', $user->getEmail()); } } لا شيء يختلف عمّا عهدتَه من قبل، فالدالتان ‎_before()‎ و ‎_after()‎ هما بديلتا setUp()‎ و tearDown()‎، وستعملان قبل وبعد كل اختبار. الميزة الأساسية لهذا الاختبار هي القدرة على توسعة عملية الاختبار بتضمين وحدات (modules) التي يمكن تفعيلها في ملف unit.suite.yml: الوصول إلى memcache وقواعد البيانات لتتبع التغيرات (قواعد MySQL و SQLite و PostgreSQL و MongoDB مدعومة). اختبار تطبيقات REST/SOAP. Queues كل وحدة (module) لها ميزاتها، لذا من الأفضل أن تنظر إلى التوثيق وتجمع المعلومات الضرورية عنها قبل الانتقال إلى كتابة اختبارات تستعملها. يمكنك أيضًا استعمال حزمة Codeception/Specify (والتي يجب إضافتها إلى ملف composer.json) وتكتب وصفًا كالآتي: <?php class HelloWorldTest extends \Codeception\TestCase\Test { use \Codeception\Specify; private $user; protected function _before() { $this->user = User::find(1); } public function testUserEmailSave() { $this->specify("email can be stored", function() { $this->user->setEmail('correct@email.com'); $this->user->save(); $user = User::find(1); $this->assertEquals('correct@email.com', $user->getEmail()); }); } } شيفرة PHP الموجودة ضمن الدوال المغلقة (closure functions) السابقة معزولةٌ، لذا لن تؤثر التعديلات داخلها على بقية شيفراتك. ستساعد عبارات الشرح بجعل الغاية من الاختبار واضحةً وتسهيل التعرف على الاختبارات التي فشلت. إضافةً إلى ما سبق، يمكنك استعمال الحزمة Codeception\Verify لكتابة اختبارات بشيفرة شبيهة بأسلوب BDD: <?php public function testUserEmailSave() { verify($map->getEmail())->equals('correct@email.com'); } ويمكنك بالطبع استعمال stub: <?php public function testUserEmailSave() { $user = Stub::make('User', ['getEmail' => 'correct@email.com']); $this->assertEquals('correct@email.com', $user->getEmail()); } الخلاصة: يساعد Codeception على توفير الوقت والجهد ما الذي تتوقع الحصول عليه من Codeception؟ من هم الأشخاص المناسبون لاستعماله؟ برأيي الشخصي، Codeception مناسب لجميع أنواع الفرق: الكبيرة والصغيرة، والمبتدئين والمحترفين، والذين يستعملون أطر عمل والذين يكتبون برمجياتهم من الصفر، أي أنَّ Codeception صالحٌ لكل زمان ومكان (تقريبًا). Codeception هو إطار عمل ناضج وموثّق جيدًا ويمكن توسعته باستعمال إضافات. وهو حديثٌ، ومبنيٌ على المكتبة الشهيرة PHPUnit، مما يطمئن المطورين الذين لا يريدون تجربة الكثير من المكتبات قبل الاستقرار. أداءُ هذه المكتبة جيدٌ، وهذا يعني أنها سريعة ولا تحتاج وقتًا ولا جهدًا كثيرًا، ومن السهل تعلمها، والتوثيق الرائع يجعل عملية التعلم تمر دون مشاكل. من السهل جدًا تثبيت وضبط Codeception، مع أنه يحتوي على الكثير من الميزات المتقدمة. وصحيحٌ أنَّ أغلبية المستخدمين لا يحتاجون جميع (أو حتى أغلب) الميزات الموجودة فيه، إلا أنَّ ذلك يعتمد على ما تشاء فعله معه. يمكنك البدء بالأساسيات، ثم ستبدأ بالتعامل مع الميزات الإضافية عاجلًا أو آجلًا. ترجمة -وبتصرّف- للمقال Jumpstart Your PHP Testing with Codeception لصاحبه Vasily Koval
  8. تُسهِّل برمجية ووردبريس إنشاءَ صفحاتٍ ومنشوراتٍ محميةٍ بكلمة مرور، وهذه ميزةٌ رائعة إذا أردتَ تقييد الوصول إلى محتوى معيّن، لكن المشكلة الوحيدة هي أنَّ أيّة صور أومقاطع فيديو تَعرِضُها في تلك الصفحات ستبقى متاحةً للوصول من صفحات الوسائط المرفقة. يمكن أن يتحول الأمر إلى إشكالية رئيسية إذا كانت تحتوي ملفات الوسائط على معلومات مهمة التي تريد أن تبقى محميةً بكلمة مرور. أحدحلول هذه المشكلة هو إلغاء دعم صفحات الوسائط (attachment pages) في ووردبريس كليًّا، مما يعني أنَّ على زوار موقعك الوصول إلى المنشور (أوالصفحة) المحمي بكلمة مرور لرؤية المحتوى. سننظر في هذا الدرس إلى طريقتين لحل هذه المشكلة.فأولًا سنحلّ المشكلة بإنشاء «قالب ابن» (child theme) وكتابة بعض الشيفرات؛ ثم سنشرح كيف نستخدم إضافةً مجانيةً لفعل ذلك تلقائيًا. متى نخفي صفحات الوسائط؟ لنقل أنك كتبتَ درسًا تعليميًا رائعًا، وكان معروضًا في صفحةٍ وحيدةٍ، وبلغ طوله بضعة آلاف من الكلمات، إضافةً إلى مقطع فيديو أو أكثر، وعدد لا بأس به من الصور التوضيحية. سنفترض أنَّك لا تريد إنشاء متجر لبيع الدورات التعليمية وإنما توفِّر في بعض الأحيان محتوى مدفوع، لذا لن تكون مهتمًا بتثبيت نظام إدارة دورات تعليمية كامل مثلCoursePressPro. حيث اخترتَ حماية الصفحة بكلمة مرور التي ستُرسِلها بطريقةٍ مؤتمتة إلى البريد الإلكتروني للشخص الذي سيدفع لقاء الوصول إلى الدرس. سأفترض أنَّك وجدتَ حلًّا بسيطًا الذي يساعدك في ما سبق دون أن يتطلب جهدًا أو وقتًا طويلًا منك. لكن بعد عدِّة أسابيع أدركتَ وجود مشكلة، حيث شارك أحدهم صورةً من مقالتك على وسائل التواصل الاجتماعي مستعملًا رابط URL لصفحة الوسائط المرفقة، وتلك الصفحة يمكن الوصول إليها مباشرةً دون إدخال كلمة مرور. وما يزيد الطين بلّةً أنَّ القالب الذي تستعمله يُضيف روابط (للصورالتالية والسابقة) إلى صفحة المرفقات مما يسمح للزوار بالتنقل بسهولة بين جميع الصور التي أرفقتها بالمنشور المخفي! حسنًا، دعني أخبرك أنَّ هذه المشكلة سهلة الحل جدًا… إخفاء صفحات المرفقات باستخدام الشيفرات إذا توفر لديك عميل FTP ومحرر نصي، فيمكنك حلّ هذه المشكلة بسرعة عبر إنشاء «قالب ابن». كل ما عليك فعله هو إضافة سطرٌ واحدٌ إلى ملف image.php ويمكنك–اختياريًّا– إضافته إلى ملف video.php. إذا ألقيتَ نظرةً إلى البنية الهيكلية لقوالب ووردبريس فستلاحظ أنَّ أكثر ملف تخصيصًا يمكن إنشاؤه لصفحة المرفقات هو ملفٌ باسم MIME_type.php وهذا يعني أنَّه لو كانت لديك صورٌ ومقاطع فيديو في صفحةٍ محميةٍ بكلمة مرور، فستحتاج إلى إخفاء صفحة المرفقات لكلا النوعين، وستحتاج إلى تعديل ملفٍ مختلفٍ لكل نوع من الوسائط. لنبدأ بملف image.php،لكن قبل أن تبدأ بتعديل أيّة ملفات، فتأكد أنَّك تستعمل «قالب ابن». بعد إنشاء وتفعيل القالب الابن، أنشِئ ملفًا باسم image.php على حاسوبك، وافتحه باستخدام المحرر النصي المفضَّل لديك، وأضف إليه السطر البرمجي الآتي: <?php wp_redirect(get_permalink($post->post_parent)) ; ?> احفظ الملف وأغلقه، ثم ارفعه إلى مجلد الجذر للقالب الابن الذي أنشأتَه منذ قليل. عندما يحاول أحدهم الوصول إلى صفحة المرفقات للصور، فسيتم تحميل ملف image.php وسيؤدي إلى إعادة توجيه المتصفح إلى المنشور المرتبط بتلك الصورة، وهذا هو السلوك الذي نرغب به؛ فأيُّ شخصٍ يحاول الوصول إلى صفحة المرفقات الخاصة بالصور التابعة للمنشور المحمي بكلمة المرور سيُعاد توجيهه إلى الصفحة المحمية بكملة المرور، وعليه عندئذٍ إدخال كلمة المرور للوصول إلى تلك الصفحة. لإضافة حماية إلى صفحات المرفقات الخاصة بمقاطع الفيديو، أنشِئ ملفًا باسم video.php وأضف إليه نفس السطر السابق. هذاحلٌ بسيطٌ جدًا لإخفاء صفحات المرفقات باستخدام الشيفرات؛ لكن إذا كنتَ تُفضِّل إتمام الأمر دون استعمال محرر نصي وعميلFTP، فأود أن أبشِّرَك بوجود حلٍ آخر… إخفاء صفحات المرفقات باستخدام إضافة إذا أردتَ تفادي إنشاء قالب ابن وكتابة (أونسخ ولصق) السطر البرمجي السابق، فيمكنك استخدام إضافة لإعادة توجيه كل صفحات المرفقات. إضافة AttachmentPages Redirect هي إضافةٌ مجانيةٌ متاحةٌ على WordPress.org التي تؤدي الغرض الواضح من اسمها: إعادة توجيه جميع صفحات المرفقات إلى صفحة المنشور الأصلي الموجودةُ فيه (إعادة توجيه دائمة 301)،أو إلى الصفحة الرئيسية (إعادة توجيه مؤقتة 302) إذا لم تكن تلك الوسائط مرفقةً إلى أحد المنشورات. هنالك أكثر من 10000 موقع يستعمل هذه الإضافة، ولها تقييم 4.7 من 5 نجوم. للبدء باستخدام إضافة Attachment Pages Redirect اذهب إلى«Plugins > Add New» (الإضافات> أضف جديد) في لوحة تحكم ووردبريس وابحث عن العبارة «Attachment Pages Redirect»، ثم اضغط Install (تثبيت) ثم Activate (تفعيل). لا تحتاج إلى ضبط أيّ شيءٍ آخر. افتح متصفحك وحاول عرض صفحة أحد المرفقات،وستجد أنَّه قد تمت إعادة توجيهك إلى صفحة المنشور الأصلي أو إلى الصفحة الرئيسية للموقع. لا تظن أنَّ هنالك حماية كاملة لمرفقاتك لا تضمن الطريقتان اللتان شرحناهما في هذا الدرس أنَّ الزوار يجب أن يستعملوا كلمة مرور للوصول إلى الصور أو مقاطع الفيديو التي تُضيفها إلى منشوراتك المحمية؛ لكن دعني أوضِّح لك أنَّه لا توجد أيّة طريقة لحماية المرفقات حمايةً تامةً. فمثلًا، يستطيع المستخدمون الذين يملكون وصولًا إلى المرفقات (عبر إدخالهم لكلمة مرور صحيحة) أن ينسخوا محتواك بكل سهولة عبر: تفحص شيفرة الصفحة المصدرية والعثور على روابط URL مباشرة لملفات الوسائط أو عبر استخدام برمجيات لأخذ لقطات شاشة للصور أو تسجيل محتوى الفيديو عند تشغيله. هنالك خطواتٌ إضافية يمكنها التصدي لهذه المحاولات مثل إعادة التوجيه من طرف الخادوم (sever-level redirects) مما يمنع الوصول المباشر إلى ملفات الوسائط،وعبر استخدام JavaScript لمنع نسخ محتواك. لكن إذا كان المستخدم عاكفًا على نسخ مقالتك والوصول إلى ملفات الوسائط، فسيجد طريقةً للالتفاف على وسائل الوقاية التي استعملتها. في النهاية، أريد أن أُذكِّرك أنَّه لا توجد طريقةٌ لحماية ملفات الوسائط، ففي اللحظة التي تسمح بها لأيّ شخص بالوصول إليها،فلن تستطيع الاحتفاظ بالقدرة على التحكم بالمحتوى، لذا أبقِ هذا في بالك في كل مرة تُضيف فيها المحتوى في موقعك، حتى لو كنتَ «تحمي»المنشورات عبر كلمة مرور (يجب أن تكون مدركًا بعد قراءتك لما سبق أنَّه لا توجد حماية مطلقة للمحتوى). إذا لم تكن تريد رؤية الصور أو مقاطع الفيديو التي ترفعها إلى موقعك مسروقةً ومنشورةً في أحد المنتديات، فلا ترفعها إلى موقعك من الأساس. الخلاصة الميزة المُضمَّنة في ووردبريس لحماية المنشورات بكلمة مرور تُسهِّل من إنشاء محتوى خاص ومحمي، لكنها لا تحمي المرفقات والوسائط المُضافة إلى الصفحات والمنشورات التي لا تستطيع الوصول إليها إلا بكلمة مرور. الحل بسيطٌ جدًا. إذ يمكنك إعادة توجيه صفحات المرفقات إلى المنشور الأصلي الذي أُرفِقَت الوسائط به عبر سطرٍ برمجيٍ وحيد، وإذا لم تكن تريد أن تُضيع وقتك بكتابة ذاك السطر، فإضافة Attachment Pages Redirect ستساعدك في ذلك. ترجمة-وبتصرّف-للمقال [Howto Hide Media Attachment Pages in WordPress] لصاحبهJon Penland
  9. Docker هي أداةٌ لإنشاء الحاويات تُستخدَم لتوفير برمجيات مع نظام ملفات الذي يحتوي كل شيءٍ تحتاج له تلك البرمجيات لتعمل. استخدام حاويات Docker يضمن أنَّ البرمجيات ستعمل عملًا صحيحًا بغض النظر عن النظام الذي ستعمل داخله، لأنَّ بيئة التشغيل ستكون متماثلة في جميع الحالات. سنوفِّر في هذا الدرس لمحةً عن العلاقة بين صور Docker (أي Docker images) وحاويات Docker (أي Docker containers). ثم سنتعلم كيفية تشغيل وإيقاف وحذف الحاويات. لمحة عن حاويات Docker يمكننا أن نتخيل «صورة Docker» على أنها قالبٌ (template) يُستخدم لإنشاء حاويات Docker. تبدأ الصور عادةً بنظام ملفات رئيسي، وتُضاف التعديلات على نظام الملفات (والمعاملات المترافقة معها) على شكل طبقات متتالية. وعلى النقيض من توزيعات لينكس الاعتيادية، تحتوي صورة Docker على المكوِّنات الأساسية اللازمة لتشغيل التطبيق (ولا تحتوي على جميع الأدوات الموجودة في التوزيعات التقليدية). ولا تتغير صور Docker، وإنما تكوِّن نقطة انطلاق لإنشاء حاويات Docker. هذا المزيج بين الطبقات المتاحة للقراءة فقط والتي تعلوها طبقةٌ قابلةٌ للكتابة والقراءة يُشكِّل ما يُعرَف بنظام الملفات الاتحادي (union file system). فعند حدوث تغيير إلى ملفٍ موجودٍ في حاوية فسيُنسَخ الملف من الفضاء المتاح للقراءة فقط إلى الطبقة التي يُسمَح فيها بالقراءة والكتابة، ثم ستُطبَّق التغييرات، والنسخة الموجودة في الطبقة المتاحة للقراءة والكتابة تؤدي إلى «إخفاء» الملف الأصلي لكنها لا تحذفه. التغييرات التي تحدث في الطبقة المتاحة للقراءة والكتابة تتواجد في الحاويات المستقلة فقط، وعندما تُحذَف إحدى الحاويات فستضيع جميع التغييرات إلا إن اتخذنا إجراءات لحفظها. التعامل مع الحاويات في كل مرة تستعمل فيها الأمر docker run سيؤدي إلى إنشاء حاوية جديدة من الصورة التي حدَّدتَها. ما سبق قد يُسبِّب بعض الالتباس، لذا لنأخذ بعض الأمثلة للتوضيح. الخطوة الأولى: إنشاء حاويتَين الأمر docker run الآتي سيُنشِئ حاويةً جديدةً تستعمل الصورة ubuntu، ويمنحنا الخيار ‎-t وصولًا إلى الطرفية، والخيار ‎-i يسمح لنا بالتفاعل معها. سنعتمد على الأمر الافتراضي الموجود في ملف Docker لتوزيعة أوبنتو (وهو bash) لكي نحصل على وصولٍ للصدفة (shell): $ docker run -ti ubuntu سيتغير مِحَث سطر الأوامر (prompt) ليُشير إلى أننا نعمل داخل الحاوية بحساب الجذر، متبوعًا بمُعرِّفٍ ذي 12 محرفًا للحاوية، ويبدو المِحث كالآتي: root@11cc47339ee1:/# سنُجري تعديلًا على الحاوية عبر كتابة جملة نصيّة إلى ملفٍ داخل مجلد ‎/tmp، ثم سنستخدم الأمر cat للتأكد من أنَّ الملف قد تعدّل: echo "Example1" > /tmp/Example1.txt cat /tmp/Example1.txt الناتج: Example1 لنخرج الآن من الحاوية بالأمر exit. سيوقف تشغيل حاويات Docker عند انتهاء تنفيذ الأمر الذي بدأت به، لذا ستتوقف حاويتنا عن العمل عند خروجنا من الصدفة bash. ولو نفَّذنا الأمر docker ps الذي يعرض الحاويات التي تعمل حاليًا، فلن نشاهد حاويتنا مذكورةً بينها: docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES أما إذا استعملنا الخيار ‎-a الذي يعرض جميع الحاويات، سواءً كانت متوقفةً أم تعمل حاليًا، فستظهر عندئذٍ حاويتنا في القائمة: docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 11cc47339ee1 ubuntu "/bin/bash" 6 minutes ago Exited (127) 8 seconds ago small_sinoussi عندما أُنشِئَت الحاوية، أُعطيَتْ مُعرَّفًا واسمٍ مولِّدٍ تلقائيًا. وكان مُعرِّف الحاوية في حالتنا هو 11cc47339ee1 والاسم المولَّد تلقائيًا هو small_sinoussi. الخيار ps -a يُظهِر هذه القيمة بالإضافة إلى اسم الصورة التي بُنيَت الحاوية عليها (ubuntu)، ومتى أُنشِئَت الحاوية (‎6 minutes ago)، وما الأمر الذي بدأت الحاوية به (‎/bin/bash). والناتج يعرض أيضًا حالة الحاوية (Exited) ومتى دخلت الحاوية بهذه الحالة (‎8 seconds ago). إذا ما زالت الحاوية تعمل عند تنفيذ الأمر، فستظهر الحالة Up مع بيان مدّة تشغيلها. إذا أعدنا تنفيذ الأمر السابق، فستُنشَأ حاويةٌ جديدة: docker run -ti ubuntu سنعرف أنَّ هذه حاويةٌ جديدة لأنَّ مُعرِّف الحاوية الظاهر في المِحَث مختلف، وإذا حاولنا عرض الملف Example1 فلن نجده: root@6e4341887b69:/# cat /tmp/Example1 الناتج: cat: /tmp/Example1: No such file or directory قد تظن أنَّ الناتج السابق يعني أنَّ البيانات قد اختفت، لكن ذلك ليس صحيحًا. سنخرج الآن من الحاوية الثانية لنتأكد من أنَّ كلا الحاويتين ما زالتا موجودتين في نظام الملفات، ولم تُحذَف الحاوية الأولى التي أنشأنا فيها الملف. docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 6e4341887b69 ubuntu "/bin/bash" About a minute ago Exited (1) 6 seconds ago kickass_borg 11cc47339ee1 ubuntu "/bin/bash" 13 minutes ago Exited (127) 6 minutes ago small_sinoussi الخطوة الثانية: إعادة تشغيل الحاوية الأولى لإعادة تشغيل حاوية موجودة مسبقًا، فسنستخدم الأمر start مع الخيار ‎-a و ‎-i، متبوعًا باسم الحاوية أو مُعرِّفها. احرص على أن تضع مُعرِّف الحاوية التي أنشأتها في نظامك في الأمر الآتي: docker start -ai 11cc47339ee1 سنجد أنفسنا داخل صدفة bash مرةً أخرى، وعندما نستخدم الأمر cat على الملف الذي أنشأناه مسبقًا فسنرى أنَّه ما يزال موجودًا: root@11cc47339ee1:/# cat /tmp/Example1.txt Example1 يمكننا الآن أن نخرج من الحاوية بالأمر exit. الناتج السابق يُظهِر أنَّ التعديلات التي أُجرِيَت داخل الحاوية ستبقى حتى لو أوقفناها ثم أعدنا تشغيلها مرةً أخرى. ولن يُحذَف محتواها إلا عند إزالة الحاوية. وهذا المثال يبيّن أنَّ التعديلات ستبقى محصورةً في الحاوية، ولن تؤثر على ما سواها، وعند بدء حاوية أخرى فستأخذ بنية نظام الملفات من الصورة الأصلية. الخطوة الثالثة: حذف كلا الحاويتين لقد أنشأنا في هذا الدرس حاويتين، وسنختمه بتوضيح كيف نحذفهما. يسمح لنا الأمر docker rm –الذي يعمل على الحاويات المتوقفة فقط– بحذف حاوية أو أكثر عبر تحديد اسمها أو مُعرِّفها كما يلي: docker rm 11cc47339ee1 kickass_borg 11cc47339ee1 kickass_borg لقد حُذِفَت الحاويتان مع جميع محتوياتها. الخلاصة لقد ألقينا نظرةً مفصّلةً على الأمر docker run ورأينا كيف يُنشِئ حاويةً جديدةً في كل مرّة يُنفَّذ فيها، ورأينا أيضًا كيفية الحصول على معلومات عن حاوية متوقفة وكيفية تشغيلها والوصول إليها. ترجمة -وبتصرّف- للمقال Working with Docker Containers لصاحبته Melissa Anderson
  10. تمهيد من المهم أن تراقب دومًا أداء الخادوم، ولمساعدتك في ذلك يوفِّر DigitalOcean مخططات Droplet Graphs التي تعرض أداء خادومك بشكلٍ بصري. سننظر في هذا الدرس إلى كيفية الاستفادة من المخططات المتوافرة افتراضيًا، إضافةً إلى المخططات التي سنحصل عليها بعد تثبيت عميل ‏DigitalOcean، الذي هو أداةٌ صغيرةٌ تجمع معلوماتٍ عن الذاكرة واستهلاك القرص وعن أكثر العمليات استعمالًا للمعالج وللذاكرة في النظام. المخططات الافتراضية لعرض مخططات أحد الخواديم، فاضغط على اسمه في صفحة Droplet: عند الضغط على اسم الخادوم، فستنتقل مباشرةً إلى صفحة Graphs. وإذا خرجتَ من صفحة Graphs فيمكنك العودة إليها بالضغط على رابط Graphs في شريط التنقل الظاهر على اليسار: يمكن عرض إحصائيات Droplet Graphs لآخر 6 ساعات، أو 24 ساعة، أو 7 أيام، أو 30 يوم. وتتوافر ثلاثة مخططات لأي خادوم: مخطط «Bandwidth public» (التراسل الشبكي الخارجي) يُظهِر استهلاك التراسل الشبكي للاتصالات الخارجية بواحدة ميغابت في الثانية. يظهر التراسل الشبكي للاتصالات الواردة باللون البنفسجي الداكن، أما التراسل الشبكي للاتصالات الصادرة فسيظهر باللون البنفسجي الفاتح. مخطط «CPU» (وحدة المعالجة المركزية) يُظهِر نسبةً مئويةً لمقدار استهلاك طاقة المعالجة، وتظهر عمليات المستخدم باللون الأزرق الفاتح، بينما تظهر عمليات النظام باللون الأزرق الداكن. مخطط «Disk I/O» (الكتابة والقراءة على القرص) يُظهِر عمليات القراءة والكتابة بواحدة ميغابايت في الثانية، وتُمثَّل عمليات القراءة باللون الأخضر الداكن، أما عمليات الكتابة فتظهر باللون الأخضر الفاتح. أما إذا فعّلتَ الشبكات الخاصة، فستستطيع الوصول إلى مخططٍ رابع لتتبع التراسل الشبكي الخاص. وكما في مخطط «Bandwidth public»، لن يظهر هذا المخطط إلا إذا كان هنالك تراسل شبكي فعلي، وعندئذٍ سيُعرَض مخططٌ باسم «Bandwidth private» التي يُظهِر استهلاك التراسل الشبكي الخاص بواحدة ميغابت في الثانية؛ يظهر التراسل الشبكي للاتصالات الواردة باللون البنفسجي الداكن، أما التراسل الشبكي للاتصالات الصادرة فسيظهر باللون البنفسجي الفاتح. عندما تضع الفأرة فوق أحد المخططات، فسيُعرَض خطٌ عموديٌ في جميع المخططات إضافةً إلى مربع صغير يحتوي على القيم المرتبطة بهذه اللحظة الزمنية. يمكنك أن تلحظ في الصورة الآتية أنَّ المربع سيعرض حالة النظام في الساعة 12:12 مساءً في تاريخ November 21, 2016: المخططات الإضافية التي تظهر بعد تثبيت العميل المخططات الافتراضية تُقاس وتُحسَب باستخدام أدوات خارجية، ولا تتطلب أي شيءٍ خاص في الخادوم نفسه. أما الإحصائيات الأخرى (كاستهلاك الذاكرة والقرص) تتطلب جمعًا للبيانات من داخل الخادوم. يوفِّر عميل DigitalOcean (الذي هو أداةٌ صغيرةٌ تعمل على الخادوم) المعلومات الإضافية حول استهلاك الذاكرة والقرص. يمكنك تعلم المزيد من المعلومات حول عميل DigitalOcean وكيفية تثبيته في درس «كيفية تثبيت واستخدام عميل DigitalOcean لإظهار المزيد من المخططات للخادوم». يمكن تثبيت العميل تلقائيًا أثناء إنشاء الخادوم عبر تفعيل الحقل «Monitoring» كما هو موضَّح في هذه الصورة: يمكن أيضًا تثبيت العميل يدويًا باتباع التعليمات الواردة في الصفحة الموجودُ رابطها أعلى المخططات: بعد تفعيل العميل، فستملك وصولًا إلى مخططاتٍ إضافيةٍ التي تتضمن: مخطط «Memory» الذي يُظهِر نسبة استهلاك الذاكرة الفيزيائية RAM. مخطط «Disk Usage» الذي يُظهِر نسبة المساحة المستخدمة في القرص الصلب المرتبط بالخادوم. مخطط «Top processes» يُظهِر أكثر العمليات التي تعمل على الخادوم استهلاكًا للموارد، والمرتّبة عبر استهلاك المعالج أو الذاكرة. وهذا المخطط لا يُظهِر أيّة بيانات من الماضي، وإنما سيُظهِر البيانات اللحظية فقط. المرور بالفأرة على أيّ مخطط سيؤدي إلى إظهار مربع يحتوي على تفاصيل استهلاك الموارد في اللحظة الحالية: أخيرًا، إذا كنتَ قد فعّلتَ الشبكة الخاصة واستخدمتها، فستُعرَض سبعة مخططات في الصفحة السابقة: الخلاصة رأينا ما هي المخططات المتوافرة لخواديم DigitalOcean. إذا دلّت تلك المخططات على مشاكل في الأداء، فيتوجّب عليك تتبّع الخلل ومُحاولة إصلاحه. ألق نظرة على دروسنا السّابقة لتجد فيها أمثلة عن تحسين أداء مُختلف جوانب الخادوم. ترجمة -وبتصرّف- للمقال How To Track Droplet Performance with DigitalOcean Droplet Graphs لصاحبته Melissa Anderson
  11. أداة البناء Grunt أو Gulp، مكتبة require.js، ‏browserify، الإصدار السادس من ES، المفسرات، أطر عمل React و Angular و Amber، التعابير المغلقة (closures)، سلسلة prototype. ارتفاع في ضغط الدم يؤدي إلى سكتة دماغية. حسنًا، تطوير الويب أمرٌ ممتع جدًا، لكن JavaScript مروعة! تجد نفسك منسجمًا تمامًا مع جميع جوانب تطوير الويب، لكن عندما يأتي الأمر إلى JavaScript فستشعر أنَّ جزءًا كبيرًا من المعلومات الأساسية ينقصك بينما يعرفه الآخرون، والذي سيؤدي إلى جعلهم يفهمون سكربتات JavaScript. نعم، الحقيقة هي أنَّك تفتقد بالفعل إلى بعض القطع؛ لكن هذا لا يعني أنَّ التوجه الحالي لتطوير الواجهات الأمامية ليس مجنونًا! اطمئن، فأنت لستَ بمفردك، لذا اسحب كرسيًا واجلس، وجهِّز نفسك لكتابة تطبيق JavaScript. أوّل خطوة هي ضبط بيئة التطوير المحلية، لذا اتخذ قرارك: هل ستستخدم Gulp، أم Grunt، لا! سأستعمل سكربتات NPM. هل أستعمل Webpack أم Browserify أم Require.js؟ هل أتخذ قرارًا مصيريًا بالانتقال إلى الإصدار السادس من ES؟ أليس ضروريًا أن أضع مرجعًا عن أمراض القلب بجواري؟ كيف سأنظِّم اختبار الشيفرات؟ هل من إطارِ عملٍ تنصحني به؟ أليس من الأفضل تشغيل الاختبارات من سطر الأوامر، لنستعمل إذًا PhantomJS؟ مع أي إطارٍ أذهب: Angular أم React؟ ربما Ember؟ ماذا عن Backbone؟ ربما قرأتَ بعض صفحات توثيق React ووجدتَ فيها أنَّ «Redux هو حاويةٌ ذاتُ حالةٍ قابلةٍ للتوقع لتطبيقات JavaScript» وبدت على وجهك أمارات الرضى، فمن المؤكد أنَّك ستحتاج إلى هذه الميزة العظيمة، بغض النظر عن أنَّك لم تفهم حرفًا من شرحها. السؤال الآن هو: لماذا أصبح تطوير تطبيقات JavaScript أمرًا يدفع إلى الجنون؟! دعني أساعدك لفهم سبب ذلك. لنبدأ بمثالٍ بسيطٍ ثم سنستعرض صورًا جميلةً توضِّح وجهة نظري. هذا تطبيق «Hello, World!‎» مكتوبٌ باستخدام React: // main.js var React = require('react'); var ReactDOM = require('react-dom'); ReactDOM.render( <h1>Hello, world!</h1>, document.getElementById('example') ); لم ننتهِ منه بعد: $ npm install --save react react-dom babelify babel-preset-react $ browserify -t [ babelify --presets [ react ] ] main.js -o bundle.js هنالك عدِّة خطوات ناقصة هنا، مثل تثبيت مكتبة browserify أو ما الذي عليك فعله لتشغيل الصفحة في المتصفح، إذ لا يبدو أنَّ ما سبق سيُنشِئ صفحة ويب قادرة على فعل أيّ شيء! بعد أن تنتهي من إنجاز ما سبق، فستجد ملفًا يدعى bundle.js يحتوي على تطبيق «Hello, World!‎» السابق المكتوب بمكتبة React والذي يضم حوالي 19374 سطرًا برمجيًا، وكل ما فعلتَه هو تثبيت browserify و babelify و react-dom، التي «تزن» آلاف الأسطر البرمجية. هذه صورة تعبيرية عن برنامج «Hello, World!‎» في React: حسنًا، هذا تطبيق «Hello, World!‎» باستخدام JavaScript دون مكتبات: <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width" /> <title>Hello World</title> </head> <body> <div id="container"></div> <script> document.body.onload = function(){ var container = document.getElementById("container"); container.innerHTML = '<h1>"Hello, world!"</h1>'; } </script> </body> </html> هذا كل ما في الأمر! 18 سطر برمجي (يمكن اختصارها إلى أقل من ذلك)، التي تستطيع نسخها ولصقها في ملفٍ باسم index.html وتنقر نقرتين عليه مما يفتحه في متصفحك. يا للبساطة! إذا كنتَ تفكِّر في هذه اللحظة «أليس إطار React يفعل أكثر من ذاك المثال البسيط الذي كتبته، والذي لا يرقى أن يكون تطبيق JavaScript» فأنت مصيبٌ (تقريبًا)، وعلى بعد خطوة واحدة من فهمك لماذا كل هذا التعقيد. انظر إلى هذه الصورة: أغلبية تطبيقات JavaScript التي ستعمل عليها ستقع في مكانٍ ما في منتصف المنحني الجرسي (bell curve) السابق. وإذا كنتَ في منتصف المنحني السابق وبدأت تطبيقًا بالاعتماد على React فسوف ينتهي بك المطاف بهندسة تطبيقك زيادةً عن اللزوم من بدايته. وهذا هو سبب تعقيد تطوير تطبيقات JavaScript، لأنَّ غرض أغلبية الأدوات التي تظن أنَّك بحاجةٍ إليها هو حلّ المشاكل التي لن تتعرض إليها بتاتًا. أصبحت حالة تطوير تطبيقات JavaScript في الآونة الأخيرة معقدةً ومربكةً لأنَّ الجميع يبالغون في هندسة تطبيقاتهم دون أن يدركوا ذلك. إذًا، كيف يجب أن نبدأ بتطوير تطبيق JavaScript؟ هل علينا استخدام مكتبة شبيهة بمكتبة React أو Angular؟ هل يجب أن نستخدم مدير للحزم؟ ماذا يفترض علينا أن نفعل إذا لم نستخدمهما؟ هل كتابة الاختبارات ضرورية؟ هل علينا أصلًا توليد شيفرات HTML عبر JavaScript؟ هذه هي الأسئلة التي يجب أن تسألها لنفسك قبل أن تبدأ بمجموعة ضخمة من أدوات التطوير. عندما تبدأ بتطوير تطبيق JavaScript فمن المهم أن تختار نقطةً في المنحني الجرسي في المكان الذي تظن أنَّ من المرجح أن يصله تطبيقك في المستقبل من ناحية التعقيد. لن أكذب عليك، فعل ذلك ليس سهلًا ويحتاج خبرةً، لكن هنالك منطقةٌ كبيرة يمكنك أن تبدأ منها أغلبية تطبيقات JavaScript: استعمل مكتبة jQuery مع قوالب لصفحات الواجهة الأمامية مع أداة بناء بسيطة لجمع الملفات وتصغيرها (بفرض أنَّ إطار العمل الذي تستعمله لتطوير السند الخلفي [backend] لا يفعل ذلك تلقائيًا). إذا أردتَ أن تتعلم كيفية هيكلة تطبيق JavaScript بطريقةٍ صحيحة، فعليك أن تبدأ بفهم كيف ومتى ولماذا تستخدم إطار عمل أو حزمة npm أو إصدار ES6 أو متى تكتب اختبارات أو هل عليك جعل الاختبارات تعمل محليًا أو في متصفح، ثم سيأتي دور بقية الأسئلة وحلّ بقية المشاكل. إن أردتَ أن تملأ الفجوات الموجودة في معلوماتك حول تطوير JavaScript وأن تتجنّب الشعور بأنّك تبالغ في تصميم تطبيق JavaScript فحاول أن تتابع ما نطرحه هنا في قسم البرمجة في أكاديمية حسوب. ترجمة -وبتصرّف- للمقال Why JavaScript Development is Crazy لصاحبه Sean Fioritto
  12. تمهيد تَتَبَّعُ مخططات الخادوم (Droplet Graphs) استخدامَ موارد خادومك مع مرور الزمن. يمكن قياس بعض الأمور مثل التراسل الشبكي والقراءة والكتابة إلى القرص من أدواتٍ خارجية. لكن للحصول على معلوماتٍ إضافية فيجب تثبيت عميل DigitalOcean على الخادوم لتوفير إمكانية قياس استخدام الذاكرة والقرص الصلب والحصول على معلومات عن أكثر البرمجيات استهلاكًا للمعالج أو الذاكرة في النظام. سنتعرّف في هذا الدرس على عميل DigitalOcean وكيف يعمل، وسنشرح كيفية تثبيته للحصول على المعلومات السابقة. وسنريك أيضًا كيفية حذف العميل تمامًا في حال لم تعد تريد تلك الإحصائيات والبيانات. ما هو عميل DigitalOcean؟ عميل DigitalOcean هو أداةٌ مفتوحة المصدر مكتوبةٌ بلغة Go التي توفِّر قياسات إضافية لكي تُعرَض كمخططات تُظهِر أداء خادومك. دون تثبيت العميل، ستُعرَض لك عبر Droplet Graphs المعلومات الآتية: استهلاك التراسل الشبكي الداخلي والخارجي نشاط المعالج القراءة والكتابة إلى القرص أما مع تثبيت العميل، فسيُعرَض لك –إضافةً إلى ما سبق–: استهلاك الذاكرة استهلاك القرص الصلب أكثر العمليات استهلاكًا للمعالج وللذاكرة. يمكن تشغيل العميل على توزيعة أوبنتو 14.04 أو ما بعدها، وتوزيعة CentOS 6 وما بعدها، ودبيان 8. زر صفحة مستودع عميل DigitalOcean على GitHub لتنظر إلى شيفرته. ما هي المجلدات التي يستطيع العميل الوصول إليها تعمل خدمة العميل كمستخدمٍ دون امتيازات الذي يملك وصولًا إلى المجلدات الثلاثة الآتية: ‎/proc: المكان الذي يجمع فيه العميل معلوماتٍ عن حالة النظام ‎/var/opt: مكان كتابة العميل لمعلومات الاستيثاق (authentication information) الخاصة به ‎/opt/digitalocean: مكان تخزين الملفات الثنائية يبلِّغ العميل عن أسماء أكثر العمليات استهلاكًا لموارد النظام، لكنه لا يُرسِل أيّة متغيرات متعلقة بالبيئة، ولا أيّة وسائط مُمرَّرة إلى العمليات تفاديًا لكشف أيّة معلومات حساسة. كيف يُرسِل العميل البيانات المُقاسة من النظام؟ يستعمل عميل DigitalOcean المنفذين 80 و 443 لإرسال البيانات، ولن يحتاج إلى استقبل أيّة بيانات. يُستعمَل المنفذ 80 للتواصل مع خدمة DigitalOcean metadata للحصول على بيانات الاستيثاق، ثم سيستعمل العميل تلك البيانات للاستيثاق مع خدمة معالجة الإحصائيات وستُشفّر جميع البيانات. كيفية تفعيل العميل عليك تثبيت العميل على خادومك للحصول على معلومات إضافية عن خادومك في Droplet Graphs. يمكن فعل ذلك تلقائيًا أثناء عملية إنشاء الخادوم، أو يدويًا في أيّ وقت. تفعيل العميل أثناء عملية إنشاء الخادوم لتثبيت العميل أثناء إنشاء الخادوم، فاختر حقل Monitoring في قسم «additional options» في صفحة الإنشاء: سيُثبّت العميل تلقائيًا وسيُفعّل أثناء مرحلة إنشاء الخادوم. تثبيت العميل يدويًا يتوافر سكربت للتثبيت لتثبيت العميل يدويًا. سيُضيف السكربت مستودعًا إلى نظامك ويستعمل مدير الحزم لتثبيت العميل. وهذا سيُبسِّط إجراء عمليات إدارة الحزم مثل تحديث العميل أو حذفه. سجِّل دخولك إلى خادومك بالمستخدم root أو بأي مستخدم يملك امتيازات الجذر عبر الأمر sudo: ssh root@droplet_IP_address بعد أن تتصل بخادومك، يمكنك تنزيل وتنفيذ سكربت التثبيت مباشرةً لو شئت تثبيت العميل من فورك: curl -sSL https://agent.digitalocean.com/install.sh | sh قد يُطلَب منك إدخال كلمة مرورك إذا كنتَ تستعمل مستخدمًا يملك امتيازات الجذر عبر الأمر sudo. ملاحظة: لو أردتَ تفحص السكربت قبل تثبيت، فيمكنك كتابته إلى القرص أولًا: curl -sSL https://agent.digitalocean.com/install.sh -o /tmp/install.sh ثم تنظر في محتويات الملف بتنفيذ الأمر: less /tmp/install.sh بعد أن تتطلع على محتوى الملف وتتأكد منه، فيمكنك إكمال عملية التثبيت وتشغيل السكربت كما يلي: sh /tmp/install.sh يجب أن يكون العميل مثبتًا على نظامك الآن. كيفية عرض المخططات الجديدة زر قسم «Graphs» في صفحة تفاصيل الخادوم بعد تثبيت العميل وتفعيله: يجب أن تصبح المخططات الجديدة ظاهرةً لك بعد برهةٍ من تفعيل العميل: استخدم القائمة المنسدلة لتعديل المدة الزمنية الظاهرة في المخططات، وضع الفأرة فوق أحد المخططات لعرض معلومات تفصيلية. كيفية حذف العميل إذا لم تعد تريد استخدام العميل، فيمكنك حذف الحزمة باستخدام مدير الحزم الموجود في توزيعتك. لخواديم أوبنتو ودبيان، اكتب: sudo apt-get purge do-agent أما لخواديم CentOS: sudo yum remove do-agent سيتم إيقاف الخدمة وستُزال الحزمة من نظامك. وإذا أردتَ إعادة تثبيت العميل لاحقًا فيمكنك فعل ذلك عبر مدير الحزم. كيفية إزالة مستودع عميل DigitalOcean لحذف مستودع العميل من خادومك، فكل ما عليك فعله هو حذف ملف ضبط المستودع. لخواديم أوبنتو ودبيان، اكتب: sudo rm /etc/apt/sources.list.d/digitalocean-agent.list أما لخواديم CentOS: sudo rm /etc/yum.repo.d/digitalocean-agent.repo سيُحذَف ملف ضبط مستودع عميل DigitalOcean من خادومك. الخلاصة يزيد عميل DigitalOcean من البيانات المعروضة في Droplet Graphs ليوفر لك معلوماتٍ إضافيةٍ عن أداء خادومك واستعماله للموارد. ترجمة -وبتصرّف- للمقال How To Install and Use the DigitalOcean Agent for Additional Droplet Graphs لصاحبه Justin Ellingwood
  13. تمهيد تتوسع قواعد البيانات بسرعة مع مرور الزمن، وتكاد في بعض الأحيان أن تملأ المساحة التخزينية المتاحة في نظام الملفات كلها. وقد تتعرض أيضًا إلى مشاكل في الإدخال والإخراج نتيجةً لمحاولة عدِّة خدمات الكتابة على (أو القراءة من) نفس القسم معًا. هذا الدرس سيفيدك لو كنتَ تريد إضافة المزيد من المساحة التخزينية، أو استخدام خصائص جهاز التخزين لزيادة الأداء (ربما عبر استخدام RAID)، أو تتطلّع إلى استعمال ميزات أخرى للتخزين. سيعلِّمُك هذا الدرس طريقة تغيير مجلد تخزين بيانات MySQL. التعليمات المذكورة هنا تناسب الخواديم التي تُشغِّل نسخةً وحيدةً من MySQL، أمّا لو كانت عندك أكثر من نسخة، فسيساعدك درس «How To Move a MySQL Data Directory to a New Location on Ubuntu 16.04» في ذلك، لأنَّه يحتوي معلومات عن كيفية تغيير مكان التخزين عبر تعديل الضبط. المتطلبات المسبقة خادوم أوبنتو 16.04 (أو 14.04) مضبوطٌ كما في درس «الإعداد الابتدائي لخادوم أوبنتو 14.04»، بما في ذلك إعداد حساب مستخدم عادي لكنه يملك امتيازات الجذر (root) عبر الأداة sudo. خادوم MySQL. لو لم يكن عندك خادوم MySQL مضبوطٌ مسبقًا، فسيساعدك الدرس «تثبيت وإعداد نظامي إدارة قواعد البياناتMySQL وPostgreSQL على أوبنتو». نسخة احتياطية من قواعد بياناتك. ما لم تكن تتعامل مع نسخةٍ حديثة التثبيت من MySQL، فاحرص على أخذ نسخة احتياطية من بياناتك. سيساعدك درس «كيف تقوم بالنسخ الاحتياطي لقواعد بيانات MySQL على Ubuntu» على فعل ذلك. سننقل البيانات من جهاز تخزينٍ موصولٍ (mounted) في نقطة الوصل ‎/mnt/volume-nyc1-01.. سيعلّمك هذا الدرس طريقة نقل مجلد تخزين بيانات MySQL إلى مكانٍ جديد بغض النظر عن وسيط التخزين الذي تستخدمه (قرص صلب، أو مصفوفة RAID، أو تخزين شبكي). الخطوة الأولى: نقل مجلد بيانات MySQL لكي نضمن سلامة البيانات، علينا أولًا إيقاف خادوم MySQL: sudo systemctl stop mysql الأمر systemctl لا يُظهِر نتيجة تنفيذ أوامر إدارة الخدمات، لذا إذا أردتَ التحقق أنَّ الخادوم قد أُغلِق بنجاح، فنفِّذ الأمر الآتي: sudo systemctl status mysql انظر إلى آخر سطر من ناتج الأمر السابق الذي يجب أن يخبرك أنَّ الخادوم قد توقف عن العمل: . . . Jul 18 11:24:20 ubuntu-512mb-nyc1-01 systemd[1]: Stopped MySQL Community Server. نستطيع الآن –بعد إغلاق الخادوم– نقل مجلد قواعد البيانات إلى مكانٍ آخر: sudo mv /var/lib/mysql /mnt/volume-nyc1-01/mysql ثم سنُنشِئ وصلةً رمزيةً (symbolic link): sudo ln -s /mnt/volume-nyc1-01/mysql /var/lib/mysql يبدو أنَّنا نستطيع تشغيل خادوم MySQL بعد إنشاء الوصلة الرمزية، لكن هنالك أمرٌ إضافيٌ يجب ضبطه. الخطوة الثانية: ضبط قواعد الوصول في AppArmor بعد أن نقلتَ مجلد MySQL إلى مكانٍ آخر في نظام الملفات، فعليك أن تُعدِّل في ضبط AppArmor، وذلك بتعديل ملف alias التابع لبرمجية AppArmor: sudo nano /etc/apparmor.d/tunables/alias أضف الآن التعليمة الآتية في نهاية الملف: . . . alias /var/lib/mysql/ -> /mnt/volume-nyc1-01/mysql/, لكي تأخذ التعديلات مفعولها، فيجب إعادة تشغيل AppArmor: sudo systemctl restart apparmor ملاحظة: إذا تخطيت خطوة ضبط AppArmor وحاولت تشغيل mysql مباشرةً، فستظهر لك رسالة الخطأ الآتية: Job for mysql.service failed because the control process exited with error code. See "systemctl status mysql.service" and "journalctl -xe" for details. يمكن تلخيص الناتج الظاهر من الأمرَين systemctl و journalctl بما يلي: Jul 18 11:03:24 ubuntu-512mb-nyc1-01 systemd[1]: mysql.service: Main process exited, code=exited, status=1/FAILURE ولمّا كانت رسائل الخطأ لا تُظهِر ربطًا مباشرًا بين AppArmor ومجلد التخزين، فقد يصعب عليك أن تعرف لماذا يحدث هذا الخطأ. لكن إن نظرنا إلى ملف syslog فسنرى ما هي المشكلة: sudo tail /var/log/syslog الناتج: Nov 24 00:03:40 digitalocean kernel: [ 437.735748] audit: type=1400 audit(1479945820.037:20): apparmor="DENIED" operation="mknod" profile="/usr/sbin/mysqld" name="/mnt/volume-nyc1-01/mysql/mysql.lower-test" pid=4228 comm="mysqld" requested_mask="c" denied_mask="c" fsuid=112 ouid=112 يمكننا الآن أن نُشغِّل خدمة MySQL: sudo systemctl start mysql sudo systemctl status mysql بعد أن تُعيد تشغيل MySQL، فتحقق أنَّ بياناتك سليمة وأنَّ خادوم MySQL يعمل عملًا سليمًا دون مشاكل. الخلاصة نقلنا في هذا الدرس مجلد تخزين بيانات MySQL واستعملها وصلةً رمزيةً لكي نخبر MySQL ما هو مكان التخزين الجديد؛ ثم حدّثنا ضبط برمجية AppArmor (الموجودة في توزيعة أوبنتو وغيرها) لكي يتوافق مع ما عدّلناه. وصحيحٌ أننا استعملنا جهاز تخزين مستقل، إلا أنَّك تستطيع اتباع تعليمات هذا الدرس لإعادة تعريف مكان تخزين البيانات بغض النظر عن تقنية التخزين المستعملة. ترجمة -وبتصرّف- للمقال How to Change a MySQL Data Directory to a New Location Using a Symlink لصاحبته Melissa Anderson
  14. تمهيد كلما كانت صفحات الموقع أسرع تحميلًا كلما ازداد احتمال بقاء الزائر متصفحًا للموقع، وعندما تمتلئ صفحات مواقع الويب بالصور والمحتوى التفاعلي الظاهر عبر سكربتات تُحمَّل في الخلفية، فلن تكون عملية فتح «صفحة» ويب أمرًا هينًا، إذ تتضمن طلب ملفاتٍ عدِّة من خادوم الويب ملفًا ملفًا، وعملية تقليل تلك الطلبيات هي إحدى سُبُل تسريع موقعك. يمكن فعل ذلك بطرائق عدِّة، لكن إحدى أهم الخطوات التي يجب إجراؤها هي ضبط التخزين المؤقت في المتصفح (browser caching)؛ وهذا يعني إخبار المتصفح بإمكانية استخدام نسخ محلية من الملفات التي حُمِّلَت في إحدى المرات بدلًا من طلبها من الخادوم مرارًا وتكرارًا؛ ولفعل ذلك يجب إضافة ترويسات لرد HTTP ‏(HTTP response headers) تخبر المتصفح بما عليك فعله. هذا هو دور وحدة header (‏header module) في خادوم Nginx، التي يمكن استعمالها لإضافة الترويسات إلى رد HTTP، لكن دورها الأساسي يمكن في ضبط الترويسات المسؤولة عن التخزين المؤقت. سننظر في هذا الدرس إلى كيفية استخدام وحدة header لتطبيق الفكرة السابقة. المتطلبات المسبقة ستحتاج قبل إكمال قراءة هذا الدرس إلى ما يلي: خادوم أوبنتو 16.04 (أو 14.04) مضبوطٌ كما في درس «الإعداد الابتدائي لخادوم أوبنتو 14.04»، بما في ذلك إعداد حساب مستخدم عادي لكنه يملك امتيازات الجذر (root) عبر الأداة sudo. خدمة Nginx مثبّتة على خادومك باتباعك لهذا الدرس «تنصيب، إعداد واستخدام nginx كخادوم ويب». سنحتاج بالإضافة إلى وحدة header إلى وحدة map التابعة لخادوم Nginx؛ لمزيدٍ من المعلومات حول وحدة map، فاقرأ درس كيفية استخدام وحدة map على خادوم أوبنتو 16.04. الخطوة الأولى: إنشاء ملفات للتجربة سنُنشِئ في هذه الخطوة عدِّة ملفات في مجلد Nginx، إذ سنستخدم هذه الملفات لاحقًا للتحقق من سلوك خادوم Nginx الافتراضي لاختبار أنَّ التخزين المؤقت في المتصفح يعمل عملًا صحيحًا. لتقرير ما هو نوع الملفات المُخدَّمة عبر الشبكة، فلن يُحلِّل Nginx محتويات الملف لأن ذلك يستهلك وقتًا كثيرًا؛ وإنما سينظر إلى لاحقة (أو امتداد) الملف لتحديد ما هو نوع MIME التابع له، والذي سيُصرِّح عن الهدف أو الغاية من الملف. ونتيجةً لهذا السلوك، فلن يكون لمحتوى الملفات الاختبارية أيّة أهمية؛ إذ سنستطيع خداع خادوم Nginx بتسمية الملفات تسميةً صحيحةً، فقد يظن خادوم Nginx أنَّ ملفًا فارغًا يُمثِّل صورةً وآخر يُمثِّل سكربت JavaScript. لنُنشِئ ملفًا باسم test.html في مجلد Nginx الافتراضي عبر الأداة truncate (يمكنك استخدام أيّة أداة أو أمر آخر وليكن touch مثلًا). لاحقة الملف تُشير إلى أنَّه مستند HTML: sudo truncate -s 1k /var/www/html/test.html لنُنشِئ المزيد من الملفات الاختبارية بنفس الطريقة السابقة: صورة jpg وملف css وسكربت js: sudo truncate -s 1k /var/www/html/test.jpg sudo truncate -s 1k /var/www/html/test.css sudo truncate -s 1k /var/www/html/test.js الخطوة التالية هي التحقق من سلوك خادوم Nginx فيما يتعلق بإرسال ترويسات للتحكم بالتخزين المؤقت للمتصفح وذلك بتخديم الملفات التي أنشأناها أخيرًا مع الضبط الافتراضي له. الخطوة الثانية: التحقق من السلوك الافتراضي لخادوم Nginx يكون لجميع الملفات نفس سلوك التخزين المؤقت افتراضيًا. ولرؤية ذلك سنستخدم ملف HTML الذي أنشأناه في الخطوة الأولى، إلا أنَّك تستطيع إجراء هذا الاختبار على أيّ ملف من الملفات التي أنشأناها سابقًا. لنتحقق من أنَّ الملف test.html يُخدَّم ومعه المعلومات التي لها علاقة بالمدة الواجب تخزين المتصفح للملف فيها مؤقتًا. سيؤدي الأمر الآتي إلى طلب الملف من خادوم Nginx المحلي ويُظهِر ترويسات رد HTTP: curl -I http://localhost/test.html يجب أن ترى عدِّة ترويسات HTTP: HTTP/1.1 200 OK Server: nginx/1.10.0 (Ubuntu) Date: Sat, 10 Sep 2016 13:12:26 GMT Content-Type: text/html Content-Length: 1024 Last-Modified: Sat, 10 Sep 2016 13:11:33 GMT Connection: keep-alive ETag: "57d40685-400" Accept-Ranges: bytes يمكنك أن تلاحظ السطر قبل الأخير الذي يبدأ بالكلمة ETag الذي يتضمن مُعرِّفًا فريدًا للنسخة الحالية من الملف المطلوب. فإذا حاولت تنفيذ أمر curl السابق أكثر من مرة فستجد نفس قيمة ETag. أما عند استخدام متصفح ويب، فستُخزَّن قيمة ETag ثم تُرسَل إلى الخادوم عبر ترويسة If-None-Match عندما يريد المتصفح أن يطلب نفس الملف مرةً أخرى (عند تحديث الصفحة على سبيل المثال). يمكنك محاكاة ذلك في سطر الأوامر بالأمر الآتي. احرص على تغيير قيمة الترويسة If-None-Match في الأمر لتُطابِق قيمة ETag في ناتج الأمر السابق: curl -I -H 'If-None-Match: "57d40685-400"' http://localhost/test.html ستجد أنَّ الرد أصبح مختلفًا الآن: HTTP/1.1 304 Not Modified Server: nginx/1.10.0 (Ubuntu) Date: Sat, 10 Sep 2016 13:20:31 GMT Last-Modified: Sat, 10 Sep 2016 13:11:33 GMT Connection: keep-alive ETag: "57d40685-400" ردّ خادوم Nginx بحالة «‎304 Not Modified»، ولن يُرسِل الملف عبر الشبكة مجددًا، وإنما يخبر المتصفح أنَّه يستطيع إعادة استخدام الملف المُخزَّن محليًا والذي نزَّله سابقًا. ما سبق مفيدٌ لأنه يُقلِّل من التراسل الشبكي، لكنه ليس مثاليًا لتحقيق أداءٍ عالٍ نتيجةٍ للتخزين المؤقت. المشكلة مع ترويسة ETag أنَّ المتصفح سيرسل الطلبية إلى الخادوم دائمًا ويسأله إن كان بإمكانه استخدام الملف المُخزَّن مؤقتًا، وحتى لو ردّ الخادوم على المتصفح بالإيجاب بحالة 304 بدلًا من إعادة إرسال الملف مجددًا، إلا أنَّ إرسال الطلبية واستلام الرد سيستهلك وقتًا. سنستخدم في الخطوة التالية وحدة headers لإضافة معلومات للتحكم بالتخزين المؤقت. وهذا سيجعل المتصفح يُخزِّن الملفات محليًا دون أخذ إذن الخادوم أولًا. الخطوة الثالثة: ضبط ترويستَي Cache-Control و Expires إضافةً إلى ترويسة التحقق من تغيّر الملف (ETag)، هنالك ترويستان للتحكم بالتخزين المؤقت هما Cache-Control و Expires. ترويسة Cache-Control هي نسخةٌ أحدث، وفيها خياراتٌ أكثر مقارنةً بترويسة Expires وهي أفيد إن شئت التحكم بسلوك التخزين المؤقت تحكمًا دقيقًا. إذا ضُبِطَت تلك الترويسات، فهي تخبر المتصفح أنَّ الملف المطلوب يمكن أن يُخزَّن محليًا لفترةٍ زمنيةٍ معيّنة (تستطيع أن تجعل صلاحيته «للأبد»!) دون طلبه مرةً أخرى. أما إن لم تُضبَط تلك الترويسات، فسيطلب المتصفحُ الملفَ دومًا من الخادوم، متوقعًا أن يحصل على الحالة «‎200 OK» أو «‎304 Not Modified». يمكننا استخدام وحدة header لإرسال ترويسات HTTP آنفة الذكر. وحدة header هي وحدةٌ من أساس خادوم Nginx لذا لن تحتاج إلى تثبيت أيّ شيءٍ لاستخدامها. لإضافة وحدة header، افتح ملف ضبط Nginx في محرر nano أو أيّ محررٍ نصيٍ تشاء: sudo nano /etc/nginx/sites-available/default اعثر على القسم المُعَنوَنَ server، والذي يبدو كما يلي: . . . # Default server configuration # server { listen 80 default_server; listen [::]:80 default_server; . . . أضف القسمَين الآتيين: أولهما قبل قسم server لتعريف المدة الزمنية لتخزين مختلف أنواع الملفات مؤقتًا، وثانيهما داخل كتلة server الذي يضبط ترويسات التخزين المؤقت ضبطًا سليمًا: . . . # Default server configuration # # Expires map map $sent_http_content_type $expires { default off; text/html epoch; text/css max; application/javascript max; ~image/ max; } server { listen 80 default_server; listen [::]:80 default_server; expires $expires; . . . القسم الموجود قبل قسم server هو قسم map الذي يربط بين أنواع الملفات ومدة التخزين المؤقت لهذا النوع من الملفات. استعملنا عدِّة خيارات ضبط فيه: القيمة الافتراضية هي off، والتي لن تُضيف أيّة ترويسات للتحكم بالتخزين المؤقت، وهذا خيارٌ جيدٌ للمحتوى الذي لا يتطلب تخزينًا مؤقتًا. لنوع text/html سنضبط القيمة إلى epoch وهي قيمةٌ خاصةٌ التي تعني عدم التخزين المؤقت نهائيًا، مما يجبر المتصفح على السؤال إن كانت الصفحة مُحدَّثة. لنوع text/css و application/javascript -والتي هي ملفات الأنماط CSS وسكربتات JavaScript- سنضبط القيمة إلى max وهذا يعني أنَّ المتصفح سيُخزِّن هذه الملفات مؤقتًا أطول مدة يستطيعها، مما يُقلِّل عدد الطلبيات لوجود عدد كبير من هذه الملفات التي ترتبط بصفحة HTML. آخر خيار لنوع ‎~image/‎ وهو تعبيرٌ نمطيٌ (regular expression) الذي يُطابِق جميع الملفات ذات النوع الذي يحتوي على العبارة image/‎ فيه (مثل image/jpg و image/png). وكما في ملفات CSS و JS، تتواجد عادةً صورٌ كثير في مواقع الويب، ومن الجيد تخزينها محليًا، لذا ضبطنا القيمة إلى max أيضًا. التعليمة expires داخل قسم server (التي هي جزءٌ من وحدة headers) تضبط ترويسات التحكم بالتخزين المؤقت؛ حيث تستخدم القيمة المأخوذة من المتغير ‎$expires الذي يربط بين أنواع الملفات ومدة صلاحيتها. وبهذه الطريقة ستختلف الترويسات المُرسَلة اعتمادًا على نوع الملف. احفظ الملف واخرج من المُحرِّر النصي، وأعد تشغيل خادوم Nginx لتفعيل الضبط الجديد: sudo systemctl restart nginx سنتحقق في الخطوة التالية من أنَّ الضبط الجديد يعمل عملًا سليمًا. الخطوة الرابعة: اختبار التخزين المؤقت في المتصفح لنُنفِّذ نفس الأمر الذي طلبنا فيه ملف HTML في القسم السابق: curl -I http://localhost/test.html سيكون الرد مختلفًا هذه المرة، إذ يجب أن ترى ترويستين جديدتين: HTTP/1.1 200 OK Server: nginx/1.10.0 (Ubuntu) Date: Sat, 10 Sep 2016 13:48:53 GMT Content-Type: text/html Content-Length: 1024 Last-Modified: Sat, 10 Sep 2016 13:11:33 GMT Connection: keep-alive ETag: "57d40685-400" Expires: Thu, 01 Jan 1970 00:00:01 GMT Cache-Control: no-cache Accept-Ranges: bytes ترويسة Expires مرتبطة بتاريخٍ في الماضي وترويسة Cache-Control مضبوطةٌ إلى no-cache، مما يجعل المتصفح يسأل الخادوم إن توفرت نسخةٌ جديدةٌ من الملف (باستخدام ترويسة ETag كما سبق). ستجد ردًا مختلفًا عندما تُجرِّب على صورة: curl -I http://localhost/test.jpg ناتج الأمر السابق: HTTP/1.1 200 OK Server: nginx/1.10.0 (Ubuntu) Date: Sat, 10 Sep 2016 13:50:41 GMT Content-Type: image/jpeg Content-Length: 1024 Last-Modified: Sat, 10 Sep 2016 13:11:36 GMT Connection: keep-alive ETag: "57d40688-400" Expires: Thu, 31 Dec 2037 23:55:55 GMT Cache-Control: max-age=315360000 Accept-Ranges: bytes ارتبطت الترويسة Expires في هذه المرة بتاريخٍ في المستقبل البعيد، واحتوت ترويسة Cache-Control على max-age التي تخبر المتصفح كم ثانية يجب أن يُبقي على الملف. وهذا يُخبِر المتصفح أن يُخزِّن الصورة المُنزَّلة مؤقتًا لأطول مدة ممكنة، وبالتالي إذا ظهرت الصورة مرةً أخرى فستُستعمَل النسخة المحلية ولن تُرسَل الطلبية إلى الخادوم بتاتًا. يجب أن تكون النتيجة مشابهةً لملفَي test.js و test.css لأنهما ملفا JavaScript و CSS ويُضبَط لهما ترويسات التخزين المؤقت أيضًا، مَثَلُهُما كَمَثل الصور. إن ظهر عندك مثلما عرضنا في أمثلتنا، فاعلم أنَّ ترويسات التحكم بالتخزين المؤقت قد ضُبِطَت ضبطًا صحيحًا وسيستفيد موقعك من ناحية الأداء، وسيقل الحِمل على خادومك نتيجةً لتخزين المتصفح للملفات محليًا وعدم طلبها من الخادوم. عليك الآن أن تُخصِّص إعدادات التخزين المؤقت اعتمادًا على محتوى موقعك، لكن قد تجد أنَّ الضبط الذي وفرناه في هذا الدرس مناسبًا لتبدأ منه. الخلاصة يمكن أن تُستعمَل وحدة headers لإضافة أي نوع من الترويسات إلى رد HTTP، لكن من أفضل تطبيقات هذه الوحدة هو ضبط ترويسات التحكم بالتخزين المؤقت. إذ سيساعد ذلك في تحسين أداء مواقع الويب المُستضافة على خادومك، خصوصًا في الشبكات ذات زمن التأخير المرتفع (نسبيًا) مثل شبكات الهواتف المحمولة. يمكن أن يؤدي ذلك إلى تحسين ظهور موقعك في محركات البحث التي تأخذ عامل سرعة الموقع بالحسبان. حيث يُعتَبَر ضبط ترويسات التخزين المؤقت من أبزر نصائح أدوات اختبار سرعة الصفحات من Google (أقصد Google PageSpeed). لمزيدٍ من المعلومات التفصيلية عن وحدة headers، فارجع إلى توثيق Nginx الرسمي. ترجمة -وبتصرّف- للمقال How to Implement Browser Caching with Nginx’s header Module on Ubuntu 16.04 لصاحبه Mateusz Papiernik.
  15. من الطبيعي أن نرتكب نحن البشر أخطاءً، لكننا نصحح تلك الأخطاء بنفسنا، أي أننا نميل إلى التعلم من أخطائنا ولا نرتكب الخطأ نفسه مرتين؛ أغلبية الأخطاء التي أقع فيها عند تطوير مواقع ووردبريس تأتي من محاولتي لتقليل الوقت اللازم لإنشاء الميزات، لكن ينتهي المطاف بمَن يعتمد هذه الطريقة بألمٍ في الرأس وندمٍ على أخطائه؛ لكن التعلم من هفوات الآخرين أفضل من الوقوع بكثير من الأخطاء وتضييع الوقت. الخطأ الشائع الأول: عدم تفعيل ميزة التنقيح في ووردبريس لماذا علينا تفعيل ميزة التنقيح (debugging) عندما تعمل الشيفرة كما يجب؟ ميزة التنقيح هي ميزةٌ مدمجة في ووردبريس التي ستؤدي إلى إظهار الأخطاء والتحذيرات والملاحظات (عن الدوال المهملة [deprecated functions] …إلخ.). عندما تكون ميزة التنقيح معطلةً فقد لا تظهر التحذيرات أو الملاحظات المهمة، مما قد يؤدي إلى حدوث مشاكل مستقبلًا إن لم تتعامل مع تلك التحذيرات مباشرةً. ونريد أن تتوافق الشيفرات الجديدة مع بقية عناصر الموقع، لذا عند إضافتها إلى ووردبريس يجب فعل ذلك في بيئة تطويرية مُفعَّلةٌ فيها ميزة التنقيح (لكن احرص على تعطيل هذه الميزة قبل نشر الموقع وتحويله إلى بيئةٍ إنتاجيةٍ). عليك تعديل ملف wp-config.php الموجود في المجلد الرئيسي لووردبريس لتفعيل هذه الميزة، هذا جزءٌ من الملف: // Enable debugging define('WP_DEBUG', true); // Log all errors to a text file located at /wp-content/debug.log define('WP_DEBUG_LOG', true); // Don’t display error messages write them to the log file /wp-content/debug.log define('WP_DEBUG_DISPLAY', false); // Ensure all PHP errors are written to the log file and not displayed on screen @ini_set('display_errors', 0); لا تضم الشيفرة السابقة جميع خيارات الضبط التي يمكن تفعيلها، لكن الضبط السابق كافٍ عادةً لأغلبية الاحتياجات. الخطأ الشائع الثاني: إضافة السكربتات وأنماط CSS عبر wp_head أين الخطأ في إضافة السكربتات إلى قالب header؟ تتضمن ووردبريس مسبقًا عددًا كبيرًا من السكربتات المشهورة، ومع ذلك يأتي المطورون ويضيفون سكربتاتٍ جديدة باستخدام الخطّاف (hook) ذو الاسم wp_head. وهذا قد يؤدي إلى تحميل نفس السكربت عدِّة مرات لكن بإصدارات مختلفة. أتت عملية طلب تحميل السكربتات (enqueuing) لحل هذه الإشكالية، وهذه العملية تمثِّل الطريقة المثُلى لإضافة السكربتات وأنماط CSS إلى موقع ووردبريس. علينا استخدام هذه الطريقة لمنع التضاربات بين السكربتات التي تتطلّب الإضافات استخدامها، وللتعامل مع (ومن ثم تضمين) المكتبات المطلوبة من أحد السكربتات. يمكن استخدام هذه التقنية عبر الدالتين الموجودتين في ووردبريس wp_enqueue_script و wp_enqueue_style لطلب تحميل السكربتات وصفحات الأنماط (على التوالي وبالترتيب). الفرق الأساسي بين الدالتين السابقتين يمكن في أنَّ الدالة wp_enqueue_script تقبل معاملًا (parameter) إضافيًا الذي يسمح لنا بنقل مكان إضافة السكربت إلى أسفل الصفحة. wp_register_script( $handle, $src, $deps = array(), $ver = false, $in_footer = false ) wp_enqueue_script( $handle, $src = false, $deps = array(), $ver = false, $in_footer = false ) wp_register_style( $handle, $src, $deps = array(), $ver = false, $media = 'all' ) wp_enqueue_style( $handle, $src = false, $deps = array(), $ver = false, $media = 'all' ) إذا لم يكن السكربت ضروريًا لعرض محتوى الصفحة، فيمكننا نقله إلى أسفلها لكي تُحمَّل محتويات الصفحة بسرعة؛ يجدر بالذكر أنَّه من المستحسن تسجيل (register) السكربت أولًا قبل طلب تحميله، لأنَّ ذلك يسمح للمطورين الآخرين بإلغاء تسجيل السكربت عبر المرجعية (أي ‎$handle) وذلك ضمن الإضافات التي يكتبونها، دون الحاجة إلى تعديل الشيفرات البرمجية لإضافتك. وعند تسجيل السكربت فيمكن ذكره ضمن مصفوفة الاعتماديات (أي ‎$deps) عبر مرجعيته إن وَجَبَ تحميله قبل تضمين أحد السكربتات الأخرى التي طُلِبَ تحميلها، فعندئذٍ سيُحمَّل السكربت تلقائيًا قبل تحميل السكربت الآخر. الخطأ الشائع الثالث: تفادي استخدام القوالب الأبناء وتعديل ملفات ووردبريس الأساسية أنشِئ قالب ابن (child theme) دومًا إذا أدرتَ تعديل أحد القوالب، فبعض المطورين يجرون تعديلات على ملفات القالب الأب ثم يكتشفون أنَّ جميع تعديلاتهم قد ذهبت أدراج الرياح ولن يستطيعوا استعادتها بعد تحديث القالب. لإنشاء قالب ابن عليك وضع ملف style.css في مجلدٍ فرعي تابعٍ للقالب الابن الخاص بك، وبه المحتويات الآتية: /* Theme Name: Twenty Sixteen Child Theme URI: http://example.com/twenty-fifteen-child/ Description: Twenty Fifteen Child Theme Author: John Doe Author URI: http://example.com Template: twentysixteen Version: 1.0.0 License: GNU General Public License v2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html Tags: light, dark, two-columns, right-sidebar, responsive-layout, accessibility-ready Text Domain: twenty-sixteen-child */ الملف السابق هو مثالٌ عن إنشاء قالب ابن يعتمد على أحد قوالب ووردبريس المُضمَّنة فيها وهو Twenty Sixteen. أهم سطر في الشيفرة السابقة هو السطر الذي يحتوي على كلمة Template والتي يجب أن يُطابِق محتواه اسمَ مجلد القالب الأب. ينطبق نفس المبدأ على ملفات ووردبريس الأساسية: لا تختر الطريق السهل وتُعدِّل الملفات الأساسية. وإنما ابذل جهدًا قليلًا وأعد تعريف الدوال القابلة للتعديل فقط (تسمى Pluggable functions) واستعمل مرشِّحات ووردبريس لمنع الكتابة فوق تعديلاتك بعد تحديث نسخة ووردبريس. تسمح لك إعادة تعريف الدوال باستخدام شيفرات خاصة بك بدلًا من الدوال الأساسية، لكن خفَّ استعمال هذه الطريقة وأصبحنا نستخدم المُرشِّحات (filters) بدلًا منها. حيث يؤدي استخدام المرشحات إلى نفس الناتج، وستُضاف التعليمات البرمجية في نهاية دوال ووردبريس للسماح بتعديل مخرجاتها. إحدى الأشياء التي عليك الانتباه إليها هي إعادة تعريف الدوال ضمن عبارة شرطية if ( !function_exists() ) لاحتمال تعديل أكثر من إضافة لنفس الدالة، فبدون تلك العبارة الشرطية فسيحدث خطأٌ من النوع fetal. الخطأ الشائع الرابع: ضبط بعض القيم ضبطًا ثابتًا نستسهل عادةً إضافة قيمٍ ثابتةٍ (مثل روابط URL) في مكانٍ ما في الشيفرات التي نكتبها، لكن الوقت الذي سننفقه لمعرفة سبب حدوث المشاكل ناتجة عن القيم الثابتة ولتصحيح تلك المشاكل سيكون أكبر. وإذا استعملنا الدالة المناسبة لتوليد المسار المطلوب ديناميكيًا، فسنُبسِّط عملية صيانة وتنقيح الشيفرة. فمثلًا لو نقلتَ موقعك من بيئةٍ تطويريةٍ إلى بيئةٍ إنتاجيةٍ لكنك استخدمتَ روابط URL ثابتة في الشيفرات؛ فعندئذٍ ستُفاجَأ أنَّ موقعك لا يعمل. وهذا هو السبب وراء وجوب استخدامنا للدوال –كتلك المذكورة أدناه– لتوليد مسارات وروابط URL توليدًا ديناميكيًا: // الحصول على رابط القالب الابن الحالي stylesheet_directory_uri(); // الحصول على رابط القالب الأب get_template_directory_uri(); // الحصول على رابط الموقع الحالي site_url(); مثالٌ آخر عن مساوئ ضبط القيم ضبطًا ثابتًا يظهر جليًا عند كتابة طلبيات SQL مخصصة. فنحن نغيّر عادةً «السابقة» (prefix) التي تأتي قبل أسماء الجداول في قاعدة بيانات ووردبريس من wp_‎ إلى شيءٍ آخر مختلف مثل wp743_‎، فلو وضعنا اسم الجدول كقيمة ثابتة فلن تُنفَّذ الطلبيات إذا نقلنا ووردبريس إلى قاعدة بيانات أخرى، لأنَّ السابقة قد تختلف حينها. ولمنع حدوث هذه الإشكالية فيمكننا استعمال خاصيات الصنف wpdb: global $wpdb; $user_count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->users" ); لاحظ أنَّنا لم نستعمل القيمة wp_users مكان اسم الجدول، وإنما تركنا الأمر بيد ووردبريس. سيساعدنا استعمال هذه الخاصيات لتوليد أسماء صحيحة للجداول. الخطأ الشائع الخامس: عدم منع فهرسة موقعك من محركات البحث لماذا قد أرغب منع فهرسة محركات البحث لموقعي؟ أليس ذلك أمرًا جيدًا؟ حسنًا، عندما تطوِّر موقع ويب فلن ترغب بالسماح لمحركات البحث بفهرسة الموقع إلى أن تنتهي من بنائه وتختار بنية «الروابط الدائمة» (permalinks) المناسبة. إضافةً إلى أنَّك إذا كنتَ تطوِّر الموقع على خادوم آخر فلن ترغب بفهرسة محركات البحث لصفحات الموقع كي لا تمسي صفحات الموقع الإنتاجي وكأنها منسوخة من هذا الموقع! وعندما تتواجد أكثر من نسخة لنفس المحتوى، فسيصعب على محركات البحث تحديد ما هو المصدر الملائم أكثر لمطابقة عبارة البحث، وستعاقب محركات البحث عادةً المواقع ذات المحتوى المتماثل مما يؤدي إلى تقليل تقييم صفحتك في نتائج البحث. وكما هو ظاهر في الصورة الآتية، هنالك خيارٌ في صفحة «Reading Settings» (إعدادات القراءة) المُعَنوَن «Discourage search engines from indexing this site» (منع محركات البحث من أرشفة هذا الموقع) لكن لا تغفل عن الملاحظة الظاهر أسفل ذاك الخيار التي تقول أنَّ الأمر متروكٌ لمحركات البحث لكي تستجيب لطلبك بعدم فهرسة الموقع. ابقِ ببالك أنَّ محركات البحث لا تستجيب عادةً لهذا الطلب، ولذا إذا أردتَ منعًا حقيقيًا لمحركات البحث من فهرسة الموقع، فعدِّل ملف ‎.htaccess وأضف السطر الآتي: Header set X-Robots-Tag "noindex, nofollow" الخطأ الشائع السادس: عدم التحقق من تفعيل إحدى الإضافات لماذا نتحقق أنَّ دالةً مرتبطةً بإحدى الإضافات موجودةٌ إذا كانت الإضافة مفعلةً دومًا؟ لكن ماذا لو كانت الإضافة مفعلةً بنسبة 99%، ماذا عن احتمال 1% أنَّ الإضافة معطلة؟ إذا حدث ذلك فمن المرجح أن يعرض موقعك رسائل خطأ، ولمنع ذلك يجب علينا أن نتأكَّد إن كانت الإضافة مفعلة قبل استدعاء إحدى دوالها. إذا كانت تلك الدالة مستدعاةً في «الواجهة الأمامية» (front-end)، فسنحتاج إلى تضمين ملف plugin.php لكي نستطيع استخدام الدالة is_plugin_active()‎: include_once( ABSPATH . 'wp-admin/includes/plugin.php' ); if ( is_plugin_active( 'plugin-folder/plugin-main-file.php' ) ) { // تنفيذ الشيفرات هنا } هذه الطريقة ناجعة وعملية، لكن هنالك بعض الحالات التي يغيّر فيها المطوِّر اسم مجلد الإضافة الرئيسي، لذا من الأفضل التحقق من وجود أحد الأصناف المُعرَّفة في الإضافة: if( class_exists( ‘WooCommerce’ ) ) { // إضافة WooCommerce مفعلة } من غير المرجح أن يُعدِّل المطورون أسماء الأصناف، لذا أفضِّلُ استخدام الطريقة الثانية. الخطأ الشائع السابع: تحميل الكثير من الموارد لماذا يجب أن ننتقي ما هي الموارد التي يجب تحميلها لصفحات الموقع؟ لا يوجد سببٌ منطقيٌ لتحميل السكربتات أو صفحات أنماط CSS التابعة لإضافةٍ معينةٍ إن لم تكن تلك الإضافة مستعملةً في الصفحة التي انتقل إليها المستخدم. يمكننا تقليل وقت تحميل الصفحة إذا حمّلنا الموارد الضرورية فقط، وبالتالي سيُحسِّن ذلك من تجربة المستخدم. خذ على سبيل المثال موقعًا يستعمل WooCommerce، حيث نحتاج إلى تحميل الإضافة في صفحات التسوق فقط، وفي هذه الحالة يمكننا حذف أيّة موارد غير ضرورية في صفحات الموقع الأخرى لتحسين الأداء. يمكننا إضافة الشيفرة الآتية إلى ملف functions.php للقالب أو للإضافة: function load_woo_scripts_styles(){ if( function_exists( 'is_woocommerce' ) ){ if(! is_woocommerce() && ! is_cart() && ! is_checkout() ) { // Dequeue scripts. wp_dequeue_script('woocommerce'); wp_dequeue_script('wc-add-to-cart'); wp_dequeue_script('wc-cart-fragments'); // Dequeue styles. wp_dequeue_style('woocommerce-general'); wp_dequeue_style('woocommerce-layout'); wp_dequeue_style('woocommerce-smallscreen'); } } } add_action( 'wp_enqueue_scripts', 'load_woo_scripts_styles'); يمكن حذف السكربتات عبر الدالة wp_dequeue_script($handle) باستخدام المرجعية التي سُجِّلَت فيها. وبشكلٍ شبيه، تمنع الدالة wp_dequeue_style($handle) تحميل صفحات الأنماط. لكن إن رأيتَ أنَّ ما سبق معقدٌ جدًا ولا تستطيع تنفيذه لبقية الإضافات، فأنصحك بتثبيت إضافة Plugin Organizer التي توفر لك القدرة على اختيار الإضافات التي ستُحمَّل مواردها بناءً على معايير معيّنة، مثل نوع المنشور أو اسمه. من الأفضل أيضًا تعطيل أيّة إضافات للتخزين المؤقت مثل W3Cache والتي فعّلتَها مسبقًا لإظهار التعديلات التي أجريتها. الخطأ الشائع الثامن: الإبقاء على شريط الإدارة لماذا لا أترك شريط الإدارة الموجود أعلى الصفحات ظاهرًا لجميع المستخدمين؟ حسنًا، يمكنك السماح لمستخدميك بالوصول إلى الصفحات الإدارية، لكن ذاك الشريط لن يندمج بشكلٍ جميل مع القالب المختار. إذا أردتَ أن يبدو موقعك احترافيًا، فعليك تعطيل شريط الإدارة وتوفير صفحة مخصصة لإدارة الحساب: add_action('after_setup_theme', 'remove_admin_bar'); function remove_admin_bar() { if (!current_user_can('administrator') && !is_admin()) { show_admin_bar(false); } } ستؤدي الشيفرة السابقة عند إضافتها إلى ملف functions.php (التابع للقالب الذي تستعمله) إلى إظهار شريط الإدارة لمدراء الموقع فقط. يمكنك إضافة أيّة أنواع من المستخدمين إلى دالة current_user_can($capability) في الشيفرة السابقة لعدم إخفاء الشريط لهم. الخطأ الشائع التاسع: عدم استخدام المرشِّح GetText أستطيع استخدام CSS أو JavaScript لتغيير لافتة أحد الأزرار، ما المانع؟ حسنًا، أنت تضيف شيفرات عجيبة وتضيع وقتك سدى لتغيير تلك اللافتة، في حين يمكنك استعمال أحد أجمل المرشِّحات في ووردبريس المدعو gettext. وعند استخدامه مع textdomain التابع للإضافة (وهو مُعرِّف فريد الذي يضمن أن تستطيع ووردبريس التفريق بين جميع الترجمات المتوافرة)، فيمكننا حينها استعمال المرشح gettext لتعديل النص قبل توليد الصفحة. إذا بحثت في الشيفرة المصدرية عن الدالة load_plugin_textdomain($domain)فستجد اسم المجال (domain name) الذي ستحتاج له لتعديل النص. جميع الإضافات الاحترافية تضبط textdomain أثناء تهيئة الإضافة. أما إذا كان لديك نصٌ ما ضمن قالب وكنت تريد تغييره، فابحث عن load_theme_textdomain($domain). سنستعمل هنا إضافة WooCommerce مرةً أخرى كمثال، يمكننا تغيير النص الظاهر لترويسة «Related Products» بإضافة الشيفرة الآتية في ملف functions.php في قالبك: function translate_string( $translated_text, $untranslated_text, $domain ) { if ( $translated_text == 'Related Products') { $translated_text = __( 'Other Great Products', 'woocommerce' ); } return $translated_text; } add_filter( 'gettext', 'translate_string', 15, 3 ); سيُطبَّق المرشِّح السابق على النص المُترجَم عبر دوال ‎__()‎ و ‎_e()‎، لطالما كان مجال النص (textdomain) مضبوطٌ ضبطًا صحيحًا أثناء استخدام تلك الدوال: _e( 'Related Products', 'woocommerce' ); ابحث في إضافتك عن دوال ترجمة النص لمعرفة ما هي السلاسل النصية التي يمكنك تخصيصها. الخطأ الشائع العاشر: الإبقاء على بنية الروابط الدائمة الافتراضية تستخدم ووردبريس افتراضيًا بنية روابط دائمة (permalinks) تتضمن مُعرِّف (ID) المنشور لإظهار محتوى معيّن، لكن هذه البنية ليست جميلة ومستحيلة القراءة، وقد يحذف الزوار أجزاءً مهمةً من روابط URL أثناء نسخها. وأهم ما في الأمر أنَّ الروابط الدائمة الافتراضية ليست «صديقةً» لمحركات البحث. وتفعيل ما ندعوه «الروابط الدائمة الجميلة» سيؤدي إلى تضمين كلمات مفتاحية واسم المنشور في روابط URL مما يُحسِّن من تقييم محركات البحث لموقعنا. يمكن أن يكون تغيير بنية الروابط مهمةً شاقةً خصوصًا إذا كان موقعك يعمل منذ فترةٍ طويلةٍ ولديك مئات المنشورات التي تمت فهرستها من محركات البحث. لذا بعد أن تثبِّت ووردبريس فاحرص على تعديل بنية الروابط الدائمة ليستطيع الزائر معرفة محتوى الصفحة من رابطها بدلًا من عرض معرِّف المنشور فقط. عمومًا أستخدمُ اسم المنشور في الروابط الدائمة لأغلبية المواقع التي أبنيها، لكن يمكننا تخصيص الروابط الدائمة كيفما شئت، انظر إلى مقالة «الدليل الشامل للروابط الدائمة في ووردبريس». الخلاصة لم تضم هذه المقالة جميع الأخطاء التي يرتكبها مطورو ووردبريس (حتى الخبراء منهم)، الفكرة الأساسية التي عليك أن تفهمها من هذه المقالة هي أنَّك عليك ألّا تأخذ الطرق المختصرة (وهذه القاعدة تنطبق أيضًا على جميع منصات التطوير، ولا أقصد بها ووردبريس بعينها). الوقت الذي توفره باتباعك للأساليب البرمجية الضعيفة ستنفق أضعافه وأنت تحاول إصلاح المشاكل التي ستواجهك مستقبلًا. شاركنا بعض الأخطاء التي كنت تقع فيها وأخبرنا ما هي الدروس التي تعلمتها منها في التعليقات. ترجمة –وبتصرّف– للمقال The 10 Most Common Mistakes That WordPress Developers Make لصاحبه Andrew Schultz
  16. سنشرح في هذه الدرس كيفية تقليل عدد السجلات المُعادة من طلبية SELECT، وطريقة ترتيب الناتج بناءً على شرطٍ معيّن. إضافةً إلى ما سبق، سنتعلم كيفية تجميع السجلات وإجراء عمليات حسابية أساسية على الحقول الرقمية، وكل ما سبق سيساعدنا على إنشاء سكربت SQL الذي سنستخدمه لإنشاء تقارير مفيدة. إذا لم تكن لديك خلفية جيّدة حول قواعد بيانات MySQL فأنصحك بقراءة هذا الدّرس أوّلا المتطلبات المسبقة عليك اتباع الخطوات الآتية قبل المتابعة: نزِّل قاعدة بيانات employees، التي تحتوي على ستة جداول تتضمن حوالي 4 ملايين سجل إجماليًا: # wget https://launchpad.net/test-db/employees-db-1/1.0.6/+download/employees_db-full-1.0.6.tar.bz2 # tar xjf employees_db-full-1.0.6.tar.bz2 # cd employees_db ادخل إلى مِحَث MariaDB وأنشِئ قاعدة بيانات باسم employees: # mysql -u root -p Enter password: Welcome to the MariaDB monitor. Commands end with ; or \g. Your MariaDB connection id is 2 Server version: 10.1.14-MariaDB MariaDB Server Copyright (c) 2000, 2016, Oracle, MariaDB Corporation Ab and others. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. MariaDB [(none)]> CREATE DATABASE employees; Query OK, 1 row affected (0.00 sec) استورد قاعدة البيانات إلى خادوم MairaDB كالآتي: MariaDB [(none)]> source employees.sql انتظر دقيقةً أو دقيقتين إلى أن ينتهي استيراد قاعدة البيانات (أبقِ في بالك أننا نتحدث هنا عن أربعة ملايين سجل!). تأكد من أنَّ استيراد قاعدة البيانات قد تمَّ بشكلٍ صحيح بعرض جداولها: MariaDB [employees]> USE employees; Database changed MariaDB [employees]> SHOW TABLES; +---------------------+ | Tables_in_employees | +---------------------+ | departments | | dept_emp | | dept_manager | | employees | | salaries | | titles | +---------------------+ 6 rows in set (0.02 sec) أنشِئ حسابًا مخصصًا لاستخدام قاعدة بيانات employees (اختر اسمًا وكلمة مرور): MariaDB [employees]> CREATE USER empadmin@localhost IDENTIFIED BY 'empadminpass'; Query OK, 0 rows affected (0.03 sec) MariaDB [employees]> GRANT ALL PRIVILEGES ON employees.* to empadmin@localhost; Query OK, 0 rows affected (0.02 sec) MariaDB [employees]> FLUSH PRIVILEGES; Query OK, 0 rows affected (0.00 sec) MariaDB [employees]> exit Bye سجِّل دخولك الآن إلى مِحَث MariaDB بالمستخدم empadmin: # mysql -u empadmin -p Enter password: Welcome to the MariaDB monitor. Commands end with ; or \g. Your MariaDB connection id is 4 Server version: 10.1.14-MariaDB MariaDB Server Copyright (c) 2000, 2016, Oracle, MariaDB Corporation Ab and others. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. MariaDB [(none)]> USE employees; Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Database changed احرص على تنفيذ جميع الخطوات الست السابقة قبل إكمالك لقراءة هذا الدرس. ترتيب وتقليل عدد السجلات المعروضة جدول الرواتب salaries يحتوي على جميع واردات كل موظف مع تاريخ البداية والنهاية. ربما تود أن تعرض رواتب الموظف emp_no=10001 خلال فترةٍ من الزمن. وهذا سيساعدك على الإجابة عن التساؤلات الآتية: هل حصل على زيادة في راتبه؟ إذا حدث ذلك، فمتى؟ نفِّذ الأمر الآتي لمعرفة ذلك: MariaDB [employees]> SELECT * FROM salaries WHERE emp_no=10001 ORDER BY from_date; +--------+--------+------------+------------+ | emp_no | salary | from_date | to_date | +--------+--------+------------+------------+ | 10001 | 60117 | 1986-06-26 | 1987-06-26 | | 10001 | 62102 | 1987-06-26 | 1988-06-25 | | 10001 | 66074 | 1988-06-25 | 1989-06-25 | | 10001 | 66596 | 1989-06-25 | 1990-06-25 | | 10001 | 66961 | 1990-06-25 | 1991-06-25 | | 10001 | 71046 | 1991-06-25 | 1992-06-24 | | 10001 | 74333 | 1992-06-24 | 1993-06-24 | | 10001 | 75286 | 1993-06-24 | 1994-06-24 | | 10001 | 75994 | 1994-06-24 | 1995-06-24 | | 10001 | 76884 | 1995-06-24 | 1996-06-23 | | 10001 | 80013 | 1996-06-23 | 1997-06-23 | | 10001 | 81025 | 1997-06-23 | 1998-06-23 | | 10001 | 81097 | 1998-06-23 | 1999-06-23 | | 10001 | 84917 | 1999-06-23 | 2000-06-22 | | 10001 | 85112 | 2000-06-22 | 2001-06-22 | | 10001 | 85097 | 2001-06-22 | 2002-06-22 | | 10001 | 88958 | 2002-06-22 | 9999-01-01 | +--------+--------+------------+------------+ 17 rows in set (0.03 sec) لكن ماذا لو أردنا معرفة آخر خمس زيادات؟ يمكننا أن نستخدم ORDER BY form_date DESC. تُشير الكلمة المحجوزة DESC إلى أنَّ الترتيب الذي نريد اتباعه هو الترتيب التنازلي. إضافةً إلى ما سبق، يسمح لنا التعبير LIMIT 5 بإعادة أوّل خمسة سجلات: MariaDB [employees]> SELECT * FROM salaries WHERE emp_no=10001 ORDER BY from_date DESC LIMIT 5; +--------+--------+------------+------------+ | emp_no | salary | from_date | to_date | +--------+--------+------------+------------+ | 10001 | 88958 | 2002-06-22 | 9999-01-01 | | 10001 | 85097 | 2001-06-22 | 2002-06-22 | | 10001 | 85112 | 2000-06-22 | 2001-06-22 | | 10001 | 84917 | 1999-06-23 | 2000-06-22 | | 10001 | 81097 | 1998-06-23 | 1999-06-23 | +--------+--------+------------+------------+ 5 rows in set (0.00 sec) يمكنك أيضًا استخدام ORDER BY على عدِّة حقول، فمثلًا ستُرتِّب الطلبية الآتية النتائج اعتمادًا على تاريخ ولادة الموظف تصاعديًا (وهو الترتيب الافتراضي) ثم عبر اسم الموظف الأخير بترتيبٍ هجائيٍ تنازلي: MariaDB [employees]> SELECT CONCAT(last_name, ', ', first_name) AS Name, gender AS Gender, hire_date AS "Hire date" FROM employees ORDER BY birth_date, last_name DESC LIMIT 10; +--------------------+--------+------------+ | Name | Gender | Hire date | +--------------------+--------+------------+ | Whitcomb, Kiyokazu | M | 1988-07-26 | | Schaad, Ronghao | M | 1988-07-10 | | Remmele, Supot | M | 1989-01-27 | | Pocchiola, Jouni | M | 1985-03-10 | | Kuzuoka, Eishiro | M | 1992-02-12 | | Decaestecker, Moni | M | 1986-10-06 | | Wiegley, Mircea | M | 1985-07-18 | | Vendrig, Sachar | M | 1985-11-04 | | Tsukuda, Cedric | F | 1993-12-12 | | Tischendorf, Percy | M | 1986-11-10 | +--------------------+--------+------------+ 10 rows in set (0.31 sec) يمكنك معرفة المزيد من المعلومات عن LIMIT في الدليل الرسمي. تجميع السجلات، واستخدام MAX و MIN و AVG و ROUND كما ذكرنا سابقًا، الجدول salaries يحتوي على رواتب الموظفين خلال فترات زمنية، وبجانب استخدام LIMIT، يمكننا استخدام الكلمتين المحجوزتين MAX و MIN لتحديد متى كان أعلى راتب وأدنى راتب لموظفين معينين: MariaDB [employees]> SELECT CONCAT(last_name, ', ', first_name) AS Name, MAX(B.salary) AS "Max. salary" FROM employees A JOIN salaries B ON A.emp_no = B.emp_no WHERE A.emp_no IN (10001, 10002, 10003) GROUP BY A.emp_no; +-----------------+-------------+ | Name | Max. salary | +-----------------+-------------+ | Facello, Georgi | 88958 | | Simmel, Bezalel | 72527 | | Bamford, Parto | 43699 | +-----------------+-------------+ 3 rows in set (0.02 sec) MariaDB [employees]> SELECT CONCAT(last_name, ', ', first_name) AS Name, MIN(B.salary) AS "Min. salary" FROM employees A JOIN salaries B ON A.emp_no = B.emp_no WHERE A.emp_no IN (10001, 10002, 10003) GROUP BY A.emp_no; +-----------------+-------------+ | Name | Min. salary | +-----------------+-------------+ | Facello, Georgi | 60117 | | Simmel, Bezalel | 65828 | | Bamford, Parto | 40006 | +-----------------+-------------+ 3 rows in set (0.00 sec) وبناءً على النتائج السابقة، هل تستطيع أن تعرف ماهو ناتج الطلبية الآتية: MariaDB [employees]> SELECT CONCAT(last_name, ', ', first_name) AS Name, ROUND(AVG(B.salary), 2) AS "Avg. salary" FROM employees A JOIN salaries B ON A.emp_no = B.emp_no WHERE A.emp_no IN (10001, 10002, 10003) GROUP BY A.emp_no; +-----------------+-------------+ | Name | Avg. salary | +-----------------+-------------+ | Facello, Georgi | 75388.94 | | Simmel, Bezalel | 68854.50 | | Bamford, Parto | 43030.29 | +-----------------+-------------+ 3 rows in set (0.01 sec) إذا توقعتَ أنَّ الناتج هو المتوسط الحسابي (عبر AVG) للراتب مقرّبًا إلى منزلتين عشريتين (عبر ROUND)، فأنت مصيب. إذا أردنا أن نعرف مجموع رواتب الموظفين وإعادة أوّل خمسة، فيمكننا حينها استعمال الطلبية الآتية: MariaDB [employees]> SELECT emp_no, SUM(salary) AS Salary FROM salaries GROUP BY emp_no ORDER BY Salary DESC LIMIT 5; +--------+---------+ | emp_no | Salary | +--------+---------+ | 109334 | 2553036 | | 43624 | 2492873 | | 66793 | 2383923 | | 237542 | 2381119 | | 47978 | 2374024 | +--------+---------+ 5 rows in set (2.22 sec) جمّعنا الرواتب في الطلبية السابقة عبر الموظف، ثم أجرينا عليها عملية SUM. تجميع المعلومات السابقة في سكربت لحسن الحظ، لا نحتاج إلى تشغيل طلبية تلو أخرى لإنشاء تقرير، وإنما نستطيع إنشاء سكربت فيه سلسلة من تعليمات SQL التي تُعيد جميع النتائج المطلوبة. بعد تنفيذ السكربت، فستظهر جميع النتائج دون تدخل منا، ولنسمِّ الملف باسم maxminavg.sql في مجلد العمل الحالي وفيه المحتويات الآتية: --Select database USE employees; --Calculate maximum salaries SELECT CONCAT(last_name, ', ', first_name) AS Name, MAX(B.salary) AS "Max. salary" FROM employees A JOIN salaries B ON A.emp_no = B.emp_no WHERE A.emp_no IN (10001, 10002, 10003) GROUP BY A.emp_no; --Calculate minimum salaries SELECT CONCAT(last_name, ', ', first_name) AS Name, MIN(B.salary) AS "Min. salary" FROM employees A JOIN salaries B ON A.emp_no = B.emp_no WHERE A.emp_no IN (10001, 10002, 10003) GROUP BY A.emp_no; --Calculate averages, round to 2 decimal places SELECT CONCAT(last_name, ', ', first_name) AS Name, ROUND(AVG(B.salary), 2) AS "Avg. salary" FROM employees A JOIN salaries B ON A.emp_no = B.emp_no WHERE A.emp_no IN (10001, 10002, 10003) GROUP BY A.emp_no; الأسطر التي تبدأ بشرطتين -- هي تعليقات وسيتم تجاهلها. وستُنفَّذ الطلبيات تلو بعضها. ويمكننا تنفيذ هذا السكربت إما من سطر أوامر لينكس: # mysql -u empadmin -p < maxminavg.sql Enter password: Name Max. salary Facello, Georgi 88958 Simmel, Bezalel 72527 Bamford, Parto 43699 Name Min. salary Facello, Georgi 60117 Simmel, Bezalel 65828 Bamford, Parto 40006 Name Avg. salary Facello, Georgi 75388.94 Simmel, Bezalel 68854.50 Bamford, Parto 43030.29 أو من مِحث MariaDB: # mysql -u empadmin -p Enter password: Welcome to the MariaDB monitor. Commands end with ; or \g. Your MariaDB connection id is 4 Server version: 10.1.14-MariaDB MariaDB Server Copyright (c) 2000, 2016, Oracle, MariaDB Corporation Ab and others. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. MariaDB [(none)]> source maxminavg.sql Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Database changed الخلاصة شرحنا في هذه المقالة كيفية استخدام عدِّة دوال في MariaDB لتحسين النتائج التي يُخرجها الاستعلام SELECT. وبعد أن تكتب الاستعلامات لأول مرة، فيمكنك بكل بساطة وضعها داخل سكربت وتنفيذها بسهولة مما سيُقلِّل من الخطأ البشري عند كتابتها. ترجمة -وبتصرّف- للمقال Learn How to Use Several Functions of MySQL and MariaDB – Part 2 لصاحبه Gabriel Cánepa.
  17. تُحدِّد خصائص مصفوفات RAID بطريقة الضبط والعلاقة بين الأقراص، وهذا يُسمى «مستوى RAID» ‏(RAID level). أكثر مستويات RAID شيوعًا هي: RAID 0 يدمج مستوى RAID 0 بين جهازين أو أكثر عبر توزيع البيانات عليها، وكما ذكرنا في الدرس السابق (مقدمة إلى اصطلاحات ومفاهيم RAID ) ، التوزيع (striping) هو الآلية التي تُقسِّم البيانات إلى أجزاء (chunks) ثم تكتب تلك الأجزاء بشكلٍ تناوبي على كل قرص في المصفوفة. الفائدة من اتباع هذه الطريقة هي أنَّ البيانات موزَّعة، ويمكن الاستفادة من كل قرص لإجراء عمليات القراءة والكتابة؛ وبالتالي سيكون الأداء النظري لمصفوفة RAID 0 هو حاصل ضرب أداء أحد الأقراص بالعدد الكلي لها (الأداء العملي والحقيقي سيكون أقل من ذلك). ميزة أخرى هي أنَّ المساحة القابلة للاستخدام من المصفوفة هي مجموع مساحات الأقراص المُشكِّلة لها. صحيحٌ أنَّ هذا المستوى يوفِّر أداءً رائعًا، إلا أنَّه يعاني من سلبيات ذات أهمية كبيرة. فلمّا كانت البيانات ستُقسَّم بين عدد من الأقراص في المصفوفة، فإن فشل أحد الأجهزة سيؤدي إلى توقف كامل المصفوفة عن العمل وستفقد جميع البيانات. وعلى النقيض من معظم مستويات RAID الأخرى، لا نستطيع إعادة بناء مصفوفات RAID 0، إذ لا توجد مجموعة من الأقراص التي تحتوي معلومات كافية عن محتوى المصفوفة للمساعدة في إعادة بنائها. إذا كنتَ ستستخدم مصفوفات RAID 0، فسيصبح أخذ نسخ احتياطية أمرًا شديد الأهمية، حيث ستفشل المصفوفة في حال فشل أحد الأقراص المكوِّنة لها. RAID 1 مستوى RAID 1 هو المستوى الذي يُنشِئ نسخةً انعكاسيةً بين جهازَين أو أكثر. أي أنَّ كل المعلومات التي تُكتَب على المصفوفة ستُكتَب بدورها مرتين في كلا القرصين المشكلَين للمجموعة. وهذا يعني أنَّ كل قرص سيحتوي على كامل البيانات الموجودة في المصفوفة، وهذا يوفِّر قدرةً تعويضيةً (redundancy) في حال فشل أحد الأقراص. ستتمكن من الوصول إلى بياناتك في مصفوفات RAID 1 في حال بقي قرصٌ وحيدٌ في المصفوفة سليمًا ويعمل دون مشاكل، ويمكن إعادة بناء المصفوفة باستبدال الأقراص التالفة، ومن ثم ستُستخدَم بقية الأقراص السليمة لنسخ البيانات إلى القرص الجديد. هنالك بعض المساوئ لهذا الضبط، فسرعة القراءة النظرية –كما في مستوى RAID 0– يمكن أن تُحسَب بضرب سرعة القراءة لأحد الأقراص بعددها. لكن سرعة الكتابة النظرية القصوى هي سرعة أبطأ قرص في المصفوفة، وذلك لأنَّ كل البيانات يجب أن تُكتَب على كل قرص في المصفوفة. إضافةً إلى ما سبق، المساحة التخزينية الكلية للمصفوفة ستكون مساويةً لمساحة أصغر قرص، لذا إذا كانت لدينا مصفوفة RAID 1 فيها قرصين لهما نفس القدرة التخزينية، فستكون القدرة التخزينية الإجمالية للمصفوفة مساوية لمساحة أحدهما. وإضافة أقراص أخرى سيزيد من عدد النسخ التعويضية للبيانات، ولن يزيد في المساحة المتوافرة. RAID 5 يملك RAID 5 بعض ميزات مستويي RAID السابقَين، لكن له أداءٌ مختلفٌ وسلبياتٌ مختلفة. ففي مستوى RAID 5، ستوزَّع البيانات على الأقراص بنفس الطريقة التي يتبعها المستوى RAID 0، لكن لكل جزء من البيانات التي تُكتَب على المصفوفة فستُكتَب معلومات parity على أحد الأقراص، والتي هي قيمةٌ محسوبةٌ رياضيًا التي تُستخدَم لتصحيح الأخطاء وإعادة بناء معلومات المصفوفة. سيتم تغيير القرص الذي سيستلم معلومات parity المحسوبة (بدلًا من البيانات الحقيقية) بعد كتابة كل جزء من البيانات. لهذا المستوى بعض المزيات المهمة. فمثل بقية المستويات التي توزِّع البيانات على أكثر من قرص، فإن أداء القراءة سيزداد نتيجةً للقدرة على القراءة من عدِّة أقراص معًا. ويمكن لمصفوفات RAID 5 أن تعمل حتى لو فشل أحد الأقراص في المصفوفة. حيث ستسمح معلومات parity بإكمال إعادة بناء البيانات إن حدث ذلك؛ وذلك لأنَّ معلومات parity موزعة (بعض مستويات RAID الأقل شيوعًا تستخدم قرصًا مخصصًا لمعلومات parity)، حيث يملك كل قرصٍ قدرًا متساويًا من معلومات parity. وصحيحٌ أنَّ المساحة التخزينية القابلة للاستخدام في مصفوفات RAID 1 مساوية لمساحة أحد الأقراص (وذلك لأنَّ جميع الأقراص فيها نسخ متماثلة من البيانات)، لكن في RAID 5، يمكن تحقيق القدرة التعويضية بالاستغناء عن المساحة التخزينية لقرصٍ وحيد، فمثلًا إذا كان لدينا أربعة أقراص 100G في مصفوفة RAID 5 فسينتج عنها مساحة تخزينية تساوي 300G (أما 100G الضائعة فستُستَخدم لتخزين معلومات parity). وكما في المستويات الأخرى، هنالك سلبياتٌ لمصفوفات RAID 5 التي يجب أخذها بعين الاعتبار. فقد تؤدي إلى تقليل أداء النظام نتيجةً لحساب معلومات parity ديناميكيًا طوال الوقت، وهذا قد يؤثر على الأداء عند كل عملية كتابة على المصفوفة. وإذا فشل أحد الأقراص في المصفوفة وأصبحت المصفوفة ذات حالة منخفضة، فسيؤدي ذلك أيضًا إلى بطئ شديد عند القراءة منها (لأنَّ البيانات الناقصة ستُحسَب من بقية الأقراص). بالإضافة إلى ذلك، عند محاولة إصلاح المصفوفة بعد استبدال القرص التالف، فيجب قراءة كل قرص بأكمله واستخدام المعالج لحساب البيانات الناقصة لإعادة بناء المصفوفة. وهذا قد يُجهِد بقية الأقراص، مما يؤدي أحيانًا إلى فشلٍ إضافيٍ في أحد الأقراص، مما يؤدي في النهاية إلى فقدان البيانات. RAID 6 يَستخدم مستوى RAID 6 بنيةً قريبةً من مستوى RAID 5، لكن مع مضاعفة كمية معلومات parity. هذا يعني أنَّ المصفوفة ستصمد حتى لو حدث عطب بقرصين. وهذه ميزة مهمة جدًا لزيادة احتمال حدوث عطب آخر أثناء عملية إعادة بناء المصفوفة عند حدوث خطأ. وفي هذا المستوى –كغيره من المستويات التي تستخدم التوزيع– سيكون أداء القراءة جيد إجمالًا. وجميع ميزات RAID 5 تنطبق أيضًا على RAID 6. أما مساوئ هذا المستوى، فهي استخدام مساحة تخزينية أكبر لمعلومات parity، وهذا يعني أنَّ المساحة الإجمالية للمصفوفة هي مجموع مساحات جميع الأقراص المكوِّنة لها منقوصًا منها مساحة قرصين. إضافةً إلى أنَّ العمليات الحسابية اللازمة لإنشاء معلومات parity لمستوى RAID 6 أكثر تعقيدًا من RAID 5، مما يعني أداءً أسوأ للكتابة مقارنةً مع RAID 5. يعاني مستوى RAID 6 من نفس المشاكل التي تحدث في RAID 5 عندما تصبح المصفوفة بحالة منخفضة، لكن وجود قرص إضافي للتعويض سيقل احتمال حدوث مشاكل إضافية تؤدي إلى حذف جميع البيانات عند عمليات إعادة البناء. RAID 10 يمكن تطبيق مستوى RAID 10 بعدِّة طرائق، والتي ستؤثِّر على خصائصه: مستويا 1 و 0 متشعبان تقليديًا، يُشير مستوى RAID 10 إلى مستوى متشعب، والذي يُنشَأ بضبط مصفوفتَي RAID 1 أو أكثر أولًا، ثم استخدام تلك المصفوفات كمكونات لإنشاء مصفوفة RAID 0 موزّعة. ويدعى هذا المستوى حاليًا أحيانًا باسم RAID 1+0 للإشارة إلى هذه العلاقة. وبسبب تصميم هذا المستوى، فإن العدد الأدنى للأقراص هو أربعة وذلك لإنشاء مصفوفة RAID 1+0 (أي مستوى RAID 0 يستعمل مصفوفتَي RAID 1 تتألف كلٌ منهما على قرصين). تملك مصفوفات RAID 1+0 أداءً عاليًا شبيهًا بمصفوفات RAID 0، لكن بدلًا من الاعتماد على أقراص مفردة لتوزيع البيانات، فسيتم استخدام مصفوفة منسوخة نسخًا انعكاسيًا (mirrored array)، مما يعني توفير قدرة تعويضية. يمكن أن يتحمل هذا النوع من الضبط أيّة أخطاء ومشاكل في الأقراص لطالما بقي قرصٌ وحيدٌ يعمل في كل مصفوفة RAID 1 مُشكِّلة للمصفوفة النهائية. يمكن أن يتحمل هذا المستوى عددًا مختلفًا من المشاكل اعتمادًا على مكان وقوعها. ولأنَّ مصفوفات RAID 1+0 توفِّر أداءً عاليًا وقدرةً تعويضيةً، فهي خيارٌ ممتازٌ إن لم تكن مقيدًا بعددٍ معيّنٍ من الأقراص. RAID 10 عبر mdadm لدى برمجية mdadm في لينكس نسخةٌ خاصةٌ بها من RAID 10، والتي تحمل نفس فوائد RAID 1+0 لكن تُغيّر في طريقة تنفيذها لكي تكون مرنةً أكثر وتوفِّر ميزاتٍ إضافيةٍ. وكما في مستوى RAID 1+0، مستوى RAID 10 في mdadm يسمح بتوزيع البيانات ونسخها أكثر من مرة. لكن الأقراص لن تُرتَّب كمجموعتَين منسوختين نسخًا انعكاسيًا، وإنما سيقرِّر مدير النظام عدد النسخ التي ستُكتَب على المصفوفة. ستُجزَّأ البيانات وتُكتَب على المصفوفة بأكثر من نسخة، مع الحرص على أن تُكتَب كل نسخة من البيانات على قرصٍ فيزيائيٍ منفصل. والنتيجة النهائية هو وجود نفس العدد من النسخ، لكن طريقة بناء المصفوفة تختلف (أي لن يكون فيها «تشعب»). هذا الضبط لمستوى RAID 10 له ميزات تجعله متفوقًا عن RAID 1+0، فلأنه لا يعتمد على تشعّب المصفوفات فذلك يعني أننا نستطيع استخدام رقم فردي من الأقراص ويمكن أيضًا تقليل عدد الأقراص الأدنى (ليصبح 3 فقط). يمكن أيضًا ضبط عدد النسخ. وستصبح الإدارة أبسط لأنك ستحتاج إلى إدارة مصفوفة وحيدة، ويمكنك استخدام أقراص بديلة (كقطع غيار) لكامل المصفوفة، بدلًا من إمكانية استخدامها لمصفوفة متشعبة وحيدة. الخلاصة أكثر مستوى من مستويات RAID يناسب خادومك يعتمد تمامًا على حالات استخدامك له وعلى هدفك من المصفوفة. التكلفة والقيود التي يضعها العتاد سيؤثران أيضًا على عملية تقرير مستوى RAID المناسب. ترجمة -وبتصرّف- للمقال An Introduction to RAID Terminology and Concepts لصاحبه Justin Ellingwood
  18. سنشرح في هذا الدرس كيفية إنشاء قاعدة بيانات، وجداول (مع تبيان أنواع بيانات حقولها)، وسنفصّل طريقة إجراء عمليات على البيانات على خادوم MySQL أو MariaDB. سنفترض أنَّك قد ثبّتَ الحزم اللازمة على نظامك، ونفّذتَ الأمر mysql_secure_installation لتحسين حماية خادوم قواعد البيانات. وإلا فانظر إلى درسنا عن كيفية تثبيت خادوم MySQL/MariaDB . للاختصار، سنشير إلى قواعد البيانات باسم «MariaDB» في هذا الدرس، لكن نفس الاصطلاحات والتعليمات المشروحة هنا ستنطبق تمامًا على MySQL. إنشاء قواعد البيانات والجداول والمستخدمين كما تعلم، يمكن تعريف قواعد البيانات بأبسط الكلمات على أنها مجموعة منظمة من المعلومات؛ وندعو قواعد MariaDB تحديدًا أنَّها نظام إدارة قواعد بيانات علائقية (relational database management system اختصارًا RDBMS) التي تستخدم لغة الاستعلام البنيوية (Structure Query Language أي SQL) لإجراء العمليات على قواعد البيانات. أبقِ في بالك أنَّ MariaDB تستعمل الاصطلاحين «database» (قاعدة بيانات) و «schema» (مخطط) لنفس المعنى تمامًا. نستعمل الجداول (tables) لتخزين المعلومات الدائمة في قواعد البيانات التي تُخزَّن فيها «سجلاتٌ» (rows) من البيانات. وعادةً يرتبط جدولان أو أكثر مع بعضها بطريقةٍ ما. وهذا هو جزءٌ مهمٌ من عملية التنظيم التي تُميّز قواعد البيانات العلائقية. إنشاء قاعدة بيانات جديدة علينا أن ندخل أولًا إلى مِحَث (prompt) قواعد بيانات MariaDB بإدخال الأمر الآتي، وذلك لإنشاء قاعدة بيانات جديدة (سيُطلَب منك إدخال كلمة مرور المستخدم root لقواعد البيانات): [root@TecMint ~]# mysql -u root -p Enter password: Welcome to the MariaDB monitor. Commands end with ; or \g. Your MariaDB connection id is 2 Server version: 10.1.14-MariaDB MariaDB Server Copyright (c) 2000, 2016, Oracle, MariaDB Corporation Ab and others. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. MariaDB [(none)]> CREATE DATABASE BookstoreDB; Query OK, 1 row affected (0.00 sec) MariaDB [(none)]> بعد إنشاء قاعدة البيانات، فسنحتاج إلى إنشاء جدولين على الأقل فيها، لكن دعنا أولًا نلقي نظرةً على مفهوم أنواع البيانات في الجداول. لمحة عن أنواع البيانات في قواعد MariaDB كما ذكرنا سابقًا، الجداول هي الكائن الرئيسية في قواعد البيانات والتي ستُخزَّن فيها المعلومات الدائمة. يتألف كل جدول من حقلين أو أكثر (تُعرَف أيضًا بالأعمدة [columns]) لها نوع بيانات معيّن (أي نوع المعلومات التي ستُخزَّن فيها) التي يستطيع كل حقلٍ (أو عمودٍ) تخزينها. سأستعرض أشهر أنواع البيانات في MariaDB (يمكنك الرجوع إلى الدليل الرسمي لتحصل على قائمةٍ كاملةٍ): أنواع البيانات الرقمية BOOLEAN: قيمٌ منطقيةٌ، وتعتبَر فيها القيم المساوية للصفر (0) على أنَّها false، وبقية القيم true. TINYINT: إذا استخدمَت مع الكلمة المحجوزة SIGNED فستُمثِّل المجال من ‎-128 إلى 127، بينما مجالها إن كانت UNSIGNED فهو من 0 إلى 255. SMALLINT: إذا استخدمت مع SIGNED فهي تمثِّل المجال من ‎-32768 إلى 32767. بينما مجال UNSIGNED فهو من 0 إلى 65535. INT: إذا استخدمت مع SIGNED فهي تغطي المجال من 0 إلى 4294967295، ومن ‎‎-2147483648 إلى 2147483647 فيما عدا ذلك. ملاحظة: في أنواع البيانات TINYINT و SMAILLINT و INT، القيمة الافتراضية المرتبطة معها هي SIGNED. DOUBLE(M, D): يُمثِّل هذا النوع الأعداد العشرية ذات الفاصلة، حيث M هو العدد الكلي للأرقام و D هو عدد الأرقام بعد الفاصلة العشرية. إذا اُستعمِلَت الكلمة UNSIGND فلن يُسمَح بإدراج قيم سالبة في هذا الحقل. أنواع البيانات النصية VARCHAR(M): يُمثِّل هذا النوع السلاسل النصية ذات الطول المتغير حيث M هو عدد الأحرف المسموح الأقصى للسلسلة النصية مقدرًا بالبايت (يمكن أن يبلغ نظريًا 65535 بايت). في حال كانت السلاسل النصية المُدخَلة بالإنكليزية، فسيأخذ كل محرف بايتًا واحدًا، لكن هنالك استثناءٌ لبعض المحارف التي قد تأخذ 3 بايت لكي تُمثَّل، فمثلًا، الحرف الإسباني ñ لا يأخذ بايتًا واحدًا وإنما ثلاثة بايتات. TEXT(M): يُمثِّل هذا النوع الأعمدة ذات الطول الأقصى المقدّر 65535 محرفًا، لكن كما في نوع البيانات VARCHAR(M) سينقص طول التخزين الأقصى إذا استعملنا محارف تأخذ أكثر من بايت لتُمثَّل في الحاسوب. إذا حُدِّدَ M فسيُنشأ العمود بأصغر نوع بيانات التي يستطيع تخزين هذا العدد من المحارف. MEDIUMTEXT(M) و LONGTEXT(M) شبيهة بنوع البيانات TEXT(M) لكن الطول الأقصى المسموح هو 16777215 و 4294967295 محرفًا على التوالي وبالترتيب. الوقت والتاريخ DATE: تمثيل التاريخ بصيغة YYYY-MM-DD. TIME: تمثيل الوقت بصيغة HH:MM:SS.sss (أي الساعة والدقيقة والثانية والملي ثانية). DATETIME: جمعٌ بين DATE و TIME بالصيغة YYYY-MM-DD HH:MM:SS. TIMESTAMP: تُستعمَل بصمة الوقت لتعريف اللحظة التي أضيف أو حُدِّث فيها سجلٌ (row) ما. بعد اطلاعك على أنواع البيانات السابقة، فيمكن أن تستطيع الآن تحديد ما هو نوع البيانات الذي ستحتاج له لإسناده إلى عمودٍ معيّن في الجدول. على سبيل المثال، يمكن أن يتسع اسم المستخدم بسهولة في عمودٍ بنوع البيانات VARCHAR(50)، لكن محتوى تدوينة سيحتاج إلى نوع TEXT (اختر قيمة M بما يلبي احتياجاتك). إنشاء جداول فيها مفاتيح أساسية وأجنبية قبل أن نشرع في إنشاء جداول، فمن الضروري استيعاب مفهومين أساسيين عن قواعد البيانات العلائقية: المفتاح الأساسي (primary key) والمفتاح الأجنبي (foreign key). يحتوي المفتاح الأساسي قيمةً تُمثِّلُ مُعرِّفًا فريدًا لكل سطرٍ أو سجلٍ (row أو record) في الجدول، لكن في المقابل يستعمل المفتاح الأجنبي للربط بين البيانات في جدولين، وللتحكم في البيانات التي يمكن تخزينها في الجدول الذي يحتوي على المفتاح الأجنبي. يُعتَبَر المفتاحان الأساسي والأجنبي قيمًا رقميةً (INT) عادةً. لشرح هذا المفهوم عمليًا، فلنستخدم قاعدة بيانات BookstoreDB ونُنشِئ جدولين باسم AuthorsTBL و BooksTBL. تُشير NOT NULL إلى أنَّ الحقل المرتبط بها يتطلب قيمةً تختلف عن NULL. يمكن أيضًا استعمال AUTO_INCREMENT لزيادة قيمة عمود المفتاح الرئيسي الرقمي بمقدار 1 عند إضافة سجل جديد إلى الجدول. MariaDB [(none)]> USE BookstoreDB; Database changed MariaDB [BookstoreDB]> CREATE TABLE AuthorsTBL ( -> AuthorID INT NOT NULL AUTO_INCREMENT, -> AuthorName VARCHAR(100), -> PRIMARY KEY(AuthorID) -> ); Query OK, 0 rows affected (0.05 sec) MariaDB [BookstoreDB]> CREATE TABLE BooksTBL ( -> BookID INT NOT NULL AUTO_INCREMENT, -> BookName VARCHAR(100) NOT NULL, -> AuthorID INT NOT NULL, -> BookPrice DECIMAL(6,2) NOT NULL, -> BookLastUpdated TIMESTAMP, -> BookIsAvailable BOOLEAN, -> PRIMARY KEY(BookID), -> FOREIGN KEY (AuthorID) REFERENCES AuthorsTBL(AuthorID) -> ); Query OK, 0 rows affected (0.05 sec) MariaDB [BookstoreDB]> يمكننا الآن المتابعة وإضافة السجلات إلى جدولَي AuthorsTBL و BooksTBL. تحديد السجلات وإضافتها وتحديثها وحذفها علينا أولًا ملء الجدول AuthorsTBL، لكن لماذا؟ لأننا سنحتاج إلى وجود قيم في العمود AuthorID قبل إضافة سجلات إلى جدول BooksTBL. نفِّذ الأمر الآتي من مِحَث MariaDB: MariaDB [BookstoreDB]> INSERT INTO AuthorsTBL (AuthorName) VALUES ('Agatha Christie'), ('Stephen King'), ('Paulo Coelho'); بعدئذٍ سنُحدِّد جميع السجلات من جدول AuthorsTBL. تذكّر أننا نحتاج إلى AuthorID لكل سجل لإنشاء استعلام INSERT في جدول BooksTBL. إذا أردتَ الحصول على سجلٍ وحيدٍ فقط، فاستعمل عبارة WHERE لكتابة شرط الذي يجب أن تُحقِّقه السجلات المُعادة. على سبيل المثال: MariaDB [BookstoreDB]> SELECT * FROM AuthorsTBL WHERE AuthorName='Agatha Christie'; يمكنك عوضًا عن ذلك أن تحصل على جميع السجلات معًا: MariaDB [BookstoreDB]> SELECT * FROM AuthorsTBL; ناتج تنفيذ الطلبيتين السابقتين: MariaDB [BookstoreDB]> SELECT * FROM AuthorsTBL WHERE AuthorName='Agatha Christie'; +----------+-----------------+ | AuthorID | AuthorName | +----------+-----------------+ | 1 | Agatha Christie | +----------+-----------------+ 1 row in set (0.00 sec) MariaDB [BookstoreDB]> SELECT * FROM AuthorsTBL; +----------+-----------------+ | AuthorID | AuthorName | +----------+-----------------+ | 1 | Agatha Christie | | 2 | Stephen King | | 3 | Paulo Coelho | +----------+-----------------+ 3 rows in set (0.00 sec) MariaDB [BookstoreDB]> لنُنشِئ الآن استعلام INSERT لجدول BooksTBL، وذلك عبر استخدام حقل AuthorID المناسب لمُطابقة مؤلف كل كتاب. القيمة 1 في حقل BookIsAvaiable تعني توافر الكتاب، بينما 0 تعني عكس ذلك: MariaDB [BookstoreDB]> INSERT INTO BooksTBL (BookName, AuthorID, BookPrice, BookIsAvailable) -> VALUES ('And Then There Were None', 1, 14.95, 1), -> ('The Man in the Brown Suit', 1, 23.99, 1), -> ('The Stand', 2, 35.99, 1), -> ('Pet Sematary', 2, 17.95, 0), -> ('The Green Mile', 2, 29.99, 1), -> ('The Alchemist', 3, 25, 1), -> ('By the River Piedra I Sat Down and Wept', 3, 18.95, 0); Query OK, 7 rows affected (0.03 sec) Records: 7 Duplicates: 0 Warnings: 0 سنستخدم الآن SELECT لرؤية السجلات في جدول BooksTBL ثم سنُحدِّث عبر UPDATE سعر كتاب «The Alchemist» لمؤلفه Paulo Coelho ثم سنعرض السجلات (عبر SELECT) مرةً أخرى. لاحظ كيف يعرض الحقل BookLastUpdated قيمةً مختلفةً، وكما شرحنا سابقًا، الحقل TIMESTAMP يُخزِّن الوقت الذي أُنشِئ أو حُدِّث فيه السجل. MariaDB [BookstoreDB]> SELECT * FROM BooksTBL; +--------+-----------------------------------------+----------+-----------+---------------------+-----------------+ | BookID | BookName | AuthorID | BookPrice | BookLastUpdated | BookIsAvailable | +--------+-----------------------------------------+----------+-----------+---------------------+-----------------+ | 1 | And Then There Were None | 1 | 14.95 | 2016-10-01 23:31:41 | 1 | | 2 | The Man in the Brown Suit | 1 | 23.99 | 2016-10-01 23:31:41 | 1 | | 3 | The Stand | 2 | 35.99 | 2016-10-01 23:31:41 | 1 | | 4 | Pet Sematary | 2 | 17.95 | 2016-10-01 23:31:41 | 0 | | 5 | The Green Mile | 2 | 29.99 | 2016-10-01 23:31:41 | 1 | | 6 | The Alchemist | 3 | 25.00 | 2016-10-01 23:31:41 | 1 | | 7 | By the River Piedra I Sat Down and Wept | 3 | 18.95 | 2016-10-01 23:31:41 | 0 | +--------+-----------------------------------------+----------+-----------+---------------------+-----------------+ 7 rows in set (0.00 sec) MariaDB [BookstoreDB]> UPDATE BooksTBL SET BookPrice=22.75 WHERE BookID=6; Query OK, 1 row affected (0.04 sec) Rows matched: 1 Changed: 1 Warnings: 0 MariaDB [BookstoreDB]> SELECT * FROM BooksTBL WHERE BookID=6; +--------+---------------+----------+-----------+---------------------+-----------------+ | BookID | BookName | AuthorID | BookPrice | BookLastUpdated | BookIsAvailable | +--------+---------------+----------+-----------+---------------------+-----------------+ | 6 | The Alchemist | 3 | 22.75 | 2016-10-01 23:35:00 | 1 | +--------+---------------+----------+-----------+---------------------+-----------------+ 1 row in set (0.00 sec) MariaDB [BookstoreDB]> لنفترض أننا نريد حذف أحد السجلات الذي لم يعد مفيدًا، فمثلًا سنحذف كتاب «The Alchemist» من جدول BooksTBL. يمكنك استخدام عبارة DELETE لفعل ذلك كما يلي: MariaDB [BookstoreDB]> DELETE FROM BooksTBL WHERE BookID=6; وكما في عبارة UPDATE، من المستحسن إجراء عملية SELECT أولًا لرؤية الحقول التي ستؤثر فيها عملية الحذف. لا تنسَ أن تُضيف عبارة WHERE وشرطًا(BookID=6) لتحديد سجل مُعيّن لحذفه. وإلا فقد تصبح جميع سجلات جدولك معرضةً للحذف. إذا أردتَ دمج حقلين (أو أكثر) معًا، فيمكنك استخدام عبارة CONCAT، فلنقل على سبيل المثال أننا نريد عرض ناتج يحتوي على عمودٍ فيه اسم الكتاب والمؤلف على الشكل الآتي «The Alchemist (Paulo Coelho)» ثم يليه عمودٌ فيه سعره. هذا سيتطلب إجراء عملية JOIN بين جدولَي AuthorsTBL و BooksTBL في الحقل المشترك بينهما (ألا وهو AuthorID): MariaDB [BookstoreDB]> SELECT CONCAT(BooksTBL.BookName, ' (', AuthorsTBL.AuthorName, ')') AS Description, BooksTBL.BookPrice FROM AuthorsTBL JOIN BooksTBL ON AuthorsTBL.AuthorID = BooksTBL.AuthorID; وكما نرى، تسمح لنا عبارة CONCAT بدمج عدِّة تعبيرات نصية المفصول بينها بفاصلة. ستلاحظ أيضًا أننا استخدمنا اسمًا بديلًا وهو Description لعرض ناتج عملية دمج الحقلين. هذا هو ناتج تنفيذ الاستعلام السابقة: MariaDB [BookstoreDB]> SELECT CONCAT(BooksTBL.BookName, ' (', AuthorsTBL.AuthorName, ')') AS Description, BooksTBL.BookPrice FROM AuthorsTBL JOIN BooksTBL ON AuthorsTBL.AuthorID = BooksTBL.AuthorID; +--------------------------------------------------------+-----------+ | Description | BookPrice | +--------------------------------------------------------+-----------+ | And Then There Were None (Agatha Christie) | 14.95 | | The Man in the Brown Suit (Agatha Christie) | 23.99 | | The Stand (Stephen King) | 35.99 | | Pet Sematary (Stephen King) | 17.95 | | The Green Mile (Stephen King) | 29.99 | | The Alchemist (Paulo Coelho) | 25.00 | | By the River Piedra I Sat Down and Wept (Paulo Coelho) | 18.95 | +--------------------------------------------------------+-----------+ 7 rows in set (0.00 sec) إنشاء مستخدم للوصول إلى قاعدة بيانات BookstoreDB لا أنصحك باستخدام المستخدم root لإجراء جميع عمليات معالجة البيانات، ولتجنب ذلك سنُنِشئ حساب مستخدم جديد (سنسميه bookstoreuser) ونُسنِد إليه جميع الامتيازات اللازمة لإدارة قاعدة البيانات BookstoreDB: MariaDB [BookstoreDB]> CREATE USER bookstoreuser@localhost IDENTIFIED BY 'tecmint'; Query OK, 0 rows affected (0.00 sec) MariaDB [BookstoreDB]> GRANT ALL PRIVILEGES ON BookstoreDB.* to bookstoreuser@localhost; Query OK, 0 rows affected (0.00 sec) MariaDB [BookstoreDB]> FLUSH PRIVILEGES; Query OK, 0 rows affected (0.00 sec) وجود حساب مستخدم منفصل ومخصص لكل قاعدة بيانات سيحمي كامل الخادوم إن حدث اختراقٌ لأحد الحسابات. نصائح إضافية في قواعد MySQL لمسح الشاشة وإظهار مِحَث MariaDB فيها فقط، فأدخِل الأمر الآتي ثم اضغط على Enter: MariaDB [BookstoreDB]> \! clear نفِّذ الأمر الآتي لرؤية بنية أحد الجداول: MariaDB [BookstoreDB]> SHOW COLUMNS IN [TABLE NAME HERE ]; لعرض الأعمدة الموجودة في جدول BooksTBL في قاعدة بياناتنا: MariaDB [BookstoreDB]> SHOW COLUMNS IN BooksTBL; +-----------------+--------------+------+-----+-------------------+-----------------------------+ | Field | Type | Null | Key | Default | Extra | +-----------------+--------------+------+-----+-------------------+-----------------------------+ | BookID | int(11) | NO | PRI | NULL | auto_increment | | BookName | varchar(100) | NO | | NULL | | | AuthorID | int(11) | NO | MUL | NULL | | | BookPrice | decimal(6,2) | NO | | NULL | | | BookLastUpdated | timestamp | NO | | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP | | BookIsAvailable | tinyint(1) | YES | | NULL | | +-----------------+--------------+------+-----+-------------------+-----------------------------+ 6 rows in set (0.02 sec) تفحص سريع لبنية الجدول سيكشف أنَّ الحقل BookIsAvailable يقبل القيم NULL. ولعدم رغبتنا في ذلك، فسنُعدِّل (ALTER) الجدول كالآتي: MariaDB [BookstoreDB]> ALTER TABLE BooksTBL MODIFY BookIsAvailable BOOLEAN NOT NULL; (تستطيع الآن عرض بنية الجدول مرةً أخرى، وستجد أنَّ كلمة YES الموجودة في عمود Null أصبحت NO). أخيرًا، لعرض جميع قواعد البيانات في خادومك، تستطيع تنفيذ الأمر: MariaDB [BookstoreDB]> SHOW DATABASES; أو MariaDB [BookstoreDB]> SHOW SCHEMAS; الناتج الآتي يعرض مخرجات الأمر السابق بعد الدخول إلى مِحَث MariaDB عبر المستخدم bookstoreuser (لاحظ أنَّ هذا الحساب لا يستطيع «رؤية» أيّة قواعد بيانات ما عدا BookstoreDB و information_schema [المتوافرة لجميع المستخدمين]): [root@TecMint ~]# mysql -u bookstoreuser -p Enter password: Welcome to the MariaDB monitor. Commands end with ; or \g. Your MariaDB connection id is 3 Server version: 10.1.14-MariaDB MariaDB Server Copyright (c) 2000, 2016, Oracle, MariaDB Corporation Ab and others. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. MariaDB [BookstoreDB]> SHOW DATABASES; +--------------------+ | Database | +--------------------+ | BookstoreDB | | information_schema | +--------------------+ 2 rows in set (0.00 sec) MariaDB [BookstoreDB]> SHOW SCHEMAS; +--------------------+ | Database | +--------------------+ | BookstoreDB | | information_schema | +--------------------+ 2 rows in set (0.00 sec) الخلاصة شرحنا في هذا الدرس كيفية إجراء عمليات على البيانات، إضافةً إلى إنشاء قاعدة بيانات، وجداولها، وتخصيص مستخدم لقاعدة البيانات، ثم رأينا فائدة بعض التلميحات التي تساعد في تسهيل إدارة قواعد البيانات عليك. ترجمة -وبتصرّف- للمقال Learn MySQL / MariaDB for Beginners – Part 1 لصاحبه Gabriel Cánepa.
  19. تمهيد من المهم أن تأخذ طريقة التخزين بعين الاعتبار عندما تضبط خادومًا، إذ أنَّ أغلبية المعلومات المهمة التي تهتمُ أنت ومستخدموك بها ستُكتَب في مرحلةً ما إلى وسيط تخزين للحصول عليها لاحقًا. ستستفيد من الأقراص المستقلة إذا كانت احتياجاتك بسيطة، لكن إن أردتَ الحصول على أداءٍ عالٍ أو ضمانٍ لعدم فقدان بياناتك، فعندئذٍ ستستفيد من تقنيات مثل RAID. سنتحدث في هذا الدرس عن اصطلاحات RAID ومفاهيمها الشائعة، وسنناقش بعض ميزات (ومساوئ) استخدام مصفوفات RAID، وسنتحدث عن الاختلافات بين طرائق تنفيذ تقنية RAID. ما هي مصفوفة RAID؟ ترمز الكلمة RAID إلى «Redundant Arrays of Independent Disks» وهي تعني «مصفوفة الأقراص التعويضية المستقلة»، أي استخدام عدِّة أقراص بأنماط مختلفة مما يمنح مدراء الأنظمة أداءً أفضل أو قدرةً تعويضيةً (ضد فشل الأقراص) مقارنةً بمجموعةٍ من الأقراص التي تعمل بمفردها. يمكن تطبيق RAID على الأقراص الخام أو على الأقسام الموجودة في قرصٍ ما. متى يُفضَّل استخدام RAID؟ الفائدة الأساسية من مصفوفات RAID هي القدرة على تعويض فقدان البيانات والزيادة في الأداء. القدرة التعويضية تعني زيادةً في إتاحية (availability) البيانات المخزنة، وهذا يعني أننا سنتمكن من الوصول إلى المعلومات المخزنة عند حدوث بعض الحالات مثل فشل أحد أقراص التخزين، وسيتمكن النظام من إكمال عمله إلى أن يُستبَدل القرص المعطوب. لكن هذا لا يعني أنَّ هذه هي آلية نسخ احتياطي (يجب أخذ نسخ احتياطية لمصفوفات RAID كغيرها من أنواع التخزين)، وإنما الغرض من هذه الآلية هو تقليل الانقطاعات عند حدوث مشكلة. فائدةٌ أخرى رئيسيةٌ توفرها مصفوفات RAID في بعض الحالات هي الزيادة في الأداء، فعادةً ستكون سرعة الكتابة أو القراءة على وسائط التخزين محدودة (ومرتبطة) بسرعة الكتابة على قرصٍ وحيد، أما البيانات في مصفوفات RAID فهي إما موجودة نفسها في أكثر من قرص (redundant) أو موزعة على أكثر من قرص (distributed)، وهذا يعني أننا نستطيع استخدام عدِّة أقراص لكل عملية قراءة مما يزيد من كمية البيانات التي يمكن قراءتها في الثانية؛ ويمكن أيضًا تحسين سرعة عمليات الكتابة في بعض حالات الضبط التي يمكن فيها كتابة قسم من البيانات على كل قرص. بعض الجوانب السلبية في مصفوفات RAID تتضمن زيادةً في تعقيد عملية الإدارة وعادةً تؤدي إلى إنقاص المساحة التخزينية المتوافرة. وهذا يعني تكاليف إضافية يجب دفعها للحصول على نفس مقدار المساحة التخزينية القابلة للاستخدام. هنالك مصاريف أخرى تتضمن استخدام قطع عتاديّة خاصة عندما لا تريد إدارة المصفوفة عبر أدواتٍ برمجيةٍ فقط. إحدى السلبيات الأخرى للمصفوفات المضبوطة للحصول على أداءٍ عالٍ دون قدرة تعويضية هي الزيادة في احتمال فقدات البيانات، حيث تعتمد البيانات المخزنة على الأقراص في تلك الحالات على أكثر من وسيط تخزين وحيد، مما يزيد من احتمال فقدان البيانات نتيجة عطب في أحد الأقراص. مصفوفات RAID التي تعتمد على العتاد، والتي تعتمد على البرمجيات، والتي تعتمد على البرمجيات بمساعدة العتاد يمكن إنشاء وإدارة مصفوفات RAID بمختلف التقنيات. وهي: مصفوفات RAID التي تعتمد على العتاد هنالك قطعٌ عتاديةٌ مخصصة تسمى «متحكمات RAID» ‏(RAID controllers) أو «بطاقات RAID» ‏(RAID cards) يمكن أن تستعمل لإعداد وإدارة مصفوفات RAID بمعزل عن نظام التشغيل، وهذا معروفٌ بالمصطلح «hardware RAID». تملك متحكمات RAID العتادية معالجًا منفصلًا لإدارة أجهزة RAID. لمصفوفات RAID التي تعتمد على العتاد عدِّة ميزات، نذكر منها: الأداء: متحكمات RAID العتادية (الحقيقة) لا تأخذ من وقت المعالج لإدارة الأقراص الموجودة ضمن المصفوفة. وهذا يعني أنَّك لن تُسبِّب تقليل أداء الخادوم لإدارة أجهزة التخزين. توفِّر المتحكمات ذات الجودة العالية تخزينًا مؤقتًا كبيرًا وله أثرٌ كبيرٌ على الأداء. إخفاء تعقيدات المصفوفة: ميزة أخرى لاستخدام متحكمات RAID هي أنَّها تخفي طريقة ترتيب الأقراص وإدارتها عن نظام التشغيل، حيث ستُمثِّل متحكماتُ RAID المصفوفةَ على أنها وحدة تخزينٍ منطقيةٍ وحيدةٍ. وليس من الضروري أن يفهم نظام التشغيل طريقة ترتيب مصفوفة RAID؛ إذ أنَّه سيتعامل مع كامل المصفوفة على أنها قرصٌ وحيدٌ. سيُتاح استخدام المصفوفة عند الإقلاع: بسبب أنَّ المصفوفة مدارة بشكلٍ كامل عتاديًا دون تدخل البرمجيات، فهذا يعني أنها متاحة الاستخدام عند الإقلاع، مما يسمح بإنشاء نظام الملفات الرئيسي (الذي سيُثبَّت داخله نظام التشغيل) داخل مصفوفة RAID. لكن هنالك بعض السلبيات لمصفوفات RAID التي تعتمد على العتاد: احتكار الشركات: لأن مصفوفات RAID العتادية مدارةٌ من برمجيات تجارية موجودة داخل العتاد، فهذا يعني أنَّنا يجب أن نستعمل المصفوفة مع نوع العتاد الذي أنشأها فقط؛ فلو تعطل متحكم RAID، ففي أغلب الحالات يجب أن نضع شريحة مماثلة أو شريحة متوافقة مع الشريحة القديمة بدلًا عنه. يَنصح بعض مدراء الأنظمة بشراء أكثر من متحكم RAID من نفس النوع كاحتياطٍ في حال حدثت مشكلة. الكلفة العالية: متحكمات RAID ذات الجودة العالية تكون ذات سعر مرتفع عادةً. مصفوفات RAID التي تعتمد على البرمجيات يمكن أيضًا ضبط RAID داخل نظام التشغيل نفسه. ولأنَّ العلاقة بين الأقراص ستُعرَّف وتُضبَط داخل نظام التشغيل عوضًا عن استخدام جهاز عتادية منفصل، فستسمى تلك المصفوفات بمصفوفات «software RAID». بعض ميزات مصفوفات RAID البرمجية: المرونة: لمّا كانت مصفوفات RAID التي تعتمد على البرمجيات تُدار داخل نظام التشغيل، فيمكن بسهولة ضبط أجهزة التخزين المتاحة دون تغيير شيء في ضبط العتاد. عملية إنشاء مصفوفات RAID في لينكس هي عملية مرنة للغاية، حيث يُسمَح بضبط مختلف أنواع ومستويات RAID. مفتوحة المصدر: البرمجيات التي نستعملها لضبط مصفوفات RAID في الأنظمة مفتوحة المصدر مثل لينكس و FreeBSD هي برمجيات مفتوحة المصدر أيضًا، وضبط المصفوفات متاحٌ لك وغير مخفي، ويمكن بكل سهولة قراءته وتطبيقه على أنظمة أخرى. فمثلًا، إذا كانت لدينا مصفوفة RAID أنشأناها على نظام أوبنتو، فسنتمكن ببساطة من نقلها إلى خادوم CentOS لاحقًا. هنالك احتمالٌ ضئيلٌ في أنَّك ستفقد الوصول إلى بياناتك بسبب بعض الاختلافات بين البرمجيات. لا توجد تكلفة إضافية: لا يتطلب إنشاء مصفوفات RAID برمجية أيّة قطع عتادية خاصة، لذا لن تكون هنالك تكلفة إضافية على خادومك عند استخدامها. بعض سلبيات استخدام مصفوفات RAID البرمجية: التبعية للبرمجيات: صحيحٌ أنَّ مصفوفات RAID البرمجية غير مرتبطة بعتاد معيّن، لكن عادةً سترتبط ببرمجية معيّنة التي أنشأتها فيها. فمثلًا يستخدم لينُكس mdadm بينما FreeBSD يستعمل مصفوفات RAID مبنية على GEOM، ولنظام ويندوز له نسخةٌ خاصةٌ به من البرمجيات التي تُتيح إنشاء مصفوفات RAID برمجية. وعلى الرغم من أنَّ مختلف نسخ البرمجيات المفتوحة المصدر يمكن أن تُصدِّر ضبطها فيما بينهما، إلا أنَّه لا يحتمل أن تتوافق الصيغة نفسها مع بقية برمجيات RAID. مشاكل في الأداء: قديمًا كانوا ينتقدون مصفوفات RAID البرمجية لأنها تُسبِّب بحملٍ إضافيٍ على النظام. فسيُستعمَل المعالج المركزي والذاكرة لإدارة المصفوفة، والتي كانت يمكن أن تُستعمَل لأمورٍ أخرى. لكن البرمجيات مثل mdadm التي تعمل على عتادٍ حديث قد أطاحت بهذا المخاوف، أي أنَّ استعمال المعالج يكون أصغريًا في أغلبية الحالات ولا نأخذه بعين الاعتبار. مصفوفات RAID التي تعتمد على البرمجيات بمساعدة العتاد النوع الثالث من مصفوفات RAID يدعى «hardware-assisted software RAID» أو «firmware RAID» أو «fake RAID». عمومًا يوفر هذا النوع ميزات RAID بتضمينه داخل اللوحة الأم أو عبر بطاقات RAID رخيصة الثمن. يتم تطبيق هذا النوع من المصفوفات عبر استخدام برمجيات على المتحكم أو البطاقة لإدارة مصفوفة RAID، لكنه يستخدم وحدة المعالجة المركزية لتشغيل تلك البرمجيات. ميزات هذا النوع من المصفوفات: دعم إقلاع أكثر من نظام تشغيل: وذلك لأنَّ مصفوفة RAID ستعمل عند الإقلاع، لذا يمكن استخدام أكثر من نظام تشغيل للمصفوفة، وهذا غير ممكن إذا استعملنا مصفوفات RAID برمجية. من سلبياتها: دعم محدود لمستويات RAID: عادةً تدعم RAID 0 أو RAID 1 فقط. تتطلب عتادًا خاصًا: مثل مصفوفات RAID العتادية، هذا النوع مقيدٌ بالعتاد الذي أنشأه ويديره، وهذه مشكلة كبيرة في حال كان موجودًا ضمن اللوحة الأم، فلو فشل متحكم RAID أو تعطل، فهذا يعني أنَّ عليك استبدال اللوحة الأم كلها لكي تستطيع الوصول مرةً أخرى إلى بياناتك. مشاكل في الأداء: مثل مصفوفات RAID العتادية، لا يوجد هنا معالج خاص بإدارة RAID، ويجب أن يتشارك المتحكم مع نظام التشغيل باستخدام المعالج المركزي. أغلبية مدراء الأنظمة يبقون بعيدين عن هذا النوع من مصفوفات RAID، لأنها تعاني من اجتماع مشاكل النوعَين الآخرَين. الاصطلاحات المستخدمة عند الحديث عن مصفوفات RAID سيساعدك التعرف على بعض هذه الاصطلاحات على فهم RAID بشكل أفضل. هذه بعض المصطلحات الشائعة التي قد تصادفك: مستوى RAID ‏(RAID level): يشير «مستوى RAID» إلى العلاقة التي تجمع أجهزة التخزين. يمكن ضبط الأقراص بعدِّة طرائق، مما يؤدي إلى خصائص مختلفة للتعويض وللأداء. التوزيع (Striping): «التوزيع» هو عملية تقسيم البيانات المكتوبة على المصفوفة إلى أكثر من قرص. تُستخدَم هذه التقنية من مختلف مستويات RAID. عندما توزَّع البيانات على المصفوفة، فستُقسَّم إلى أجزاء صغيرة، ثم يكُتَب كل جزء إلى قرص واحد أو أكثر من الأقراص المُشكِّلة للمصفوفة. حجم الجزء (Chunk Size): عند توزيع البيانات، سيُحدِّد «حجم الجزء» مقدار البيانات التي سيحتوي كل جزء عليها. وتعديل حجم الجزء ليُطابِق خصائص الدخل والخرج التي تتوقعها قد يساعد في زيادة أداء المصفوفة. آلية تعادل القيمة (Parity): هذه آلية يتم إنجازها عبر حساب المعلومات من كتل البيانات (data blocks) المكتوبة إلى المصفوفة. وقد تستخدم معلومات parity لإعادة بناء البيانات إذا فشل أحد الأقراص. قد توضع معلومات parity في قرص منفصل عن الأقراص التي تحتوي البيانات التي تُحسَب بيانات parity منها، وتوزَّع في أغلبية حالات الضبط على عدِّة أجهزة للمقدرة على تعويض فشل أحد الأقراص ولزيادة الأداء. مصفوفات ذات الحالة المخفَّضة (Degraded Arrays): يمكن أن تعاني المصفوفات التي لها قدرة تعويضية (redundancy) أنواعًا مختلفةً من فشل الأقراص دون خسارة البيانات. فعندما تخسر المصفوفة قرصًا لكنها تستطيع إكمال عملها، فيُقال أنَّها أصبحت «بحالة مُخفَّضة» (degraded mode). يمكن إعادة بناء المصفوفات ذات الحالة المخفَّضة لترجع كما كانت بعد استبدال القرص المعطوب، لكنها قد تعاني من أداءٍ منخفض أثناء تلك الفترة. إعادة المزامنة (Resilvering أو Resyncing): «إعادة المزامنة» هو المصطلح المستخدم لإعادة بناء مصفوفة ذات الحالة المخفَّضة. واعتمادًا على ضبط RAID ونوع الفشل، سنتمكن من إجراء عملية إعادة المزامنة إما بنسخ البيانات من الملفات الموجودة في المصفوفة، أو عبر حساب البيانات باستخدام معلومات parity‏ (parity information). المصفوفات المتشعبة (Nested Arrays): يمكن دمج مجموعات من مصفوفات RAID في مصفوفات أكبر. ونفعل ذلك عادةً للاستفادة من ميزات مستويي RAID أو أكثر. عادةً تُستخدَم المصفوفات ذات القدرة التعويضية (مثل RAID 1 أو RAID 5) كمكونات لإنشاء مصفوفة RAID 0 لزيادة الأداء. الامتداد (Span): للأسف، مصطلح «الامتداد» له معانٍ مختلفة عندما نتحدث عن المصفوفات. ..- في سياقات معيّنة، «الامتداد» يعني وصل قرصين أو أكثر مع بعضهما لتمثيلهما كجهاز منطقي وحيد، بدون تحسين في الأداء أو القدرة التعويضية. وهذا يُعرَف أيضًا بالترتيب الخطي (linear arrangement) عند التعامل مع برمجية mdadm في لينكس. ..- يمكن أن يشير «الامتداد» إلى المصفوفات التي تُجمَّع مع بعضها لإنشاء مستوى جديد من مستويات RAID وذلك في المصفوفات المتشعبة، مثل المصفوفات من المستوى RAID 10 (أي RAID 1+0). التحقق (Scrubbing أو Checking): هي عملية قراءة كل كتلة في المصفوفة للتأكد من عدم وجود أخطاء. وهذا يساعد في التأكد من أنَّ البيانات متماثلة في أكثر من وسيط تخزين، وسيمنع ذلك من حدوث أخطاء قد تؤدي إلى تلف في البيانات، وخصوصًا خلال العمليات الحساسة مثل إعادة بناء المصفوفة. ترجمة -وبتصرّف- للمقال An Introduction to RAID Terminology and Concepts لصاحبه Justin Ellingwood
  20. رأينا في الدرس السابق أكثر ثلاثة أنماط شيوعًا لتحديد مواقع العناصر في صفحات HTML عبر CSS، وهي static و relative و absolute. سننظر في هذا الدرس إلى fixed و sticky، ثم سنناقش طريقة ترتيب العناصر فوق بعضها عبر z-index. طريقة fixed لتحديد مواقع العناصر هنالك قاعدة background-attachment: fixed تُطبَّق على صور الخلفية، وأيضًا توجد قاعدة position: fixed التي تُطبَّق على العناصر؛ حيث تسمح بأن يكون موقع العنصر ثابتًا نسبةً إلى الصفحة، مما يسمح بتمرير بقية العناصر مع بقاء العنصر في مكانه. ويُطبَّق ذلك عادةً على حاويات، فمن الأمثلة الشائعة هي الترويسات والتذييلات الثابتة. وكما عند ضبط العناصر ذات القاعدة position: absolute، ستُصبِح جميع العناصر ذات القاعدة position: static تحت أيّة محتوى له القاعدة position: fixed. هذه شيفرة HTML لعنصر ثابت يظهر على يسار الصفحة: <div id="fixed-pull-tab"> <img src="red-tab.png" style="float: right" alt="LEVI’S"> <p>This is an example of an element with <code>position: fixed;</code> applied to it, for this blog’s article on the CSS property. </div> استخدمتُ الخاصية box-shadow في CSS على الصورة لكي أوضِّح أنَّ الحاوية موجودة في «طبقة» تعلو بقية المستند: div#fixed-pull-tab { width: 300px; border: 1px solid #000; padding-left: 1em; background-color: rgba(255, 255, 37, 0.7); position: fixed; left: -265px; top: 200px; } div#fixed-pull-tab p { margin-right: 70px; } div#fixed-pull-tab img { box-shadow: 4px 4px 4px rgba(0, 0, 0, 0.33); } div#fixed-pull-tab:hover { left: 0; } وكما في position: absolute، يجب أن تُستخدَم القيمة fixed بحذر، حيث تسمح هذه الخاصية بإنشاء عناصر في صفحات الويب التي تتداخل مع غيرها من محتوى الصفحة أو تغطي عليها. دورة تطوير واجهات المستخدم ابدأ عملك الحر بتطوير واجهات المواقع والمتاجر الإلكترونية فور انتهائك من الدورة اشترك الآن حالة خاصة: العناصر الثابتة الموجودة داخل حاوية في الحالات العادية، سيُزال العنصر المُطبَّق عليه القاعدة position: fixed من المستند، وأيّة معلومات تُحدِّد مكانه نسبةً إلى العنصر <body> ستصبح غير متاحة. لكن، من الممكن وضع عنصر مُطبَّق عليه القاعدة position: fixed داخل عنصر آخر ومنسوبًا إليه، وذلك إذا أُجري CSS transform عليه. فلو كانت لدينا الشيفرة الآتية: <div id="container"> <div id="fixed"></div> </div> يمكن أن يصبح العنصر الداخلي «ثابتًا» بالنسبة إلى العنصر الأب باستخدام شيفرة CSS الآتية (دون السابقات الخاصة بالمتصفحات، لغرض وضوح الشيفرة): #container { transform: translateZ(0); } #fixed { position: fixed; } هذه «الميزة» الغربية هي جزء من مواصفات CSS، وهي مدعومة في جميع المتصفحات الحديثة التي جربتها (باستثناء IE 11)، وحسب معلوماتي، أول من اكتشف ذلك هو Eric Meyer، وأتوقع أنَّ هذه الميزة نادرة الاستخدام (أغلبية المطورين يفضلون استخدام absolute مع relative) لكن لا ضير من معرفة هذه المعلومة. طريقة sticky لتحديد مواقع العناصر مرت فترةٌ أصبحت فيها العناصر «الثابتة ديناميكيًا» هي ميزة التصميم الأساسية في الموقع، فإذا مرّرتَ إلى الأسفل فسيتحرك كل شيءٍ كما هو متوقع، لكن عندما يبلغ عنصرٌ معيّن (عادةً يكون شريط القائمة، أو إعلان في بعض الأحيان) أعلى الصفحة فسيثبت مكانه، بينما ستستمر بقية عناصر المستند بالتمرير أدناه؛ وعند التمرير إلى الأعلى فسيربط العنصر نفسه مرةً أخرى بالمستند. هذا السلوك (الذي هو دمجٌ بين position: static و position: fixed) كان يُضاف إلى الصفحة باستخدام jQuery (عبر إحدى الإضافات الكثيرة الموجودة لهذا الغرض). وكالعديد من الميزات المشهورة، سينتهي بهذا الأمر إلى أن يصبح جزءًا من المواصفة الرسمية، مما يعني أننا سنتمكن من فعل ذلك باستخدام CSS بمفردها، دون إطارات عمل، أو سكربتات، أو غير ذلك… لكن في هذه الحالة، كانت (وما زالت) عملية التحويل إلى مواصفة مليئةً بالمشاكل. كيف كان يُفتَرض لهذه الميزة أن تعمل كنّا نكتب هذه الميزة كقيمة جديدة: position: sticky، وذلك مع استخدامٍ ذكيٍ للخاصية top (وهي ترمز عند استخدامها مع sticky إلى المسافة من أعلى العنصر bodyالذي بعدها سيصبح العنصر «ثابتًا» عند التمرير؛ البدائل هي الخاصياتleftوbottomوright` للتمرير في تلك الاتجاهات)، وكان ذلك كافيًا لتغطية طيفٍ واسعٍ من حالات الاستخدام؛ وبهذا ستصبح طريقة الاستخدام سهلةً جدًا: #stickytest { position: sticky; top: 0px; } وعند تطبيق ما سبق على صورة (مثلًا)، فستبدو الشيفرة كما يلي: <img src="geckofoot.jpg" alt id="stickytest"> <p>Lorem ipsum… ملاحظة: افتراضنا أنَّ الصفحة تحتوي على محتوى بعد العنصر لكي يصبح العنصر ‎#stickytest في أعلى نافذة المتصفح، وإذا لم تكن تحتوي الصفحة على عناصر أخرى، فلن يصل العنصر إلى المكان اللازم لكي «يثبت» مكانه. أنصحك بتعبئة الصفحة بكثير من النصوص لغرض التجربة. إذا جربتَ الشيفرة السابقة على نسخةٍ حديثةٍ من متصفح Firefox، فيجب أن تعمل عملًا صحيحًا. لكن عند الانتقال إلى بقية المتصفحات، فسنجد أمورًا عجيبة! 1. متصفح Safari 6.1+‎ (على الحاسوب وعلى الهاتف) يدعم القيمة sticky عبر سابقة (prefix) خاصة به. صحيحٌ أنَّ من غير الشائع أن تُطبَّق السابقة على القيمة، وليس على الخاصية. 2. هنالك أمرٌ بسيطٌ عليك الانتباه إليه ألا وهو أنَّ خاصية sticky في Safari تعمل إذا كانت العناصر لها القاعدة display: block، لذا يجب تعديل مثال الصورة السابق ليصبح: #stickytest { display: block; position: -webkit-sticky; position: sticky; top: 0px; } بالإضافة إلى: وضع عنصر sticky داخل حاوية لها القاعدة overflow: hidden سيؤدي إلى عدم تطبيق سلوك sticky. رسميًا، يجب أن يعمل sticky مع display: table، بما في ذلك خلايا الجدول، وهذا مفيدٌ عند التنقل في الجداول الطويلة مع إبقاء عناوين الأعمدة ظاهرةً. لكن للأسف لم تُطبَّق هذه الميزة في المتصفح. متصفح Chrome ينسحب من السباق ربما تجد في الويب بعض الصفحات التي تُصّر أنَّ متصفح Chrome يدعم القاعدة position: sticky كخيارٍ اختباري، وكان هذا صحيحًا… إلى أن أُلغي هذا الخيار تمامًا في Chrome 37. شعر فريق تطوير Google أنَّ إبقاء position: sticky هو تحدٍ لهم في مسعاهم في تحسين سرعة العرض في المتصفح، لهذا ألغيت هذه الميزة. وهذا يعني أنَّ علينا العودة إلى الطرق الالتفافية لإنشاء هذا السلوك في متصفح Chrome و Internet Explorer (الذي لم يدعم القيمة الجديدة قط). لحسن الحظ، هنالك بعض الخيارات المتاحة أمامنا: فلدى Filament Group حلٌّ يعتمد على jQuery، بالإضافة إلى غيره من الحلول. أفضِّل -شخصيًا- استخدام stickyfill من تطوير Oleg Korsunsky، والذي يمكن أن يعمل مع أو بدون jQuery. المثال الموجود في صفحة StickFill يُضيف سلوك sticky كفئة CSS، لكن من المرجَّح أن تُطبِّق ذلك على عنصرٍ وحيد، وإنشاء مُحدِّد يعتمد على المُعرِّف id هو أمرٌ منطقي. وفي هذا المثال، سأضيف إلى السكربت الموجود أسفل الصفحة السطرَ الآتي: Stickyfill.add(document.getElementById('stickytest')); لاحظ أنَّ قيم top و left و bottom و right للعنصر مُقاسةٌ من العنصر body، وهذا يتضمن أيّة هوامش موجودة للعنصر body. أبقِ الأمر بسيطًا صحيحٌ أنَّ position: sticky لها العديد من الميزات، لكن تسهل إساءة استخدامها. ولتفادي الكوارث التي تتعلق بواجهة المستخدم فسأنصحك باتباع ما يلي: - نظريًا، يمكن استخدام position: sticky لإبقاء العناصر ثابتةً داخل أيّ حاوية قابلة للتمرير، كما في هذا المثال؛ لكن رجاءً لا تستخدمها في أكثر من مكان في الصفحة، فلا نحتاج إلى ذلك! (إضافةً إلى أنَّ جميع الحلول الالتفافية المتوافرة لقاعدة position: sticky ستفشل في توفير هذه الميزة، وسيعمل المثال السابق في حاويةٍ في الصفحة لأنها تلك الحاوية عبارة عن عنصر iframe). - كن حذرًا للغاية عند استخدام position: sticky على شاشات الهاتف المحمول: فكل شيءٍ سيثبت مكانه سيأخذ مساحةً كبيرةً من الشاشة، مما يقلّل من المساحة الباقية لعرض محتواك. يجب أن تضبط العنصر ليكون ثابتًا sticky إذا كان ذلك ضروريًا جدًا أو كان مفيدًا للغاية، وليس لأنه «يبدو بشكلٍ جميل». تذكّر أنَّه من الأسهل للمستخدمين أن يُمرِّروا في الصفحة باللمس أعلى أو أسفل الشاشة، لذا لا تقف بطريقهم! لضمان استخدام sticky استخدامًا حكيمًا، فاكتب قاعدة ‎@media التي تبطل تأثير position: sticky على الشاشات الصغيرة: @media all and (max-width: 600px) { #stickytest { position: static !important; } } ما سبق سيُعيد العنصر إلى مكانه الطبيعي في المستند إذا كان عرض الشاشة 600 بكسل أو أقل. ستحتاج إلى كتابة قاعدة مشابهة في JavaScript باستخدام matchMedia إذا كنتَ تستعمل حلًا التفافيًا. أعد كتابة السكربت أعلاه إلى شيءٍ شبيهٍ بما يلي: var sticky = document.getElementById('stickytest'); var screencheck = window.matchMedia("(max-width: 600px)"); Stickyfill.add(sticky); if (screencheck.matches) { Stickyfill.remove(sticky); } كقاعدة عامة، يجب أن تكون العناصر «الثابتة» هي نقطة لانقطاع المحتوى، فلا «تُثبِّت» العناصر في منتصف قطعة من المحتوى، مما يجعل النص يظهر أعلى وأسفل العنصر الثابت، وهذا أمرٌ يُشتِّت القارئ كثيرًا، ويُصعِّب من قراءة النص. وشبيهًا بما سبق، حاول أن تتفادى تثبيت العناصر التي تقسم جزءًا من محتوى نصي، مما يجبر المستخدم على التمرير بسرعة أبطأ لقراءة السطر بأكمله. احرص أنَّ لا تحتل العناصر الثابتة أكثر من الحد الأدنى المتوقع لنافذة المتصفح، فلو كانت أطول من حاويتها فلن يتمكن المستخدمون من رؤية كامل محتواها ولن يستطيعوا أيضًا قراءة بقية المحتوى الموجود في الصفحة. إضافةً إلى ما سبق، ربما تريد أن تُشير إلى أنَّ المحتوى تحت العنصر الثابت سيكون مخفيًا، وربما تستعمل تأثير الشفافية مثلًا عندما يُثبَّت العنصر، كتأثير عدم الوضوح على المحتوى خلف شريط الانتقال. فكرة أخرى هي تطبيق القاعدة position: sticky على سلسلة من العناصر، مما يجعلها تظهر بعضها تلو بعض عند نقاط مُحدِّدة أثناء التمرير. وعلى الرغم من أنَّ هذه الطريقة قد تكون فعالة، لكن يُحتَمَل أن تُربِك المستخدمين، لذا نفِّذها بعد أخذ الحيطة. للأسف، لا يوجد حدث باسم stuck في JavaScript للتبليغ أنَّ أحد العناصر قد أصبح بموضعٍ ثابتٍ. لكن يمكنك الآن اختبار ذلك يدويًا (بعض الطرائق الالتفافية تستخدم فئة class خاصة بالعناصر الثابتة للتعويض عن عدم وجود الحدث stuck). ترتيب العناصر فوق بعضها عبر z-index عندما توضع العناصر في الصفحة بمكانٍ مطلق (absolute) فيمكن أن تتداخل وتغطي على المحتوى العادي، وعلى بقية العناصر التي لها القاعدة position: absolute أيضًا. لكن عندما يحدث ذلك، فكيف سيُحدِّد المتصفح ما هو العنصر الذي يجب أن يكون في الأعلى؟ افتراضيًا، يُحدِّد المتصفح ترتيب العناصر فوق بعضها عبر ترتيب ورودها في الشيفرة. أفضل طريقة لتصور ذلك هي تخيل مجموعة أوراق لعب، وبعض أوراقها موزعةٌ على الطاولة، وتلك الأوراق هي المحتوى العادي للصفحة بما في ذلك العناصر ذات القيمة static و relative و fixed. وإذا تداخلت مع أوراقٍ أخرى موضوعة مسبقًا على الطاولة (وتلك هي العناصر ذات القيمة absolute)، فسيتم ترتيب الأوراق بناءً على ترتيب سحبها (أي أنَّ العناصر الحديثة ستظهر فوق العناصر الأقدم…). ففي مثالنا في الدرس السابق [أضف رابط درس 34676-CSS-Positioning-static-relative-absolute]، ظهرت الصور بترتيبٍ معيّن في الشيفرة، فعندما تداخلت الصور كانت صورة إسخيلوس في أسفل المجموعة، ثم أفلاطون فوقها، وفي الأعلى صورة ألكيبيادس. أسهل طريقة لإنشاء «طبقات» لعناصر ذات القاعدة position: absolute هي تغيير ترتيب ورودها في الشيفرة، فمثلًا لو غيرنا الشيفرة إلى: <div id="greek-figures"> <img src="plato-bust.jpg" alt="Plato" id="plato"> <img src="aeschylus-bust.jpg" alt="Aeschylus" id="aeschylus"> <img src="alcibiades-bust.jpg" alt="Alcibiades" id="alcibiades"> </div> فستنتج الصورة الآتية: صورة أفلاطون في المنتصف تحت الصورتين الأخريتين، لأنها تأتي أولًا في الشيفرة. ستبقى الصورة في نفس المكان، لكن ترتيبها قد تغيّر. لأسبابٍ مختلفة، قد لا نستطيع تغيير ترتيب العناصر في الشيفرة (أو لا نرغب في ذلك) لكننا نريد ترتيب العناصر بترتيبٍ يختلف عن تسلسل ورودها في الشيفرة. سنُعيد الشيفرة إلى حالتها الأصلية: <div id="greek-figures"> <img src="aeschylus-bust.jpg" alt="Aeschylus" id="aeschylus"> <img src="plato-bust.jpg" alt="Plato" id="plato"> <img src="alcibiades-bust.jpg" alt="Alcibiades" id="alcibiades"> </div> إذا أردنا إعادة ترتيب العناصر، لكننا لا نريد تعديل الشيفرة، فيمكننا إضافة الخاصية z-index إلى أنماط CSS التي تتحكم في الصور: body p { text-align: justify; } div#greek-figures { float: right; width: 250px; height: 450px; margin-left: 2em; } div#greek-figures img { height: 150px; width: 150px; position: absolute; } img#aeschylus { z-index: 2; } img#plato { top: 155px; right: 15px; z-index: 1; } img#alcibiades { top: 290px; z-index: 2; } الشيفرة السابقة ستؤدي إلى إظهار نفس نتيجة المثال الأول، لكن بدون الحاجة إلى تغيير شيفرة HTML. تُعتَبَر قيمة الخاصية z-index للعنصر body هي 0، وأيّة قيمة موجبة للخاصية z-index لأي عنصر آخر ستجعله يظهر فوق العنصر <body>. العناصر ذات الخاصية z-index والمسند إليها قيمةٌ سالبةٌ ستكون أسفله. انتبه أنَّه لو كان للعنصر body لون خلفية (عبر خاصية background-color) فستختفي تلك العناصر تحته. ما هو أعلى رقم يمكن إسناده للخاصية z-index القيمة العظمى للخاصية z-index هي 2147483647 في المتصفحات الحديثة. من المستحيل أن يكون لديك أكثر من مليارَي عنصر مكدس فوق بعضه في الصفحة، لذا لا تحاول استخدام هذا الرقم عند كتابة شيفرات CSS. ترجمة –وبتصرّف– لكل من: CSS Positioning: fixed position sticky: scroll-to-top-then-fixed in pure CSS Stacking order and z-index لصاحبها Dudley Storey
  21. أحد أهم الأسئلة التي يطرحها أغلب المبتدئين بعد تعلّمهم أساسيات تعدد المواقع على ووردبريس هو: كيف أنقل موقع ووردبريس مستقل إلى موقع ضمن شبكة؟ أتى هذا الدرس ليجيب عن السؤال السابق، وذلك عبر اتباعك للخطوات الموجودة فيه لنقل موقعك إلى شبكة. سننظر في هذا الدرس إلى: كيفية استخدام الأداة Import/Export لنقل المحتوى. كيفية استخدام إضافة «Widget Settings» لنقل الودجات. كيفية تهيئة وتنظيف الموقع بعد نقله. اختيار طريقة للنقل سأريك في هذا الدرس طريقةً سهلةً لنقل موقعك عبر استعمال إضافتين، ومن ميزات هذه الطريقة السهولة والسرعة، وستؤدي إلى نقل كل شيء تقريبًا في أغلبية المواقع الاعتيادية. لكن إن أجريتَ العديد من التخصيصات على موقعك، أو أنفقتَ وقتًا طويلًا على ضبط الإضافات، فلن تؤدي هذه الطريقة إلى نقل جميع تلك الأمور. وعندها إما أن تأخذ وقتك بإعادة ضبط كل شيء يدويًا (انظر إلى آخر قسم من هذا الدرس) أو أن تنقل الموقع يدويًا عبر نسخ جداول قاعدة البيانات الضرورية. نقل جداول قاعدة البيانات يدويًا سيتطلب استخدام phpMyAdmin لتنزيل ملف SQL لجداول قاعدة البيانات، ثم تعديل الملف الناتج وإعادة رفعه لاستيراده عبر phpMyAdmin. سيوفر عليك ذلك «تنظيف» الموقع بعد نقله لكن العملية شاقة وصعبة بعض الشيء. ربما سنشرح ذلك في درسٍ منفصل، وأرجو ألّا تتحمس لتلك الطريقة، وإنما اتبع ما ورد في هذه المقالة أولًا. قبل أن تبدأ انتظر قليلًا، هنالك شيءٌ مهمٌ جدًا عليك فعله قبل أن تبدأ؛ ألا وهو أخذ نسخة احتياطية من الموقع الذي تريد نقله ولا تغفل أبدًا عن أخذ نسخة احتياطية أخرى للشبكة. للتذكرة، استخدم إحدى إضافات النسخ الاحتياطي المفضلة لديك. هل أخذتَ نسخةً احتياطيةً؟ حسنًا، لنبدأ الآن. تصدير المحتوى من الموقع القديم توفِّر ووردبريس أداة تُمكِّنك من استيراد وتصدير محتوى موقعك بسهولة. افتح الآن موقعك الذي تريد نقله (هذه صورةٌ لموقعي): اذهب إلى لوحة التحكم، ثم إلى «Tools > Export» (الأدوات > تصدير): ما لم تكن تريد تصدير أنواع معيّنة من المنشورات، فاترك خيار «All content» (كل المحتوى) مفعلًا ثم اضغط على زر «Download Export File» (تحميل ملف التصدير). ستُنشِئ ووردبريس ملف XML ثم سيُنزِّله متصفحك على حاسوبك. إنشاء موقع جديد في الشبكة افتح الشبكة التي تريد نقل الموقع إليها، وأنشِئ موقعًا جديدًا كما شرحنا سابقًا. تثبيت وتفعيل الإضافات والقوالب قبل أن تستورد المحتوى، عليك تثبيت نفس القالب الذي كنت تستخدمه على موقعك القديم وتفعيله للموقع الجديد الموجود على الشبكة. وافعل المِثل لأيّة إضافات أخرى. إذا لم تكن تعرف كيفية تثبيت وتفعيل القوالب والإضافات على الشبكة، فراجع السّلسلة آنفة الذّكر استيراد المحتوى إلى موقعك الجديد قبل أن تستورد المحتوى من موقعك القديم، فاذهب إلى قائمتَي المنشورات والصفحات في لوحة التحكم للموقع الجديد واحذف أي محتوى تجريبي أضافته ووردبريس عند إنشاء الموقع. احذف أيّة ودجات أيضًا، يجب أن يكون موقعك فارغًا. قبل أن تتمكن من استيراد المحتوى من موقعك القديم، ستحتاج إلى تثبيت إضافة WordPress Importer، وذلك بالذهاب إلى صفحة الإضافات في لوحة تحكم الشبكة وتثبيت تلك الإضافة (عليك البحث عن «WordPress Importer»). اذهب الآن في لوحة تحكم الموقع الجديد إلى «Tools > Import» (أدوات > استيراد) ثم مرِّر إلى الأسفل لتجد خيار الاستيراد من ووردبريس (WordPress). ستطلب ووردبريس منك اختيار ملف لرفعه: اضغط على زر اختيار الملف ثم انتقِ الملف الذي نزّلتَه منذ قليل على جهازك (يُفترَض به أن يتواجد في مجلد التنزيلات إلا إذا نقلتَه). تذكر أنَّك تبحث عن ملف XML. بعد أن تنتهي من ذلك فاضغط على زر «Upload file and import» (ارفع الملف واستورده). سيُطلَب منك اختيار فيما إذا كنت تريد إسناد المحتوى إلى مستخدمين موجودين في الشبكة، أو استيراد المستخدمين: اختر ما يناسبك. سأسندُ المنشورات إلى مستخدمٍ موجودٍ مسبقًا، لكن إذا كان موقعك متعدد المستخدمين ولكل مستخدمٍ له منشوراتٌ خاصةٌ به، فربما عليك استيراد المستخدمين أيضًا. في النهاية، اختر «Download and import file attachments» (تنزيل واستيراد المرفقات) لنسخ ملفات الوسائط من موقعك القديم ووضعها في مكانها الصحيح في الموقع الجديد. هذه إحدى أكثر ميزات أداة الاستيراد فائدةً، ولا أظن أنني لم أفعِّل هذا الخيار لأي موقع أستورد مقالاته من قبل! اضغط على زر الإرسال وستقوم أداة الاستيراد بعملها. في النهاية سترى رسالة تفيدك بانتهاء الاستيراد. انظر حينها إلى قائمة المنشورات في لوحة التحكم لترى أنَّ جميع المنشورات قد تم استيرادها بشكلٍ صحيح، وستجد صفحات موقعك أيضًا. لقد أنهينا هنا استيراد المحتوى، وحان الآن الوقت لاستيراد الودجات، وذلك عبر استخدام إضافة أخرى. نقل الودجات قبل نقل أيّة ودجات، عليك أن تتأكّد أنَّك قد فعَّلتَ نفس القوالب والإضافات على موقعك الجديد والتي كانت موجودةً في موقعك القديم. وأثناء تثبيتك للإضافات، فثبّت بطريقك إضافة «Widget Settings Import/Export» وفعِّلها على الموقع الجديد. ملاحظة: ستوضع الودجات في مناطق معيّنة مُعرَّفة من قِبَل قالبك، لذا إذا لم تُفعِّل القالب الصحيح فلن تعمل هذه الخطوة عملًا سليمًا. أيضًا إن كانت هنالك إضافات توفِّر ودجات خاصة بها، فلن يتم استيرادها إن لم تُثبِّت تلك الإضافات على الموقع الجديد. اذهب إلى لوحة التحكم في موقعك القديم وثبِّت وفعِّل إضافة Widget Settings أيضًا، ثم اذهب إلى «Tools > Widget Settings Export» للانتقال إلى صفحة التصدير: وما لم تكن تريد انتقاء بعض الودجات من موقعك القديم، فاختر «Select All Active Widgets»، ثم اضغط على زر «Export Widget Settings» لتنزيل ملف التصدير. بدِّل الآن إلى الموقع الجديد واذهب إلى «Tools > Widget Settings Import». اضغط على زر اختيار الملف واعثر على الملف الذي نزلتَه من قليل، لاحظ أنَّك تبحث عن ملف json هذه المرة، وليس XML. ثم اضغط على زر «Show widget settings» لرؤية الودجات الموجودة في الملف: اختر الودجات التي تريد استيرادها، أو اضغط على زر «Select All Active Widgets» وتأكّد أنك فعّلتَ خيار «Clear Current Widgets Before Import»، ثم اضغط على زر «Import Widget Settings». اذهب الآن إلى موقعك الجديد وتأكّد أنَّه أصبح شبيهًا بالقديم: «تنظيف» الموقع ستستورد الإضافتان السابقتان محتوى موقعك القديم إلى الجديد، لكنهما لن تستوردا جميع الضبط. وهذا يعني أنَّك قد تحتاج إلى تعديل بعض الإعدادات يدويًا. وهذا قد يتضمن: تعديل عنوان الموقع والوصف الخاص به. إجراء أيّة تعديلات أو تخصيصات على تصميم الموقع والتي أجريتها عبر صفحة «تخصيص القالب». تعديل إعدادات الإضافات. أنصحك بفتح كلا الموقعين في نافذتين مختلفين في متصفحك والنظر إلى ضبط الموقع القديم وتعديل ضبط الموقع الجديد بما يتوافق معه. التأكد أنَّ قوائم التنقل قد أضيفت إلى أمكانها الصحيحة في قالبك. ستنسخ إضافة الاستيراد القوائم لكن ربما لن تُسنِد القائمة الصحيحة إلى مكانها الصحيح إن كانت عندك أكثر من قائمة. إذا كان لديك نطاق وأردت نقله من موقع القديم، فعليك ربطه إلى موقعك الجديد الموجود في الشبكة. فبعد أن تنتهي من نسخ كل شيء بين الموقعين فستحتاج إلى تعديل ضبط DNS لنطاقه ليشير إلى الشبكة. ومن ثم ستحتاج إلى استخدام إضافة Domain Mapping في الشبكة لكي يُشير النطاق إلى موقعك بصورة سليمة. لتعليماتٍ تفصيليةٍ انظر إلى درس كيفية ربط النطاقات في شبكة ووردبريس في السّلسلة آنفة الذّكر [أضف رابط الدّرس] نقل موقع مستقل إلى شبكة أسهل مما تتخيل! أعلم من كثرة الأسئلة حول موضوع نقل موقع مستقل إلى شبكة أنَّ الكثيرين يظنون أنَّ الأمر مرعبٌ ومخيف، لكن كما رأيتَ في هذا الدرس أنَّ ذلك سهلٌ وبسيط! وأنصحك أن تضيف هذا الدرس إلى قائمة المفضلة في متصفحك وترجع إليه في كل مرة ستحتاج فيها إلى نقل موقع مستقل إلى شبكة. يمكنك عبر اتباعك للخطوات السابقة أن تنقل موقعك (أو موقع عميلك) إلى شبكة متعددة المواقع دون جهدٍ كبير. ترجمة -وبتصرّف- للمقال How to Move a Single WordPress Site into a Multisite Network لصاحبته Rachel McCollin
  22. يهرع الكثير من المبتدئين إلى طرائق تحديد مواقع العناصر في CSS معتقدين أنها ستحل لهم جميع المشاكل التي يواجهونها في تخطيط الصفحة، لكن هذا ليس صحيحًا بالمطلق: هنالك جوانب أخرى في CSS مسؤولةٌ عن تخطيط الصفحة. وصحيحٌ أنَّ طرائق تحديد المواقع العناصر لها دورٌ لتلعبه في تخطيط الصفحة، لكن من الأحسن أن تعرف كيف ومتى عليك استخدام مختلف أنماط تحديد المواقع، عوضًا عن محاولة تجريبها لربما «حلّت» لك مشكلتك! طريقة static لتحديد مواقع العناصر افتراضيًا، يملك كل عنصر في صفحتك القاعدة position: static مطبقةً عليه، ولهذا السبب لن نحتاج إلى التصريح عن هذه الطريقة، إلا إذا كان ذلك ضروريًا لإلغاء تأثير خاصية position أخرى قد ورثها العنصر من غيره. الكلمة static لا تعني أنَّ العنصر سيبقى في مكانه في الصفحة، وفي الواقع أرى أنَّ هذه الكلمة غير دقيقة وكان يجب استخدام كلمة أخرى (مثل fluid) بدلًا عنها. طريقة تحديد مواقع العناصر الافتراضية تعني أنَّ العناصر لن تتداخل وتظهر فوق بعضها، أي أنَّ كل عنصر «سيدفع» العناصر الأخرى بعيدًا عنه، مستجيبًا إلى قياس ودقة والنسبة بين طول وعرض الجهاز الذي يعرض صفحة الويب. لنأخذ مثالًا بسيطًا لمحتوى صفحة: <p><img src="assets/images/pericles.jpg" style="float: left;" alt="Bust of Pericles"> Pericles was a prominent statesman, orator, and naval general of Athens during the city-states's <q>Golden Age</q>, from 448BCE until his death in 429. Pericles was a promoter of the arts (particularly plays), architecture (it was under his patronage that the Parthenon was built), and the principles of democracy, but he was also an instigator of war: Pericles is widely held to be responsible for maneuvering Athens into the disastrous Peloponnesian War with Sparta.</p> حجز كل عنصرٍ –في المثال السابق– مساحةً خاصةً به، فلن تظهر الصورة فوق النص، وسيبتعد النص قليلًا عن الصورة. تغيير قياس نافذة المتصفح سيُسبِّب بتضييق عرض الصفحة، وستلتف أسطر الفقرة حول الصورة وستدفع أي محتوى أدناها إلى الأسفل. وهذا جيد، لأنَّ الصفحة ستتأقلم مع أيّ قياس للشاشة وأي نسبة عرض إلى ارتفاع وأي دقة، ولن تظهر أيّة عناصر فوق بعضها. لاحظ أنَّ قاعدة «لا شيء سيتداخل، وكل شيء سينساب حول بقية العناصر» هي المبدأ الرئيسي لطريقة position: static إلا أنَّ هنالك بعض الاستثناءات. فمثلًا سيظهر النص فوق صورة الخلفية، ويمكن أن نلغي انسيابية العناصر بتحديد عرض ثابت على عناصر div الحاوية لبقية العناصر، ويمكن أن تتداخل العناصر أو تُزاح من الصفحة إذا طبقنا هامشًا (margin) سلبيًا عليها. لكن في الحالة العامة ستُطبَّق قاعدة static كما هي. العناصر المُطبَّق عليها القاعدة position: static –سواءً بتصريحنا بذلك، أو افتراضيًا– لا يمكن أن تملك الخاصيات التي سنتحدث عنها في الأقسام التالية (وهي top و left و bottom و right). العناصر التي لها القيمة static للخاصية position يمكن أن تُحرَّك فقط بتعديل قيم الخاصيتَين margin أو padding، أو عبر تعديل موقعها في شيفرة HTML. وهذا أمرٌ مقبولٌ بين المطورين وبسيطٌ وسهل التطبيق في أغلبية التصاميم. طريقة relative لتحديد مواقع العناصر من المهم ملاحظة أنَّ تطبيق القاعدة position: relative على أحد العناصر لن يُغيّر شيئًا بمفردها، فسيبقى العنصر يسلك سلوك العناصر ذات القيمة static لخاصية position (كما في القسم السابق). لكن القيمة relative قد أعطتنا وصولًا إلى الخاصيات top و left و bottom و right. فعند تطبيق القاعدة position: relative بالإضافة إلى إحدى الخاصيات السابقة، فسيحدث أمران: سيَخرُجُ العنصر من مكانه في المستند، لكن ستبقى المساحة الفارغة المحجوزة له باقيةً (كما لو كان static). سيُزاح العنصر بمقدارٍ مساوٍ للقيم المنسدة إلى الخاصيات top و left و bottom و right، نسبةً إلى موقعه الأصلي (static). ما يزال بالإمكان تطبيق الخاصية float على العناصر ذات القاعدة position: relative. سنُعدِّل في المثال السابق ليصبح كما يلي: <p><img src="assets/images/pericles.jpg" style="position: relative; top: 2em; right: 4em;" alt="Bust of Pericles"> Pericles was a prominent statesman, orator, and naval general of Athens during the city-states's <q>Golden Age</q>, from 448BCE until his death in 429. Pericles was a promoter of the arts (particularly plays), architecture (it was under his patronage that the Parthenon was built), and the principles of democracy, but he was also an instigator of war: Pericles is widely held to be responsible for maneuvering Athens into the disastrous Peloponnesian War with Sparta.</p> كما لاحظت، ستُزاح الصورة بمقدار 2em إلى الأسفل انطلاقًا من أعلى موقعها الأصلي، و 4em من اليمين. لاحظ كيف بقيت المساحة محجوزةً للعنصر الأصلي، وكيف يتلف النص حولها، وأنَّ الصورة ستتداخل مع بعض الأسطر النصية. ربما أسهل طريقة لكي تفهم فيها position: relative هي أنَّ تتخيل أنَّها تستخدم «لإزاحة» العناصر لكنك لا ترغب بالتأثير على تخطيط بقية الصفحة. وذلك لأنَّ المساحة المعطاة إلى العنصر الأصلي ما تزال موجودةً. إذ يمكنك بكل سهولة إعطاء قيم إلى خاصيات top و left و bottom و right (يمكن أيضًا استخدام القيم السلبية) دون أن تقلق من تأثير ذلك على بقية عناصر الصفحة. طريقة absolute لتحديد مواقع العناصر المطورون الذين يملكون المعلومات الكافية في CSS لكي يقعوا في مشاكل، مع المصممين المهووسين بأن تكون تصاميمهم دقيقة جدًا، سيجنحون إلى استخدام position: absolute استخدامًا مفرطًا؛ ويجادلون قائلين «بإمكاننا وضع أي شيء في صفحة الويب في المكان الذي نريده». لكنهم للأسف يغفلون عدِّة نقط مهمة، وسيقعون حتمًا في حالتين اللتين ستؤديان إلى حدوث «متاهة» مقعدة في الصفحة. لكن دعنا أولًا نرى ماذا تفعل قاعدة position: absolute بصفحتنا. ستصبح الشيفرة كما يلي: <p><img src="assets/images/pericles.jpg" style="absolute; top: 0; left: 30px;" alt="Bust of Pericles"> Pericles was a prominent statesman, orator, and naval general of Athens during the city-states's <q>Golden Age</q>, from 448BCE until his death in 429. Pericles was a promoter of the arts (particularly plays), architecture (it was under his patronage that the Parthenon was built), and the principles of democracy, but he was also an instigator of war: Pericles is widely held to be responsible for maneuvering Athens into the disastrous Peloponnesian War with Sparta.</p> عند تطبيق قاعدة position: absolute، فستفقد الخاصتان float و margin تأثيرهما، لذا أزلتُهما. تؤدي القيمة absolute إلى نزع الصورة من مكانها في المستند تمامًا، أي ستؤخذ وتُرفَع إلى أعلى المستند الذي سيظهر تحتها. باختصار: ستسلك صفحة الويب سلوكًا مشابهًا لسلوكها كما لو أنَّ الصورة غير موجودة من الأساس. لماذا إذًا يستعمل الكثيرون position: absolute؟ لأنَّنا نستطيع تحديد موضع العنصر –عند استخدام absolute– انطلاقًا من الزاوية العليا اليسرى للعنصر الحاوي لها، وهو العنصر body في حالتنا. قد تبدو لك position: absolute جذابةً، حيث يبدو أنَّها تعدك بوضع العناصر في مكانها بدقة شديدة. وهذا مغرٍ جدًا للمصممين التقليديين الذين يصممون تصاميم لسطح المكتب أو للطباعة، والمعتادين على التحكم بمكان كل شيء في صفحات A4، والذي لا يرون داعٍ للتصميمات المتجاوبة. لكنهم يرفضون أن يلاحظوا بضع نقاط: صفحات الويب ليست كالورق ذي القياس المعياري. فالشاشات والمتصفحات والأجهزة تملك أحجام ونسب ودقة مختلفة. واستخدام absolute لوضع العناصر في الصفحة يعني افتراض مجموعة من المتغيرات عن جهاز المستخدم، مما يؤثِّر سلبًا في مرونة الويب. بعد أن تُطبِّق position: absolute على أحد العناصر، فستجد نفسك تُطبِّق نفس القاعدة على كل العناصر الأخرى. وذلك لأنَّ position: absolute سينتزع العنصر من المستند، ويجب علينا تعديل موضع بقية العناصر للتأقلم مع ذلك ولضمان عدم تداخل العناصر التي لا تريدها أن تتداخل. وهذا سيؤدي إلى إنشاء قواعد CSS معقدة والتي لن تعمل كما يجب عند إضافة محتوى جديد إلى الصفحة (أكرِّر أنَّ الويب ليس صفحةً مطبوعةً، حيث تُعدَّل المحتويات وتُضاف إضافات بين الحين والآخر، ويجب أن يكون تصميمك مرنًا كفايةً للاستجابة إلى تلك التعديلات). واستخدام position: absolute يجعل التعديلات على الصفحة تأخذ وقتًا طويلًا وجهدًا كبيرًا. لماذا نستخدم إذًا position: absolute من الأساس؟ حسنًا، إذا استخدمنا absolute استخدامًا حكيمًا، فيمكن أن نستفيد منها لتداخل العناصر المنفصلة التي كانت لتُدمَج في صورةٍ وحيدةٍ. على سبيل المثال، لنقل أنَّك تريد الإضافة على المقالة السابقة ووضع صور أخرى من العصر الذهبي لأثينا. أي لديك عدِّة صور (إسخيلوس وأفلاطون وألكيبيادس)، وتريد أن تضعها على الجانب الأيمن لمستندك، وتريدها أن تتداخل مع بعضها. أحد الحلول هي تعديل الصور باستخدام فوتوشوب، بدمجها مع بعضها في صورةٍ وحيدة، لكن هذا سيمنعك من تعديل ترتيبها ومحاذاتها والتباعد بينها لاحقًا، وعليك حينها العودة إلى ملف فوتوشوب لإجراء تعديلاتك ثم إعادة التصدير ورفع الصورة من جديد. لكن بدلًا من كل ما سبق، لنحاول الإبقاء على الصور معزولةً ونضعها داخل عنصر <div>. شيفرة HTML هي: <div id="greek-figures"> <img src="aeschylus-bust.jpg" alt="Marble bust of Aeschylus" id="aeschylus"> <img src="plato-bust.jpg" alt="Marble bust of Plato" id="plato"> <img src="alcibiades-bust.jpg" alt="Marble bust of Alcibiades" id="alcibiades"> </div> نعلم أنَّ لهذه الصور نفس الأبعاد، لذا لن نحتاج إلى تحديد خاصيات height و width لكل واحدة على حدة. وإنما سنجعل ذلك ضمن أنماط CSS: div#greek-figures { float: right; } div#greek-figures img { height: 150px; width: 150px; position: absolute; } إذا حاولتَ عرض الصفحة الآن، فستجد أنَّ عنصر <div> قد تضائل، وظهرت صورةٌ وحيدةٌ فقط وهي خارجة عن مكانها. هل لديك أيِّة فكرة عن السبب؟ …[تريّث قليلًا وفكِّر بالسبب قبل إكمال القراءة]… الجواب: لقد طُبِّقَت القاعدة position: absolute على الصورة، لذا تم انتزاعها من مكانها في الصفحة، ولم تعد تستطيع «دفع» العناصر التي حولها. أما عنصر div بعد وضع الخاصية float له فسيحاول تحديد عرضه عبر محتوياته التي بداخله؛ لكن المحتوى (أي الصور) لها القيمة absolute، فلن تُحتَسَب، والصور هي المحتوى الوحيد الموجود في العنصر <div>، أي أنَّ العنصر <div> ليس له عرض! في النهاية، ستحاول كل صورة أن تضع نفسها داخل عنصر <div> في الزاوية العليا اليسرى، وهذا يعني أنَّ الصور ستتكدس فوق بعضها. لاحظ أنَّ آخر صورة داخل العنصر div ستكون في الأعلى (وسنتحدث عن هذا الأمر في درسٍ لاحق). لنحاول الآن حلّ المشاكل السابقة بتعديل CSS: body p { text-align: justify; } div#greek-figures { float: right; width: 250px; height: 450px; margin-left: 2em; } div#greek-figures img { height: 150px; width: 150px; position: absolute; } img#plato { top: 155px; right: 15px; } img#alcibiades { top: 290px; } (وفرنا خاصية height لعنصر div لأنَّ الصور التي موضعها absolute لن تساهم في توفير ارتفاع للعنصر؛ وبدون ارتفاع فلن تجد الأسطر النصية شيئًا لتلتف حوله. أضفنا بقية الخاصيات مثل text-align والهوامش لإظهار الصفحة بشكل جميل). سيعمل ما سبق كما ينبغي، لكنك ستتكشف شيئًا غريبًا. إذا عدَّلتَ الخاصية right لصورة أفلاطون إلى left، فلن تحصل على ما كنتَ تتوقعه. فبدلًا من قياس الموضع من الحاوية، فستُنسَب الصورة نفسها إلى أكبر حاوية وهي العنصر <body>. يمكننا الالتفاف على هذه المشكلة بإضافة قاعدة في أنماط CSS: div#greek-figures { float: right; width: 250px; height: 450px; margin-left: 2em; position: relative; } وستكون النتيجة النهائية هي: إذا كان العنصر الذي له القيمة absolute للخاصية position موجودًا ضمن عنصرٍ آخر تُطبَّق عليه القاعدة position: relative، فستُقاس إحداثيات العنصر انطلاقًا من الزاوية العليا اليسرى للحاوية التي يقع فيها؛ وإلا فستُقاس الإحداثيات انطلاقًا من الزاوية العليا اليسرى من العنصر body. عمومًا، إذا كنتَ تُصرّ على استخدام position: absolute فأنصحك بوضع عدِّة عناصر (التي لها القاعدة position: absolute) في «حاوية» (إما عنصر <div> عادي أو حتى <canvas>). مما يسمح لك بنقلها مع العناصر الموجودة داخلها دون مشاكل. ترجمة -وبتصرّف- للمقالات CSS Positioning: static, the default CSS Positioning: relative, the underappreciated CSS Positioning: absolute, the overused لصاحبها Dudley Storey
  23. أصبحت تطبيقات الويب ذات الصفحة الوحيدة Single Page Apps رائجةً في هذه الفترة في تطوير الويب، فأمسى كل شخصٍ يريد أن ينُشِئ تطبيق ويب ذا صفحةٍ وحيدة. سأريك في هذا الدرس طريقةٍ سهلة لإنشاء تطبيقات الويب ذات الصفحة الوحيدة باستخدام jQuery ودون استخدام أيّة إطارات عمل مثل React أو Angular أو Vue …إلخ. لمحة لأننا نريد إنشاء تطبيق ويب ذا صفحةٍ وحيدة، فسنستخدم sammy.js للتوجيه (routing)، مكتبة Sammy هي مكتبة jQuery بمساحة تخزينية لا تتجاوز 5.2 كيلوبايت. سيبدو سكربت التوجيه المكتوب لإضافة Sammy كما يلي: var app = $.sammy(function() { this.get('#/', function() { //your function }); this.get('#about/', function() { //your function }); this.get('#contact/', function() { //your function }); }); علينا أولًا تهيئة التطبيق باستخدام ‎$.sammy وتخزين نسخة من الكائن في المتغير app. يمكننا تعريف تعليمة «توجيه» (route) في sammy بالطريقة الآتية: this.get('path/',function(){ // ... }); هنالك دالة ستُستدعى لكل عملية توجيه، والتي يمكن أن نكتب بداخلها البنية المنطقية لها، ونربط البيانات اللازمة إلى كل «صفحة»، لأن كل عملية توجيه ستؤدي إلى إظهار «صفحة» مختلفة. بعد تعريف كل تعليمات التوجيه، فسنتمكن من تشغيل تطبيق الويب ذي الصفحة الوحيدة باستدعاء الدالة run()‎ التابعة للكائن app كالتالي app.run()‎. تطبيق التدوين الذي سننشئه لتوضيح المفهوم الذي سنشرحه، فسنحاول إنشاء مثال واقعي. لنفترض أننا نريد إنشاء مدونة بسيطة، التي يوجد فيها صفحة رئيسية (أي صفحة Home) وصفحة معلومات (About). سنُنشِئ في صفحة index قائمة بالتدوينات، والضغط على أي واحدة منها سيأخذنا إلى صفحتها. يمكنك أن تجرب هذا التطبيق عمليًا هنا. سنستخدم صيغة JSON لقائمة التدوينات، وسنستخدم إضافة Sammy لعرضها. بنية الملفات - index.html <!-- main layout --> - app.js <!-- stores all our routing logic --> - css --- style.css - js --- jquery-1.11.3.min.js --- sammy.min.js --- sammy.template.js - data --- articles.json - templates <!-- the templates pages that will be injected into the main layout --> --- article.template --- article-detail.template --- about.template شيفرة HTML سنستخدم قالب HTML boilerplate لإنشاء شيفرة HTML: <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script src="js/jquery-1.11.3.min.js" type="text/javascript"></script> <script src="js/sammy.min.js" type="text/javascript"></script> <script src="js/sammy.template.js" type="text/javascript"></script> <link rel="stylesheet" href="css/style.css" /> <script src="app.js"></script> </head> <body> <div class="header-container"> <header class="wrapper clearfix"> <nav> <ul> <li><a href="#/">Home</a></li> <li><a href="#/about/">About</a></li> <!-- defining nav url according to route--> </ul> </nav> </header> </div> <div class="main-container"> <div class="main wrapper clearfix"> <div id="app"> <!-- template will be injected here --> </div> </div> </div> </body> </html> تعريف التوجيهات //app.js (function($) { var app = $.sammy('#app', function() { this.use('Template'); this.around(function(callback) { var context = this; this.load('data/articles.json') .then(function(items) { context.items = items; }) .then(callback); }); this.get('#/', function(context) { context.app.swap(''); $.each(this.items, function(i, item) { context.render('templates/article.template', {id: i, item: item}) .appendTo(context.$element()); }); }); this.get('#/about/', function(context) { var str=location.href.toLowerCase(); context.app.swap(''); context.render('templates/about.template', {}) .appendTo(context.$element()); }); this.get('#/article/:id', function(context) { this.item = this.items[this.params['id']]; if (!this.item) { return this.notFound(); } this.partial('templates/article-detail.template'); }); this.before('.*', function() { var hash = document.location.hash; $("nav").find("a").removeClass("current"); $("nav").find("a[href='"+hash+"']").addClass("current"); }); }); $(function() { app.run('#/about/'); }); })(jQuery); هيئنا في البداية التطبيق داخل العنصر ‎#app، حيث سنُضيف القوالب المختلفة بناءً على مسار التوجيه. سنستخدم أيضًا محرك قوالب باسم sammy template engine. this.use('Template'); سنحصل على بيانات المدونة من ملف articles.json باستخدام الدالة load()‎ التابعة لمكتبة jQuery (يمكنك أيضًا استخدام ‎$.get أو ‎$.post)، وتخزين الناتج في المتغير context. حان الآن الوقت لتعريف تعليمة التوجيه لصفحة index باستخدام ‎#/‎: this.get('#/', function(context) { context.app.swap(''); $.each(this.items, function(i, item) { context.render('templates/article.template', {id: i, item: item}) .appendTo(context.$element()); }); }); لدينا بيانات مخزنة في المتغير context، سنمرّ الآن عبر تلك البيانات عبر حلقة تكرار باستعمال الدالة ‎$.each ثم عرضها في article.template. سنستخدم أيضًا الدالة context.app.swap()‎ لكي نُفرِّغ العنصر الذي سنضع فيه المحتويات (أي ‎#app) مما فيه قبل عرض القالب. <article> <section> <a href="#/article/<%= id %>"><h2><%= item.title %></h2></a> </section> </article> سنستخدم هنا مُحرِّك القوالب (templating engine) لكي نعرض القيم القابلة للتغيير باستخدام الصيغة ‎<%= yourdata %>‎، يمكنك أيضًا إضافة رابط لصفحة تفاصيل كل مقالة باستخدام الصيغة ‎#/article/<%= id %>‎. الضغط على رابط التدوينة سيأخذنا إلى قالب article-detail.template، حيث نستطيع إظهار صورة التدوينة وملخص عنها …إلخ. وخلف الكواليس، سنُعرِّف تعليمة توجيه إلى تفاصيل التدوينة في ملف app.js، وسنحصل على مُعرِّف التدوينة كمعامل (parameter) ثم نُمرِّر البيانات إلى article-detail.template. this.get('#/article/:id', function(context) { this.item = this.items[this.params['id']]; if (!this.item) { return this.notFound(); } this.partial('templates/article-detail.template'); }); وبشكلٍ شبيه، يمكننا إنشاء صفحة ثابتة باسم «about» ونعرضها عبر قالب about.template. this.get('#/about/', function(context) { var str=location.href.toLowerCase(); context.app.swap(''); context.render('templates/about.template', {}) .appendTo(context.$element()); }); لقد نسينا أهم خطوة، ألا وهي تهيئة التطبيق باستخدام app.run()‎ حيث يمكنك تحديد مكان التوجيه الافتراضي لتطبيقك. إذا أردتَ أن تفتح صفحة about أولًا فيمكنك أن تكتب: $(function() { app.run('#/about/'); }); إذا أردتَ إجراء بعض العمليات قبل استدعاء كل تعليمة توجيه، فيمكنك استخدام الدالة before()‎. سنُجري في الأسطر الآتية تعديلاتٍ على قائمة التنقل (nav) اعتمادًا على مسار التوجيه الحالي: this.before('.*', function() { var hash = document.location.hash; $("nav").find("a").removeClass("current"); $("nav").find("a[href='"+hash+"']").addClass("current"); }); الخلاصة هذا درسٌ بسيطٌ جدًا لكي تأخذ فكرة عن كيفية عمل تطبيق ذي صفحةٍ وحيدةٍ باستخدام jQuery. يمكنك الآن المضي قدمًا وإنشاء تطبيقات ذات صفحة وحيدة. إحدى الأشياء التي عليك أن تأخذها بعين الاعتبار هي أنَّك ستحتاج إلى خادوم ويب لتشغيل التطبيق، ويمكنك أيضًا تجربة متصفح Firefox، لأنَّ متصفح Chrome يحجب طلبيات Ajax لأيّة بروتوكولات ما عدا http://‎ أو https://‎، لذا إذا كنتَ ستُشغِّل هذا السكربت على جهازك المحلي فلن يعمل لأنَّ مسارات الملفات المحلية تبدأ بالسابقة file://‎. ترجمة –وبتصرّف– للمقال Single Page Apps with jQuery Routing لصاحبه Arkaprava Majumder
  24. أهلًا بك إلى الدّرس الأخير من الدورة التدريبية الشاملة في ميزة تعدد المواقع في ووردبريس. سنواصل في هذا الدّرس استعراض ما تحتاجه لإدارة شبكة مواقعك على ووردبريس مُتعدّد المواقع بفاعلية. إن لم تقرأ الجزء السّابق فقد ترغب في الاطّلاع عليه أولا من هنا. قد ترغب أيضا في الاطّلاع على باقي مقالات هذه السّلسلة من هنا. أخذ نسخ احتياطية من شبكتك من المهم جدًا أن تأخذ نسخًا احتياطيةً من كامل شبكتك –ومن كل موقع موجود فيها– دوريًا. التواتر الذي عليك أخذ النسخ الاحتياطية فيه يتعمد على الزمن الفاصل بين تحديث مستخدميك للمحتوى الموجود على مواقعهم. الأمور التي يجب أخذها بعين الاعتبار عند النسخ الاحتياطي هنالك جانبان رئيسيان للنسخ الاحتياطي في شبكتك: نسخ المواقع في شبكتك على حدة احتياطيًا. نسخ كامل الشبكة احتياطيًا. من المستحسن أن تجري كلا الأمرين، وبهذه الطريقة ستتمكن من استعادة موقع معيّن إذا حدثت مشكلة فيه، أو ستتمكن من استعادة كامل الشبكة إن حدثت فيها مشكلة، أو حدثت مشكلة في أكثر من موقع. عليك أن تضعن بعين الاعتبار ما الذي ستنسخه عندما تنسخ المواقع أو الشبكة احتياطيًا: ملفات الإضافات والقوالب: لستَ مضطرًا إلى نسخ هذه الملفات احتياطيًا بنفس وتيرة نسخك لباقي العناصر، لأنها لا تتغير كثيرًا. الملفات المرفوعة: عليك أن تنسخ هذه الملفات احتياطيًا بوتيرة أكبر، لأن مستخدميك قد يضيفون ملفات مرفوعة جديدة في كل مرة يضيفون فيها محتوى جديد. قاعدة البيانات: عليك إبقاء قاعدة البيانات منسوخةً نسخًا احتياطيًا دوريًا، لأنَّك لا تريد المخاطرة بفقدان أيّ محتوى جديد أضافه مستخدموك. ربما تُقرِّر أن تنسخ قاعدة البيانات ومجلد uploads احتياطيًا في كل يوم، وملفات القوالب والإضافات كل أسبوع. لكن ربما يُنشِئ مستخدموك المحتوى بوتيرة أكبر، وبهذا عليك تقليل الفترة الفاصلة بين عمليات النسخ الاحتياطي. أمر آخر عليك أخذه بعين الاعتبار هو مكان احتفاظك بالملفات المنسوخة احتياطية وقاعدة البيانات. إضافات النسخ الاحتياطي الشهيرة تعطيك بعض الخيارات: تخزين النسخ الاحتياطية على خادومك: لا أنصحك بفعل ذلك بمفرده لأنَّه إذا واجه خادومك مشاكل، فربما لن تتمكن من الوصول إلى ملفات النسخ الاحتياطي. تسمح لك أغلبية الإضافات بتخزين ملفات النسخ الاحتياطي على خادومك وعلى مكانٍ آخر. استخدام خدمة تخزين سحابي من طرف ثالث مثل Dropbox أو Google Drive: وهذا فكرةٌ جيدةٌ إن كنتَ تملك حسابًا على أحد تلك المواقع، أو يمكنك أن تُهيئ حسابًا فيها إن شئت. إرسال النسخ الاحتياطية لك بالبريد الإلكتروني: بعض الإضافات المجانية لا توفر خيارًا إلا هذا الخيار. لكن ذلك قد يؤدي إلى امتلاء صندوق الوارد عندك وليس عمليًا كاستخدام خيار التخزين السحابي. عليك أن تستعمل إضافةً تُسهِّل إجراء الخيار الذي اخترته، والتي عليك ضبطها مرةً واحدةً فقط دون القلق على أداءها. أنصح دائمًا باستخدام إضافة مدفوعة مثل Snapshot Pro، التي تجعل عملية استعادة الملفات المنسوخة أسهل بكثير فيما إذا كنتَ تستعمل إضافةً مجانيةً. القدرة على استعادة المواقع بسرعة على شبكتك هو أمرٌ مهمٌ جدًا، لأنه يقلل من وقت توقف الموقع (أو الشبكة) عن العمل. استخدام إضافة Snapshot Pro لنسخ المواقع على شبكتك يمكنك استخدام إضافة Snapshot Pro لأخذ نسخ احتياطية من كل موقع على شبكتك على حدة. ستحتاج إلى فعل ذلك في كل مرة تُضيف فيها موقعًا إلى الشبكة، إذًا هذه الإضافة مناسبة للشبكات التي تتألف من مواقع خاصة بك أو خاصة بعملائك، أو الشبكات الصغيرة التي يُنشِئ فيها المستخدمون مواقعهم. إذا كان لديك الكثير من المستخدمين الذين يُنشِئون مواقعهم، فربما ستحتاج إلى استخدام إضافة بديلة لإبقاء كل الشبكة منسوخة احتياطيًا، وهذه أمرٌ حسنٌ، وسنفصِّله لاحقًا. بدايةً، ثبِّت إضافة Snapshot Pro عبر لوحة تحكم WPMU DEV وفعِّلها لكامل الشبكة. قبل إنشاء أيّة نسخ احتياطية، عليك أن تُضيف وجهات للأماكن التي تريد أن تُرسَل إليها النسخ الاحتياطية. اذهب إلى «Snapshots > Destinations» لكي ترى الخيارات: انقر على زر «Add New» لكل وجهة تريد إضافتها واتبع التعليمات. ستحتاج إلى السماح للإضافة بالوصول إلى حسابك إن كنتَ ستستخدم خدمة من طرف ثالث مثل Dropbox. بعد أن تفعل ذلك، يمكنك أن تضبط النسخ الاحتياطية. اذهب إلى «Snapshots > Add New» في لوحة تحكم مدير الشبكة لإنشاء نسخة من أحد المواقع. ابدأ بتعريف ما هو الموقع الذي ستأخذ نسخةً منه ثم أضف أيّة ملاحظات: ثم اختر الملفات التي تريد أخذ نسخةٍ احتياطيةٍ منها. ربما تريد إنشاء نسختين من نفس الموقع، واحدة فيها كل الملفات، وواحدة فيها الملفات المرفوعة فقط. أو ربما تريد إنشاء نسخة احتياطية وحيدة: إذا اخترتَ «Include common files» فسيتم تضمين الملفات المرفوعة في النسخة الاحتياطية فقط. أما إذا اخترت «Include selected files» فعليك حينها أن تختار ما هي الملفات التي عليك تضمينها في هذه النسخة الاحتياطية. في لقطة الشاشة السابقة، اخترتُ أخذ نسخة احتياطية من كل شي. الخطوة التالية هي اختيار ما هي جداول قاعدة البيانات التي تريد نسخها احتياطيًا، وما هي المدة الفاصلة بين عمليات النسخ: يمكنك تحديد جداول معيّنة أو كل الجداول المتعلقة بالموقع (وهذا هو الخيار الذي اخترتُه). يمكنك أن تختار تواتر أخذ النسخ الاحتياطية التلقائية: اخترتُ في الصورة السابقة أخذ النسخة الاحتياطية يوميًا. عليك بعد ذلك أن تُحدِّد عدد النسخ التي تريد الاحتفاظ بها على الخادوم. إذا تركت هذا الحقل مضبوطًا إلى 0، فسيتم الإبقاء على جميع النسخ الاحتياطية، لكن ذلك سيستهلك الكثير من المساحة التخزينية. غيّرتُ الرقم إلى 7، للإبقاء على النسخ الاحتياطية لأسبوعٍ كامل. إذا كنتَ تستضيف مواقع العملاء على شبكتك، فأنصحك بإبقاء ملفات النسخ الاحتياطي لشهر. في النهاية، حدِّد الوجهة التي تريد أن تحفظ النسخة الاحتياطية فيها: إذا اخترتَ وجهةً بعيدةً، فستحفظ الإضافة النسخ الاحتياطية محليًا أيضًا، إذا كنت تستخدم خدمة من طرف ثالث فيمكنك تحديد المجلد الذي تريد تخزين هذه النسخة الاحتياطية فيه. أجد أنَّ هذا مفيدٌ لأن هنالك أكثر من نسخة احتياطية لأكثر من موقع على كل شبكة من شبكاتي، ولا أريد تخزينها جميعًا في نفس المجلد. لذا سأنشِئ مجلدًا باسم الموقع في حقل «Directory (optional)»، ويمكنك استخدام نفس المجلد لتخزين أكثر من نسخة احتياطية، لذا لو كنتَ تُنشِئ أكثر من نسخة لكل موقع في شبكتك (على سبيل المثال، نسخة لقاعدة البيانات والملفات المرفوعة، ونسخة أخرى ذات تواتر أقل لكل الملفات) فيمكنك أن تحفظها جميعًا بنفس المكان. اضغط على زر «Create Snapshot» للانتهاء. ستُعرَض تفاصيل النسخة الاحتياطية الآن في صفحة Snapshots الرئيسية: يمكنك من هذه الصفحة تعديل النسخ الاحتياطية، أو تغيير تواتر النسخ، واستخدامها للقيام بعملية استعادة بضغطة زر واحدة. أخذ نسخ احتياطية من كامل الشبكة استخدام إضافة Snapshot Pro يسمح لك باستعادة موقع بضغط رز واحدة، وذلك يعني أيضًا أنَّه لو كان هنالك أكثر من موقع يعاني من مشاكل في شبكتك، فيمكنك أن تستعيد النسخة الاحتياطية دون التأثير على المواقع الأخرى في الشبكة. لكن ماذا لو حدثت مشكلة لكامل شبكتك وعليك أن تستعيد كل شيء؟ هنالك الكثير من الإضافات التي تسمح لك بأخذ نسخ احتياطية من كامل تثبيت ووردبريس (وستأخذ نسخة احتياطية لكامل الشبكة أيضًا). هنالك بعض الإضافات المدفوعة للقيام بذلك، لكن إن كنتَ تستعمل إضافة Snapshot Pro لأخذ نسخ احتياطية لكل موقع على حدة، فمن المرجح أنَّك تريد إضافةً مجانيةً للقيام بهذه المهمة. أنصحك بتثبيت وتفعيل إضافة من قائمة الإضافات المجانية للنسخ الاحتياطي واستخدامها لتهيئة عملية نسخ احتياطي مؤتمت لكامل الشبكة بشكلٍ دوري. شخصيًا أستخدمُ إضافة BackWPup التي تُرسِل نسخةً من قاعدة البيانات والملفات الخاصة بي إلى حساب Dropbox بشكلٍ دوري. المشكلة الوحيدة مع هذه النسخة المجانية هي أنَّها لا تحتوي على ميزة الاستعادة بنقرة واحدة، وهذا يعني أنَّه لو أردتُ استعادة شبكتي، فعليّ فعل ذلك يدويًا من قاعدة البيانات والملفات. إن لم تكن مرتاحًا بفعل ذلك، فربما ستفضِّل استخدام إضافة مدفوعة لنسخ كامل تثبيت ووردبريس (وليس فقط المواقع الموجودة على شبكتك كلًا على حدة). تحسين أمان الشبكة يعتمد مستخدموك وعملاؤك على شبكتك لاستضافة مواقعهم الثمينة، لذا عليك أن تتأكد أنَّ مواقعهم ستبقى آمنة. ولفعل ذلك عليك استخدام إضافة أمنية، مثل إضافة Defender. لننظر إلى كيفية تهيئتها. أولًا، ثبِّت إضافة Defender باستخدام لوحة تحكم WPMU DEV وفعِّلها على كامل الشبكة. ثم اذهب إلى «Defender > Dashboard» لتبدأ عملية التهيئة: تأمين الشبكة تأمين الشبكة يعني تصعيب حصول الأشخاص على صلاحيات غير مُصرَّح بها. ويمكنك فعلك ذلك في الصفحة الرئيسية للإضافة، أو في صفحة «Defender > Hardener». اتبع التعليمات التي تتلوها عليك الإضافة لجعل موقعك أكثر أمانًا. وهذا يتضمن أشياء مثل: تعديل سابقة قاعدة البيانات: جعل بنية قاعدة البيانات الخاصة بك أكثر غموضًا. تعطيل مُحرِّر الملفات: هذا يعطل صفحات التحرير للإضافات والقوالب، وهذا أمرٌ حسن! تعطيل trackbacks و pingbacks: حاجتك إلى فعل ذلك تعتمد فيما إذا كنتَ تريد أن تكون أنت ومستخدموك اجتماعيين. وهي تمنع الأشخاص من عمل pinging لشبكتك بشكل متكرر، مما قد يؤدي إلى حدوث خلل فيها. الوقاية من حدوث تسريب في المعلومات: وهذا يتضمن وضع ملفات ‎.htaccess في المجلدات المناسبة لمنع المهاجمين من الوصول إلى محتواها. تعطيل تنفيذ PHP: وهذا يمنع المهاجمين من استخدام أيّة ثغرات في القوالب أو الإضافات لرفع لملفات PHP إلى موقعك وتشغيل شيفرات خبيثة. تحديث المفاتيح الأمنية: من الجيد تحديثها في فتراتٍ متقاربة لمنع القدرة على الوصول إليها واستعمالها. إذا فعلتَ ذلك، فسيتم تسجيل خروجك من موقعك (وكذلك الأمر لجميع المستخدمين)، لذا أفضِّل فعل ذلك في آخر الليل. مراقبة قوائم الحجب (blacklist): التحقق من أنَّك غير محجوب من محرك Google. سأذهب الآن إلى صفحة «Defender > Hardener» وستخبرني أنَّ كل شيءٍ مضبوطٌ ضبطًا صحيحًا: فحص موقعك بحثًا عن مشاكل يمكنك في Defender أن تقوم بفحصٍ يدوي وأن تضبط فحصًا مؤتمتًا للتحقق من أنَّ كل شيء على ما يرام في موقعك. لنبدأ بعمل فحص يدوي. اذهب إلى صفحة «Defender > Scan» ثم اضغط على زر «Scan my Website» وسيبدأ Defender بفحص شبكتك. اصبر قليلًا، قد يأخذ ذلك بعض الوقت، خصوصًا إذا كانت لديك الكثير من المواقع في شبكتك. يمكنك أن تهيئ عملية فحص مؤتمتة وذلك بالذهاب إلى «Defender > Dashboard» ثم التمرير إلى «Setup Automated Scans» والضغط على زر «Enable» لتفعيله: تحسين أداء الشبكة يرغب مستخدموك بأن تعمل مواقعهم بكفاءة عالية وسترغب أنت بأن تعمل الشبكة بسلاسة. يمكنك استخدام إضافة Hummingbird لتحسين الأداء. ابدأ بتثبيت الإضافة من لوحة تحكم WPMU DEV ثم تفعليها على كامل الشبكة. ثم اذهب إلى «Hummingbird > Dashboard» لترى نتائج اختبار إضافة Hummingbird لشبكتك: إذا مررتَ إلى الأسفل، فستجد أنَّ إضافة Hummingbird تعرض اقتراحات لتحسين الأداء. تلك الاقتراحات تعتمد على طريقة ضبطك للموقع. هذه هي النتائج الخاصة بموقعي: اتبع التعليمات الموجودة لتحسين الأداء، والتي تتضمن: التخزين المؤقت: يمكن للتخزين المؤقت أنَّ يُسرِّع وقت تحميل الصفحة. مراقبة الموقع: ستتحقق إضافة Hummingbird من أنَّ موقعك يعمل، وسترسِل إليك إشعارًا لتنبيهك إن كان لا يعمل. الضغط: وهذا يجعل الملفات أصغر مما يؤدي مما يُسرِّع من تحميلها. حذف الموارد التي توقف عرض الصفحة: وهذا ينقل الموارد مثل ملفات JavaScript و CSS إلى مكانها الصحيح في صفحات الموقع. تحسين حجم الصور: ثبِّت إضافة WP Smush Pro التي ستُحسّن الصور أثناء رفعك لهم. ثبِّتها ثم فعلها على كامل الشبكة، واتركها تعمل عملها بصمت. بعد أن اتبعتَ النصائح السابقة، فأجرِ اختبارًا آخر بالضغط على الزر الموجود في صفحة الإضافة الرئيسية. تحسّنت نتيجة موقعي من 70% إلى 86%: إذا أردتَ أن تُجري تعديلات أخرى لتصل إلى 100% تقريبًا، فتوفِّر لك إضافة Hummingbird نصائح إضافية لتتبعها. اذهب إلى «Hummingbird > Performance Report» ثم اضغط على أزرار «Improve Score» لمزيدٍ من المعلومات: إدارة شبكتك بكفاءة ستساعدها لكي تعمل بسلاسة إدارة شبكة ناجحة لا يعني تفعيل ميزة تعدد المواقع وانتظار إنشاء المواقع فيها، وإنما عليك أيضًا أن تراقب شبكتك للتأكد من تجنبك لأيّة مشاكل، ولإبقائها محدثةً دومًا ولتحسّن أداءها. إذا اتبعت الخطوات التي ذكرناها في هذا الجزء من الدورة، فستتمكن من تشغيل شبكتك بسلاسة دون مشاكل. أكملتَ الآن الدورة التدريبية لتعلم إنشاء وإدارة شبكة متعددة المواقع في ووردبريس، يجب أن تكون قد تعلمتَ فيها: ما هي ميزة تعدد المواقع وما فرقها عن المواقع المفردة المستقلة. كيفية تفعيل وضبط الشبكة متعددة المواقع. كيف تُضيف مواقع ومستخدمين إلى شبكتك بصفتك مديرًا للشبكة. كيف تُفعِّل قابلية إنشاء المواقع من قِبل المستخدمين وتُحسِّن من تجربة المستخدم خلال هذه العملية. كيف تستخدم الشبكة متعددة المواقع لاستضافة مواقع العملاء (أو مواقعك الخاصة) وتستعمل ربط النطاقات لتفعيل استخدام أكثر من نطاق. كيفية إنشاء مجتمع لمستخدمي شبكتك. كيفية إدارة الشبكة، بما في ذلك إبقاءها مُحدَّثةً دومًا، وتفادي المدونات المزعجة، وتحسين الأمان والأداء. إذا أردتَ المزيد من النصائح أو حلًا لمشاكل، فيمكنك أن تسأل في التعليقات أو أن تضيف أسئلة جديدة إلى قسم الأسئلة. ترجمة –وبتصرّف– للمقال WordPress Multisite Masterclass: Managing your Network لصاحبته Rachel McCollin.
  25. سنشرح في هذا الدرس كيفية إعداد بيئة برمجية محليّة للغة بايثون 3 في توزيعة أوبنتو 16.04 أو دبيان 8. بايثون هي لغةٌ سهلة القراءة للغاية ومتنوعة ومتعددة الاستخدامات، واسمها مستوحى من مجموعة كوميدية بريطانية باسم «Monty Python»، وكان أحد الأهداف الأساسية لفريق تطوير بايثون هو جعل اللغة مرحةً وسهلة الاستخدام، وإعدادها بسيطٌ، وطريقة كتابتها مباشرة وتعطيك تقريرًا مباشرًا عند حدوث أخطاء، وهي خيارٌ ممتازٌ للمبتدئين والوافدين الجدد على البرمجة. إصدار بايثون 3 هو الإصدار الحالي من اللغة ويُعتَبَر أنَّه مستقبل بايثون. سيُرشِدُك هذا الدرس خطوةً بخطوة إلى كيفية تثبيت بايثون 3 على نظام لينكس المحلي عندك وذلك عبر سطر الأوامر، وصحيحٌ أنَّ هذا الدرس يشرح عملية التثبيت لتوزيعة أوبنتو 16.04 أو دبيان 8، إلا أنَّ المفاهيم الأساسية فيه تنطبق على أيّة توزيعة أخرى. المتطلبات المسبقة يجب أن يكون لديك حاسوبٌ يعمل بتوزيعة أوبنتو 16.04 أو دبيان 8 (أو أيّ إصدار آخر من دبيان)، وأن تكون لديك امتيازات إدارية على النظام، بالإضافة إلى اتصالٍ بالإنترنت. الخطوة الأولى: إعداد بايثون 3 سنُثبِّت ونضبط بايثون عبر سطر الأوامر، والذي هو طريقةٌ غيرُ رسوميةٍ للتعامل مع الحاسوب، فبدلًا من الضغط على الأزرار فستكتب نصًا وتعطيه للحاسوب لينفذه وسيُظهِر لك ناتجًا نصيًا أيضًا. يمكن أن يساعدك سطر الأوامر على تعديل أو أتمتة مختلف المهام التي تنجزها على الحاسوب يوميًا، وهو أداةٌ أساسيةٌ لمطوري البرمجيات، وهنالك الكثير من الأوامر التي عليك تعلمها لكي تتمكن من الاستفادة منه. هنالك مقالات في أكاديمية حسوب (كدرس مدخل إلى طرفيّة لينكس Linux Terminal) ستعلمك أساسيات سطر الأوامر، وهنالك كتاب «سطر أوامر لينكس» الذي يُعتَبر مرجعًا تفصيلًا لطريقة التعامل مع سطر الأوامر. ستجد تطبيق «Terminal» (البرنامج الذي تستعمله للوصول إلى سطر الأوامر) بالضغط على أيقونة Dash في الزاوية العليا اليسرى من الشاشة ثم كتابة «terminal» في شريط البحث، ثم الضغط على أيقونة التطبيق التي ستظهر بعدئذٍ. يمكنك بشكلٍ بديلٍ أن تضغط على Ctrl+Alt+T في لوحة المفاتيح بنفس الوقت لتشغيل تطبيق Terminal. أما على دبيان 8 فيمكنك فتح القائمة الموجودة أيضًا في الزاوية العليا اليسرى من الشاشة ثم البحث عن «terminal» في شريط البحث، ثم النقر على أيقونة التطبيق. يمكنك أيضًا أن تضغط على Ctrl+Alt+T في لوحة المفاتيح بنفس الوقت لتشغيل تطبيق Terminal. تأتي توزيعات أوبنتو 16.04 ودبيان 8 وإصدارات دبيان الأخرى مثبتةً مسبقًا مع بايثون 3 وبايثون 2. للتأكد أنَّك تملك آخر الإصدارات منها فحدِّث نظامك باستخدام apt-get: sudo apt-get update sudo apt-get -y upgrade الخيار ‎-y يعني أنَّك توافق على تثبيت جميع الحزم القابلة للتحديث، لكن قد تحتاج إلى تأكيد ذلك عند تحديث النظام وذلك اعتمادًا على الحزم التي ستُحدَّث ونسخة نظامك. بعد إكمال العملية، يمكننا التحقق من إصدار بايثون 3 المُثبّت في النظام بكتابة: python3 -V ستحصل على مخرجات في نافذة الطرفية والتي ستريك ما هو إصدار بايثون المثبّت. قد يختلف الرقم بناءً على النسخة المثبتة في توزيعتك، لكن يجب أن يكون شبيهًا بما يلي: Python 3.5.2 لإدارة الحزم البرمجية الخاصة ببايثون، فثبّت pip: sudo apt-get install -y python3-pip الأداة pip هي أداةٌ تعمل مع لغة بايثون تُثَبِّت وتدير الحزم البرمجية التي قد نحتاج إلى استخدامها في تطوير مشاريعنا. يمكنك تثبيت حزم بايثون بكتابة الأمر: pip3 install package_name حيث عليك وضع اسم الحزمة أو المكتبة التابعة لبايثون مكان package_name مثل Django لتطوير الويب أو NumPy لإجراء حسابات علمية. لذا إن شئتَ تنزيل NumPy فيمكنك تنفيذ الأمر pip3 install numpy. بعد أن انتهينا من ضبط بايثون وتثبيت pip، فيمكننا الآن إنشاء «بيئة وهمية» (virtual environment) لمشاريعنا. الخطوة الثانية: إعداد بيئة وهمية تُمكِّنك البيئات الوهمية من إنشاء مساحة معزولة في حاسوبك مخصصة لمشاريع بايثون، مما يعني أنَّ كل مشروع تعمل عليه يملك مجموعة من الاعتماديات (dependencies) والتي لن تؤثِّر على غيره من المشاريع. يوفِّر لنا ضبط بيئةٍ برمجيةٍ تحكمًا أكبر بمشاريع بايثون وإمكانية التعامل مع إصداراتٍ مختلفةٍ من حزم بايثون. وهذا مهمٌ كثيرًا عندما تتعامل مع الحزم الخارجية. يمكنك ضبط أيُّ عددٍ تشاء من البيئات الوهمية، وكل بيئة تُمثِّل مجلدًا في حاسوبك الذي فيه عددٌ من السكربتات. علينا أولًا تثبيت وحدة (module) برمجية باسم venv، وهي جزءٌ من مكتبة بايثون3 القياسية، وذلك لكي نتمكن من استخدام الأمر pyvenv الذي سيُنشِئ البيئات الوهمية لنا. لنثبِّت venv على نظامنا بكتابة: sudo apt-get install -y python3-venv سنتمكن الآن من إنشاء بيئات وهمية بعد إتمام التثبيت، لنختار ما هو المجلد الذي سنضع فيه بيئات بايثون، أو لننشِئ مجلدًا جديدًا باستخدام الأمر mkdir كما يلي: mkdir environments cd environments بعد أن انتقلتَ إلى المجلد الذي تريد احتواء البيئات فيه، فتستطيع الآن إنشاء بيئة جديدة بتنفيذ الأمر الآتي: pyvenv my_env سيُنشِئ الأمر pyvenv مجلدًا جديدًا فيه بعض الملفات التي يمكننا عرضها باستخدام الأمر ls: ls my_env bin include lib lib64 pyvenv.cfg share تعمل هذه الملفات مع بعضها لضمان أنَّ مشاريعك معزولةٌ عن بقية النظام، لكي لا تختلط ملفات النظام مع ملفات المشاريع. وهذا أمرٌ حسنٌ لإدارة الإصدارات ولضمان أنَّ كل مشروع يملك وصولًا إلى حزمٍ معيّنة التي يحتاج لها. تتوافر أيضًا Python Wheels والتي هي صيغة بناء حزمٍ لبايثون والتي يمكن أن تُسرِّع من تطوير البرامج بتقليل عدد المرات التي تحتاج فيها إلى بناءٍ (compile) للمشروع، وهي موجودةٌ في مجلد share في توزيعة أوبنتو 16.04 لكنها ستكون في دبيان 8 في أحد مجلدات lib وليس في share. عليك تفعيل البيئة لاستخدامها، وذلك بكتابة الأمر الآتي الذي سيُنفِّذ سكربت التفعيل: source my_env/bin/activate يجب أن تظهر الآن سابقةٌ (prefix) في المِحث (prompt) والتي هي اسم البيئة المستخدمة، وفي حالتنا هذه يكون اسمها my_env، وقد يكون مظهر المِحَث مختلفًا في توزيعة دبيان، وذلك اعتمادًا على الإصدار المستخدم؛ لكن يجب أن تشاهد اسم البيئة بين قوسين في بداية السطر: (my_env) sammy@sammy:~/environments$ السابقة ستسمح لك بمعرفة أنَّ البيئة my_env مفعلة حاليًا، وهذا يعني أننا سنستخدم إعدادات وحزم هذه البيئة عند إنشائنا لمشاريع جديدة. ملاحظة: يمكنك داخل البيئة الوهمية أن تستخدم الأمر python بدلًا من python3 والأمر pip بدلًا من pip3 إن شئتَ. أما إذا كنتَ ستستخدم بايثون 3 خارج البيئة الوهمية، فيجب عليك حينها استخدام python3 و pip3 حصرًا. يجب أن تكون بيئتك الوهمية جاهزةً للاستخدام بعد اتباعك للخطوات السابقة. الخطوة الثالثة: إنشاء برنامج بسيط بعد أن أكملنا ضبط بيئتنا الوهمية، لننشِئ برنامجًا بسيطًا يعرض العبارة «Hello World!‎»، وبهذا سنتحقق من أنَّ البيئة تعمل عملًا صحيحًا، وستصبح طريقة إنشاء برامج بايثون مألوفةً لديك إن كنتَ وافدًا جديدًا على اللغة. علينا أولًا تشغيل محرر ملفات نصية لإنشاء ملف جديد، وليكن المحرر nano الذي يعمل من سطر الأوامر: (my_env) sammy@sammy:~/environments$ nano hello.py بعد فتح الملف في نافذة الطرفية، فاكتب البرنامج الخاص بنا: print("Hello, World!") أغلق محرر nano بالضغط على Ctrl+x ثم اضغط على y عندما يسألك عن حفظ الملف. بعد أن يُغلَق محرر nano وتعود إلى سطر الأوامر، فحاول تشغيل البرنامج: (my_env) sammy@sammy:~/environments$ python hello.py سيؤدي برنامج hello.py الذي أنشأتَه إلى طباعة الناتج الآتي في الطرفية: Hello, World! للخروج من البيئة، فاكتب الأمر deactivate وستعود إلى مجلدك الأصلي. الخلاصة تهانينا! لقد ضبطتَ الآن بيئة تطوير للغة بايثون 3 في جهازك الذي يعمل بتوزيعة أوبنتو أو دبيان، حان الآن الوقت للتعمق بلغة بايثون وإنشاء برامج رائعة! بالتوفيق. ترجمة -وبتصرّف- للمقال How To Install Python 3 and Set Up a Local Programming Environment on Ubuntu 16.04 لصاحبته Lisa Tagliaferri اقرأ أيضًا المقالة التالية: كيف تكتب أول برنامج لك المقالة السابقة: اعتبارات عملية للاختيار ما بين بايثون 2 و بايثون 3 المرجع الشامل إلى تعلم لغة بايثون كتاب البرمجة بلغة بايثون
×
×
  • أضف...