البحث في الموقع
المحتوى عن 'تحميل'.
-
أعتبرُ أنَّ شريط العنوان في المتصفح هو أشهرُ عنصرٍ من عناصر الواجهات الرسومية في العالم، إذ أصبحت تُعرَض روابط URL على اللوحات الإعلانية، وعلى جوانب الطرقات، وحتى في الكتابات على الجدران؛ مجتمعًا مع زر الرجوع إلى الخلف –أحد أهم الأزرار في المتصفح– ستحصل على مقدرةٍ على التنقل إلى الأمام وإلى الخلف في شبكة المعلومات الكبيرة التي نسميها الويب. الواجهة البرمجية للتعامل مع التأريخ في HTML5 هي طريقةٌ معياريةٌ لتعديل تأريخ (history) المتصفح باستخدام السكربتات. جزءٌ من هذه الواجهة البرمجية (التنقل في التأريخ) موجودٌ في الإصدارات السابقة من HTML؛ أما الأجزاء الجديدة في HTML5 تضمَّنت طرائق لإضافة مدخلات إلى تأريخ المتصفح، ولتغيير رابط URL الظاهر في شريط المتصفح (دون الحاجة إلى تحديث الصفحة)، وإضافة حدث سيُفعَّل عندما تُحذَف تلك المدخلات من المكدس (stack، وهذه هي آلية التعامل الداخلية مع التأريخ) بوساطة المستخدم عند ضغطه لزر الرجوع في المتصفح. وهذا يعني أنَّ رابط URL في شريط العنوان في المتصفح سيستمر بأداء عمله كمُعرِّف فريد للمورد الحالي (current resource)، حتى في التطبيقات التي تعتمد اعتمادًا كبيرًا على السكربتات التي لن تُجري تحديثًا كاملًا للصفحة. السبب وراء تعديل التأريخ لماذا تريد تعديل تأريخ المتصفح يدويًا؟ بكل بساطة، يمكن لرابطٍ بسيطٍ أن ينقلك إلى URL جديد؛ وهذه هي الطريقة التي عَمِلَ بها الويب لأكثر من 25 سنة، وسيستمر بذلك. هذه الواجهة البرمجية لن تحاول تقويض الويب، وإنما العكس. ففي السنوات الأخيرة، وجَدَ مطورو الويب طرائق جديدة ومثيرة لتخريب الويب دون أي مساعدة من المعايير الناشئة. صُمِّمَت واجهة التأريخ البرمجية في HTML5 للتأكّد من أنَّ روابط URL ستستمر بأداء وظيفتها وأن تبقى مفيدةً حتى في تطبيقات الويب التي تعتمد تمامًا على السكربتات. بالعودة إلى المفاهيم الأساسية، ما وظيفة رابط URL؟ إنَّه يُعرِّف موردًا فريدًا (unique resource). يمكنك إضافة رابط مباشر إليه، أو وضع علامة مرجعية إليه، ويمكن لمحركات البحث فهرسته، ويمكنك نسخه ولصقه في رسالةٍ عبر البريد الإلكتروني لشخصٍ ما، الذي يستطيع أن يضغط عليه ويرى نفس المورد الذي رأيته أنت. هذه الميزات الرائعة تُريك أهمية روابط URL. حسنًا، نحن نريد أن تكون للموارد المختلفة روابط URL فريدة. لكن المتصفحات تُعاني من قصورٍ أساسي: إذا غيّرتَ رابط URL، حتى باستخدام السكربتات، فستطلب من خادوم الويب البعيد تحديثًا لكامل الصفحة. وهذا يأخذ وقتًا ومواردَ، وسيبدو لك كم أنَّ ذلك مُبدِّدٌ للموارد إذا كانت الصفحة التي ستنتقل إليها شبيهةً جدًا بالصفحة الحالية. ستُنزَّل كل أجزاء الصفحة الجديدة، حتى الأجزاء المُماثِلة للصفحة الحالية. لا يوجد هنالك طريقةٌ لتغيير رابط URL وتنزيل نصف الصفحة فقط. أتت الواجهة البرمجية للتعامل مع التأريخ في HTML5 لحل هذه الإشكالية. فبدلًا من تحديث كامل الصفحة، يمكنك أن تستخدم سكربتًا لتنزيل «نصف صفحة». هذه الخدعة صعبة قليلًا وتحتاج إلى قليلٍ من العمل… لنقل أنَّ لديك صفحتان، الصفحة A و الصفحة B. الصفحتان متماثلتان بنسبة 90%؛ وهنالك 10% فقط من المحتوى يختلف بين الصفحتين. زار المستخدمُ الصفحةَ A، ثم حاول الانتقال إلى الصفحة B، لكن بدلًا من تحديث الصفحة تحديثًا كاملًا، حاولتَ اعتراض هذه العملية وإجراء الخطوات الآتية يدويًا: تحميل 10% من الصفحة B المختلفة عن A (ربما باستخدام XMLHttpRequest)، وهذا سيتطلب بعض التعديلات من جهة الخادوم لتطبيق الويب الخاص بك. إذ ستحتاج إلى كتابة شيفرة لكي تُعيد 10% فقط من الصفحة B المختلفة عن A، وربما تفعل ذلك عبر رابط URL مخفي أو عبر تمرير وسيط في الطلبية الذي لا يستطيع المستخدم النهائي رؤيته في الحالات العادية. تبديل المحتوى الذي تغيّر (عبر استخدام innterHTML أو دوال DOM الأخرى)، قد تحتاج أيضًا إلى إعادة ضبط أيّة دوال لمعالجة الأحداث المرتبطة بالعناصر الموجودة في المُحتوى المُبدَّل. تحديث شريط العنوان في المتصفح لكي يحتوي على رابط URL للصفحة B، وذلك عبر دالة خاصة من الواجهة البرمجية للتعامل مع التأريخ في HTML5 التي سأريك إياها بعد قليل. في نهاية هذه الخدعة (إذا طبّقتها تطبيقًا صحيحًا)، سينتهي الأمر بحصول المتصفح على شجرة DOM مماثلة لتلك التي سيحصل عليها لو انتقل إلى الصفحة B مباشرةً. وسيُصبِح العنوان في شريط المتصفح مساويًا لرابط URL للصفحة B، كما لو أنَّك انتقلت إلى الصفحة B مباشرةً. لكنك في الحقيقة لم تنتقل إلى الصفحة B ولم تُجرِ تحديثًا كاملًا للصفحة. وهذه هي الخدعة التي كنت أتحدث عنها. لأنَّ الصفحة النهاية مماثلةٌ تمامًا للصفحة B ولها نفس رابط URL للصفحة B، لكن المستخدم لم يلاحظ الفرق (ولم يكن ممنونًا لك على جهودك لتحسين تجربته مع التطبيق). طريقة تعديل التأريخ يوجد في واجهة التأريخ البرمجية عددٌ من الدوال في كائن window.history، بالإضافة إلى حدثٍ وحيد في الكائن window. يمكنك استخدامها لاكتشاف الدعم لواجهة التأريخ البرمجية، وهنالك دعمٌ لا بأس به من أغلبية المتصفحات الحديثة لهذه الواجهة البرمجية. IE Firefox Safari Chrome Opera iPhone Android 10+ 4.0+ 5.0+ 8.0+ 11.5+ 4.2.1+ *4.3+ * من المفترض أنَّ هنالك دعمٌ لواجهة التأريخ البرمجية في الإصدارات السابقة من متصفح أندرويد، لكن لوجود عدد كبير من العلل البرمجية في تطبيق هذا الدعم في إصدار 4.0.4، فسنعتبر أنَّ الدعم «الحقيقي» لواجهة التأريخ البرمجية قد بدأ منذ الإصدار 4.3. «Dive Into Dogs» هو مثالٌ بسيطٌ وعمليٌ لكيفية الاستفادة من الواجهة البرمجية للتأريخ في HTML5. حيث يُبرِز مشكلةً شائعةً: مقالةٌ طويلةٌ يرتبط بها معرضُ صورٍ. سيؤدي الضغط على رابط «Next» أو «Previous» في معرض الصور إلى تحديث الصورة آنيًا وتحديث رابط URL في شريط العنوان في المتصفح دون الحاجة إلى تحديث كامل الصفحة. أما في المتصفحات التي لا تدعم واجهة التأريخ البرمجية –أو في المتصفحات الداعمة لها، لكن المستخدم عطَّل السكربتات– سيعمل الرابطان «Next» و «Previous» كرابطين اعتياديين، ويأخذانك إلى الصفحة التالية بعد تحديث كامل الصفحة. وهذا يثير النقطة المهمة الآتية: لنتعمّق في مثال «Dive Into Dogs» ونرى كيف يعمل. هذه هي الشيفرة التي تُستعمل لعرض صورة وحيدة: <aside id="gallery"> <p class="photonav"> <a id="photonext" href="casey.html">Next ></a> <a id="photoprev" href="adagio.html">< Previous</a> </p> <figure id="photo"> <img id="photoimg" src="gallery/1972-fer-500.jpg" alt="Fer" width="500" height="375"> <figcaption>Fer, 1972</figcaption> </figure> </aside> لا يوجد شيءٌ غير اعتياديٍ فيما سبق. الصورة نفسها هي عنصر <img> موجودٌ ضمن عنصر <figure>، جميع الروابط هي عناصر <a>، وكل شيء محتوىً في عنصر <aside>، من المهم أن نلاحظ أنَّ هذه الروابط العادية تعمل عملًا صحيحًا. جميع الشيفرات التي تتحكم بالتأريخ موجودةٌ بعد سكربت لاكتشاف الدعم، فإن لم يكن متصفح المستخدم داعمًا للواجهة البرمجية للتأريخ، فلا حاجة إلى تنفيذ الشيفرات المتعلقة بها، وكذلك الأمر للمستخدمين الذي عطَّلوا تنفيذ السكربتات بالمجمل. الدالة الرئيسية تحصل على كل رابط من تلك الروابط وتمرِّره إلى الدالة addClicker()، التي عملها هو إعداد دالة خاصة للتعامل مع الحدثclick. function setupHistoryClicks() { addClicker(document.getElementById("photonext")); addClicker(document.getElementById("photoprev")); } هذه هي دالة addClicker() التي تأخذ عنصر <a> وتُضيف إليه دالة للتعامل مع الحدث click، التي يحدث فيها أمرٌ مثيرٌ للاهتمام. function addClicker(link) { link.addEventListener("click", function(e) { swapPhoto(link.href); history.pushState(null, null, link.href); e.preventDefault(); }, false); } الدالة swapPhoto() تقوم بأول خطوتين من الخطوات الثلاث التي تحدثنا عنها. أول قسم من الدالة swapPhoto() يأخذ جزءًا من URL لرابط التنقل –أي casey.html و adegio.html وهكذا…– ويبني رابط URL جديد يُشير إلى صفحةٍ مخفيةٍ التي لا تحتوي إلا على الشيفرة اللازمة لعرض الصورة التالية. function swapPhoto(href) { var req = new XMLHttpRequest(); req.open("GET", "http://diveintohtml5.org/examples/history/gallery/" + href.split("/").pop(), false); req.send(null); هذا مثالٌ عن الشيفرات التي تُنتِجها تلك الصفحات (يمكنك التأكد من ذلك في متصفحك بزيادة الرابط السابق مباشرةً). <p class="photonav"> <a id="photonext" href="brandy.html">Next ></a> <a id="photoprev" href="fer.html">< Previous</a> </p> <figure id="photo"> <img id="photoimg" src="gallery/1984-casey-500.jpg" alt="Casey" width="500" height="375"> <figcaption>Casey, 1984</figcaption> </figure> هل تبدو الشيفرة السابقة مألوفةً لديك؟ هذا طبيعي، لأنها نفس الشيفرة التي استخدمناها في صفحتنا الرئيسية لعرض أول صورة. القسم الثاني من دالة swapPhoto() يجري الخطوة الثانية من الخطوات الثلاث التي تحدثنا عنها: وضع الشيفرة الجديدة التي نُزِّلَت في الصفحة الحالية، تذكَّر أنَّ هنالك عنصر <aside> محيطٌ بالصورة والشرح التوضيحي لها، لذلك تكون عملية إضافة شيفرة الصورة الجديدة بسيطةً عبر ضبط خاصية innterHTML لعنصر <aside> إلى خاصية responseText من كائن XMLHttpRequest. if (req.status == 200) { document.getElementById("gallery").innerHTML = req.responseText; setupHistoryClicks(); return true; } return false; } لاحظ أيضًا استدعاء الدالة setupHistoryClicks()، وهذا ضروريٌ لإعادة ضبط دوال التعامل مع الحدث click في روابط التنقل الجديدة، لأن ضبط المحتوى باستخدام innerHTML سيمسح كل آثار الروابط القديمة مع دوال التعامل مع أحداثها. لنعد الآن إلى الدالة addClicker()، فبعد النجاح بتغيير الصورة، هنالك خطوةٌ إضافيةٌ علينا عملها من الخطوات الثلاث: ضبط رابط URL في شريط عنوان المتصفح دون تحديث الصفحة. history.pushState(null, null, link.href); تأخذ الدالة history.pushState() ثلاثة وسائط: state: يمكن أن يكون أي بنية من بُنى بيانات JSON، وستُمرَّر إلى الدالة التي تتعامل مع الحدث popstate، الذي ستتعلم المزيد من المعلومات عنه بعد قليل. لا نحتاج إلى تتبع أي حالة في هذا المثال، لذا سأترك القيمة مساويةً إلى null. title: يمكن أن يكون أي سلسلة نصية. لكن هذا الوسيط غير مدعوم من أغلبية المتصفحات الرئيسية، فإذا أردت ضبط عنوان الصفحة، فعليك تخزينه في الوسيط state ثم ضبطه يدويًا في الدالة التي تتعامل مع الحدث popstate. url: يمكن أن يكون أي رابط URL، وهو الرابط الذي سيظهر في شريط العنوان في متصفحك. استدعاء الدالة history.pushState سيؤدي إلى تغيير رابط URL الظاهر في شريط العنوان في المتصفح على الفور، لكن أليست هذه نهاية القصة؟ ليس تمامًا، بقي علينا أن نتحدث عمّا سيحصل إذا ضغط المستخدم على الزر المهم «الرجوع». الحالة العادية عندما يزور المستخدم صفحة جديدة (بتحميلها كلها) هي إضافة المتصفح لرابط URL الجديد في «مكدس» التأريخ (history stack) ثم يُنزِّل ويعرض الصفحة الجديدة. وعندما يضغط المستخدم على زر الرجوع إلى الخلف، فسيزيل المتصفح الصفحة الحالية من مكدس التأريخ ويعرض الصفحة التي تسبقها، لكن ماذا سيحدث عندما عبثتَ بهذه الآلية لكي تتفادى تحديثًا لكامل الصفحة؟ حسنًا، لقد زيّفتَ «التقدم إلى الأمام» إلى رابط URL جديد، وحان الوقت الآن لتزييف «الرجوع إلى الخلف» إلى رابط URL السابق. والمفتاح نحو تزييف «الرجوع إلى الخلف» هو الحدث popstate. window.addEventListener("popstate", function(e) { swapPhoto(location.pathname); } بعد أن استخدمتَ الدالة history.pushState() لإضافة رابط URL مزيّف في مكدس التأريخ الخاص بالمتصفح، سيُطلِق المتصفح الحدث popstate في الكائن window عندما يضغط المستخدم على زر الرجوع. وهذه هي فرصتك لإكمال عملك في إيهام المستخدم أنَّه انتقل فعلًا إلى تلك الصفحة. في هذا المثال، عملية إعادة الصفحة السابقة بسيطةٌ جدًا، فكل ما عليك فعله هو إعادة الصورة الأصلية، وذلك باستدعاء الدالة swapPhoto() مع تمرير رابط URL الحالي لها. لأنَّه عند استدعاء الدالة التي تُعالِج الحدث popstate، يكون رابط URL الظاهر في المتصفح قد تغيّر إلى رابط URL السابق. وهذا يعني أيضًا أنَّ قيمة الخاصية العامة location قد حُدِّثَت لرابط URL السابق. لكي أساعدك في تخيل الوضع، دعني أتلو عليك العملية «السحرية» من بدايتها إلى نهايتها: يُحمِّل المستخدم الصفحة http://diveintohtml5.org/examples/history/fer.html ويشاهد القصة وصورةً للكلب Fer. يضغط المستخدم على الرابط المُعَنوَن «Next»، الذي هو عنصر <a> تكون قيمة خاصية herf فيه هي http://diveintohtml5.org/examples/history/casey.html. بدلًا من الانتقال إلى http://diveintohtml5.org/examples/history/casey.html مباشرةً وتحديث الصفحة تحديثًا كاملًا، فستعترض دالة خاصة للحدث click عملية الضغط على عنصر <a> وتُنفِّذ شيفرةً خاصةً بها. تستدعي تلك الدالة التي تُعالِج الحدث click الدالةَ swapPhoto()، التي تُنشِئ كائن XMLHttpRequest لكي ينُزِّل جزء HTML الموجود في http://diveintohtml5.org/examples/history/gallery/casey.html بشكل تزامني. الدالة swapPhoto() تضبط الخاصية innerHTML للعنصر الذي يحوي الصورة (عنصر <aside>)، وبهذا ستُبدَّل صورة Casey بصورة Fer. في النهاية، ستستدعي الدالةُ التي تتعامل مع الحدث click الدالةَ history.pushState() لتغيير رابط URL يدويًا في شريط عنوان المتصفح إلى http://diveintohtml5.org/examples/history/casey.html. يضغط المستخدم على زر الرجوع في المتصفح. يلاحظ المتصفح أنَّ رابط URL قد تغيّر يدويًا وأُضيف إلى مكدس التأريخ (عبر الدالة history.pushState()) وبدلًا من الانتقال إلى رابط URL السابق وإعادة تحديث الصفحة، فسيُحدِّث المتصفح الرابط الموجود في شريط العنوان إلى رابط URL للصفحة السابقة (http://diveintohtml5.org/examples/history/fer.html) ثم يُطلِق الحدث popstate. الدالة التي تُعالِج الحدث popstate ستستدعي الدالة swapPhoto() مرةً أخرى، لكن هذه المرة مع تمرير الرابط القديم إليها الذي أصبح موجودًا الآن في شريط عنوان المتصفح. ثم باستخدام XMLHttpRequest مرةً أخرى، ستُنزِّل الدالة swapPhoto() جزءًا من صفحة HTML الموجودة في http://diveintohtml5.org/examples/history/gallery/fer.html ثم ستضبط الخاصية innerHTML للعنصر الذي يحوي الصورة (عنصر <aside>)، وبهذا ستُبدَّل صورةFer بصورة Casey. اكتملت خدعتنا، جميع الأدلة الظاهرة (محتوى الصفحة، وعنوان URL في المتصفح) تُشير إلى أنَّ المستخدم قد انتقل إلى الأمام صفحةً وإلى الخلف صفحةً. لكن لم يحصل تحديثٌ كاملٌ للصفحة. مصادر إضافية Session history and navigation في معيار HTML5 Manipulating the browser history على Mozilla Developer Center استعراض بسيطة لواجهة التأريخ البرمجية Using HTML5 today التي تشرح كيف يستعمل موقع فيسبوك الواجهة البرمجية للتأريخ The Tree Slider التي تشرح استعمال موقع GitHub للواجهة البرمجية للتأريخ History.js هي مكتبةٌ للتعامل مع التأريخ في المتصفحات الحديثة والقديمة ترجمة -وبتصرّف- لفصل History من كتاب Dive Into HTML5 لمؤلفه Mark Pilgrim. اقرأ أيضًا المقال التالي: تحديد الموقع الجغرافي (GeoLocation) في HTML5 المقال السابق: إضافة مقاطع الفيديو عبر العنصر <video> في HTML5 النسخة العربية الكاملة من كتاب نحو فهم أعمق لتقنيات HTML5
-
إدارة الصور هي واحدة من الأشياء التي غالبا ما أجدها صعبة، فأنا معتاد على التعامل مع النماذج وجداول قواعد البيانات، لكن التعامل مع الملفات ليس سهلا بالنسبة لي، فإذا كنت قد بدأت للتو مع Laravel وبدأت تشعر بالصعوبة والضيق، فأنا أشعر بألمك. لحسن الحظ، أساسيات إدارة الصور في Laravel لن تكون صعبة إذا فهمت بعض الأمور الأساسية. ملاحظة: إذا كنت جديدا في Laravel، فلا أنصحك بالبدء بهذا الدرس، أنصحك بقراءة بعض الدروس والدورات حول Laravel قبل المتابعة. سوف تلاحظ أن الدرس طويل لذلك قمت بتقسيمه إلى 3 أجزاء، وسوف تلاحظ أيضا أنني لست بارعا في تصميم الواجهة الأمامية (front-end)، فلذلك فالواجهات التي سنقوم بعملها لن تكون جميلة جدا، يمكنك تزيينها بنفسك لاحقا، فالهدف الأساسي هنا هو تعلم أساسيات إدارة الصور. لذلك على أية حال، ماهي هذه الأساسيات ؟ فكرتُ بالمتطلبات التي قد تحتاجها في تطبيقك وكتبتها في هذه القائمة: إنشاء الصورتخزين الصورتعديل الصورتحديث الصورإنشاء صور مصغرةتعديل صور مصغرةإنشاء وتعديل صور الهواتف بشكل منفصلبالنسبة للذين اعتادوا على إجراءات المتحكِّمات (controller actions) المريحة، ستظهر الاستمارات عند إجراءات (actions) الإنشاء create والتعديل edit وأما إجراءات التخزين store والتحديث update فوظيفتهم إنشاء وتعديل السجلات والملفات. سنتعامل مع شيئين مهمين عند تعاملنا مع الصور، الشيء الأول هو النموذج الذي يعمل على البيانات مثل اسم الصورة و مسارها، وأما الشيء الثاني فهو ملف الصورة نفسه الذي سوف يتم تخزينه في مجلد الذي سنقوم بإسناده له. تثبيت Interventionسوف نبدأ بتثبيت حزمة Intervention/image، فإذا لم تقُم بذلك، عدل على ملف composer.json وأضف التعليمة التالية في جزء الاستدعاء (require) : "intervention/image": "~2.2"ثم أضف السطر التالي ضمن مصفوفة Providers في ملف app\config\app.php (انتبه للفواصل) : Intervention\Image\ImageServiceProvider::class,في نفس الملف أضف السطرين التاليين لمصفوفة Aliases: Intervention\Image\ImageServiceProvider::class,أنصحك بالإطلاع على صفحة Intervention للتأكد من الإصدار الأخير للحزمة. في آخر مرة تأكدتُ فيها،كانت تعليمات التَدَخّل تستخدم النمط القديم للإشارة إلى المرجع: 'Intervention\Image\ImageServiceProvider'يمكنك أن تلاحظ في الأعلى أننا في كلتا الحالتين استخدمنا ::class والتي هي ممارسة جيدة تعلمتها من Laracasts.com، فإذا كنت تستخدم على سبيل المثال PHP Storm، فسيكون باستطاعتك الوصول إلى الفئة class الأساسية. إن حزمة Intervention تقوم بإعطائنا صياغة (syntax) وطريقة سهلة لصناعة الصور المصغرة بالإضافة إلى الكثير من التوابع (method) الأخرى الرائعة، لذلك سوف نستخدمها في هذا الدرس، ولمزيد من المعلومات حول هذه الحزمة أنصحك بالإطلاع على التوثيق الرسمي. لاحظ أيضا أننا سنقوم باستخدام حزمة laravelcollective/html وحزمة patricktalmadge/bootstrapper، ولذلك قم بتثبيتهم قبل أن تتابع الدرس. إنشاء نموذجسوف نبدأ بإنشاء نموذج Marketingimage، يمكننا فعل ذلك عن طريق artisan من سطر الأوامر بكتابة السطر التالي: php artisan make:model Marketingimage -mسوف تلاحظ علم m- الذي سيخبر Laravel أنك تريد إنشاء تهجير migration في نفس الوقت، لذلك فهذه الميزة مفيدة للغاية. حسنا، قم بتنفيذ الأمر السابق وستحصل على ملف النموذج والمسمى Marketingimage.php مباشرة تحت دليل تطبيقك وستحصل أيضا على ملف التهجير في مجلد database/migrations. دعونا نقوم بتعديل التابع في أعلى ملف التهجير إلى ما يلي: public function up() { Schema::create('marketing_images', function(Blueprint $table) { $table->increments('id'); $table->boolean('is_active')->default(false); $table->boolean('is_featured')->default(false); $table->string('image_name')->unique(); $table->string('image_path'); $table->string('image_extension', 10); $table->string('mobile_image_name')->unique(); $table->string('mobile_image_path'); $table->string('mobile_extension', 10); $table->timestamps(); }); }أول شيئ يمكنك رؤيته أنني قد غيرت اسم الجدول، فأنا افضل فصل الكلمات في جدول الأسماء بسطر سفلي underscore. أنت حر في اختيار الطريقة التي تعجبك، لكن يجب أن نختار صيغة الجمع لإتباع قواعد Laravel بشكل صحيح. ففي Laravel، النموذج يكتب بصيغة المفرد وأما اسم الجدول فيكتب بصيغة الجمع. بعد عمود المعرف الرقمي، قمنا باستخدام عمودين من نوع المنطقي boolean والتي سوف تسمح لنا بمعرفة هل الصورة نشطة أو مميزة، هذه القيود المفيدة سوف تساعد على العمل مع الصور في وقت لاحق. ثم قمنا بإضافة أعمدة الاسم، المسار والامتدادات للصور وصور الهاتف، وهذا سيسمح لنا بالمرونة الكافية إذا أردت حفظ صورة مختلفة للهاتف، وهذا الأمر ضروري لأن تغيير حجم الصورة قد لا ينتج لنا النتائج المرجوة. وبما أننا سوف نقوم بإنشاء صور مصغرة من الصور الأصلية، لن نحتاج إلى حفظ أية بيانات لذلك. عن طريق حفظ مسار وإمتداد الصورة، سيكون لدينا مرجع سهل نستطيع استخدامه لإظهار الصورة في تطبيقنا، بالإضافة إدارة الصورة في قائمة الصور. عدل على التابع ليبدو على النحو التالي: public function down() { Schema::drop('marketing_images'); }بمجرد أن تقوم بذلك، قم بتنفيذ أمر php artisan migrate من سطر الأوامر وتأكد من أن الجدول قد تم إنشاءه. بعد هذا، سوف نقوم بتعديل نموذج Marketingimage كما يلي: <?php namespace App; use Illuminate\Database\Eloquent\Model; class Marketingimage extends Model { protected $table = 'marketing_images'; protected $fillable = ['is_active', 'is_featured', 'image_name', 'image_path', 'image_extension', 'mobile_image_name', 'mobile_image_path', 'mobile_extension' ]; }سوف تلاحظ أننا قمنا بإخبار النموذج الجدول الذي سيتخذه كمرجع، بالإضافة إلى توفير أعمدة مملوءة تلقائيا، حتى لا نواجه مشكلة الإحالة الكتلية mass-assignment. المتحكمجيد، نحن الآن مستعدين للاستمرار للخطوة القادمة، سنقوم بإنشاء المتحكِّم بإستخدام artisan: php artisan make:controller MarketingImageControllerوبهذا سوف تحصل على متحكِّم في app/Http/Controllers مع التوابع التالية: indexcreatestoreshoweditupdatedestroyوسوف نستخدم جميع هذه التوابع. وكنصيحة مفيدة للمبتدئين، قُم بوضع السطر التالي في تابع index: return 'Here is the index method.';فهذا سوف يعطيك فرصة لتجربة هذا الطريق route. (يستطيع بقية المبرمجين المحترفين تجاوز هذه الخطوة إذا أرادوا) وبعد ذلك، سنقوم بتثبيت الطرق routes. عدل على ملف app/Http/routes.php وأضف التعليمة التالية: Route::resource('marketingimage', 'MarketingImageController');سوف ترى أننا قد قمنا بإضافة مورد resource، والذي سوف يقوم بإعطائنا الطرق routes لجميع الإجراءات actions بطريقة مريحة للغاية. حسنا، سوف تستطيع الآن الذهاب إلى yourproject.com/marketingimage وسوف تحصل على النتيجة التالية: Here is the index method.الخطوة المنطقية التالية هي إعداد العروض views، أنشئ مجلدا باسم marketingimage أسفل resources/views، ثم أنشئ الملفات الفارغة التالية داخل مجلد marketingimages: create.blade.phpedit.blade.phpindex.blade.phpshow.blade.phpإعداد المجلداتسوف نعود إلى تلك الملفات في وقت لاحق، في الوقت الحالي، سنقوم بإنشاء مكان لتخزين صورنا الحالية، سوف أجعل هذا الأمر سهلا، أنشئ مجلدا باسم imgs مباشرة تحت مجلدك العام (public folder)، وبداخل مجلد imgs، أنشئ مجلد marketing، وبداخله أنشئ مجلدا باسم mobile وآخر بإسم thumbnails. الآن قمنا بإنشاء جميع المجلدات للصور. عرض الإنشاء The Create Viewحسنا، دعونا الآن نتعامل مع عرض الإنشاء The Create View. أضف الأسطر التالية داخل ملف `create.blade.php`: @extends('layouts.master') @section('content') {!! Breadcrumb::withLinks(['Home' => '/', 'marketing images' => '/marketingimage', 'create']) !!} <h1>Upload a Photo </h1> <hr/> @if (count($errors) > 0) <div class="alert alert-danger"> <strong>Whoops! </strong> There were some problems with your input. <br> <br> <ul> @foreach ($errors->all() as $error) <li>{{ $error }} </li> @endforeach </ul> </div> @endif {!! Form::open(array('route' => 'marketingimage.store', 'class' => 'form', 'files' => true)) !!} <!-- image name Form Input --> <div class="form-group"> {!! Form::label('image name', 'Image name:') !!} {!! Form::text('image_name', null, ['class' => 'form-control']) !!} </div> <!-- mobile_image_name Form Input --> <div class="form-group"> {!! Form::label('mobile_image_name', 'Mobile Image Name:') !!} {!! Form::text('mobile_image_name', null, ['class' => 'form-control']) !!} </div> <!-- is_something Form Input --> <div class="form-group"> {!! Form::label('is_active', 'Is Active:') !!} {!! Form::checkbox('is_active') !!} </div> <!-- is_featured Form Input --> <div class="form-group"> {!! Form::label('is_featured', 'Is Featured:') !!} {!! Form::checkbox('is_featured') !!} </div> <!-- form field for file --> <div class="form-group"> {!! Form::label('image', 'Primary Image') !!} {!! Form::file('image', null, array('required', 'class'=>'form-control')) !!} </div> <!-- form field for file --> <div class="form-group"> {!! Form::label('mobile_image', 'Mobile Image') !!} {!! Form::file('mobile_image', null, array('required', 'class'=>'form-control')) !!} </div> <div class="form-group"> {!! Form::submit('Upload Photo', array('class'=>'btn btn-primary')) !!} </div> {!! Form::close() !!} @endsectionلاحظ أن الشيفرة في الأعلى ليست صعبة ويمكنك فهمها بسهولة دون أن تضطر إلى قراءة الشرح، ولاحظ أيضا أننا نقوم بتوسيع صفحتنا الرئيسية master page، التي بداخلها مجلد المخططات في مجلد العروض. والتي قمنا باستدعائها عن طريق هذه التعليمة: @extends('layouts.master')إذا كانت لديك صفحة رئيسية أخرى أو أنها موجودة في مكان مختلف، قم بتعديل ذلك حسب الحاجة، وإذا كنت لا تعرف مفهوم الصفحة الرئيسية master page، قُم بالبحث عن دروس حوله وتعلمه قبل أن تتابع الدرس. إذا كنت لا تستخدم حزمة `Bootstrapper`، قم بحذف السطر التالي: {!! Breadcrumb::withLinks(['Home' => '/', 'marketing images' => '/marketingimage', 'create']) !!}لاحظ أيضا، لهذا الدرس، قمت بتضمين `if` لطباعة أخطاء الإدخال، لكن في العادة، يجب وضع هذا الجزء في جزئية العرض view partial ومن ثم الإشارة إليه بشيء مثل هذا: @include('errors.errors')يمكنك أيضا ملاحظة أننا نستخدم مساعد الاستمارة Form helper من حزمة `laravelcollective/html`، فلقد وجدت أن مساعد الاستمارة مفيد جدا خاصة عند استخدامه لفتح الاستمارة: {!! Form::open(array('route' => 'marketingimage.store', 'class' => 'form', 'files' => true)) !!}تستطيع أن ترى أننا قمنا بتضمين 'files => 'true والتي تسمح لنا برفع ملفات متعددة. إذا كنت جديدا في استخدام مساعد الاستمارة، فيمكنك ملاحظة أننا لا نحتاج إلى استخدام POST خاصة وأننا لا نحتاج إلى استدعاء رمز CSFR لأنه يتم ذلك تلقائيا. ثم لدينا مدخلات المختلفة للاستمارة، لا شيء مجنون للغاية هنا، ولدينا أيضا `Form::submit` والتي نستخدمها كزر، بالإضافة إلى `Form::close()`. يمكنك أن ترى من مساعد الاستمارة أن الطريق route تم تعيينه إلى `marketingimage.store`، لذلك سوف نعرف من مورد الطريق في `routes.php` أن هذا سوف يأخذنا إلى تابع `store` في `MarketingImageController.php` والذي هو دليل المتحكِّمات. نُكمل في الجزء الثاني من الدرس. ترجمة -وبتصرّف- للمقال Basic Image Management Part 1 لصاحبه Bill Keck. حقوق الصورة البارزة: Designed by Freepik.
-
قبل أن نذهب إلى الموضوع الرئيسيّ للمقال، سأعطيك لمحة قصيرة عن مشاكل التصميم التي قد تواجهها. لقد اشتكى لي أحد زبائني بأن بعض الصفحات تفتح ببطء شديد. وعندما أقول ببطء شديد، فإنني أعني ذلك! فقررت أن أصحح تلك الصفحة (بعمل debugging)، وما رأيته قد صدمني. لقد أظهر لي قسم الاستعلامات (queries) أن تلك الصفحة كانت تنفَّذ بعد القيام بكم هائل من الاستعلامات تعدّى 16500 استعلامًا!! لقد وجدت أن جزءًا من النصّ البرمجي هو سبب تلك المشكلة. لقد كانت هناك ثلاث حلقات foreach تستعلم عن خاصيّة والخواص الفرعيّة التابعة لها. لقد كانت تعمل جيدًا إلى أن صار في قاعدة البيانات 5500 عنصرًا. وفيما يلي ما كان يحدث: $main_object = MainObject::all(); foreach($main_object as $object) { echo $object->some_property; foreach($object->related_object as $related) { echo $related->some_property; echo $related->another_property; } foreach($object->another_related as $another) { echo $another->some_property; echo $another->another_property; } }إذا كان الاستعلام ;()main_object = MainObject::all$ يعيد 5500 نتيجة، فستعيد حلقة foreach الأولى ذلك القدر أيضًا، وكذلك بالنسبة للثانية والثالثة. باستخدام ORM، كثيرًا ما يقع المبرمجون في فخّ كتابة استعلامات قواعد بيانات غير كفؤة، وتجعلها ORM أكثر صعوبة في الاكتشاف. تُسمّى هذه المشكلة بمشكلة N+1 (بالإنجليزيّة N+1 problem). وأظن المطور السابق لم يكن على علم بذلك. ولتفادي هذه المشكلة، نستخدم التحميل الحثيث (eager loading). ما هو التحميل الحثيث؟لتبسيط الأمر، التحميل الحثيث طريقة تُعنى بعمل كل شيء عند الطلب. وهذه الطريقة أيضًا على العكس تمامًا من التحميل الكسول (lazy loading) عندما ننفذ المهام عند الحاجة. يساعدنا التحميل الحثيث على تجنب مشكلات الأداء، كما رأيت في مثالي أعلاه. ستفهم الأمر أكثر من خلال مثال، لذا لنتخيل الوضع التالي: لدينا نموذج علاقة هيئة محسّنة (بالإنجليزيّة: Enhanced Entity Relationship، واختصارًا EER)، بثلاث هيئات، كلّ منها مرتبطة بالأخرى. يمكنك أن تقرأ EER كما يلي: يمكن لكل عضو أن يملك العديد من المحلات، ولكن المحل الواحد ملك لعضو واحد فقط. يمكن للمحل الواحد أن يحوي العديد من المنتجات، ولكن المنتج الواحد لا يكون إلّا في محل واحد. الخطوة التالية هي إنشاء نماذج Eloquent لهذه الهيئات: العضو: <?php namespace App; use Illuminate\Database\Eloquent\Model; class Member extends Model { protected $fillable = ['username', 'email', 'first_name', 'last_name']; public function stores() { return $this->hasMany('App\\Store'); } }المحلّ: <?php namespace App; use Illuminate\Database\Eloquent\Model; class Store extends Model { protected $fillable = ['name', 'slug', 'site', 'member_id']; public function member() { return $this->belongsTo('App\\Member'); } public function products() { return $this->hasMany('App\\Product'); } }المُنتَج: <?php namespace App; use Illuminate\Database\Eloquent\Model; class Product extends Model { protected $fillable = ['name', 'short_desc', 'long_desc', 'price', 'store_id', 'member_id']; public function store() { return $this->belongsTo('App\\Store'); } }تخيّل أنك تبني تطبيقًا يسمح لمستخدميك أن يُنشئوا محالّهم التجاريّة الخاصّة. يمكن للمستخدمين –كما هو الحال بالنسبة للمحال الأخرى كلها طبعًا– أن يُنشئوا منتَجات عديدة. وأيضًا، يمكننا أن ننشئ صفحة واحدة تعرض كل المحلات وأفضل المنتجات لكل محلّ. شيء من قبيل هذا: يمكن أن ينتهي بك المطاف إلى الحصول على شيء كهذا في المتحكّم لديك: <?php namespace App\Http\Controllers; use App\Repositories\StoreRepository; class StoresController extends Controller { protected $stores; function __construct(StoreRepository $stores) { $this->stores = $stores; } public function index() { $stores = $this->stores->all(); return \View::make('stores.index')->with('stores', $stores); } }وفي العرض الذي ستقدم فيه تلك البيانات: @foreach($stores as $store) <h1>{{ $store->name }}</h1> <span>Owner: {{ $store->member->first_name . ' ' . $store->member->last_name }}</span><br> <h2>Products:</h2> @foreach($store->products as $product) <h3>{{$product->name}}</h3> <span>{{$product->short_desc}}</span><br/><br/> <span>Price: {{$product->price}}</span><br/> <?php Debugbar::info('Product displayed'); ?> @endforeach <br/>========================<br/> @endforeachوالنتيجة كالتالي: ومن اجل هذا المثال، زوّدت قاعدة البيانات بخمس مستخدمين، وثلاثة محالّ، وأربعة منتَجات. يقوم الاستعلام الأول باستدعاء كل المحال من قاعدة البيانات، وهذا هو الجزء +1 من مشكلة N+1. في هذا المثال تحديدًا، حرف N يمثّل عدد المحلات التي أرجعها لنا الاستعلام الأول، حيث أنها تمثل عدد المرات التي سنقوم فيها بالاستعلام select * from على جدولي products و members. وبما أن لدينا 3 محلات، فسنستعلم 3 مرات على جدول المستخدمين، وثلاث مرات على جدول المنتجات. وفي النهاية، قمنا بتنفيذ الاستعلامات بعدد مرات قدره 3+3+1. تخيل الآن ما الذي يمكن أن يحدث لو كان لديك 5000 أو 10000 محل؟ سيكون لديك في تلك الحالة عشرة آلاف إلى عشرين ألف استعلام في كل مرة يقوم فيها أحد المستخدمين بزيارة الصفحة. وماذا لو كانت لديك عشرة آلاف أو مئة ألف زيارة كلّ أربع وعشرين ساعة؟ هذا كابوس! من الواضح الآن أن هذا التوجّه مدمّر للأداء. وبغض النظر عن نوع قاعدة البيانات التي تستخدمها، وعن مدى قوة الخادم الذي لديك، فستصل دائمًا إل تلك النقطة التي يقف فيها العتاد القوي لديك عاجزًا. يمكنك أن تحسّن الأداء بعمل cache لهذه الاستعلامات، باستخدام Redis على سبيل المثال. سيؤدي هذا الغرض، ولكن لبعض الوقت فقط. وبتلك الطريقة، أنت فقط تؤجل النهاية الحتميّة التي ستكلّفك الكثير من المال والوقت، وفي الغالب ستفقد بعض الزبائن، أو أنّ قاعدة بياناتك ستضعف كثيرًا. وهنا يأتي التحميل الحثيث لينقذك من هذه الورطة. استخدام التحميل الحثيث في Laravel بسيط للغاية. العلاقات التي ترغب أن يتم تحميلها بشكل حثيث تحددها في طريقة with كما يلي: $stores = Store::with('member','products')->get();الآن، بدل استخدام 7 استعلامات، قلّلنا باستخدام التحميل الحثيث عدد الاستعلامات إلى 3 فقط: وستكون ثلاثة استعلامات حتى ولو كانت لديك عشرة آلاف مدخلة في جدول المحلات. وكما ترى، فإن الاستخدام السليم للتحميل الحثيث يمكن أن يؤدي إلى تحسين أداء تطبيقك بقدر هائل. ولكي نحصل على تحسن للأداء بالفعل، فعلينا أن نوجد فهرسًا لحقل الهويّة id في جدولي members و products. ومع وجود كمّ هائل من السجلات، فإن تنفيذ (... ,'in( '1', '2 على حقل غير مفهرس سيأخذ وقتًا طويلًا. وبعد هذه المقدمة عن التحميل الحثيث، هيا بنا نرى كيف يمكننا أن نستخدم العلاقات مع المستودعات. تمديد فئة المستودعسأريك طريقة واحدة يمكنك فيها أن تستخدم العلاقات في فئات مستودعات concrete. وهنا مثال عن النتيجة النهائيّة: function __construct(StoreRepository $stores) { $this->stores = $stores; } public function index() { $stores = $this->stores->with('member', 'products')->all(); .... }وكما ترى هنا، لدينا طريقة with يمكنك أن تسلسل فيها نموذج العلاقات. ستكون هذه الطريقة شبيهة بطريقة with في Laravel’s Query Builder. public function with($relations) { if (is_string($relations)) $relations = func_get_args(); $this->with = $relations; return $this; }نحتاج الآن لأن نربط كلّ علاقة من العلاقات التي قمنا بتقديمها بالنموذج: protected function eagerLoadRelations() { if(!is_null($this->with)) { foreach ($this->with as $relation) { $this->model->with($relation); } } return $this; }وها هو ذا، والشيء الوحيد الذي تبقّى هو أن نحدّث طريقة مستودع ()all (وأي شيء آخر ترغب بتحديثه) لاستخدام التحميل الحثيث: public function all($columns = array('*')) { $this->applyCriteria(); $this->newQuery()->eagerLoadRelations(); return $this->model->get($columns); }وكما سبق وذكرت، فيمكنك أن تضيف عدّة علاقات ضمن طريقة ()with. وفيما يلي مثال على StoresControler: <?php namespace App\Http\Controllers; use App\Repositories\StoreRepository; class StoresController extends Controller { protected $stores; function __construct(StoreRepository $stores) { $this->stores = $stores; } public function index() { $stores = $this->stores->with('member', 'products')->all(); return \View::make('stores.index')->with('stores', $stores); } }وفي العرض يمكنك أن تعرض البيانات بالطريقة التي تريدها، ولغرض التجربة يكفي هذا: @foreach($stores as $store) <h1>{{ $store->name }}</h1> <span>Owner: {{ $store->member->first_name . ' ' . $store->member->last_name }}</span><br> <h2>Products:</h2> @foreach($store->products as $product) <h3>{{$product->name}}</h3> <span>{{$product->short_desc}}</span><br/><br/> <span>Price: {{$product->price}}</span><br/> <?php Debugbar::info('Product displayed'); ?> @endforeach <br/>========================<br/> @endforeachوكما هو متوقع، لدينا الآن هذه الاستعلامات الثلاثة فقط: الخلاصةيمكنك باستخدام التحميل الحثيث أن تحسّن أداء تطبيقك. وأحيانًا، عندما يكبر التطبيق، حتى التحميل الحثيث ليس كافيًا للحفاظ على أعلى أداء. في الدرس التالي سأريك كيف يمكنك تجميل مستودعاتك لتقوم بعمل cache للاستعلامات من أجل أداء أفضل. ترجمة -وبتصرف- للمقال: Using Repository Pattern in Laravel 5 - Eloquent Relations and Eager Loading.
- 1 تعليق
-
- 1
-
- علاقات
- قاعدة بيانات
- (و 10 أكثر)
-
تتميز خدمة Google Analytics بروعة أدائها في تتبع حركة مرور زوار موقع ما، دون الحاجة إلى أي إعدادات أو إضافات، إلا أنها لا تتتبع تحميل الملفات مثل: ملفات PDF ،MP3، مستندات Word أو مقاطع الفيديو نظرا لاعتمدها على JavaScript. آخِذِينَ ما سبق بعين الاعتبار، سنتطرق وإياكم في هذا الموضوع إلى كيفية استخدام الأحداث (events) في Google Analytics بغرض تتبع تحميل الملفات. سنتطرّق بشكل سريع إلى بعض الطّرق التي تصلح على جميع المواقع ثم سنختم بمجموعة مُلحقات خاصة بووردبريس. ما يجب أن تعرفه قبل الشروع في العمل بحكم أنّك تقرأ هذا الموضوع، سنفترض أنك تملك مسبقا حساب Google Analytics جاهزا للعمل، إن كنت حديث العهد بهذه الخدمة فمن الأفضل أن تلقي أوّلًا نظرة على سلسلة مدخل إلى Google Analytics هنا على أكاديمية حسوب. يجب عليك أيضا أن تتأكد من استخدامك لأحدث نسخة Universal Analytics من شفرة التتبع (tracking code)، سنفترض أنك فعلا تستخدمها، أما إن كنت بحاجة إلى ترقية نسخة السكربت، يوفر لك جوجل كما هائلا من الموارد لمرافقتك في تطبيق مختلف الخيارات خلال كل مراحل عملية الترقية. استعمال الأحداث (Events) في Google Analytics يعمل جوجل على توفير طريقة أكثر مرونة لقياس تفاعل المستخدمين مع موقعك وذلك من خلال الأحداث (Events) في خدمة Analytics دون الحاجة إلى التقيد بشكل كامل بعدد الزيارات (page loads). يمكنك الولوج إلى الأحداث (Events) على حسابك من خلال الذهاب إلى تبويب Reporting ثم اختيار Behavior < Events. قسم Events في Google Analytics قبل تفعيله يتميز تصميم الأحداث (Events) في Google Analytics بقابلية الإعداد والتهيئة حسب ما ترغب فيه، يتوفر كل حدث على أربع مكونات أساسية يمكنك التعديل عليها بكل حرية لتناسب احتياجاتك ومتطلباتك: Category (التصنيف): الوصف أو المصطلح الذي يعود على نوع محدد من الأحداث، يعطيك هذا الخيار إمكانية تجميع الأحداث المتشابهة مثلا الكتب الإلكترونية (e-book) أو ما كل ما يتعلق بمقاطع الفيديو. Action (الإجراء): النتيجة أو الهدف الذي ترغب في تَتَبّعَهُ، يمكن أن يكون تحميلا، ضغطة على زر مُعيّن أو أي نوع آخر من الإجراءات أو العمليات التي تود أن تستهدفها. Label (الوصف): يوفر لك خيار الوسوم هذا بعض المساحة الإضافية لإضافة أي معلومات أخرى قد تبدو لك ذات أهمية. هذا الحقل اختياري Value (القيمة): لنفترض على سبيل المثال أن العرض المجاني الذي تقدمه لزوارك مقابل التسجيل في موقعك هو تحميل كتاب إلكتروني ما ولنفترض أيضا أن معدل التحويل على هذا التحميل هو %10 من قيمة المنتوج التابع لهذا الكتاب والتي تبلغ 150 دولارا، يمكن إذا في هذه الحالة ربط كل تحميل بقيمة 15 دولارا. هذا الحقل اختياري أيضًا يمكنك الاطلاع على المزيد من التفاصيل فيما يخص الخيارات (options) المتوافرة لكل واحدة من المكونات السابقة على توثيق جوجل لأحداث خدمة Analytics. لننتقل الآن إلى الاستخدام الفعلي لخاصية الأحداث هذه بغرض تجميع البيانات. كيفية إنشاء حدث جديد قم أولا بتسجيل الدخول إلى حسابك، تحديد الموقع (من قسم property) ثم الضغط على رابط Admin أعلى الشاشة: اختر Goals من الخانات الثلاث الظاهرة. يمكنك الآن أن ترى ثلاث خانات كما في الصورة أعلاه، اضغط على رابط Goals في خانة View ثم New Goal لتبدأ بعملية الإعداد: إنشاء هدف الحدث (Event Goal) ما يأخذنا إلى الشاشة أعلاه حيث يمكن إدخال المعلومات التالية: name (الاسم) وgoal ID (المُعرف الخاص بالإجراء المستهدف) ثم اختر Event لتحديد نوع الإجراء المستهدف. إنشاء تفاصيل الإجراء المستهدف (goal) نصل أخيرا إلى مرحلة إدخال المكونات المشار إليها مسبقا، عند انتهائك من ذلك ومُراجعتك للإعدادات التي اخترتها ما عليك إلا أن تضغط Save لحفظ التغييرات ما ينهي هذه المرحلة من الإعداد. إضافة تتبع حدث (Event Tracking) أولي إلى الصفحة بعد الانتهاء من إعداد الحدث في خدمة Analytics، علينا الآن أن نصبح قادرين على تفعيله على الموقع، يعتبر استخدام حدث من نوع onclick أسهل طريقة لإضافة التتبع إلى صفحة معينة، حيث يتم إرسال المعلومات إلى Analytics بمجرد تفاعل المستخدم مع الإجراء المستهدف. على سبيل المثال، إذا أردنا تتبع أداء كتاب إلكتروني مجاني، يجب فقط إضافة الشيفرة التالية إلى الرابط المعني كما هو مبين أسفله: <a onclick="ga('send', 'event', 'Downloads', 'Click', 'E-book downloaded', '0');" href="http://mysamplesite.com/wp-content/uploads/2015/12/my-free-lead-magnet.pdf">Download Our Guide to Speeding up Your Site</a> يمكنك الاطلاع على التفصيل الكامل للمزيد من الخيارات على صفحة تتبع الأحداث (Event Tracking) في Google Developers. قد تكون إضافة هذا الكود يدويًا حلًا عمليًا لما يكون لديك حدث واحد لتتبّعه، أما إذا كنت ترغب في تتبّع العديد من الأحداث، فهنالك بعض الطرق البديلة التي يمكنك استخدامها. يُمكن مثلا الاستعانة بـ jQuery لإضافة آلية تتبّع الأحداث بشكل آلي، ويُمكن مثلًا أن نُحدّد الرّوابط التي نرغب في استهدافها عبر إضافة id خاص إليها. كما يُمكننا الاستعانة بـ Google Tag Manager لإدارة هذه الأحداث دون الحاجة إلى التّعديل على شيفرة الموقع. أما إذا كنت تستخدم ووردبريس كنظام إدارة مُحتوى على موقعك فهناك عدّة مُلحقات تسمح لك بالقيام بالأمر دون أيّ عناء. تتبع تحميل الملفات باستعمال ملحق ووردبريس إذا بدا لك أن ما أسلفنا الذكر من الطرق والتقنيات كثير التعقيد، فإليك مجموعة من الملحقات التي يمكنها أن تتكفل بتتبع وإدارة تحميل الملفات على ووردبريس، إليك فيما يلي ثلاثا من أكثرها شعبية وانتشارا: Google Analytics Dashboard for WP يعمل ملحق Google Analytics Dashboard for WP على إحضار بيانات موقعك مباشرة إلى ووردبريس كما يسمح لك بالاطلاع على المعلومات بخصوص التحميلات المسجلة كأحداث (events). تم تنزيل هذا المُلحق حوالي 600 000 مرة ويُقارب تقييمه الخمس نجمات. يعتبر هذا الملحق طريقة عملية لجلب قوة Analytics مباشرة إلى لوحة التحكم الخاصة بك. Download Monitor يَتَّبِعُ ملحق Download Monitor مقاربة مختلفة قليلا فيما يخص تحميل الملفات عن طريق وضعها على قدم المساواة مع المنشورات والصفحات على موقعك. يسمح لك هذا الملحق بتصنيف ووسم تحميلاتك، إضافة بيانات وصفية مخصصة لها فضلا عن تتبعها، كما يوفر خصائص متقدمة مثل التحميلات الخاصة بالأعضاء. يوفر الملحق أيضا مجموعة من الإضافات الرائعة إن أردت التحكم بشكل أفضل في تحميل الملفات على موقعك. WordPress Download Manager يوفر ملحق WordPress Download Manager خصائص عَدِّ التحميلات وإرسال التقارير إضافة إلى إمكانية الدمج السلس مع Google Drive ،Dropbox و Box.com كما توفر النسخة المدفوعة من الملحق الكثير من خيارات التجارة الإلكترونية. هل تقوم أنت بتتبع تحميل الملفات على موقعك؟ كيف تقوم بذلك؟ شاركنا في التعليقات أسفله نصائحك والحيل التي تتبعها كما يمكنك أن تطرح علينا أي أسئلة تراودك. ترجمة وبتصرف للمقال: Tracking File Downloads With Google Analytics And Wordpress لصاحبه: TOM EWER.
-
عادة ما تستخدم الصور لزيادة قبول المقالات ووضوحها، ولكن بالرغم من انتشارها الواسع، فقد أُهملت صفحة المرفق للأسف، وصفحة المرفق هي صفحة مخصصة لمرفق بسيط، صورة مثلًا. لا تخصص العديد من القوالب صفحة للمرفق، مما يعني أن موقعك ومستخدميك يفتقدون مصدر مهم للمعلومات، وإضافة جيدة لـ SEO. في هذا المقال سنشرح صفحة المرفق مع كيفية إنشاءها. ما هو المرفق؟في ووردبريس المرفق هو ملف مرفوع على الموقع، قد يكون صورة، ملف مضغوط، فيلم أو ملف PDF. وتُخزن المرفقات في جدول المقالات بقاعدة بيانات الموقع، أي في نفس المكان الذي تُخزن فيه المقالات والصفحات. في الواقع، المرفقات هي مقالات من نوع مختلف – وهو "مرفق”. وهذه الطريقة في التخزين صُممت لتسهيل النفاذ والبحث، مثمرة عن تطوير أسهل وتقدم أسرع. ما هي صفحة المرفق؟صفحة المرفق هي صفحة منفردة. مصطلح الصفحة المنفردة هو مصطلح شامل، يتضمن أنواع مختلفة مثل صفحة المقال المفرد وصفحات المقال المنفرد المُخَصّص. وبما أن المرفق عبارة عن مقال، فهو أيضًا له صفحة منفردة، وتدعى صفحة المرفق. الصفحة المنفردة الخاصة بك هي صفحة مُخَصّصة لمقالك، وتعرض العنوان، المحتوى، البيانات الوصفية، التعليقات وما شابه. بالمثل، لا بد أن تكون صفحة المرفق مُخَصّصة للمرفق المعروض. أي لو كان المرفق صورة فيجب أن تحتوي الصفحة على العنوان، الصورة، والوصف، وقد تتضمن التعليقات ومعلومات أخرى عن الصورة. كيف تُعرض صفحة المرفق؟تستخدم كل صفحة في موقع ووردبريس ملف للقالب في عرض محتواها. و يمكنك مطالعة الهرم التسلسلي للقوالب لتحدد أي ملف يُستخدم في العرض. يُدعى الملف المستخدم لعرض صفحة المرفق في القالب attachment.php. قوالب كثيرة تتجاهل هذا الملف، وتستخدم الملف البديل وهو single.php. هذا في حد ذاته ليس بمشكلة، فكثير من صفحات المقال المنفرد مبنية بشكل يدعم العرض الجيد للمرفقات بدون أي نص برمجي إضافي. بعض الصفحات المنفردة تستخدم جمل الشروط "if-else"، التي تحدد كيفية ظهور الصفحة المنفردة في حالة عرض مرفق. للأسف، لا يهتم مطوري القوالب بتصميم صفحة المرفق، إذ أنهم عادة ما يكتفون بتصميم صفحة المقالة المنفردة، مما يثمر عن نتائج غير مستحبة: هذا مثال توضيحي لعرض صفحة المرفق في قالب X الغني عن التعريف. كما ترى فإنه لا يوضح أي معلومات عن الصورة ويعرضها كصورة مصغرة، بالرغم من أن الصورة كبيرة جدًا في الواقع. بالإضافة إلى فاصل مزدوج نتيجة لجزء فارغ لعدم وجوده في قسم المعلومات التفصيلية. يمكن لمطوري القوالب أداء أفضل من ذلك بتخصيص صفحات المرفق بحيث تعرض روابط إلى الصور التالية والسابقة، ومعلومات تفصيلية متعلقة بالصورة وهكذا دواليك. انظر إلى المثال الموضح من موقعي الخاص الذي يستخدم قالب Twenty Fifteen، القالب الافتراضي لووردبريس. ستجد أن الصورة معروضة مع الوصف المناسب أسفلها. وهي معروضة بأكبر مساحة تتناسب مع مقاييس الصفحة والأبعاد الأصلية مذكورة ضمن المعلومات التفصيلية. وهناك أيضًا جزء غير ظاهر في المثال هو استمارة التعليق ورابط إلى المقال الذي نشرت فيه الصورة. هذا أفضل بكثير! كيف أصل إلى صفحة المرفق؟أسهل طريق للوصول إلى صفحة المرفق هو الذهاب إلى قسم الوسائط في لوحة التحكم، ثم اختيار الصورة التي تريدها، وستجد خيار مشاهدة صفحة المرفق "View attachment page” تحت عنوان الصورة. يمكنك أيضًا استنتاج رابط الصفحة بسهولة إذا كنت تعلم رابط المقال المرفق إليه، ويستخدم موقعك روابط دائمة واضحة. فمثلًا إذا كان رابط المقال هو "http://mywebsite.com/my-post/” واسم ملف الصورة شيء مثل "my-image.jpg”، فمن المحتمل أن يكون رابط صفحة المرفق للصورة هو "http://mywebsite.com/my-post/my-image/”. كيف تنشء صفحة مرفقسنستخدم في مثالنا هذا قالب Hueman المجاني، قالب رائع لكن ينقصه عرض لطيف لصفحة المرفق. ملف صفحة المرفقاتسنبدأ بإنشاء قالب فرعي قبل إجراء أي تعديلات. وهذا سيكون صعبًا بعض الشيء لإنك ستحتاج لنسخ ملفات قالبك الحالي كيفما أمكن للتأكد من أن قالبك الجديد مماثل في الرؤية مع القالب الرئيسي. وتعتمد صعوبة أو سهولة هذه الخطوة على وضوح النص البرمجي للقالب الخاص بك. حيث توجد قوالب عديدة صعب قراءة نصها البرمجي، مما يؤثر على سهولة عملية التعديل. بدايةً، أنشء ملف attachment.php في قالبك الفرعي. ويمكنك التأكد من هذه الخطوة بزيارة صفحة المرفق. يجب أن تكون بيضاء، حيث أن الملف نفسه فارغ. ثم انسخ محتويات ملف single.php بالكامل من القالب الرئيسي إلى ملف attachment.php الجديد. وعندئذ عندما تفتح صفحة المرفق مجددًا فيجب أن تجدها عادت كما كانت سابقًا إذ أنها تستخدم نفس الكود (لا تنس أن ووردبريس يستخدم ملف صفحة المقال المنفرد في حالة عدم وجود ملف صفحة المرفق)، فقط من ملف مختلف. تخصيص صفحة المرفقنريد أن نتجنب التعديلات غير الضرورية في الملف. وستجد بالنظر إلى النص البرمجي أن ما نحتاج إلى تغييره هو ما يقع ضمن عنصر post-inner. فقط. وهذا يعني، في النسخة التي نعمل عليها، كل شيء يقع ضمن السطور 12-27 في هذا الملف. وهذا ما صار إليه الآن: <h1 class="post-title"><?php the_title(); ?></h1> <p class="post-byline"> <?php _e('by','hueman'); ?> <?php the_author_posts_link(); ?> · <?php the_time(get_option('date_format')); ?> </p> <?php if( get_post_format() ) { get_template_part('inc/post-formats'); } ?> <div class="clear"></div> <div class="entry"> <div class="entry-inner"> <?php the_content(); ?> <?php wp_link_pages(array('before'=>'<div class="post-pages">'.__('Pages:','hueman'),'after'=>'</div>')); ?> </div> <div class="clear"></div> </div><!--/.entry-->والآن سننشء التصميم الذي يُظهر الصورة بالأعلى، يليها العنوان والوصف. دعنا نتخلص من قسم البيانات الوصفية بالإضافة إلى جزء أنواع المقالات إذ لا نحتاجهم. <?php echo wp_get_attachment_image( get_the_ID(), 'large' ); ?> <h1 class="post-title"><?php the_title(); ?></h1> <div class="entry"> <div class="entry-inner"> <?php the_content(); ?> </div> <div class="clear"></div> </div><!--/.entry-->يوضح النص البرمجي بالأعلى المحتوى المُعدل الذي يقع ما بين وسميّ البداية والنهاية لعنصر post-inner. وقد استخدمت وظيفة ()wp_get_attachment_image لاستحضار الصورة بتوفير رقم ID للمقال الحالي – لا تنس أنه أيضًا رقم ID للصورة حيث أننا في صفحة المرفق – واخترت الحجم الكبير للصورة. والآن أصبح لدينا العنوان والوصف بنفس تنسيق القالب الأصلي. وحذفت قسم البيانات الوصفية وترقيم الصفحات للمقال، الآن أصبح أفضل بكثير: يمكننا الاكتفاء بهذا، لكني أريد أن أعرض فكرة لإضافة تفاصيل أكثر إلى الصفحة، دعنا نضيف روابط للأحجام المختلفة من الصورة لتسهيل التحميل. عن طريق وظيفة ()wp_get_attachment_metadata التي تتيح عرض قائمة لكل أحجام الصورة وتصفحها في حلقة Loop. وهذا مثال لكتابتها: <div class="entry"> <div class="entry-inner"> <?php the_content(); ?> <p class='resolutions'> Downloads: <?php $images = array(); $image_sizes = get_intermediate_image_sizes(); array_unshift( $image_sizes, 'full' ); foreach( $image_sizes as $image_size ) { $image = wp_get_attachment_image_src( get_the_ID(), $image_size ); $name = $image_size . ' (' . $image[1] . 'x' . $image[2] . ')'; $images[] = '<a href="' . $image[0] . '">' . $name . '</a>'; } echo implode( ' | ', $images ); ?> </p> </div> <div class="clear"></div> </div><!--/.entry-->ترى أنني عدّلت محتوى جزئية entry.، حيث أضفت عنصرًا جديدًا لعرض الأحجام. وفيه استخرجت أحجام الصورة المعرّفة باستخدام ()get_intermediate_image_sizes. وما تبقى هو مجرد حلقة بسيطة، تدور عبر أحجام الصورة مع استخراج الأبعاد بالإضافة إلى الرابط لكل صورة. وقد استخدمت هذه البيانات لإنشاء بعض الروابط وكتابتها في النهاية. الختامصفحات المرفق رائعة لأنها توفر لك فرصة لتقدم شيئًا قيمًا للقراء: معلومات أكثر عن صورك، خواطرك عنها، وربما روابط التحميل أو حتى معلومات عن الكاميرا والتقنيات التي استُخدمت في إلتقاطها. هذه المعلومات الغنية لها تقييم عالٍ على جوجل إذا استُخدمت بشكل دائم وصحيح. فهي أكثر من مجرد خاصية جذابة. إذا لم تمتلك صفحة مرفق بعد، فإني أقترح عليك إتباع هذا المقال وإنشاءها الآن. يمكنك أيضًا تحميل قالب Hueman الفرعي الذي أنشأته في هذا المقال، بالرغم من أنه سيكون ذا فائدة فقط إذا استخدمت قالب Hueman بالفعل كقالب رئيسي. هل وجدت هذا المقال مفيدًا؟ سنسعد بمعرفة آرائكم والرد على استفساراتكم في التعليقات. مترجم بتصرف من مقال Creating an Advanced Attachment Page in WordPress لصاحبه Daniel Pataki.
-
- مرفق
- attachment
- (و 5 أكثر)
-
أصبحت تقنيّة أجاكس موضة العصر في السنوات الأخيرة وعن جدارة واستحقاق، إن تقنيّة أجاكس، والتي هي بمسماها الكامل Asynchronous JavaScript and XML، هي طريقة لإجراء "محادثة" مع الخادوم، وعرض النتائج بدون إعادة تحميل الصّفحة. يُقدّم هذا الأسلوب للمطورين أمور عدّة: تحديث عدّاد الإعجاب (زر أعجبني).إضافة عناصر إلى سلّة التسوّق.إنشاء نماذج (forms) ديناميكيّة.وإليه من هذه الأمور، وكل ذلك بدون إعادة تحميل الصّفحةستتطرّق هذه المقالة إلى كيفيّة تحميل المنشورات (posts) مع تقنيّة أجاكس وباستخدام القالب الافتراضي Twenty Fifteen كمثال وأساس للشرح. سيتمّ التطرّق إلى لماذا يجب استخدام أجاكس من خلال مثالٍ مُبسّط، ومن ثُمّ سيتمّ العمل على مثال آخر وتحميل المنشورات باستخدام أجاكس وذلك باستخدام القالب Twenty Fifteen. لماذا يجب استخدام تقنية AJAX؟يَطلب سكريبت ووردبريس المنشورات من قاعدة البيانات عندما يتمّ تحميل الصفحة الأولى للمقالات، ويعرضهم مستخدمًا التوصيف (markup) المحدّد، وبجانب ذلك، سيتم تحميل قوائم التنقّل (navigation menus)، والإضافات المُصغّرة ودجت (widgets)، والرسومات، وملفّات جافا سكريبت، والعديد من الأمور الأخرى، وكما توضّح الصورة عدد الطلبات الّتي تمّ تطلبها في الصفحة الواحدة. يتّضح من الصورة السابقة (المأخوذة من أدوات المطورين الخاصّة بالمُتصفّح كروم) عدد لا بأس به من الأصول الّتي يتمّ تحميلها، وعلى الرغم من أنّه سيتمّ استخدام بعض التقنيات في تحسين الأداء، واستخدام التخبئة (cache) لبعض الأصول (assets) كما هو الأمر مع ملفّات جافا سكريبت، سيبقى عدد الطلبات عددًا لا بأس به. وإن تحميل الصّفحة الثّانية من المنشورات سيُحمّل كل ما سبق مرّة أُخرى، حيثُ سيجلب ووردبريس المنشورات ويعرضها باستخدام التوصيف (markup) المحدّد، ويتم تحميل عناصر الصّفحة مرّة أخرى أيضًا، ولذلك يُعتبر هذا الأسلوب مضيعة للموارد في أغلب الحالات، ولا يخدم تجربة المُستخدم أيضًا، فلا أحد يرغب بالانتظار بينما يتمّ استكمال تحميل الصّفحة بطبيعة الحال. إنشاء قالب فرعي Creating Child Themeسيتمّ التعديل على القالب Twenty Fifteen، ولكن قبل ذلك سيتمّ إنشاء قالب (theme) فرعي، وذلك للحفاظ على التعديلات في حال تحديث القالب. البداية مع تقنية AJAXسيتمّ البدء بمثال مُبسّط يشرح آلية عمل تقنيّة أجاكس، وذلك من خلال روابط شريط التصفيح (pagination)، في أسفل الصّفحة بحيثُ عندما يتمّ الضغط على رقم الصّفحة، يتم تحميل الصّفحة ديناميكيًّا، فعندما يتمّ الضغط على أحد هذه الروابط سيتمّ إرسال طلب (request) إلى الخادوم وتنبيه (alert) النتيجة. صف ملفات JavaScriptستكون الخطوة الأولى هي إنشاء ملفّ جافا سكريبت وصفّه باستخدام ملفّ القالب functions.php. تمّ إنشاء المجلّد js و الملفّ ajax-pagination.js بداخله، ومن ثُمّ إضافة الشيفرة التّالية إلى الملفّ functions.php. function my_enqueue_assets() { wp_enqueue_style( 'parent-style', get_template_directory_uri().'/style.css' ); wp_enqueue_script( 'ajax-pagination', get_stylesheet_directory_uri() . '/js/ajax-pagination.js', array( 'jquery' ), '1.0', true ); }يُمكن الرجوع إلى المقال صفّ وتسجيل ملفات Javascript و CSS في قوالب ووردبريس للمزيد من التفصيل حول صف ملفات جافا سكريبت، ولكن بالمُختصر ما سيتمّ عمله هو إخبار ووردبريس باسم السكريبت (كما في المُعامل الأوّل)، ومكانه (المُعامل الثّاني)، والمُتطلّبات (المُعامل الثّالث)، الإصدار (المعامل الرابع)، والتحميل سيكون في ذيل الصّفحة (المعامل الخامس). يجدر الانتباه هنا إلى إنه عند صفّ ملفّ التنسيق، تمّ استخدام ()get_template_directory_uri، وهذه الدالة دائمًا تُشير إلى مسار القالب الرئيسي (parent theme)، وعند صف السكريبت تمّ استخدام ()get_stylesheet_directory_uri، والّذي يُشير إلى مسار القالب الفرعي (chiled theme). بعد أن تمّ تحميل السكريبت في ذيل الصّفحة، يُمكن ببساطة كتابة: alert( ‘Script Is Enqueued’ ) داخل الملفّ ajax-pagination.js، ومع إعادة تحميل الصّفحة سيتمّ معرفة فيما إذا كان السكريبت يعمل أم لا. إنشاء حدث Creating an Eventستكون الخطوة التّالية هي إنشاء حدث (event) مهمته هي بدء استدعاء (call) أجاكس، وسيكون الحدث في هذه الحالة هو الضغط على رابط مُعيّن، ولاستهداف الرابط يجب معرفة أصناف (classes) العناصر والمعرّفات (IDs) المحيطة به. يُمكن الوصول إلى الشيفرة السابقة باستخدام أدوات المطورين المُضمّنة ضمن المُتصفّح Chrome. توضّح الصورة السابقة كيف أنّ روابط التصفيح تملك الصنف page-numbers، ورابط الصّفحة التّالية يملك الصنف السابق بالإضافة إلى الصنف next، وجميعها داخل الوسم nav الّذي يملك الصنف nav-links، يوجد أيضًا رابط الصّفحة السابقة، ولكنه لا يظهر في الصورة السابقة، والّذي يحمل الصنف prev بالإضافة إلى الصنف page-numbers. سيتمّ الآن استهداف أحد الروابط داخل حاوية التصفيح (pagination)، ومن ثم إنشاء تنبيه (alert) وبالرسالة Clicked Link: (function($) { $(document).on( 'click', '.nav-links a', function( event ) { event.preventDefault(); alert( 'Clicked Link' ); }) })(jQuery);يُلاحظ كيف أنّ كل شيء محتوى داخل دالّة مجهولة (anonymous function)، وهو ما يُنصح به، بعد ذلك تمّ إنشاء حدث الضغط، ومن ثُمّ منع الوظيفة الافتراضيّة للحدث (تحميل الصّفحة)، ومن ثُمّ عرض رسالة نصيّة باستخدام دالّة التنبيه (alert). إنشاء استدعاء AJAXسيتمّ الآن العمل على جلب بيانات ديناميكيّة من الخادوم بدلًا من التعامل مع واجهة الموقع فقط (تنبه نص معدّ مُسبقًا) كما في المثال السابق، ولذلك يجب إتمام بعض الإعداد المُسبق، وذلك للأسباب التّالية: يجب إعطاء استدعاء أجاكس رابطًا لاستخدامه هذا أوّلًا.ثانيًا، لا يَعلم ملفّ الجافا سكريبت المُنشئ ببيئة العمل الخاصّة بسكريبت ووردبريس. لذلك لا يُمكن استخدام دالة على الشكل ()get_stylesheet_directory_uri فيه، ولكن من المُمكن استخدام أسلوب المَوضَعة (localization) لتمرير المُتغيّرات إلى جافا سكريبت، وذلك في الملفّ functions.php: wp_localize_script( 'ajax-pagination', 'ajaxpagination', array( 'ajaxurl' => admin_url( 'admin-ajax.php' ) ));إن إضافة الشيفرة السابقة داخل الدالة ()my_enqueue_assets، سيقوم بتعريف الكائن ajaxpagination (المُعامل الثاني)، حيثُ سيَستلم هذا الكائن الـ members الخاصّة به تبعًا إلى المصفوفة المزوّدة كمُعامل ثالث في الدالة ()wp-localize_script، بمعنى آخر، عندما يتمّ إضافة هذه الشيفرة سيصبح من المُمكن استخدام ajaxpagination.ajaxurl لتعيين الرابط URL إلى admin-ajax.php، والّذي سيُستخدم لتولّي استدعاءات أجاكس. <script type='text/javascript'> /* <![CDATA[ */ var ajaxpagination = {"ajaxurl":"http:\/\/wordpress.local\/wp-admin\/admin-ajax.php"}; /* ]]> */ </script> أصبح كل شيء جاهزًا لبناء استدعاء أجاكس في ملفّ جافا سكريبت كما هو موضّح في الشيفرة التالية: $(document).on( 'click', '.nav-links a', function( event ) { event.preventDefault(); $.ajax({ url: ajaxpagination.ajaxurl, type: 'post', data: { action: 'ajax_pagination' }, success: function(result ) { alert( result ); } }) })تمّ استخدام الدالة ()ajax.$، مع العلم أنّه يوجد دوال خاصّة من أجل post و get، ولكن يُفضّل البعض استخدام هذه الدالة بسبب مرونتها، والّتي يُمكن القراءة عن كافّة مُعاملاتها من خلال التوثيق الرسمي لمكتبة jQuery. تمّ استخدام المُعامل url لتمرير مسار السكريبت المُراد إرسال البيانات إليه، وهو الملفّ admin-ajax.php، والّذي سيكون في المسار wp-admin، بعد أن تمّ تعريف ذلك سابقًا في الدالة ()wp_localize_script. يقوم أخيرًا المُعامل success، والّذي هو دالّة، بتنبيه (alert) نتيجة استدعاء أجاكس، وبذلك يكون قد تمّ اختبار هذه الدالة، والّتي سيتمّ التعديل عليها لاحقًا، ولكن الآن سيعمل الرابط إن تمّ الضغط عليه، ولكنه ليس بذو نفعٍ في الوقت الحالي، بما أنّه لم يتمّ إعداد الشيفرة من جهة الخادوم بعد، مع ذلك ستكون النتيجة هي الرقم 0، وهي النتيجة الافتراضيّة عندما لا تكون شيفرة الخادوم قد كتبت. يَكمن السبب في ظهور النتيجة السابقة رغم عدم كتابة شيفرة من طرف جهة الخادم إلى وجود شيفرة مكتوبة بالفعل من قِبل ووردبريس، وذلك بالملف admin-ajax.php المُستخدم في المثال، والّذي يحتوي في طياته على الدالة ('die('0. سيَخمد السكريبت admin-ajax.php ويعود من الدالة (return) بالقيمة 0، في حال عدم تزويد أي إجراء (action)، ولكن إن تمّ تزويد إجراء ولم يتمّ تزويد الخطافات (hooks) المطلوبة من قِبل ووردبريس فلن يحدث شيء، وفي نهاية الملفّ سيتمّ الخمود (die) مرّة أُخرى والعودة بالقيمة 0. الاتصال مع ووردبريسيجب تعريف بعضًا من إجراءات ووردبريس للحصول على إجابة ذو معنى من ووردبريس، ويتم ذلك من خلال استخدام نمط محدّد: add_action( 'wp_ajax_nopriv_ajax_pagination', 'my_ajax_pagination' ); add_action( 'wp_ajax_ajax_pagination', 'my_ajax_pagination' ); function my_ajax_pagination() { echo get_bloginfo( 'title' ); die(); }تمّ ربط دالّة مع خطافين (hooks)، فالخطافات الّتي تأخذ الشكل [wp_ajax_[action_nam تُنفّذ للمُستخدمين أصحاب العضويّة، والخطافات الّتي تأخذ الشكل [wp_ajax_nopiv_[action_name تُنفّذ للمُستخدمين الزوّار، وهذا أمر جيّد لفصل الوظائف عن بعضها بسهولة. إن أسماء الإجراءات (actions) الّتي تمّ ذكرها في الأعلى تُشير إلى الإجراء المعرّف في استدعاء أجاكس في ملفّ جافا سكريبت المُنشئ سابقًا (action: ajax_pagination)، أي من المُفترض أنّ يتطابقا، أما اسم الدالة فمن الممكن أنّ يكون أي اسم، وتم اختيار الاسم my_ajax_pagination للوضوح. يُمكن للدالة أنّ تحتوي على أي شيء، فمن المُمكن تسجيل خروج المُستخدمين، جلب بياناتهم، أو نشر منشور، ومهما كان المطلوب عودته (return) إلى جافا سكريبت فمن الضروري استخدام echo، وفي المثال الحالي تمّ استخدام echo مع عنوان المُدوّنة، والّذي يُمكن الوصول إليه عن طريق الدالة ()get_bloginfo. ستكون الخطوة الأخيرة هي استخدام ()die، فبعدم تعريفها، فإن الدالة (dir(0 المعرّفة في نهاية الملف admin-ajax.php سيتمّ تنفيذها ليتم طباعة 0 بالإضافة إلى ما سيتمّ طباعته في الدالة الّتي يتمّ كتابتها حاليًّا، والآن إن تمّت تجربة الشيفرة السابقة فمن المفترض رؤية عنوان الموقع. تلخيصتمّ إلى هنا الوصول إلى نهاية المثال، وقبل الانتقال إلى كيفيّة استعراض المنشورات باستخدام أجاكس، سيتمّ مراجعة الخطوات الضروريّة لإتمام استدعاء أجاكس: صف (enqueue) ملفّ جافا سكريبت، إن لم يكن متوفرًّا بالأساس.استخدام ()wp_localize_script لتمرير URL للملف admin-ajax.php.إنشاء استدعاء أجاكس في جافا سكريبت.ربط دالة باستخدام اسم خطّاف (hook) مناسب.كتابة شيفرة الدّالة والّتي ستعود بالبيانات إلى جافا سكريبت.تحميل المنشورات باستخدام AJAXسيتمّ البدء بكتابة شيفرة جافا سكريبت، والّتي ستكون مبدئيًّا بالشكل التّالي، والتعديل عليها لاحقًا. (function($) { function find_page_number( element ) { element.find('span').remove(); return parseInt( element.html() ); } $(document).on( 'click', '.nav-links a', function( event ) { event.preventDefault(); page = find_page_number( $(this).clone() ); $.ajax({ url: ajaxpagination.ajaxurl, type: 'post', data: { action: 'ajax_pagination', query_vars: ajaxpagination.query_vars, page: page }, success: function( html ) { $('#main').find( 'article' ).remove(); $('#main nav').remove(); $('#main').append( html ); } }) }) }) (jQuery);إن الشيفرة السابقة مُشابهة إلى المثال المُبسّط السابق، مع ملاحظة أنه تمّ إضافة طريقة لمعرفة أي صفحة أراد المُستخدم طلبها، وكل رابط لديه عنصر span بداخله وهو في حالة عدم الظهور، كما تمّ استنساخ (clone) العنصر لكيلا يتمّ التعديل على العنصر الأصلي، ومن ثم حذف العنصر span وتحليل (parse) الباقي كعدد صحيح (integer) باستخدام الدالة parseInt، ليكون الناتج رقم الصّفحة. سيكون من الضروري أيضًا معرفة مُعاملات الاستعلام (query parameters) المُستخدمة، وسيكون الأمر سهلًا في الصفحة الرئيسيّة، باستخدام المُعامل paged، وذلك لأنّه يتمّ التعامل مع الاستعلام الافتراضي، إن تمّ البدء على صفحة أرشفة (archive page)، مثل أرشيف التصنيفات (category)، فعندها يجب معرفة اسم الصنف أيضًا. سيتمّ تمرير مُتغيّرات الاستعلام باستخدام طريقة الموضعة (localization) المُستخدمة سابقًا، ولكن الآن سيتمّ استخدام ajaxpagination.query_vars على الرغم من عدم تعريفها بعد، وأخيرًا وفي success سيتمّ حذف جميع العناصر article من الحاوية الرئيسية، وحذف عنصر التصفيح (pagination) وإلحاق (append) القيمة المُعادة من (return) استدعاء أجاكس المُنشئ إلى الحاوية الرئيسية. ستحتوي القيمة المُعادة (return) على المنشورات وعنصر التنقل الجديد، مع ملاحظة تغيير اسم المُعامل من response إلى html ليصبح الاسم معبّرًا أكثر، أخيرًا تمّ استخدام مصفوفة الموضعة لتمرير معاملات الاستعلام الأصليّة. يجب على الدالة التّالية أنّ توضع في الدالة ()my_enqueue_assets لتبديل الموضعة (localization) الّتي تمت سابقًا. global $wp_query; wp_localize_script( 'ajax-pagination', 'ajaxpagination', array( 'ajaxurl' => admin_url( 'admin-ajax.php' ), 'query_vars' => json_encode( $wp_query->query ) ));ستكون الدالة ()my_ajax_pagination في صورتها النهائيّة على الشكل التّالي: add_action( 'wp_ajax_nopriv_ajax_pagination', 'my_ajax_pagination' ); add_action( 'wp_ajax_ajax_pagination', 'my_ajax_pagination' ); function my_ajax_pagination() { $query_vars = json_decode( stripslashes( $_POST['query_vars'] ), true ); $query_vars['paged'] = $_POST['page']; $posts = new WP_Query( $query_vars ); $GLOBALS['wp_query'] = $posts; add_filter( 'editor_max_image_size', 'my_image_size_override' ); if( ! $posts->have_posts() ) { get_template_part( 'content', 'none' ); } else { while ( $posts->have_posts() ) { $posts->the_post(); get_template_part( 'content', get_post_format() ); } } remove_filter( 'editor_max_image_size', 'my_image_size_override' ); the_posts_pagination( array( 'prev_text' => __( 'Previous page', 'twentyfifteen' ), 'next_text' => __( 'Next page', 'twentyfifteen' ), 'before_page_number' => '<span class="meta-nav screen-reader-text">' . __( 'Page', 'twentyfifteen' ) . ' </span>', ) ); die(); } function my_image_size_override() { return array( 825, 510 ); }إن استخدام المُعاملات المُمرّرة مكّن من بناء استعلام مُخصّص (custom query) وذلك عن طريق استخدام مُتغيّرات الاستعلام الّتي تمّ تمريرها والتأكّد أنّ رقم الصّفحة الّذي تمّ تمريره يستبدل المُعامل paged، ومن ثُمّ تمّ استخدام المصفوفة query_vars لإنشاء الاستعلام الجديد. يجب جعل المُتغيّر ['wp_query']اGLOBALS$ مساويًا إلى كائن المنشورات الجديد، والسبب في ذلك هو أنّ الدالة ()the_posts_pagination تَستخدم هذا المُتغيّر العام (global variable). يُلاحظ أنّه تمّ إضافة دالة إلى المُرشح editor_max_image_size، ومن ثُمّ وبعد أسطر قليلة تمّ إزالتها، وذلك لمشكلة في سكريبت ووردبريس نفسه، وفي الحقيقة تمّ تسجيل هذا المُشكلة في مُتتبّع المشاكل الخاصّ بووردبريس حيث أن المشكلة هي كالتالي: ستتحمل الصور بشكل مناسب عندما يتم تحميلها في صفحة المنشور، ولكن بدون هذه المُرشحات فإن الصور ستكون ضيقة، أي ستكون 660px عرضًا بدلًا من 825px، والسبب في ذلك هو أنّ الدالة الّتي تحمّل الصور ستستدعي دالة بالاسم ()image_constrain_size_for_editor، وهذه الدالة تتأكّد من أنّ الصور في محرّر المنشور ليست عريضة بشكل كبير، ولتحديد فيما إذا كان يجب تقلّيص الحجم أم لا، تُستخدم الدالة ()is_admin في ذلك، وبما أنّ شيفرة المثال تعمل عبر admin-ajax.php، والّذي يعتبر admin في نهاية الأمر، سيُقلّص ووردبريس من حجم الصور، معتقدًا أنّه يتمّ استخدامهم في المُحرّر. يُمكن استخدام المرشح editor_max_image_size لتحديد الحجم الأعظمي للصور في المُحرّر، وبما أنّه المطلوب هو ترك كل شيء على ما هو عليه، باستثناء استدعاء أجاكس، فقد تمّ إضافة المرشح باستخدام القيمة المخصّصة (array( 828, 510 ومن ثُمّ إزالتها مباشرة لتأكّد من أنها لا تسبب مشاكل في أي مكان آخر. ستكون الخطوة التّالية استخدام الاستعلام في عرض المنشورات، حيثُ تمّ نسخ الكثير من الملفّ index.php في القالب الرئيسي (parent theme)، وفي حال عدم توفّر منشورات سيتمّ استخدام عارضة (template) مُخصّصة لهذا الأمر، وأخيرًا سيتمّ استخدام شكل التصفيح (pagination) كما هو في الملفّ index.php. تجربة مُستخدِم أفضليجب التركيز على تجربة المُستخدم دائمًا، حيثُ أنّه عند العمل في بيئة التطوير يبدو التصفّح سريعًا للغاية، ولكن تأخذ الصور وبقيّة الأصول (assets) وقتًا أطول في التحميل في بيئة العمل الحقيقيّة. يُفترض إضافة مُحمّل (loader) أو على الأقل نصّ يُشير إلى جريان تحميل المنشورات وذلك في سبيل تحسين تجربة المُستخدم، وبالإضافة إلى ذلك من المُمكن تعطيل أي نقرات (clicks) إضافيّة على عناصر شريط التنقل، وهذا ما سيتمّ في المثال الحالي: إخفاء المنشورات وشريط التنقل مباشرةً بعد عملية الضغط من قبل المُستخدِم ومن ثم عرض رسالة فحواها هو "loading new posts" (جاري تحميل المنشورات)، وعند الحدث success سيتمّ إزالة نص الرسالة وعرض المنشورات، وسيكون استدعاء أجاكس بصيغته النهائيّة على الشكل التّالي: $.ajax({ url: ajaxpagination.ajaxurl, type: 'post', data: { action: 'ajax_pagination', query_vars: ajaxpagination.query_vars, page: page }, beforeSend: function() { $('#main').find( 'article' ).remove(); $('#main nav').remove(); $(document).scrollTop(); $('#main').append( '<div class="page-content" id="loader">Loading New Posts...</div>' ); }, success: function( html ) { $('#main #loader').remove(); $('#main').append( html ); } })أصبح هناك دالتين منفصلتين وهما beforeSend و الدالة success، الأولى يتمّ تنفيذها حالما يتمّ الضغط على الرابط، قبل أنّ يُرسل استدعاء أجاكس إلى الخادوم، أما الثانية فيتم تنفيذها حالما يتمّ استقبال البيانات من الخادوم. سيتمّ إزالة المقالات وشريط التنقّل قبل إرسال الاستدعاء، ويفيد هذا الأمر في منع المُستخدم من الضغط باستمرار على روابط شريط التنقّل إلى حين انتهاء التحميل، ومن ثُمّ سيتمّ التدرّج إلى أعلى الصّفحة، وإضافة تنبيه يوضّح جريان عملية التحميل لتوضيح الأمور للمُستخدم، وفي الدالّة success تمّ إزالة المُحمّل loader ومن ثُمّ تحميل المُحتوى. مشاكل أجاكسإن أجاكس تقنيّة متقدّمة وقويّة بلا شك، فبجانب المثال المٌقدّم في هذا الدليل، يُمكن إجراء العديد من الأمور باستخدام استدعاءات أجاكس، ولكن يجب الحذر في بعض المواضع عند تطبيق هذه التقنيّة، سيتمّ التطرّق إلى بعضها: الحماية: تُعبر الحماية من الأمور الأساسيّة والهامّة الّتي يجب التركيز عليها، فعند الرغبة في حذف منشور باستخدام أجاكس يجب التأكد من عزم المُستخدم على ذلك، وأيضًا يجب التأكّد من صلاحياته (باستخدام nonces)، خاصّة بعد العلم أنّ ووردبريس مُجهز بحماية مُضمّنة عند العمل بشكله الافتراضي، ولكن عند تطبيق تقنيّة أجاكس فإن مسألة الحماية تقع على المطوّر.أجاكس بدون جافا سكريبت: تعمد تقنيّة أجاكس على جافا سكريبت، بمعنى آخر عدم توفّر جافا سكريبت يعني عدم توفّر أجاكس، فإن تمّ الاعتماد على أجاكس بشكل كبير في الموقع، فلن يستطيع المستخدمين الذين قاموا بتعطيل جافا سكريبت على متصفحاتهم من استخدام الموقع، على الرغم من أن جافا سكريبت أصبحت متوفّرة دائمًا، ولكن لا يخلو الأمر من هذه الحالات، وعليه من الجيّد ضمان عمل التطبيق في حال عدم توفّرها.تجربة المُستخدم: تُهمل تجربة المُستخدم غالبًا، وما يقدّمه أجاكس بالتأكيد أمر في غاية الأهميّة، ولكن الأهم هو موقع يعمل كما هو مفروضٌ له أنّ يَعمل، فالمُستخدمين معتادين على تحميل الصّفحة من جديد عند الضغط على الروابط، لذا من مهمّة المُطوّر جعل كل شيء واضح بالنسبة لهم، وعلى المُستخدمين معرفة ما الذي يحدث ولماذا، بمعنى آخر يجب استخدام أجاكس لتحسين الموقع، وعدم الإفراط في الاستخدام لكيلا ينقلب السحر على الساحر.تلخيصكما هو مُلاحظ إن تطبيق تقنيّة أجاكس يتطلّب القليل من التحضير والتدريب، ولكن مع التكرار ستصبح الأمور سهلة وميسرة، وربّما قراءة هذا الدرس قد استغرقت بعض الوقت، وسيستغرق تطبيق المثال أيضًا وقتًا أطول وخاصّة في المرّة الأولى، ولكن مع المُمارسة ستغدو الأمور أوضح والتطبيق أسرع. ترجمة –وبتصرّف- للمقال Loading WordPress Posts Dynamically With AJAX لصاحبه Daniel Pataki.
-
واحدة من الإمكانيات الأساسيّة التي يجب أن تتمتّع بها معظم خوادم الإنترنت؛ القدرة على استقبال وإرسال المعلومات إلى الأجهزة الأخرى المُتصلة بالشبكة، فعلى الرغم من أن الناس تنظر عمومًا إلى الخوادم باعتبارها منصات تزويد بالمحتوى، إلا أنها يجب أن تملك القدرة على استقبال المحتوى لأسبابٍ عديدة. وفي حين أنّ معظم حزم البرامج في غنو لينكس متوفرة ضمن المستودعات الرسمية لكل توزيعة ويمكن تحميلها وتركيبها باستخدام أدوات مدراء الحزم المعروفة، إلا أنّ باقي أنواع الملفات والمعلومات تستخدم آليات مختلفة. نناقش في درسنا هذا بعضًا من الطرق الشائعة لتحميل الملفات والمعلومات إلى خادوم لينكس الخاص بك. سوف نستخدم بشكل رئيسي خادوم يعمل بنظام Ubuntu 14.04 لتطبيق الأمثلة الواردة هنا، إلا أنه يمكنك المتابعة معنا بالتأكيد بغض النظر عن إصدار ونوع توزيعتك. الحصول على البيانات والتطبيقات من المستودعاتقد يكون استخدام المستودعات الرسميّة لجلب الحزم والتطبيقات إلى خادومك أكثر الطرق شيوعًا. تُشير المستودعات في سياقنا هنا إلى عدّة أشياء مختلفة، فقد تُعبّر مثلًا عن المجموعات الكبيرة من التطبيقات المتوفرة بصيغة مُترجمة compiled جاهزة للتثبيت، والتي تمّ اختبارها وضبطها بحيث تناسب التوزيعة التي تستخدمها. إضافةً لذلك لدينا المستودعات المصدرية، والتي تحتوي على كافة الملفات الضروريّة لبناء تطبيق ما انطلاقًا من المصدر. وسنتناول كلا النوعين من المستودعات فيما يلي. تركيب البرامج من المستودعات العاديّة لتوزيعتكالطريقة القياسيّة لتركيب البرامج في بيئة غنو لينكس هي استخدام مدير الحزم، والمُعدّ مسبقًا للاتصال مع مجموعة من الخوادم المُجهزّة بمستودعات تضم آلاف الحزم التي تمّ فحصها، تحزيمها، واختبار توافقها مع النظام. تستخدم توزيعات غنو لينكس أنواعًا مختلفة من صيغ التحزيم ومدراء الحزم لإنجاز ذلك. وتعتبر صيغة التحزيم deb. الأكثر شهرةً، وهي الصيغة المُستخدمة في توزيعة دبيان و Ubuntu وعدد آخر من مشتقاتهما، ولدينا أيضًا صيغة التحزيم rpm. والتي تُستخدم عادةً في توزيعة RedHat والتوزيعات المبنية عليها مثل CentOS و Fedora، أخيرًا هناك بعض التوزيعات التي تستخدم نظامًا ثالثًا أبسط مما سبق وهو صيغة التحزيم tar.xz. كتوزيعة Arch Linux. وفي العموم، فإن التوزيعات التي تستخدم التحزيم ذو اللاحقة deb. تعتمد على مدير الحزم apt، بينما تعتمد التوزيعات التي تستخدم تحزيم rpm. على مدير الحزم yam أو إصداره المُحدّث dnf. وباعتبار أن Arch Linux تُحزّم البرامج بصيغة ثالثة، فإنها تملك كذلك مدير حزمها الخاص والذي يدعى pacman لإدارة عمليات التثبيت والحذف وما إلى هنالك، يمكنك قراءة المزيد عن كيفيّة استخدام pacman من خلال صفحة الويكي الخاصّة به في موسوعة Arch. كيفيّة استخدام أرشيف الحزم الشخصيّ PPAواحدة من الطرق الإضافيّة للحصول على البرامج والتطبيقات في الأجهزة العاملة بنظام Ubuntu هي استخدام أراشيف الحزم الشخصية أو ما يعرف بـ PPA، والتي تُكسب توزيعة Ubuntu مرونة جيّدة. تشير الـ PPA بشكل أساسيّ إلى مستودع على الإنترنت، عادةً ما يشمل واحدة أو عددًا قليلًا من الحزم، و يدار بواسطة شخص أو فريق عمل مستقل عن قنوات Ubuntu الرسميّة، مما يزوّد المستخدمين بمصادر إضافيّة لمدير الحزم، بحيث تصبح التطبيقات المخزّنة ضمن هذه المستودعات متاحة للتركيب بشكل سلس إلى جانب الحزم الأخرى. تتمتع أراشيف الحزم الشخصيّة بعددٍ من المميزات إذ تُمكّنك من الحصول على أحدث نسخ التطبيقات بين إصدارات Ubuntu الرسميّة كلّ ستّة أشهر، حيث عادةً ما يترك فريق Ubuntu مهمة تحديث نسخ البرامج الجديدة حتى موعد الإصدار القادم من التوزيعة، إضافةً إلى إتاحة الوصول لمجموعة أوسع من التطبيقات التي لا يقوم فريق Ubuntu الرسمي بتحزيمها أصلًا، فيما لو توفر فريق من المتطوعين الذين يتخذون على عاتقهم مهمة توفير هذه الحزم. والميزة الأهم عن البناء من المصدر هي أن هذه الحزم تُدار بواسطة أدوات مدير الحزم التقليدي، وهذا يشمل إمكانية استقبالها للتحديثات بشكل دوري ودمجها مع نظام التحزيم العام، الأمر الذي يسهّل عليك عددًا من المهام كحل مشاكل الاعتماديات. وفي المقابل هناك بعض المساوئ التي تعتري هذا الأسلوب بطبيعة الحال، أحدها أنك ستضع الكثير من الثقة في مشرفي ومطوري مستودعات PPA. فبينما هناك أسباب وجيهة لمنح منتجي Ubuntu هذه الثقة، فإنه يتوجب عليك أن تسائل نفسك فيما إذا كانت مستودعات PPA تُقدَّم من قِبل جهة جديرة بالثقة. فحتى لو لم يملك المطورون أغراضًا خبيثة، فقد لا يدركون بالشكل المثالي المحاذير الأمنيّة مما قد يُسبّب مخاطر جديّة عن غير قصد. أمرٌ آخر يجب أن تحتفظ به في ذهنك، ألا وهو فترة حياة مستودعات PPA، إذ عليك أن تملك خطّة عمل فيما لو توقّف الدعم فجأةً عن هذه المستودعات من قبل المصدر، ثم هل تملك الوقت لمراقبة الحالات التي تُقرّر فيها توزيعتك أخيرًا إضافة الدعم رسميًا لهذه الحزم من خلال المستودعات الافتراضية؟ قبل أن نتابع، قد يتوجب عليك تركيب الحزمة التالية في Ubuntu لتسهيل إدارة مستودعات PPA، والتي يختلف اسمها تبعًا للإصدار الذي تستخدمه، إلا أنه يجب أن تكون قادرًا على استخدام أحد هذين الخيارين: sudo apt-get update sudo apt-get install python-software-properties # For Ubuntu 12.04 and lower sudo apt-get install software-properties-common # For Ubuntu versions > 12.04 بعد ذلك يمكنك إضافة مستودعات PPA بواسطة الصيغة العامة التالية: sudo add-apt-repository ppa:PPA_name ولتفعيل المستودع الجديد ينبغي تحديث فهرس الحزم للحصول على المعلومات الجديدة من PPA المُضاف، وأخيرًا يمكنك تركيب البرنامج الجديد الذي يُقدّمه المستودع كالعادة: sudo apt-get update sudo apt-get install new_packageمستودعات Gitتُعتبر Git نوعًا آخر من المستودعات والتي يُرجّح أنك سمعت بها من قبل، في الأصل فإن Git هو برنامج مُوزّع وغير مركزي لإدارة إصدارات البرامج وتسهيل المشاركة في تطويرها وإدارة نُسخها، فإذا كان البرنامج الذي تبحث عنه مُستضافًا على مستودع git أو بواسطة إحدى خدمات الوِب لاستضافة البرمجيات باستخدام git مثل GitHub، Bitbucket، private GitLab، فيمكنك حينها تنزيل نسخة من الملفات بسهولة عن طريق الأمر git. في البداية دعنا نتأكد من وجود الأداة git مُثبّتة على نظام التشغيل: sudo apt-get update sudo apt-get install git بعد ذلك يمكنك إنشاء مجلّد جديد والانتقال إليه لتحفظ المشروع وتستنسخ مستودعه باستخدام المعلومات التي يقدّمها موقع الاستضافة. فعلى سبيل المثال للحصول على رابط URL لمستودع مشروع في موقع GitHub انظر إلى الجانب الأيمن: يمكنك الآن نسخ عنوان الرابط URL وتمريره بعد ذلك إلى الطرفية باستخدام الأمر: git clone https://github.com/user/project.git ينسخ الأمر السابق المشروع بالكامل إلى الدليل النشط في الطرفيّة. موارد الويب العامّةفي حين أن استخدام المستودعات لإدارة البرامج أمرٌ سهل، ويوفّر طريقة رائعة لتتبّع الترقيعات والإصدارات الجديدة، إلا أنها قد لا تكون الطريقة المتاحة دومًا لأسباب عديدة؛ من ذلك أن عددًا آخر من التطبيقات غير موجودة لا يتوفر ضمن مستودعات، كما أنك قد تحتاج إلى أنواع أخرى من البيانات (غير حزم البرمجيات) على الخادوم الخاص بك. ولهذه الحالات نحن نحتاج إلى مجموعة أخرى من الأدوات التي يمكن أن تساعدنا. سنناقش فيما يلي عددًا من الطرق متفاوتة التعقيد، لهذا الغرض. تحميل ونقل البيانات عن بُعدقد تكون الطريقة الأكثر بداهةً لتحميل البيانات إلى الخادوم الخاص بك هي تنزيل هذه البيانات إلى حاسوبك المنزلي أولًا ومن ثم إعادة رفعها إلى الموقع. وعلى الأرجح أنك استخدمت هذه الطريقة بالفعل لرفع بعض المحتوى إلى موقعك، فرغم أنها قد لا تكون الأكثر أناقة إلا أنها سهلة بالتأكيد. أي نوع من المحتوى، كالملفات والحزم، والتي ترغب بتضمينها في موقعك، يمكن تنزيلها إلى حاسوبك باستخدام متصفحات الوبِ التقليديّة. تأكد عند تحميلك تطبيق ما من حصولك على الإصدار الصحيح المطابق للتوزيعة المُثبتة على خادومك، بما في ذلك نوع الحزمة، إصدارها، ومعماريتها (في حال كان المصدر يتيح ذلك). بعد ذلك، يمكنك نقل هذه الملفات بسهولة إلى خادومك، الطريقة التي أنصح باتباعها هي الاتصال عبر sftp، والتي ستؤّمن لك اتصالًا آمنًا ويسيرًا لنقل الملفات، يمكنك قراءة درسنا عن استخدام sftp من سطر الأوامر. الطريقة الأخرى هي استخدام عميل FTP مع إمكانية sftp، والتي شرحناها في درسنا هنا عن استخدام تطبيق FileZilla مع sftp. هذه غالبًا الطرق الأكثر مرونةً لتزويد خادومك بالمحتوى، حيث تتيح لك نقل الملفات الجديدة التي أنشأتها إضافةً إلى تلك الموجودة على الوِب. تصفح الوب من خلال الطرفيّةهناك طريقة أخرى أجدها ممتعة لتزويد موقعك بالمحتوى وهي استخدام متصفح الإنترنت ضمن الخادوم. وعلى الرغم من أنه يمكنك تثبيت واجهة رسومية على الخادوم الخاص بك ومن ثم استخدام أحد المتصفحات التقليدية إلا أنني أعتبر ذلك نوعًا من المبالغة المُسرفة غير الضرورية، طالما هناك بديل آخر، ألا وهو استخدام المتصفحات المُخصّصة للاستعمال ضمن الطرفيّة نفسها والتي تسمح لك بزيارة المواقع واستعراض محتواها النصيّ. لنستعرض الآن بعضًا من الخيارات المتوفرة لمتصفحات الوِب من خلال الطرفية. lynxيعتبر lynx أقدم متصفح وِب لا يزال تطويره واستخدامها نشطًا، كما أنه سهل الاستخدام، بشكل أساسي يتمّ التصفح باستخدام السهمين العلوي والسفلي للتنقل بين روابط الصفحة، وللضغط على رابط ما يتم تحديده بدايةً ثم الضغط على مفتاح الإدخال Enter أو السهم اليمني. قد لا يكون lynx متاحًا بشكل افتراضي على نظام التشغيل لديك، إلا أنه يمكنك تثبيته بسهولة من مدير الحزم: sudo apt-get update sudo apt-get install lynx يدعم متصفح lynx كلًا من ملفات تعريف الارتباط cookie والعلامات المرجعيّة bookmarks، كما يمكنه تلوين خرجه فيما لو دعمت الطرفية التي تستخدمها ذلك، وفي العموم يمكنك استخدامه لزيارة أي نوع من المواقع باستثناء تلك التي تعتمد على إضافات خارجية (كجافاسكربت أو فلاش) لتوفير وظائفها. هنا على سبيل المثال استعرضنا موقع أكاديمية حسوب باستخدام المتصفح lynx ضمن طرفية mlterm: linksيقدّم links أداةً أخرى رائعة لتصفح الوِب من خلال الطرفيّة، ويتميز عن سابقه بأنه يحتوي على شريط قوائم علوي مماثلًا للمتصفحات التقليديّة (يمكن تفعيل شريط القوائم بالضغط على زر ESC). لتثبيت links في حال لم يكن مُثبتا بالفعل؛ استخدم مدير الحزم كالعادة: sudo apt-get update sudo apt-get install links وفي حين أنه لا يدعم تلوين النصّ بشكل افتراضي، مما قد يجعل من الصعب إلى حدٍ ما التمييز بين النصوص الصرفة وعناوين الروابط، إلا أنه يستفيد من ميزات مكتبة ncurses البرمجيّة لتقديم واجهة مرتبة بشكل جيّد، حيث أن استعراض موقع رسومي من خلال متصفحٍ نصيّ سيسبب دومًا مشاكل في التنسيق، links يتولى المهمة على نحوٍ جيّد. ميزة أخرى مهمة قد تجعلك تُقرّر استخدام links وهو دعمه لاستخدام الفأرة، وهذا يعني إمكانية الدخول إلى الروابط واستعراضها عن طريق النقر على عناوينها باستخدام المؤشّر كما لو كنت تتعامل مع متصفحك التقليدي. elinksفي عام 2001 اشتق elinks من متصفح links وأضيفت إليه ميزة دعم الامتدادات extended مع الاستفادة من قوّة وآليات عمل البرنامج الأب. للحصول على elinks في Ubuntu عن طريق مدير الحزم apt نكتب: sudo apt-get update sudo apt-get install elinks يتفوقelinks على links بعددٍ من الميزات، كقدرته على التعامل مع كلمات المرور وإدارة النماذج forms، تعدّد الألسنة، ودعم الجافاسكربت جزئيًا، بالإضافة إلى دعم بروتوكولي التورنت وIPv6، ورغم أن هذه الميزات قد تأتي على حساب السرعة، إلا أنه فرق بسيط للغاية. w3mw3m متصفحٌ نصيٌّ آخر يمكن اعتباره الأسهل في الاستخدام وبشكلٍ مشابه للتعامل مع المتصفح الرسومي، كما يأتي مع العديد من الميزات الأخرى، فعلى سبيل المثال تسمح لك معظم المتصفحات النصيّة بالتنقل بين الروابط، لكن التنقل خلال الصفحة نفسها قد لا يكون متاحًا بسهولة، w3m يسهّل هذه العملية عن طريق استخدام TABs للتنقل بين الروابط واستخدام مفاتيح الأسهم لتحريك المؤشر بشكل مستقل لتمرير الصفحة. عادةً ما يأتي w3m مُثبت بشكل افتراضي مع العديد من الأنظمة، أما إذا لم يكن مضمنًا في خادومك فيمكنك إضافته عن طريق تنفيذ: sudo apt-get update sudo apt-get install w3m إحدى المزايا التي قد تهم البعض هي إمكانية استخدام الأوامر المستعملة في برنامج vi، على سبيل المثال يمكن تحريك مؤشر الفأرة بواسطة الأزرار ‘j’, ‘k’, ‘l’, و ‘h’. أدوات التنزيلسيكون من المفيد أحيانًا أن تكون قادرًا على تصفح الإنترنت من الخادوم نفسه باستخدام الأدوات السابقة، إلا أنك ستجد نفسك في نهاية المطاف ترغب بالعودة إلى حاسبك الخاص للتصفح من خلال متصفحات الوِب الرسوميّة باعتبار ذلك أمرًا أكثر كفاءة، كما ستشعر بالثقة بأنّ ما تشاهده هو تمامًا ما يُفترض أن تحصل عليه. لهذه الأسباب يلجأ معظم الناس إلى تصفح الوِب من خلال المستعرضات التقليدية ومن ثم نسخ ولصق الروابط إلى الطرفية لاستخدامها مع أحد أدوات التنزيل. wgetتُعتبر الأداة wget خيارًا ممتازًا للحصول على الصفحات أو الملفات من المواقع. إذا لم تكن تملك wget مسبقًا على خادومك، يمكنك الحصول عليها عن طريق تنفيذ: sudo apt-get update sudo apt-get install wget كلّ ما عليك فعله بعد ذلك لتنزيل الملفات من الإنترنت هو لصق عنوان الرابط URL في الطرفيّة بعد استدعاء الأداة: wget www.example.com إذا كان عنوان الرابط URL المُستخدم يُشير إلى موقع على شبكة الإنترنت فإنه سيجري تحميل الفهرس أو الصفحة الرئيسيّة له، وفي حال كان الرابط يعيد توجهيك إلى ملف فسيتم تحميل هذا الملف ضمن الدليل النشط. وهكذا فأثناء تصفحك الإنترنت من خلال جهاز الحاسوب الخاص بك في المنزل، وحالما ترغب في تحميل ملف ما من الشبكة، انقر بزر الفأرة الأيمن على الرابط ثم اختر شيئًا مشابهًا لـ "انسخ عنوان الموقع" أو "copy link location"، ثم قم بلصق العنوان في الطرفية مسبوقًا باستدعاء الأداة wget. إذا حصل وقوطعت عملية التحميل لأي سبب (مثل ضُعف الاتصال بالإنترنت)، فإنه يمكنك استخدام wget مع الخيار c- والذي يستأنف التحميل الجزئي في حال تمّ العثور على ملف غير مكتمل في الدليل النشط. wget -c www.example.com تدعم الأداة wget التعامل مع ملفات تعريف الارتباط Cookies مما يجعلها مرشحًا جيدًا للنصوص التنفيذية scripting إضافةً إلى قدرتها على تحميل موقع وِب بالكامل. curlتُعتبر الأداة curl خيارًا جيدًا كذلك لهذا النوع من العمليات، ففي حين تعمل wget بواسطة جلب الملفات، فإن curl تستخدم الخرج القياسي مما يجعلها أداة مثالية للاستخدام مع السكربتات والأنابيب scripts and pipes، بالإضافة إلى دعمها عددًا كبيرا من البروتوكولات، وتمكّنها من التعامل مع أساليب توثيق http بشكل أكفأ من wget. تأتي العديد من أنظمة التشغيل مجهزة مع curl بشكل افتراضي، إذا لم يكن نظام تشغيلك كذلك: sudo apt-get update sudo apt-get install curl وبينما تستخدم curl الأنابيب عادةً، إلا أنه يمكنك أيضًا حفظ خرجها بسهولة إلى ملف، وهذا ما تريده غالبًا إذا كنت ترغب بتحميل ملفات لرفعها إلى خادومك. لتنزيل ملف وحفظه بالإبقاء على اسمه الافتراضي نفّذ: curl -O www.example.com/index.html يتوجب علينا تحديد الملف لأن هذه هي الطريقة التي نُعلم بها curl بالاسم المحليّ للملف. أما إذا كنت تريد أن تختار اسم للملف المحلي، فنحن لسنا بحاجة للإشارة إلى ملف معيّن في عنوان الموقع إذا كان ما نريده هو فهرس دليل الموقع، بدلًا من ذلك يمكننا أن نشير اختياريًا إلى الموقع وأيا يكن ملف الفهرس فإنه سيُهيئ ليوضع في الملف الذي اخترناه: curl -o file.html www.example.com لا تقتصر فائدة هذه الطريقة على تنزيل فهارس الأدلة وإنما تعمل بشكل جيّد أيضًا لتنزيل ملف بالاسم الذي تختاره. الخاتمةكما ترى فإنه لدينا عدد غير قليل من الخيارات المختلفة للحصول على الملفات، التطبيقات، والمواد المختلفة من الإنترنت لتمريرها إلى الخادوم الخاص بك. وفي حين أن كلا منها لديه القدرة على جلب المحتوى من شبكة الإنترنت فلا يوجد أداة واحدة من بينها مناسبة لجميع أنواع التحميلات؛ لذا فمن المفيد أن نتعرف على الأدوات المتاحة أمامنا لنكون قادرين على الاستفادة من نقاط القوّة في كلّ منها والتي صُممت أساسًا من أجلها، وهذا ما سوف يساعدك على تجنب القيام بأعمال لا لزوم لها، ويعطيك المرونة في الطريقة التي تقارب بها مشاكلك. تُرجم وبتصرف من مقال How To Download Software and Content onto your Linux VPS لكاتبه Justin Ellingwood. ncurses: هي مكتبة برمجيّة تُزوّد التطبيقات بواجهة برمجيّة لتسهّل على المطورين كتابة واجهات نصيّة لبرامجهم تعمل ضمن الطرفية بطريقة أقرب للبرامج الرسوميّة. Pipes: الأنبوب، هي أداة يمكن أن تُشغّل عدّة أوامر في لينكس بشكل متعاقب بحيث تفصل بين كل أمرين؛ مُرسلةً خرج العملية السابقة ليكون دخل العملية اللاحقة.