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

المساهمون
  • المساهمات

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

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

السُّمعة بالموقع

69 Excellent

8 متابعين

آخر الزُوّار

3,274 زيارة للملف الشّخصي
  1. تمهيد تتوسع قواعد البيانات بسرعة مع مرور الزمن، وتكاد في بعض الأحيان أن تملأ المساحة التخزينية المتاحة في نظام الملفات كلها. وقد تتعرض أيضًا إلى مشاكل في الإدخال والإخراج نتيجةً لمحاولة عدِّة خدمات الكتابة على (أو القراءة من) نفس القسم معًا. هذا الدرس سيفيدك لو كنتَ تريد إضافة المزيد من المساحة التخزينية، أو استخدام خصائص جهاز التخزين لزيادة الأداء (ربما عبر استخدام 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
  2. تمهيد كلما كانت صفحات الموقع أسرع تحميلًا كلما ازداد احتمال بقاء الزائر متصفحًا للموقع، وعندما تمتلئ صفحات مواقع الويب بالصور والمحتوى التفاعلي الظاهر عبر سكربتات تُحمَّل في الخلفية، فلن تكون عملية فتح «صفحة» ويب أمرًا هينًا، إذ تتضمن طلب ملفاتٍ عدِّة من خادوم الويب ملفًا ملفًا، وعملية تقليل تلك الطلبيات هي إحدى سُبُل تسريع موقعك. يمكن فعل ذلك بطرائق عدِّة، لكن إحدى أهم الخطوات التي يجب إجراؤها هي ضبط التخزين المؤقت في المتصفح (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.
  3. من الطبيعي أن نرتكب نحن البشر أخطاءً، لكننا نصحح تلك الأخطاء بنفسنا، أي أننا نميل إلى التعلم من أخطائنا ولا نرتكب الخطأ نفسه مرتين؛ أغلبية الأخطاء التي أقع فيها عند تطوير مواقع ووردبريس تأتي من محاولتي لتقليل الوقت اللازم لإنشاء الميزات، لكن ينتهي المطاف بمَن يعتمد هذه الطريقة بألمٍ في الرأس وندمٍ على أخطائه؛ لكن التعلم من هفوات الآخرين أفضل من الوقوع بكثير من الأخطاء وتضييع الوقت. الخطأ الشائع الأول: عدم تفعيل ميزة التنقيح في ووردبريس لماذا علينا تفعيل ميزة التنقيح (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
  4. استخدام الدوال في قواعد بيانات MySQL

    سنشرح في هذه الدرس كيفية تقليل عدد السجلات المُعادة من طلبية 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.
  5. تمهيد السلاسل النصية في بايثون هي مجموعةٌ مُنشأةٌ من المحارف المنفصلة والتي تُمثِّل الأحرف والأرقام والفراغات والرموز. ولأنَّ السلسلة النصية هي «سلسلة»، فيمكن الوصول إليها كغيرها من أنواع البيانات المتسلسلة، عبر الفهارس والتقسيم. سيشرح لك هذا الدرس كيفية الوصول إلى السلاسل النصية بفهارسها، وتقسيمها إلى مجموعات من المحارف، وسيُغطي طرائق إحصاء المحارف وتحديد مواضعها. آلية فهرسة السلاسل النصية وكما في نوع البيانات list الذي فيه عناصر مرتبطة بأرقام، فإنَّ كل محرف في السلسلة النصية يرتبط بفهرس معيّن، بدءًا من الفهرس 0. فللسلسلة النصية Sammy Shark!‎ ستكون الفهارس والمحارف المرتبطة بها كالآتي: وكما لاحظت، سيرتبط المحرف S بالفهرس 0، وستنتهي السلسلة النصية بالفهرس 11 مع الرمز !. لاحظ أيضًا أنَّ الفراغ بين كلمتَي Sammy و Shark له فهرسٌ خاصٌ به، وفي مثالنا سيكون له الفهرس 5. علامة التعجب (!) لها فهرسٌ خاصٌ بها أيضًا، وأيّة رموز أو علامات ترقيم أخرى مثل *#$&.;? سترتبط بفهرسٍ مخصصٍ لها. وحقيقة أنَّ المحارف في بايثون لها فهرسٌ خاصٌ بها ستعني أنَّ بإمكاننا الوصول إلى السلاسل النصية وتعديلها كما نفعل مع أنواع البيانات المتسلسلة الأخرى. الوصول إلى المحارف عبر أرقام الفهارس الموجبة يمكننا الحصول على محرف من سلسلة نصية بالإشارة إليه عبر فهرسه. يمكننا فعل ذلك بوضع رقم الفهرس بين قوسين مربعين. سنُعرِّف في المثال الآتي سلسلةً نصيةً ونطبع المحرف المرتبط بالفهرس المذكور بين قوسين مربعين: ss = "Sammy Shark!" print(ss[4]) الناتج: y عندما نُشير إلى فهرسٍ معيّنٍ في سلسلةٍ نصيةٍ، فستُعيد بايثون المحرف الموجود في ذاك الموضع، ولمّا كان المحرف y موجودًا في الفهرس الرابع في السلسلة النصية ss = "Sammy Shark!"‎ فعندما طبعنا ss[4] فظهر الحرف y. الخلاصة: أرقام الفهارس ستسمح لنا بالوصول إلى محارف معيّنة ضمن سلسلة نصية. الوصول إلى المحارف عبر أرقام الفهارس السالبة إذا كانت لديك سلسلةٌ طويلةٌ وأردنا تحديد أحد محارفها لكن انطلاقًا من نهايتها، فعندئذٍ نستطيع استخدام الأرقام السالبة للفهارس، بدءًا من الفهرس ‎-1. لو أردنا أن نستخدم الفهارس السالبة مع السلسلة النصية Sammy Shark!‎، فستبدو كما يلي: يمكننا أن نطبع المحرف r في السلسلة السابقة في حال استخدمنا الفهارس السالبة بالإشارة إلى المحرف الموجود في الفهرس ‎-3 كما يلي: print(ss[-3]) الخلاصة: يمكننا الاستفادة من الفهارس السالبة لو أردنا الوصول إلى محرف في آخر سلسلة نصية طويلة. تقسيم السلاسل النصية يمكننا أن نحصل على مجال من المحارف من سلسلة نصية، فلنقل مثلًا أننا نريد أن نطبع الكلمة Shark فقط، يمكننا فعل ذلك بإنشائنا «لقسم» من السلسلة النصية، والذي هو سلسلةٌ من المحارف الموجودة ضمن السلسلة الأصلية. فالأقسام تسمح لنا بالوصول إلى عدِّة محارف دفعةً واحدة باستعمال مجال من أرقام الفهارس مفصولة فيما بينها بنقطتين رأسيتين [x:y]: print(ss[6:11]) الناتج: Shark عند إنشائنا لقسم مثل [6:11] فسيُمثِّل أوّل رقم مكان بدء القسم (متضمنًا المحرف الموجود عند ذاك الفهرس)، والرقم الثاني هو مكان نهاية القسم (دون تضمين ذاك المحرف)، وهذا هو السبب وراء استخدمنا لرقم فهرس يقع بعد نهاية القسم الذي نريد اقتطاعه في المثال السابق. نحن نُنشِئ «سلسلةً نصيةً فرعيةً» (substring) عندما نُقسِّم السلاسل النصية، والتي هي سلسلةٌ موجودةٌ ضمن سلسلةٍ أخرى. وعندما نستخدم التعبير ss[6:11] فنحن نستدعي السلسلة النصية Shark التي تتواجد ضمن السلسلة النصية Sammy Shark!‎. إذا أردت تضمين نهاية السلسلة (أو بدايتها) في القسم الذي ستُنشِئه، فيمكننا ألّا نضع أحد أرقام الفهارس في string[n:n]. فمثلًا، نستطيع أن نطبع أوّل كلمة من السلسلة ss –أي Sammy– بكتابة ما يلي: print(ss[:5]) فعلنا ذلك بحذف رقم الفهرس قبل النقطتين الرأسيتين، ووضعنا رقم فهرس النهاية فقط، الذي يُشير إلى مكان إيقاف اقتطاع السلسلة النصية الفرعية. لطباعة منتصف السلسلة النصية إلى آخرها، فسنضع فهرس البداية فقط قبل النقطتين الرأسيتين، كما يلي: print(ss[7:]) الناتج: hark! بكتابة فهرس البداية فقط قبل النقطتين الرأسيتين وترك تحديد الفهرس الثاني، فإنَّ السلسلة الفرعية ستبدأ من الفهرس الأول إلى نهاية السلسلة النصية كلها. يمكنك أيضًا استخدام الفهارس السالبة في تقسيم سلسلة نصية، فكما ذكرنا سابقًا، تبدأ أرقام الفهارس السلبية من الرقم ‎-1، ويستمر العد إلى أن نصل إلى بداية السلسلة النصية. وعند استخدام الفهارس السالبة فسنبدأ من الرقم الأصغر لأنه يقع أولًا في السلسلة. لنستخدم فهرسين ذوي رقمين سالبين لاقتطاع جزء من السلسلة النصية ss: print(ss[-4:-1]) الناتج: ark السلسلة النصية «ark» مأخوذة من السلسلة النصية «Sammy Shark!‎» لأنَّ الحرف «a» يقع في الموضع ‎-4 والمحرف k يقع قبل الفهرس ‎-1 مباشرةً. تحديد الخطوة عند تقسيم السلاسل النصية يمكن تمرير معامل ثالث عند تقسيم السلاسل النصية إضافةً إلى فهرسَي البداية والنهاية، وهو الخطوة، التي تُشير إلى عدد المحارف التي يجب تجاوزها بعد الحصول على المحرف من السلسلة النصية. لم نُحدِّد إلى الآن الخطوة في أمثلتنا، إلا أنَّ قيمته الافتراضية هي 1، لذا سنحصل على كل محرف يقع بين الفهرسين. لننظر مرةً أخرى إلى المثال السابق الذي يطبع السلسلة النصية الفرعية «Shark»: print(ss[6:11]) Shark سنحصل على نفس النتائج بتضمين معامل ثالث هو الخطوة وقيمته 1: print(ss[6:11:1]) Shark إذًا، إذا كانت الخطوة 1 فهذا يعني أنَّ بايثون ستُضمِّن جميع المحارف بين فهرسين، وإذا حذفتَ الخطوة فستعتبرها بايثون مساويةً للواحد. أما لو زدنا الخطوة، فسنرى أنَّ بعض المحارف ستهمل: print(ss[0:12:2]) SmySak تحديد الخطوة بقيمة 2 كما في ss[0:12:2] سيؤدي إلى تجاوز حرف بين كل حرفين، ألقِ نظرةً على المحارف المكتوبة بخطٍ عريضٍ: Sammy Shark! لاحظ أنَّ الفراغ الموجود في الفهرس 5 قد أُهمِل أيضًا عندما كانت الخطوة 2. إذا وضعنا قيمةً أكبر للخطوة، فسنحصل على سلسلةٍ نصيةٍ فرعيةٍ أصغر بكثير: print(ss[0:12:4]) Sya حذف الفهرسين وترك النقطتين الرأسيتين سيؤدي إلى إبقاء كامل السلسلة ضمن المجال، لكن إضافة معامل ثالث وهو الخطوة سيُحدِّد عدد المحارف التي سيتم تخطيها. إضافةً إلى ذلك، يمكنك تحديد رقم سالب كخطوة، مما يمكِّنك من كتابة السلسلة النصية بترتيبٍ معكوس إذا استعملتَ القيمة ‎-1: print(ss[::-1]) الناتج: !krahS ymmaS لنجرِّب ذلك مرةً أخرى لكن إذا كانت الخطوة ‎-2: print(ss[::-2]) الناتج: !rh ma في المثال السابق (ss[::-2])، سنتعامل مع كامل السلسلة النصية لعدم وجود أرقام لفهارس البداية والنهاية، وسيتم قلب اتجاه السلسلة النصية لاستخدامنا لخطوةٍ سالبة. بالإضافة إلى أنَّ الخطوة ‎-2 ستؤدي إلى تخطي حرف بين كل حرفين بترتيبٍ معكوس: !krahS[whitespace]ymmaS سيُطبَع الفراغ في المثال السابق. الخلاصة: تحديد المعامل الثالث عند تقسيم السلاسل النصية سيؤدي إلى تحديد الخطوة التي تُمثِّل عدد المحارف التي سيتم تخطيها عند الحصول على السلسلة الفرعية من السلسلة الأصلية. دوال الإحصاء بعد أن تعرفنا على آلية فهرسة المحارف في السلاسل النصية، حان الوقت لمعاينة بعض الدوال التي تُحصي السلاسل النصية أو تعيد أرقام الفهارس. يمكننا أن نستفيد من ذلك بتحديد عدد المحارف التي نريد استقبالها من مدخلات المستخدم، أو لمقارنة السلاسل النصية. ولدى السلاسل النصية –كغيرها من أنواع البيانات– عدِّة دوال تُستخدم للإحصاء. لننظر أولًا إلى الدالة len()‎ التي تُعيد طول أيّ نوع متسلسل من البيانات، بما في ذلك string و lists و tuples و dictionaries. لنطبع طول السلسلة النصية ss: print(len(ss)) 12 طول السلسلة النصية «Sammy Shark!‎» هو 12 محرفًا، بما في ذلك الفراغ وعلامة التعجب. بدلًا من استخدام متغير، فلنحاول مباشرةً تمرير سلسلة نصية إلى الدالة len()‎: print(len("Let's print the length of this string.")) 38 الدالة len()‎ تُحصي العدد الإجمالي من المحارف في سلسلة نصية. إذا أردنا إحصاء عدد مرات تكرار محرف أو مجموعة من المحارف في سلسلة نصية، فيمكننا استخدام الدالة str.count()‎، لنحاول إحصاء الحرف «a» في السلسلة النصية ss = "Sammy Shark!"‎: print(ss.count("a")) 2 يمكننا البحث عن محرفٍ آخر: print(ss.count("s")) 0 صحيحٌ أنَّ الحرف «S» قد ورد في السلسلة النصية، إلا أنَّه من الضروري أن تبقي بذهنك أنَّ بايثون حساسةٌ لحالة الأحرف، فلو أردنا البحث عن حروفٍ معيّنة بغض النظر عن حالتها، فعلينا حينها استخدام الدالة str.lower()‎ لتحويل حروف السلسلة النصية إلى حروفٍ صغيرة أولًا. يمكنك قراءة المزيد من المعلومات عن دالة str.lower()‎ في درس «مقدمة إلى دوال التعامل مع السلاسل النصية في بايثون 3» لنحاول استخدام الدالة str.count()‎ مع سلسلة من المحارف: likes = "Sammy likes to swim in the ocean, likes to spin up servers, and likes to smile." print(likes.count("likes")) الناتج هو 3، حيث تتواجد مجموعة المحارف «likes» ثلاث مرات في السلسلة النصية الأصلية. يمكننا أيضًا معرفة موقع الحرف أو مجموعة الحروف في السلسلة النصية، وذلك عبر الدالة str.find()‎، وسيُعاد موضع المحرف بناءً على رقم فهرسه. يمكننا أن نعرف متى يقع أوّل حرف «m» في السلسلة النصية ss كالآتي: print(ss.find("m")) 2 أوّل مرة يقع فيها الحرف «m» في الفهرس 2 من السلسلة «Sammy Shark!‎»، يمكنك أن تراجع بداية هذا الدرس لرؤية جدول يبيّن ارتباطات المحارف مع فهارسها في السلسلة السابقة. لنرى الآن مكان أوّل ظهور لمجموعة المحارف «likes» في السلسلة likes: print(likes.find("likes")) 6 أوّل مرة تظهر فيها السلسلة «likes» هي في الفهرس 6، أي مكان وجود الحرف l من likes. ماذا لو أردنا أن نعرف موضع ثاني تكرار للكلمة «likes»؟ نستطيع فعل ذلك بتمرير معاملٍ ثانٍ إلى الدالة str.find()‎ الذي سيجعلها تبدأ بحثها من ذاك الفهرس، فبدلًا من البحث من أوّل السلسلة النصية سنبحث انطلاقًا من الفهرس 9: print(likes.find("likes", 9)) 34 بدأ البحث في هذا المثال من الفهرس 9، وكانت أوّل مطابقة للسلسلة النصية «likes» عند الفهرس 34. إضافةً إلى ذلك، يمكننا تحديد نهاية إلى مجال البحث بتمرير معامل ثالث. وكما عند تقسيم السلاسل النصية، يمكننا استخدام أرقام الفهارس السالبة للعد عكسيًا: print(likes.find("likes", 40, -6)) 64 يبحث آخر مثال عن موضع السلسلة النصية «likes» بين الفهرس 40 و ‎-6، ولمّا كان المعامل الأخير هو رقم سالب، فسيبدأ العد من نهاية السلسلة الأصلية. دوال الإحصاء مثل len()‎ و str.count()‎ و str.find()‎ مفيدةٌ في تحديد طول السلسلة النصية وعدد حروفها وفهارس ورود محارف معيّنة فيها. الخلاصة ستمنحنا القدرة على تحديد محارف بفهارسها أو تقسيم سلسلة نصية أو البحث فيها مرونةً عاليةً عند التعامل مع السلاسل النصية. ولأنَّ السلاسل النصية هي نوعٌ من أنواع البيانات المتسلسلة (كنوع البيانات list)، فيمكننا الوصول إلى عناصرها عبر فهارس خاصة. ترجمة -وبتصرّف- للمقال How To Index and Slice Strings in Python 3 لصاحبته Lisa Tagliaferri.
  6. تمهيد لدى بايثون عدِّة دوال مبنية فيها للتعامل مع السلاسل النصية. تسمح هذه الدوال لنا بتعديل وإجراء عمليات على السلاسل النصية بسهولة. يمكنك أن تتخيل الدوال على أنها «أفعال» يمكننا تنفيذها على عناصر موجودة في الشيفرة. الدوال المبنية في اللغة هي الدوال المُعرَّفة داخل لغة بايثون وهي جاهزة مباشرةً للاستخدام. سنشرح في هذا الدرس مختلف الدوال التي نستطيع استخدامها للتعامل مع السلاسل النصية في بايثون 3. جعل السلاسل النصية بأحرف كبيرة أو صغيرة الدالتان str.upper()‎ و str.lower()‎ ستُعيدان السلسلة النصية بعد تحويل حالة جميع أحرفها الأصلية إلى الأحرف الكبيرة أو الصغيرة (على التوالي وبالترتيب). ولعدم قدرتنا على تعديل السلسلة النصية بعد إنشائها، فستُعاد سلسلةٌ نصيةٌ جديدةٌ. لن تُعدَّل أيّة محارف غير لاتينية في السلسلة النصية الأصلية وستبقى على حالها. لنحوِّل السلسلة النصية Sammy Shark إلى أحرفٍ كبيرة: ss = "Sammy Shark" print(ss.upper()) الناتج: SAMMY SHARK لنحوِّلها الآن إلى أحرفٍ صغيرة: print(ss.lower()) وسينتج: sammy shark ستُسهِّل الدالتان str.upper()‎ و str.lower()‎ عملية التحقق من مساواة سلسلتين نصيتين لبعضهما أو لمقارنتهما وذلك عبر توحيد حالة الأحرف. فلو كتب المستخدم اسمه بأحرفٍ صغيرةٍ فسنستطيع أن نتأكد إن كان مُسجَّلًا في قاعدة البيانات بمقارنته بعد تحويل حالة أحرفه. الدوال المنطقية تتوفر في بايثون عدِّة دوال التي ستتحقق من القيم المنطقية (Boolean). هذه الدوال مفيدةٌ عند إنشائنا للنماذج التي يجب على المستخدمين ملأها؛ فمثلًا، إذا سألنا المستخدم عن الرمز البريدي وأردنا أن نقبل السلاسل النصية التي تحتوي أرقامًا فقط، أو عندما نسأله عن اسمه فسنقبل سلسلةً نصيةً تحتوي حروفًا فقط. هنالك عددٌ من الدوال التي تُعيد قيمًا منطقيةً: str.isalnum()‎: تُعيد true إذا احتوت السلسلة النصية على أرقام وأحرف فقط (دون رموز). str.isalpha()‎: تُعيد true إذا احتوت السلسلة النصية على أحرف فقط (دون أرقام أو رموز). str.islower()‎: تُعيد true إذا كانت جميع أحرف السلسلة النصية صغيرة. str.isnumeric()‎: تُعيد true إذا احتوت السلسلة النصية على أرقام فقط. str.isspace()‎: تُعيد true إذا لم تحتوي السلسلة النصية إلا على الفراغات. str.istitle()‎: تُعيد true إذا كانت حالة أحرف السلسلة النصية كما لو أنها عنوان (أي أنَّ أوّل حرف من كل كلمة كبير، والبقية صغيرة). str.isupper()‎: تُعيد true إذا كانت جميع أحرف السلسلة النصية كبيرة. لننظر إلى بعضها عمليًا: number = "5" letters = "abcdef" print(number.isnumeric()) print(letters.isnumeric()) الناتج: True False استخدام الدالة str.isnumeric()‎ على السلسلة النصية 5 سيُعيد القيمة True، بينما استخدام نفس الدالة على السلسلة النصية abcdef سيُعيد False. وبشكلٍ مماثل، يمكننا معرفة إن كانت حالة الأحرف في سلسلةٍ نصيةٍ كما لو أنها عنوان، أو أنها كبيرة أو صغيرة. لنُنشِئ بدايةً بعض السلاسل النصية: movie = "2001: A SAMMY ODYSSEY" book = "A Thousand Splendid Sharks" poem = "sammy lived in a pretty how town" لنجرّب الدوال المنطقية لمعرفة الناتج (سنعرض كل دالتين وناتجهما تحتهما): print(movie.islower()) print(movie.isupper()) False True print(book.istitle()) print(book.isupper()) True False print(poem.istitle()) print(poem.islower()) False True ستساعدنا معرفة إن كانت أحرف السلسلة النصية بحالة صغيرة أو كبيرة أو كأنها عنوان في تصنيف البيانات تصنيفًا سليمًا، وتوفِّر لنا الفرصة لتوحيد طريقة تخزين البيانات بالتحقق من حالة أحرفها ثم تعديلها وفقًا لذلك. الدوال المنطقية التي تعمل على السلاسل النصية مفيدةٌ أيضًا عندما نريد التحقق إن حقَّقَت مدخلات المستخدم شروطًا معيّنة. الدوال join()‎ و split()‎ و replace()‎ توفِّر الدوال str.join()‎ و str.split()‎ و str.replace()‎ إمكانياتٍ إضافيةً لتعديل السلاسل النصية في بايثون. الدالة str.join()‎ تجمع سلسلتين نصيتين مع بعضهما، لكنها تفعل ذلك بتمرير إحداها إلى الأخرى. لنُنشِئ سلسلةً نصيةً: balloon = "Sammy has a balloon." لنستخدم الآن الدالة str.join()‎ لإضافة فراغات إلى تلك السلسلة النصية كالآتي: " ".join(balloon) إذا طبعنا الناتج: print(" ".join(balloon)) فسنجد أنَّ السلسلة النصية الجديدة هي السلسلة الأولى لكن بين كل حرفين فراغ: S a m m y h a s a b a l l o o n . يمكننا أيضًا استخدام الدالة str.join()‎ لإنشاء مقلوب سلسلة نصية: print("".join(reversed(balloon))) .noollab a sah ymmaS لم نرغب في إضافة أيّة سلسلة نصية إلى أخرى، لذا أبقينا على السلسلة النصية فارغةً دون محتوى داخلها. الدالة str.join()‎ مفيدةٌ أيضًا لجمع قائمة (list) من السلاسل النصية وإخراجها إلى سلسلةٍ وحيدة. لنُنشِئ سلسلة نصية يُفصَل بين كلماتها بفاصلة من القائمة الآتية: print(",".join(["sharks", "crustaceans", "plankton"])) sharks,crustaceans,plankton إذا أردتَ وضع فاصلة ثم فراغ بين القيم في المثال السابق، فيمكنك أن تُعيد كتابة التعبير البرمجي السابق لإضافة فراغ بعد الفاصلة كما يلي: ", ".join(["sharks", "crustaceans", "plankton"]). وكما نستطيع جمع السلاسل النصية مع بعضها بعضًا، نستطيع أيضًا تجزئتها، وذلك عبر الدالة str.split()‎: print(balloon.split()) ['Sammy', 'has', 'a', 'balloon.'] الدالة str.split()‎ ستُعيد قائمة (list) تحتوي سلاسل نصية كانت مفصولةً بالفراغات في السلسلة النصية الأصلية إذا لم يُمرَّر معاملٌ لتحديد محرف الفصل. يمكنك أيضًا استخدام الدالة str.split()‎ لحذف أجزاء معيّنة من السلسلة النصية الأصلية، فلنحاول مثلًا حذف الحرف a: print(balloon.split("a")) ['S', 'mmy h', 's ', ' b', 'lloon.'] حُذِفَ الحرف a من السلسلة النصية وأصبح الناتج مقسومًا عند كل ورود للحرف a مع الإبقاء على الفراغات. تأخذ الدالة str.replace()‎ سلسلةً نصيةً وتُعيد نسخةً محدَّثةً منها بعد إجراء بعض عمليات الاستبدال عليها. لنفترض أنَّ البالون الذي يملكه سامي قد ضاع، ولعدم امتلاك سامي للبالون في الوقت الراهن، فسنُبدِّل الكلمة "has" إلى "had": print(balloon.replace("has","had")) أوّل سلسلة نصية داخل أقواس الدالة replace()‎ هي السلسلة النصية التي نريد استبدالها، والسلسلة النصية الثانية هي السلسلة التي نريد وضعها بدلًا من الأولى. ناتج تنفيذ السطر السابق هو: Sammy had a balloon. استخدام دوال تعديل السلاسل النصية مثل str.join()‎ و str.split()‎ و str.replace سيمنحك تحكمًا كبيرًا بمعالجة السلاسل النصية في بايثون. الخلاصة لقد تعلمنا في هذا الدرس بعض الدوال المُضمَّنة في لغة بايثون لمعالجة النصوص والتعامل معها ومعرفة بعض خصائصها. ترجمة -وبتصرّف- للمقال An Introduction to String Methods in Python 3 لصاحبته Lisa Tagliaferri
  7. مقدمة إلى مستويات RAID

    تُحدِّد خصائص مصفوفات 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
  8. لشبكات ووردبريس متعددة المواقع الكثير من الاستعمالات، حيث يمكنك استخدمها لإنشاء شبكة مثل Edublogs التي تُساعِد الآخرين على إنشاء موقعٍ خاصٍ بهم، أو يمكنك استخدمها لشبكةٍ من المواقع أو المدونات التابعة لمنظمتك، ويمكنك أيضًا استخدامها لاستضافة مواقع لك أو لعملائك. إذا كنتَ كالغالبية من مطوري ومصممي الويب، فمكن المرجح أنَّك تملك عددًا كبيرًا من النطاقات التي سجّلتها والمواقع التي أنشأتها (أو التي ما زلتَ تعمل عليها)، ولا شكّ أنَّ ذلك قد يسبب لك الصداع. وبشكلٍ مشابه، إذا كنتَ تدير شركة أو كنتَ عاملًا حرًا توفِّر استضافةً لمواقع عملائك، فستحتاج إلى إبقاء عدِّة نسخ من ووردبريس محدثةً باستمرار، وهذا سيستهلك وقتًا منك. تستطيع ميزة الشبكات متعددة المواقع أن تحل هذه المشكلة لك. هذا هو الدرس السادس في سلسلتنا المكونة من عشرة دروس التي تشرح التعامل مع الشبكات متعددة المواقع في ووردبريس، ستتعلم في هذه السلسلة كل ما تحتاج له لكي تُنشِئ شبكتك، وتضيف المواقع إليها أو تسمح للمستخدمين بذلك، بالإضافة إلى إدارة الشبكة. وستتعلم كيف تتأكد أنَّ شبكتك آمنة وأداؤها عالٍ، وكيف يمكنك أن تُنشِئ مجتمعًا ناجحًا من المستخدمين والمواقع. ستتعلم في هذا الجزء من السلسلة كيف تستعمل الشبكات متعددة المواقع لاستضافة مواقع لك أو لعملائك، التي ستبدو وكأنها مواقع منفصلة. حيث سيمتلك كلٌ منها نطاقًا خاصًا به، وسيشعر الزوار أنَّ تلك المواقع منفصلة، سأريك أيضًا كيفية استخدام ميزة تعدد المواقع لتحسين سير عملك وسأعطيك بعض النصائح حول كيفية تتفادى المشاكل المحتملة، ثم سأشرح لك (في الدرس القادم) عملية ربط النطاقات بالتفصيل لكي تتمكن من ضبط الشبكة متعددة المواقع الخاصة بك ضبطًا سليمًا. استخدام الشبكة متعددة المواقع لاستضافة مواقعك أو مواقع عملائك أتوقع أنَّك تشعر كم أنني معجبٌ بميزة تعدد المواقع وأنني أستعملها لمختلف التطبيقات. أحد تلك التطبيقات هو استضافة مواقع العملاء. أدير شركةً صغيرة لتطوير الويب، وتكون أغلبية مواقع عملائي متشابهة وليست كبيرة جدًا والتي يمكن استضافتها بسهولة على شبكة من المواقع؛ بغض النظر أنَّ بعض عملائي لديهم استضافة خاصة أو يحتاجون إلى تثبيت ووردبريس مستقل بسبب بعض الخصوصيات في مواقعهم. أنا أفعل ذلك لمواقع العملاء، لكن لا يوجد سببٌ يمنعك من استخدام شبكة لاستضافة مواقعك الخاصة على أكثر من نطاق. التقنيات التي سنستعملها متماثلة. لننظر أولًا على إيجابيات وسلبيات ما سبق. إيجابيات استخدام شبكة متعددة المواقع لمواقع العملاء هنالك بعض المنافع الأساسية لاستضافة أغلبية مواقع العملاء في تثبيت ووردبريس وحيد: توفير مساحة على الخادوم. توفير الوقت اللازم لتثبيت ووردبريس في كل مرة أبدأ فيها العمل على موقع جديد. توفير الوقت اللازم لتحديث أكثر من نسخة ووردبريس. توفير الوقت اللازم لتحديث الإضافات والقوالب في عدِّة نسخ من ووردبريس. هذا يعني أنني أستطيع استخدام نفس الإضافات على أكثر من موقع دون الحاجة إلى تثبيتها مرارًا وتكرارًا. هذا يعني أنَّني أستطيع تثبيت إضافات مثل Support System للتواصل مع العملاء في مكانٍ واحد. بالنسبة لي، هذا يجعل الأمور أكثر فعالية، لكن علي أنَّ أعمل بطريقة تلائم المواقع التي تعمل على شبكة، وإجراء بعض الأمور للتأكد من عدم وقوعي ببعض المشاكل. سلبيات استضافة الكثير من المواقع في تثبيت ووردبريس وحيد من المحتمل أنَّ يقرأ هذا الكلام أشخاصٌ يجزعون من إبقاء كل هذه المواقع الثمينة في مكانٍ واحد. أعلم أنَّ هنالك البعض الذين يظنون أنَّ مخاطر فعل ذلك تفوق فوائده. إن كنتَ من هؤلاء، فيمكنك أن تستمر باستخدام تثبيت ووردبريس منفصل لكل موقع. هنالك بالفعل بعض السلبيات الناجمة عن استخدام شبكة لاستضافة جميع مواقعك: حجم قاعدة البيانات: قد تبدأ قاعدة البيانات بالتضخم بعد فترة من الزمن، لكن إذا تمكنت مواقع WordPress.com و Edublogs من استخدام شبكة متعددة المواقع لاستضافة ملايين المواقع، فلا يوجد سببٌ يمنعك من التعلم من تجربتهم لتوسعة تثبيت ووردبريس عندك. الحماية: إذا تم اختراق الشبكة متعددة المواقع، فستتأثر جميع المواقع المُستضافة عليها. هذا أمرٌ مرعبٌ، أليس كذلك؟ بلى! لكن من خبرتي عندما كنتُ ضحيةً للاختراق، كان ذلك على مستوى الخادوم، وأثّرَ ذلك على جميع المواقع المستضافة على أيّة حال. لكن عليك أنَّ تتأكد أنَّ مستوى الحماية والأمان في شبكتك عالٍ، خصوصًا إن كنتَ تستضيف عددًا كبيرًا من المستخدمين. إذا تعطّل شيءٌ ما، فسيُعطِّل كل الشبكة. هذه هو السبب لماذا لا يجدر بك إجراء تحديث على شبكتك الإنتاجية إلّا بعد أن تجرّبه على موقعٍ تجريبي أولًا. سننظر في هذه النقطة قريبًا. الأداء: لن تستطيع استضافة العديد من المواقع في تثبيت ووردبريس وحيد إن كنت تعمل على استضافة رخيصة وبطيئة أو محدودة بحجم قاعدة البيانات أو التراسل الشبكي أو رفع الملفات. إذا قررتَ أنت أو أحد عملائك أن تنقل الموقع إلى خارج الشبكة في المستقبل، فذلك أصعب من عملية نقل قاعدة بيانات موقع مفرد إلى خادوم جديد أو شركة استضافة أخرى. لكن ذلك ليس مستحيلًا، وهنالك طريقتان لفعل ذلك: يمكنك استخدام إضافات (طريقة أسهل لكنها قد تكون غير عملية) أو يمكنك تصدير الجداول اللازمة من قاعدة البيانات (وهذه الطريقة أصعب لكن أسرع وعملية ومرنة أكثر). هذا يعني أنَّ استخدام شبكة لاستضافة أكثر من موقع ستعمل بأفضل ما يمكن إذا تحقق الشرطان الآتيان: أخذت احتياطاتك وتأكدتَ أنَّ الشبكة آمنة ولن تتعطل بسهولة. تعمل بطريقة معينة تجعل نفس الشيفرة تتكرر في جميع أو أغلبية المواقع التي تطورها (مثلًا الإضافات والقوالب). تذكَّر أنَّ لأغلبنا قائمةٌ فيها الإضافات الأساسية التي نُثبِّتُها على كل موقع جديد نطوره. لننظر إلى معنى ما سبق عمليًا. تفادي السلبيات: الاحتياطات الوقائية هنالك بعض الاحتياطات الوقائية التي عليك اتخاذها لتتفادى المشاكل التي ذكرتا سابقًا: خذ نسخة احتياطيةً لشبكتك بشكلٍ دوري: كل يوم على الأقل، وأكثر من ذلك إن كانت مواقعك تُحدَّث يوميًا. استخدامُ إضافة Snapshot Pro لأخذ نسخة احتياطية للمواقع على شبكتي، وأستعمل Updraft Plus للتأكد من أخذ نسخة احتياطية من كامل قاعدة البيانات، وهذا يعني أنَّ لو تعطَّل موقعٌ واحد، فسأستخدم ميزة الاستعادة في إضافة Snapshot لاستعادته، وإذا حدثت مشاكل مع كل الشبكة، فسيكون عندي نسخةٌ احتياطيةٌ من قاعدة البيانات وسأعيد تثبيتها يدويًا. ابقِ شبكتك آمنةً قدر الإمكان. راجع سلسلة «تنصيب ووردبريس بأمان» وطبِّق النصائح الموجودة فيها. المخاطر الموجودة في شبكة المواقع زيادةً عن المواقع المفردة هي أنَّك تملك عددًا أكبر من المواقع ومستخدمين أكثر. يمكنك أن تُطبِّق الاحترازات الأمنية أثناء تسجيل الموقع وعملية تسجيل المستخدم لكي تتأكد أنَّ الآخرين لن يستطيعوا استعمال أسماء معيّنة لمواقعهم، ولحجب القدرة على تسجيل المواقع من نطاقات معينة، ولإبعاد المستخدمين المزعجين وعدم السماح للمستخدمين بضبط كلمات مرور ضعيفة. إن لم تسمح بتسجيل المواقع والمستخدمين من قِبل الزوار، فتأكّد أنَّ العملية التي ستتبعها في إنشاء المواقع آمنة. لا تُحدِّث ووردبريس أو الإضافات أو القوالب على شبكتك الإنتاجية قبل تجربة التحديث على نفس الإصدار في نسخة تجريبية من الشبكة أولًا. النسخة التجريبية هي نسخة من شبكتك التي يكون الغرض منها هو اختبار التحديثات أو ما شابه ذلك (وهي مختلفة عن النسخة «التطويرية» التي تتواجد عادةً في حاسوبك المحلي). من المستحسن أن تستضيف النسخة التجريبية على نفس الخادوم الذي يُشغِّل شبكتك الإنتاجية لذا ستتمكن من التجربة في نفس الشروط تقريبًا. احرص على حجب وصول محركات البحث وحاول إبقاء قاعدة البيانات مُحدَّثةً قدر الإمكان باستخدام إضافة للنسخ الاحتياطي أو أداة مثل Vagrant. استضف شبكتك عند مزود استضافة مختص بإدارة مواقع ووردبريس إن استطعتَ تحمل التكاليف. حيث سيعتنون بعملية النسخ الاحتياطي والحماية ويتمكنون من استعادة الشبكة ويوفرون لك بيئة تجريبية أيضًا. تحدَّث مع مزود الخدمة عن أفضل إعداد للاستضافة يلائم حالتك، مثل خادوم VPS (اختصار للعبارة virtual private server) الذي يناسب تثبيت الشبكات متعددة المواقع. استخدم إضافة أو أكثر من الإضافات التي ننصح بها لإدارة الشبكة لتسهيل عملية الإدارة. إحدى الإضافات التي أرى أنَّها مفيدةً كثيرًا هي Multisite Enhancements، لأنها تُساعدني بموضوع التحديثات عبر إظهار ما هي المواقع التي تستعمل قوالب أو إضافات معيّنة. وهذا يعني أنَّني سأعرف ما هي المواقع التي عليّ اختبارها. استخدم الشيفرات من مصادر تثق بها فقط، أو اكتب الشيفرات الخاصة بك، هذا الأمر مهمٌ لجميع أنواع مواقع ووردبريس، لكنه مهم جدًا في الشبكات متعددة المواقع، لأنه إذا كان لديك عدِّة مواقع تُشغِّل عدِّة قوالب وإضافات، فعليك أن تتأكَّد أنَّ كل إضافاتك وقوالبك تعمل عملًا صحيحًا مع بعضها بعضًا. إذا كنتَ تستعمل شيفراتٍ من طرف ثالث، فأنا أنصحك بأن تستقي تلك الشيفرات من موقع WPMU DEV، إذ أنَّ القوالب والإضافات من ذاك الموقع تعمل بتناغم مع بعضها وهي مُحسّنة خصيصًا لشبكات المواقع. أحاول اتباع النصائح السابقة ولم أواجه مشاكل مع الشبكات متعددة المواقع حتى الآن. الشبكات متعددة المواقع، وأسلوب عملك لكن استخدام الشبكات متعددة المواقع لاستضافة مواقع العملاء لا يعني تفادي المشكلات التي ذكرناها سابقًا فحسب، لكنه متعلقٌ أيضًا بأسلوب وسير العمل. هنالك جوانب متعلقة بأسلوب عملك التي ستجعل التطوير لشبكة متعددة المواقع أسهل وأيسر. وهذا يعني اتباع أسلوب عمل يجعل استخدام الشبكات أمرًا يزيد الكفاءة والفعالية، ويجعل من استخدام الشبكات طريقًا أفضل للتطوير بدلًا من استخدام عدد كبير من مواقع ووردبريس المفردة المستقلة. لننظر إلى بعض هذه الجوانب: ستعمل مع نفس القوالب لجميع مواقع شبكتك، أو «قالب ابن» (child theme) لنفس القالب الأب الأساسي. قد يتحقق هذا الشرط إذا استخدمتَ نظامًا لبناء القوالب مثل Upfront أو أحد إطارات عمل ووردبريس الموجودة. شخصيًا، لدي إطار عمل للقوالب خاصٌ بي الذي طوَّرتُه لأستخدمه في مواقع العملاء، ومن ثم أُنشِئ قالبًا ابنًا لكل مشروع جديد. يملك هذا القالب الكثير من الدوال (functions) والخطافات (hooks) التي أستفيد منها لجعل كل موقع مميز وفريد ولكي أضيف شيفراتٍ خاصة. وأستخدم أيضًا شيفرات CSS مكتوبة بطريقة تجعلني أستطيع تعديل مظهر الموقع الجديد بأقل قدر من العناء. يمكنك تثبيت مجموعة الإضافات نفسها لكل موقع تُنشِئه. كانت لدي قائمة بالإضافات الأساسية التي أثبِّتها على كل موقع قبل أن أستعمل الشبكات متعددة المواقع لاستضافة مواقع العملاء؛ وهذه القائمة تتضمن إضافات لأخذ نسخ احتياطية، ولتحسين SEO، ولتحسين الأداء والحماية. أما الآن، فبدلًا من الحاجة إلى تثبيت تلك الإضافات على كل موقع جديد، فأثبتها وأفعِّلها على الشبكة فحسب. تستطيع تحسين خدمة الزبائن باستخدام إضافات للدعم أو التدريب. استخدام إضافة مثل Support System تسمح لعملائك بطرح مشكلاتهم لكي تستطيع الإجابة عنها ثم تحول ذلك إلى «أسئلة شائعة» (FAQ) لعرضها في موقعك الرئيسي إن شئت. أستخدم هذا على إحدى شبكاتي لكي أُبقي على جميع نقاشات الدعم مع العملاء في مكانٍ واحد. إذا أردت أن توفِّر درسًا تعليميًا لعملائك وتُظهِر لهم كيف يستعملوا موقعهم، فيمكنك تثبيت إضافة مثل Integrated Video Tutorials التي تسمح لك بتقديم شرح مصور للعملاء؛ كل ما عليك فعله هو نشر الدرس التعليمي على شبكتك وسيصبح متاحًا لجميع العملاء. إذا أردتَ أن تُنشِئ مجتمعًا بين عملائك، فإن الشبكة متعددة المواقع هي منصةٌ رائعةٌ لإنشاء مجتمع، ولا يوجد سببٌ يمنعك من فعل ذلك مع عملائك. سنتحدث بالتفصيل عن هذا الموضوع في الجزء القادم من هذه السلسلة. حسنًا، يمكنك الآن أن تعي ماذا يعني استخدام شبكة متعددة المواقع لاستضافة عدِّة مواقع لعملائك (أو مواقع خاصة بك) على نطاقات مختلفة، ما ستحتاج له الآن هو معرفة كيف ستفعل ذلك، وهذا ما سنناقشه في الدرس القادم. ترجمة –وبتصرّف– للمقال WordPress Multisite Masterclass: Client Sites and Domain Mapping لصاحبته Rachel McCollin
  9. تعلم أساسيات MySQL

    سنشرح في هذا الدرس كيفية إنشاء قاعدة بيانات، وجداول (مع تبيان أنواع بيانات حقولها)، وسنفصّل طريقة إجراء عمليات على البيانات على خادوم 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.
  10. مقدمة إلى اصطلاحات ومفاهيم RAID

    تمهيد من المهم أن تأخذ طريقة التخزين بعين الاعتبار عندما تضبط خادومًا، إذ أنَّ أغلبية المعلومات المهمة التي تهتمُ أنت ومستخدموك بها ستُكتَب في مرحلةً ما إلى وسيط تخزين للحصول عليها لاحقًا. ستستفيد من الأقراص المستقلة إذا كانت احتياجاتك بسيطة، لكن إن أردتَ الحصول على أداءٍ عالٍ أو ضمانٍ لعدم فقدان بياناتك، فعندئذٍ ستستفيد من تقنيات مثل 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
  11. كيفية تنسيق النصوص في بايثون 3

    تمهيد تتألف السلاسل النصية عادةً من النص المكتوب، وهنالك عدِّة حالات نحتاج فيها إلى تحكمٍ أكبر بكيفية إظهار النص وجعلها أسهل قراءةً للبشر عبر وضع علامات الترقيم والسطور الجديدة والمحاذاة. سنشرح في هذا الدرس كيفية التعامل مع السلاسل النصية في بايثون لكي يظهر النص الناتج بتنسيقٍ صحيح. القيم الحرفية لنفرِّق أولًا بين القيم الحرفية للسلاسل النصية (string literal) والسلاسل النصية نفسها (string value)، فالقيم الحرفية هي ما نراه في الشيفرة المصدرية للبرنامج، بما في ذلك علامتَي الاقتباس. أما السلسلة النصية نفسها فهي ما نراها عندما نستدعي الدالة print()‎ عند تشغيل البرنامج. ففي برنامج «Hello, World!‎» التقليدي، تكون القيمة الحرفية هي "Hello, World!‎" بينما السلسلة النصية هي Hello, World!‎ دون علامتَي الاقتباس. أي أنَّ السلسلة النصية هي ما نراه في نافذة الطرفية عندما نُشغِّل برنامج بايثون. لكن بعض السلاسل النصية قد تحتوي على علامات اقتباس، مثل اقتباسنا لمقولةٍ ما. ولأنَّ القيم الحرفية والقيم الفعلية للسلاسل النصية غير متساوية، فمن الضروري في أغلب الحالات إضافة تنسيق إلى القيم الحرفية لعرض السلاسل النصية كما ينبغي. علامات الاقتباس بسبب إمكانيتنا استخدام علامات الاقتباس المفردة أو المزدوجة في بايثون، فمن السهل تضمين الاقتباسات بوضعها بين علامتَي اقتباس مزدوجتين في سلسلةٍ نصيةٍ محاطةٍ بعلامتَي اقتباس مفردتين كما في السلسلة الآتية: 'Sammy says, "Hello!"' أو يمكننا استخدام علامة اقتباس فردية (أو كما يسمونها «فاصلة عليا» [apostrophe]) في سلسلةٍ نصيةٍ محاطةٍ بعلامتَي اقتباس مزدوجتين: "Sammy's balloon is red." إذًا، يمكننا التحكم بطريقة عرض علامات الاقتباس والفواصل العليا في سلاسلنا النصية عبر استخدام النوع الصحيح من علامات الاقتباس لإحاطة كامل السلسلة النصية. كتابة النص على أكثر من سطر طباعة السلاسل النصية على أكثر من سطر ستجعل منها واضحةً وسهلة القراءة. إذ يمكن تجميع النصوص المكتوبة بعدِّة أسطر لزيادة وضوحها، أو لتنسيقها كرسالة، أو للحفاظ على تعدد الأسطر في الأشعار. نستخدم ثلاث علامات اقتباس فردية ''' أو ثلاث علامات اقتباس مزدوجة """ للإحاطة بالسلسلة النصية التي تمتد على أكثر من سطر: ''' This string is on multiple lines within three single quotes on either side. ''' """ This string is on multiple lines within three double quotes on either side. """ يمكنك الآن طباعة السلاسل النصية في عدِّة أسطر لجعل النصوص سهلة القراءة –خصوصًا الطويلة منها– وذلك عبر استخدام ثلاث علامات اقتباس متتالية. «تهريب» المحارف طريقة أخرى لتنسيق السلاسل النصية هي استخدام «محرف التهريب» (escape character). فجميع عمليات تهريب المحارف تبدأ بالخط المائل الخلفي (backslash، أي \) متبوعًا بمحرفٍ آخر الذي له معنى خاص يفيد في تنسيق السلسلة النصية. هذه قائمة بأكثر محارف التهريب شيوعًا: \: سطرٌ جديدٌ في سلسلةٍ نصيةٍ متألفةٍ من عدِّة أسطر. \\: طباعة رمز الخط المائل الخلفي. ‎\'‎: طباعة علامة اقتباس فردية. ‎\"‎: طباعة علامة اقتباس مزدوجة. ‎\‎n‎: طباعة محرف الانتقال إلى سطرٍ جديد. ‎\t: طباعة محرف الجدولة (Tab). لنستخدم محرف التهريب لإضافة علامات الاقتباس إلى سلسلتنا النصية السابقة، لكن هذه المرة سنُحيط السلسلة النصية بعلامتَي اقتباس مزدوجتين: print("Sammy says, \"Hello!\"") Sammy says, "Hello!" تمكننا عبر محرف التهريب ‎\"‎ من استخدام علامات الاقتباس المزدوجة للإحاطة بالسلسلة النصية التي تحتوي على نصٍ مقتبسٍ ومحاطٍ بعلامتَي اقتباس مزدوجتين. نستطيع أيضًا استخدام محرف التهريب ‎\'‎ لإضافة علامة اقتباس مفردة ضمن السلسلة النصية المحاطة بعلامتَي اقتباس مفردتين: print('Sammy\'s balloon is red.') Sammy's balloon is red. ولأننا نستخدم الآن محرف التهريب فنستطيع وضع علامات الاقتباس المفردة حتى لو كانت السلسلة النصية كلها موجودة بين علامتَي اقتباس مفردتين. عندما نستخدم علامات الاقتباس الثلاثية –كما فعلنا أعلاه– فسنجد فراغًا في أعلى وأسفل النص عند طباعته. نستطيع حذف تلك الفراغات عبر استخدام محرف التهريب \ في بداية ونهاية السلسلة النصية مع الإبقاء على النص مقروءًا بسهولة في الشيفرة. """\ This multi-line string has no space at the top or the bottom when it prints.\ """ وبشكلٍ شبيهٍ بما سبق، يمكننا استخدام محرف التهريب ‎\n لوضع أسطر جديدة دون الحاجة إلى الضغط على زر Enter أو Return: print("This string\nspans multiple\nlines.") This string spans multiple lines. يمكننا الدمج بين محارف التهريب، إذ سنطبع في المثال الآتي سلسلةً نصيةً على أكثر من سطر، ونستعمل فيها مسافة جدولة (tab) بين الترقيم ومحتوى السطر: print("1.\tShark\n2.\tShrimp\n10.\tSquid") 1. Shark 2. Shrimp 10. Squid علامة الجدولة الأفقية التي وضعناها عبر محرف التهريب ‎\t ستؤدي إلى محاذاة الكتابة في العمود النصي الثاني في المثال أعلاه، مما يجعل قراءتها سهلةً جدًا. وصحيحٌ أنَّ محرف التهريب ‎\n يعمل عملًا جيدًا في النصوص القصير، لكن لا نُغفِل أهمية أن تكون الشيفرة المصدرية مقروءةً بسهولةٍ أيضًا. فلو كان النص طويلًا، فأرى أنَّ من الأفضل استخدام علامات الاقتباس الثلاثية. رأينا أنَّ محارف التهريب تُستعمَل لإضافة تنسيق إلى السلاسل التي كان من الصعب (أو حتى المستحيل) عرضها عرضًا سليمًا دونها. فهل تستطيع مثلًا أن تطبع السلسلة النصية الآتية دون استخدام محارف التهريب Sammy says, "The balloon's color is red.‎"‎؟ السلاسل النصية «الخام» ماذا لو أردنا تجاهل كل محارف التنسيق الخاصة في سلاسلنا النصية؟ فلربما أردنا مقارنة أو التحقق من صحة بعض الشيفرات الحاسوبية التي تستخدم الخط المائل الخلفي، ولا نريد من بايثون تفسيره على أنها محرف تهريب. أتت «السلاسل النصية الخام» (raw strings) في بايثون لتحل هذه المشكلة، وتتجاهل جميع محارف التنسيق داخل سلسلة نصية، بما في ذلك محارف التهريب. يمكننا إنشاء سلسلة نصية خام بوضع الحرف r في بداية السلسلة النصية، قبل علامة الاقتباس الأولى مباشرةً: print(r"Sammy says,\"The balloon\'s color is red.\"") Sammy says,\"The balloon\'s color is red.\" سنستطيع الإبقاء على محارف التهريب كما هي في السلاسل النصية إن أسبقناها بالحرف r لتحويلها إلى سلاسل نصية خام. الخلاصة شرحنا في هذا الدرس عدِّة طرائق لتنسيق النص في بايثون 3؛ وعرفنا كيف نُظهِر السلاسل النصية كما نريد عبر استخدامنا لمحارف التهريب أو السلاسل النصية الخام، لكي يستفيد المستخدم من النص الناتج ويقرأه بسهولة. ترجمة -وبتصرّف- للمقال [How To Format Text in Python 3] لصاحبته Lisa Tagliaferri
  12. رأينا في الدرس السابق أكثر ثلاثة أنماط شيوعًا لتحديد مواقع العناصر في صفحات 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
  13. أحد أهم الأسئلة التي يطرحها أغلب المبتدئين بعد تعلّمهم أساسيات تعدد المواقع على ووردبريس هو: كيف أنقل موقع ووردبريس مستقل إلى موقع ضمن شبكة؟ أتى هذا الدرس ليجيب عن السؤال السابق، وذلك عبر اتباعك للخطوات الموجودة فيه لنقل موقعك إلى شبكة. سننظر في هذا الدرس إلى: كيفية استخدام الأداة 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
  14. تمهيد «السلسلة النصية» هي مجموعة من المحارف (أي الأحرف والأرقام والرموز) التي إما أن تكون قيمة ثابتة أو قيمة لمتغير. وهذه السلاسل النصية مُشكَّلة من محارف يونيكود (Unicode) والتي لا يمكن تغيير مدلولها. ولأنَّ النص هو شكلٌ شائعٌ من أشكال البيانات الذي نستعمله يوميًا، لذا فإنَّ السلاسل النصية مهمة جدًا وتُمثِّل لُبنةً أساسيةً في البرمجة. سيستعرض هذا الدرس كيفية إنشاء وطباعة السلاسة النصية، وكيفية جمعها مع بعضها وتكرارها، وآلية تخزين السلاسل النصية في متغيرات. إنشاء وطباعة السلاسل النصية تتواجد السلاسل النصية إما داخل علامات اقتباس فردية ’ أو علامات اقتباس مزدوجة "، لذا لإنشاء سلسلة نصية، كل ما علينا فعله هو وضع مجموعة من المحارف بين أحد نوعَي علامات الاقتباس السابقَين: 'This is a string in single quotes.' "This is a string in double quotes." يمكنك الاختيار بين النوعَين السابقَين، لكن أيًّا كان اختيارك، فعليك أن تحافظ على استخدامك له في كامل برنامجك. يمكنك طباعة السلاسل النصية إلى الشاشة باستدعاء الدالة print()‎ بكل بساطة: print("Let's print out this string.") Let's print out this string. بعد أن فهمتَ كيفية تهيئة السلاسل النصية في بايثون، لنلقِ نظرةً الآن إلى كيفية التعامل مع السلاسل النصية في برامجك وتعديلها. جمع السلاسل النصية عملية الجمع (concatenation) تعني إضافة سلسلتين نصيتين إلى بعضهما بعضًا لإنشاء سلسلة نصية جديدة. نستخدم المعامل + لجمع السلاسل النصية؛ أبقِ في ذهنك أنَّ المعامل + يعني عملية الجمع عند التعامل مع الأعداد، أما عندما نستخدمه مع السلاسل النصية فيعني إضافتها إلى بعضها. لنجمع السلستين النصيتين "Sammy" و "Shark" مع بعضها ثم نطبعهما باستخدام الدالة print()‎: print("Sammy" + "Shark") SammyShark إذا أردتَ وضع فراغ بين السلسلتين النصيتين، فيمكنك بكل بساطة وضعه عند نهاية السلسلة النصية الأولى، أي بعد الكلمة “Sammy”: print("Sammy " + "Shark") Sammy Shark لكن احرص على عدم استعمال المعامل + بين نوعَين مختلفَين من البيانات، فلن نتمكن من جمع السلاسل النصية والأرقام مع بعضها، فلو حاولنا مثلًا أن نكتب: print("Sammy" + 27) فسنحصل على رسالة الخطأ الآتية: TypeError: Can't convert 'int' object to str implicitly أما إذا أردنا أن نُنشِئ السلسلة النصية "Sammy27" فعلينا حينها وضع الرقم 27 بين علامتَي اقتباس ("27") مما يجعله سلسلةً نصيةً وليست عددًا صحيحًا. سنستفيد من تحويل الأعداد إلى سلاسل نصية عندما نتعامل مع أرقام الهواتف على سبيل المثال، لأننا لن نحتاج إلى إجراء عملية حسابية على رمز الدولة ورمز المنطقة في أرقام الهواتف، إلا أننا نريدهما أن يظهرا متتابعَين. عندما نجمع سلسلتين نصيتين أو أكثر فنحن نُنشِئ سلسلةً نصيةً جديدةً التي يمكننا استخدامها في برنامجنا. تكرار السلاسل النصية هنالك أوقاتٌ نحتاج فيها إلى استخدام بايثون لأتمتة المهام، وإحدى الأمور التي يمكننا أتمتتها هي تكرار سلسلة نصية لعدِّة مرات. إذ نستطيع فعل ذلك عبر المعامل *، وكما هو الأمر مع المعامل + فإنَّ المعامل * له استخدامٌ مختلف عندما نتعامل مع أرقام، حيث يُمثِّل عملية الضرب. أما عندما نستخدمه بين سلسلةٍ نصيةٍ ورقمٍ فإنَّ المعامل * هو معامل التكرار، فوظيفته هي تكرار سلسلة نصية لأي عدد مرات تشاء. لنحاول طباعة السلسلة النصية “Sammy” تسع مرات دون تكرارها يدويًا، وذلك عبر المعامل *: print("Sammy" * 9) SammySammySammySammySammySammySammySammySammy يمكننا بهذه الطريقة تكرار السلسلة النصية لأيِّ عددٍ نشاء من المرات. تخزين السلاسل النصية في متغيرات المتغيرات هي «رموز» التي يمكننا استعمالها لتخزين البيانات في برنامج. يمكنك تخيل المتغيرات على أنها صندوقٌ فارغٌ يمكنك ملؤه بالبيانات أو القيم. السلاسل النصية هي نوعٌ من أنواع البيانات، لذا يمكننا استعمالها لملء المتغيرات. التصريح عن السلاسل النصية كمتغيرات سيُسهِّل علينا التعامل معها في برامجنا. لتخزين سلسلة نصية داخل متغير، فكل ما علينا فعله هو إسنادها إليه. سنُصرِّح في المثال الآتي عن المتغير my_str: my_str = "Sammy likes declaring strings." أصبح المتغير my_str الآن مُشيرًا إلى سلسلةٍ نصيةٍ، والتي أمسى بمقدورنا طباعتها كما يلي: print(my_str) وسنحصل على الناتج الآتي: Sammy likes declaring strings. استخدام المتغيرات لاحتواء قيم السلاسل النصية سيساعدنا في الاستغناء عن إعادة كتابة السلسلة النصية في كل مرة نحتاج استخدامها، مما يُبسِّط تعاملنا معها وإجراءنا للعمليات عليها في برامجنا. الخلاصة لقد تعلمنا في درسنا هذا أساسيات التعامل مع السلاسل النصية في لغة بايثون 3. بما في ذلك إنشاءها وطباعتها وجمعها وتكرارها، إضافةً إلى تخزينها في متغيرات، وهذه هي المعلومات الأساسية التي عليك فهمها للانطلاق في تعاملك مع السلاسل النصية في برامج بايثون 3. ترجمة -وبتصرّف- للمقال An Introduction to Working with Strings in Python 3 لصاحبته Lisa Tagliaferri
  15. يهرع الكثير من المبتدئين إلى طرائق تحديد مواقع العناصر في 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