المحتوى عن 'قاعدة البيانات'.



مزيد من الخيارات

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

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

نوع المُحتوى


التصنيفات

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

التصنيفات

  • PHP
    • Laravel
    • ووردبريس
  • جافاسكريبت
    • Node.js
    • jQuery
    • AngularJS
    • Cordova
  • HTML
    • HTML5
  • CSS
  • SQL
  • سي شارب #C
    • منصة Xamarin
  • بايثون
    • Flask
    • Django
  • لغة روبي
    • Sass
    • إطار عمل Bootstrap
    • إطار العمل Ruby on Rails
  • لغة Go
  • لغة جافا
  • لغة Kotlin
  • برمجة أندرويد
  • لغة Swift
  • لغة R
  • لغة TypeScript
  • سير العمل
    • Git
  • صناعة الألعاب
    • Unity3D
  • مقالات برمجة عامة

التصنيفات

  • تجربة المستخدم
  • الرسوميات
    • إنكسكيب
    • أدوبي إليستريتور
    • كوريل درو
  • التصميم الجرافيكي
    • أدوبي فوتوشوب
    • أدوبي إن ديزاين
    • جيمب
  • التصميم ثلاثي الأبعاد
    • 3Ds Max
    • Blender
  • نصائح وإرشادات
  • مقالات تصميم عامة

التصنيفات

  • خواديم
    • الويب HTTP
    • قواعد البيانات
    • البريد الإلكتروني
    • DNS
    • Samba
  • الحوسبة السّحابية
    • Docker
  • إدارة الإعدادات والنّشر
    • Chef
    • Puppet
    • Ansible
  • لينكس
  • FreeBSD
  • حماية
    • الجدران النارية
    • VPN
    • SSH
  • مقالات DevOps عامة

التصنيفات

  • التسويق بالأداء
    • أدوات تحليل الزوار
  • تهيئة محركات البحث SEO
  • الشبكات الاجتماعية
  • التسويق بالبريد الالكتروني
  • التسويق الضمني
  • التسويق بالرسائل النصية القصيرة
  • استسراع النمو
  • المبيعات
  • تجارب ونصائح

التصنيفات

  • إدارة مالية
  • الإنتاجية
  • تجارب
  • مشاريع جانبية
  • التعامل مع العملاء
  • الحفاظ على الصحة
  • التسويق الذاتي
  • مقالات عمل حر عامة

التصنيفات

  • الإنتاجية وسير العمل
    • مايكروسوفت أوفيس
    • ليبر أوفيس
    • جوجل درايف
    • شيربوينت
    • Evernote
    • Trello
  • تطبيقات الويب
    • ووردبريس
    • ماجنتو
  • أندرويد
  • iOS
  • macOS
  • ويندوز

التصنيفات

  • شهادات سيسكو
    • CCNA
  • شهادات مايكروسوفت
  • شهادات Amazon Web Services
  • شهادات ريدهات
    • RHCSA
  • شهادات CompTIA
  • مقالات عامة

أسئلة وأجوبة

  • الأقسام
    • أسئلة ريادة الأعمال
    • أسئلة العمل الحر
    • أسئلة التسويق والمبيعات
    • أسئلة البرمجة
    • أسئلة التصميم
    • أسئلة DevOps
    • أسئلة البرامج والتطبيقات
    • أسئلة الشهادات المتخصصة

التصنيفات

  • ريادة الأعمال
  • العمل الحر
  • التسويق والمبيعات
  • البرمجة
  • التصميم
  • DevOps

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

  1. قد تكون عملية نقل المحتوى من موقع ووردبريس إلى آخر عمليةً تُضيّع الوقت وتُسبِّب الصداع، إذ لا تستطيع ببساطة نقل الملفات وقاعدة البيانات؛ لا تعمل ووردبريس هكذا! لحسن الحظ، هنالك أداة "استيراد" (Import) و"تصدير" (Export) في ووردبريس، لكن تلك الأدوات -لسوء الحظ- بسيطة جدًا ويلزم تطويرها لكي تلبي احتياجاتك الأخرى. سأشرح في هذا المقال خطوةً بخطوة كيفية نقل ووردبريس من مكانٍ إلى آخر. قبل أن نبدأ: خذ نسخة احتياطية من موقعك قد تكون هنالك خصوصيات لبعض حالات تثبيت ووردبريس ستؤدي إلى عرقلة عملية نقل المحتوى؛ وعلى الرغم من أنَّ هذه المقالة ستتعامل مع بعض الحالات الخاصة (عملية نقل جزء من محتوى ووردبريس تحديدًا)، لكن لا توجد ضمانة أنَّ الخطوات الموضحة هنا ستعمل بسلاسة لكل حالات التثبيت. ومن الغني عن الذكر أنَّك مسؤولٌ وحدك عن موقعك، حتى لو اتبعت هذا الدليل حتى النهاية؛ هنالك أيضًا بعض العمليات التي عليك إجراؤها على قاعدة البيانات اعتمادًا على ما الذي تريد فعله؛ وإذا أخطأتَ وحذفتَ كمية كبيرةً من البيانات فلا تلومن إلا نفسك! أنصحك بتوخي الحذر كثيرًا في هذا الصدد. أنشأتُ نسختين منفصلتين من ووردبريس مثبتتين على الخادوم المحلي لكي أوفر لك صورًا لكل خطوة من الخطوات. ربما قد ترغب في نقل محتواك إلى موقع تجريبي كبداية لتتحقّق من أن العملية ستتم كما هو مخطّط له. في النهاية، أنصحك أن تأخذ نسخةً احتياطيةً لكل الموقع في هذه المرحلة؛ لكنك بالطبع تفعل ذلك بشكلٍ دوري، صحيح؟ (إن لم تكن تأخذ نسخًا احتياطية، فأنصحك بفعل ذلك فورًا). إذا أردت القيام بعملية النقل يدويًا، فتذكر أن تُضمِّن قاعدة البيانات الخاصة بك بالإضافة إلى ملفات الموقع (وذلك لأنها تتضمن الملفات التي رفعتها على موقعك). نسخ الملفات احتياطيا يمكنك إنشاء وتنزيل نسخة ZIP من موقعك عبر FTP؛ وهذا يختلف باختلاف عميل FTP الذي تستخدمه، لكن العملية بسيطة وسهلة التطبيق. تأكد أنَّك تنزل وتحفظ ملف النسخة الاحتياطية المضغوط بشكل آمن، مثَلهُ كَمَثَلِ أيّ نسخةٍ احتياطيةٍ أخذتها سابقًا. نسخ قاعدة البيانات احتياطيا سجل دخولك إلى حسابك في phpMyAdmin واختر قاعدة البيانات التي ثبتت فيها ووردبريس. اضغط على Export في القائمة العلوية، ثم اختر Quick الذي يكون ملائمًا لأغلبية الحالات، لكن إن كان عندك جداول لأكثر من نسخة من ووردبريس في نفس قاعدة البيانات، فاضغط على Custom واختر الجداول التي تريد أخذ نسخة احتياطية منها؛ اترك جميع الخيارات البقية على حالها، وفي النهاية اضغط على Go لتنزيل ملف النسخ الاحتياطي لقاعدة بياناتك (بصيغة sql). إذا جرت الأمور كما هو متوقع، فلن نحتاج إلى النسخة الاحتياطية، لكن من المُستحسن دائمًا أخذ نسخة احتياطية قبل الإقدام على إجراء عملية كالتي سنفعلها. وإن كان في الموقع الذي ستنقل المحتوى إليه بعض المقالات، فيجب أخذ نسخة احتياطية له كذلك. لنبدأ العمل بعد أخذ الاحتياطات اللازمة. تغيير عنوان URL موقع ووردبريس: نقل موقع كامل إذا كنت تفكر في تغيير رابط URL لموقعك أو تريد نقل كل شيء من نسخة إلى أخرى، فاعلم أنَّك قد انتقيت أسهل خيار؛ فأدوات ووردبريس للتصدير (Export) والاستيراد (Import) ستفي تمامًا بالغرض، وليس عليك القيام بأي عمليات معقدة. إليك طريقة نقل جميع محتويات موقع ووردبريس من صفحات وصور وملفات ومنشورات …إلخ. إلى موقع جديد. من الأسهل أن تُنشِئ نسخة ووردبريس جديدة على خادومك الجديد (أو في مكانٍ آخر في نفس الخادوم) ثم تستخدم التصدير/الاستيراد بدلًا من نقل ملفات الضبط. يجب أن تكون نسخة ووردبريس عندك محدثةً إلى آخر إصدار، وإن لم تكن محدَّثةً فعليك ترقيتها أولًا؛ وإن لم تستطع الترقية لسببٍ من الأسباب -مثل الحاجة إلى استخدام إضافة معينة لا تعمل على الإصدارات الحديثة- فيمكن أن تُثبِّت إصدارًا قديمًا، لكن ذلك ليس مستحسنًا لاحتمال وجود مشكلات أمنية في الإصدارات القديمة من ووردبريس. أولا: التصدير من الموقع القديم افتح لوحة التحكم في ووردبريس واضغط على Export (تصدير) من قائمة Tools (أدوات). أبقِ على خيار All content (كل المحتوى) مُفعَّلًا لأنك تريد تصدير كل شيء، ثم اضغط على Download Export File (تحميل ملف التصدير). سيتم إنشاء ملف XML. أبقه في مكانٍ آمن ريثما تنقله إلى الموقع الجديد. ثانيا: تثبيت إضافة الاستيراد تثبيت إضافة WordPress Importer: الآن في موقع ووردبريس الجديد الذي تريد الانتقال إليه، اذهب مرةً أخرى إلى Tools (الأدوات) لكن هذه المرة اختر Import (استيراد). ستظهر لك قائمة بآليات الاستيراد المتوفرة، التي عليك أن تختار WordPress من بينها. اضغط على Install Now ثم انتظر تنزيل وتثبيت الإضافة. عليك بعدها -إن جرت الأمور على ما يرام- أن تضغط على Activate Plugin & Run Importer في الصفحة التي ستظهر لك. يجب أن تكون قادرًا في هذه المرحلة على استيراد ملف XML الذي أنشأتَه في الخطوة السابقة. ثالثا: رفع المحتوى اضغط على Choose File (اختر ملف) واختر ملف XML الذي أنشأتَه في موقعك القديم. اضغط بعدها على زر Upload file and import (رفع الملف واستيراده). رابعا: اختيار المحتوى ستُعطى خيارًا لإسناد المحتوى الذي سيتم استيراده إلى مستخدمين موجودين في موقعك الجديد (إذا كان لديك حساب مستخدم في الموقع القديم والجديد، فيمكنك إسناد مقالاته في الموقع القديم إلى حسابه في الموقع الجديد)، أو يمكنك إنشاء المستخدمين إما بأن تكون لهم نفس أسمائهم القديمة أو أن تُنشِئ واحدًا جديدًا تختاره أنت، وهذا يضمن أنَّ كل المحتوى الذي سيتم استيراده سيُسنَد إلى حساب مستخدمٍ ما موجود في الموقع الجديد. إذا كانت لديك أيّة صور أو ملفات تريد نقلها إلى الموقع الجديد، فعليك بكل تأكيد أن تختار الخيار Download and import file attachments (تنزيل واستيراد المرفقات)، لاحظ أنَّه ليس مفعلًا افتراضيًا. اضغط على زر Submit لإكمال عملية الاستيراد. قد يكون تحميل الصفحة أطول من المعتاد بسبب إنشاء سجلات جديدة في قاعدة البيانات، لكن سينتهي تحميلها حكمًا. انتظر بصبر إلى أن تكتمل العملية وبعدها ستجد أنَّ جميع محتوى موقعك السابق قد تم استيراده إلى موقعك الجديد الذي دبَّت فيه الحياة! نقل جزئي للمحتوى حسنًا، ما سبق كان سهلًا للغاية، لكن إن كنت تتطلع نحو تصدير بعض محتويات موقعك، فللأسف لن تكون أدوات ووردبريس وحدها كافيةً بالنسبة إليك. اختيار All content (كل المحتوى) هو الطريقة الوحيدة لتصدير المرفقات (أي الملفات التي تظهر في قسم Media أو الوسائط)؛ أي لو أردت نقل أجزاء مُحدَّدة من المحتوى بالإضافة إلى الصور المرتبطة بتلك الأجزاء، فأنت هنا أمام خيارين: إما أن تنقل كل المحتوى ثم تحذف الأجزاء التي لا تريدها (وهذه مضيعةٌ للوقت، خصوصًا في المواقع الكبيرة)، أو أن تحاول نقل الملفات وقاعدة البيانات يدويًا، وهذا ما سأريك إياه الآن. سأشرح عملية تصدير وتعديل SQL التي سأريك إياها لغرض ألا وهو نقل المرفقات؛ لكن يمكنك استخدام طريقة مماثلة لها بنقل قاعدة البيانات كلها، وهذا مفيدٌ إذا أردت نقل كل المحتوى لكن ملف XML الناتج كبيرٌ للغاية ولا يمكن رفعه عبر أداة الاستيراد. أولا: اختيار المحتوى الذي تريد تصديره اذهب مرةً أخرى إلى Tools (الأدوات) ثم Export (تصدير). بعد اختيارك للمحتوى الذي تريد تصديره، اضغط على Download Export File. إذا كنت تريد اختيار أكثر من مستخدم على سبيل المثال، أو أكثر من مجال زمني، أو جميع الصفحات (pages) وجميع المنشورات من مستخدم معيّن، فيمكنك العودة إلى هذه الصفحة وإنشاء عدِّة ملفات تصدير. ثانيا: استيراد الملفات المصدرة بعد أن تصبح لديك جميع ملفات WXR XML التي تريد استيرادها، فانتقل إلى الموقع الجديد وثبِّت إضافة WordPress Importer كما رأيت سابقًا. يمكنك رفع ملفات (ملف في كل مرة) كما في الفقرة السابقة وستتم إضافة المحتوى الموجود فيها إلى الموقع الجديد. لكن هذه ليس النهاية، لأنك ستلاحظ عدم وجود أيّة مرفقات (الصور المرفقة على سبيل المثال) في موقعك الجديد. نسخ ملفات الوسائط انتقل إلى مجلد /wp-content/uploads/ في موقعك القديم عبر عميل FTP. سأستخدم هنا مستكشف ملفات ويندوز 10 عميلًا لبروتوكول FTP، لكن أغلبية تطبيقات FTP قادرة على ضغط وتنزيل الملفات. نزِّل ملف ZIP الذي ولَّدته ثم ارفعه إلى موقعك الجديد عبر FTP (أو انسخ الملف والصقه في عميل FTP إن استطعت الوصول إلى كلا الموقعين منه [أي العميل]). يمكنك الآن استخراج جميع الملفات من المجلد المضغوط في مجلد Uploads. لسوء الحظ، هذه ليست آخر خطوة؛ فعلى الرغم من أنَّ الملفات في مكانها الصحيح، لكن ووردبريس لا تعلم عنها شيئًا لأنَّ بيانات تلك المرفقات لم تُنسَخ من قاعدة البيانات. رابعا: تصدير بيانات المرفقات من قاعدة البيانات اذهب إلى phpMyAdmin في موقعك القديم وابحث عن جدول wp_posts (ضع البادئة المناسبة بدلًا من wp_‎). عليك الآن العثور على بيانات المرفقات (أي بيانات الوسائط)، ولهذا استخدم تعليمة SQL الآتية (لا تنسَ تغيير بادئة الجدول إن لزم الأمر) ثم اضغط على Go. SELECT * FROM wp_posts WHERE post_type = “attachment” مرِّر إلى أسفل نتائج الطلبية ثم اضغط على Show all لكي تظهر جميع السجلات ثم اضغط على Check All لاختيارها جميعًا ثم Export لتصديرها. ستتعقد بعض الأمور عند هذه النقطة، لكن لن تواجهك مشكلاتٌ إن تابعت الخطوات بحذافيرها. اختر Custom لإظهار كل الخيارات الممكنة. مرِّر إلى الأسفل إلى قسم Format-specific Options. اختر data. اترك كل شيءٍ على حاله ثم اضغط على Go. خامسا: تعديل ملف SQL هذه الخطوة ضرورية إن كان لقاعدة بيانات موقعك الجديد بادئة مختلفة عن قاعدة بيانات الموقع القديم الذي صدَّرتَ ملف SQL منه. عدِّل ملف ‎.sql الناتج باستخدام محرر نصي مثل Notepad++‎ وابحث عن البادئة القديمة وضع البادئة الحديثة بدلًا عنها. يمكنك تجاهل هذه الخطوة إن كانت بادئة قاعدتَي البيانات متماثلة (أي أنَّ كلا الجدولين هما wp_posts). سادسا: استيراد بيانات المرفقات اذهب إلى قاعدة البيانات للموقع الجديد واعثر على جدول wp_posts (أو ما يكافؤه) ثم اضغط على Import. اضغط على Choose File -اضغط ولا تسحب وتُفلِت، لعدم توفر هذه الميزة في phpMyAdmin- ثم اختر ملف SQL الذي صدَّرتَه. اترك بقية الخيارات على حالها ثم اضغط على Go لتنفيذ الطلبية، ثم سترى رسالة تخبرك أنَّ الطلبية قد نُفِّذَت بنجاح، وسترى حينها المرفقات ظاهرةً في صفحة الوسائط، لكن هنالك خطوة إضافية عليك اتباعها حتى تظهر الصور بشكلٍ صحيح. سابعا: تصدير البيانات الوصفية للمنشورات بشكلٍ مشابه لما فعلناه سابقًا في قاعدة البيانات: اعثر على جدول wp_postmeta في قاعدة بيانات الموقع القديم ثم اضغط على Export. اختر Custom ثم اختر data بدلًا من structure and data كما سبق معنا. عليك هذه المرة العثور على قسم Data Creation Options واختيار REPLACE لخيار function to use when dumping data. اضغط الآن على GO لإنشاء وتنزيل ملف SQL. ثامنا: تعديل ملف SQL أكرر مرةً أخرى أنَّ عليك تعديل البادئة في ملف SQL إن اختلفت بادئة جداول قاعدة البيانات الجديدة عن القديمة. عليك أيضًا البحث عن روابط URL القديمة واستبدال الجديدة بها. تاسعا: استيراد البيانات الوصفية للمنشورات اذهب إلى جدول wp_postmeta (أو أيًّا كانت بادئة الجدول) في قاعدة بيانات الموقع الجديد، ثم استورد ملف SQL المُعدَّل كما سبق. يجب أن تكون مكتبة الوسائط جاهزةً عندك تمامًا، وقادرةً على الاندماج مع المحتوى الذي نقلته سابقًا. خاتمة تهانينا إذا نجحتَ بالمرور عبر كل تلك الخطوات، هذه طريقة التفافية لنقل بعض الصور الخاصة بمنشورات معيّنة. لكن ما تزال هنالك بعض المشكلات الصغيرة مع هذه الطريقة: لو أردت نقل بعض الصور، فعليك اختيار المجلدات يدويًا عند رفعها (أرجو أن تكون الصور التي تريدها مصنفةً حسب التاريخ، وإلا فستأخذ منك هذه المهمة وقتًا طويلًا جدًا!)؛ وقد تواجه بعض المشاكل بتكرار المفاتيح الرئيسية (primary keys) عند نقل جدول wp_posts إن كانت عندك منشوراتٌ في الموقع الجديد. من الجلي أنَّه يجب تطوير عملية التصدير/الاستيراد في الإصدارات المستقبلية من ووردبريس بدلًا من هذه الجلبة بتعديل قاعدة البيانات يدويًا. لكن استعن بالخطوات السابقة إلى أن يحين ذاك الوقت. إن كانت لديك أيّة أفكار أو طرق حول نقل أجزاء من موقع ووردبريس إلى آخر، فأرجو أن تشاركنا إياها بالتعليقات. ولا تتردد بالسؤال عن أيّة مشكلة تواجهك عند تنفيذ الخطوات السابقة، نحن موجودون دائمًا للمساعدة. ترجمة -وبتصرّف- للمقال A Step By Step Guide to Moving Content From One WordPress Site to Another لصاحبه Tom Ewer.
  2. يكثر الحديث هذه الأيام عن أنماط تصميم البرمجيات، ومن الأسئلة الأكثر شيوعًا "كيف يمكنني استخدام النمط الفلاني مع التقنية (التكنولوجيا) الفلانيّة؟". أما في حالة Laravel ونمط المستودع (Repository pattern)، فكثيرًا أرى أسئلة من قبيل "كيف يمكنني أن أستخدم نمط المستودع في Laravel 4؟" أو في هذه الأيام "مع Laravel 5؟". من المهم أن تتذكر أنّ أنماط التصميم لا تعتمد على تقنيّة محدّدة، أو إطار برمجي محدد، أو لغة برمجة محددة. إذا كنت قد فهمت نمط المستودع بالفعل، فلا يهم الإطار البرمجي أو اللغة البرمجة التي ستستخدمها. المهم أن تفهم المبدأ الذي يقف خلف نمط المستودع، ومن ثم يمكنك استخدامه في أيّ تقنية تريد. آخذين هذا بعين الاعتبار، لنبدأ بتعريف نمط المستودع: يفصل نمط المستودع منطق الوصول إلى البيانات (data access logic) ويربطه مكانيًّا بكيانات الأعمال (business entities) في المنطق الأعمال (business logic). ويتم التواصل بين منطق الوصول إلى البيانات ومنطق الأعمال باستخدام واجهات (interfaces). ولتبسيط ذلك، فإن نمط المستودع نوع من الحاويات يخزّن فيه منطق الوصول إلى البيانات، بحيث يخفي منطق الوصول إلى البيانات عن منطق الأعمال. وبعبارة أخرى، نسمح لمنطق الأعمال أن يصل إلى كائن البيانات دون معرفة هيكلية الوصول إلى البيانات التي تتم خلف الكواليس. ولفصل الوصول إلى البيانات عن منطق الأعمال فوائد عدّة، منها: مركزية منطق الوصول إلى البيانات تجعل النصوص البرمجية أسهل في الصيانة.يمكن فحص منطق الأعمال ومنطق الوصول إلى البيانات كلًّا على حدة.تقليل تكرار الأكواد البرمجية.فرصة أقل للوقوع في أخطاء برمجية.الأمر كلّه يتعلق بالواجهاتكلّ ما في نمط المستودع ذو علاقة بالواجهات. تعمل الواجهة كاتفاقية تحدّد ما على فئة concrete تنفيذه.لنفكّر قليلًا. إذا كان لدينا كائني بيانات، الممثل والفلم، فما هي مجموعة العمليات الشائعة التي يمكن تطبيقها على هذين العنصرين؟ سنرغب في غالب الأحيان بالقيام بالعمليات التالية: الحصول على جميع السجلات.الحصول على مجموعة السجلّات مرقّمة.إنشاء سجلّ جديد.الحصول على سجل باستخدام المفتاح الرئيسيّ.الحصول على سجل باستخدام خواص (attributes) أخرى.تحديث سجلّ.حذف سجلّ.هل يمكنك الآن أن ترى كميّة النصوص البرمجيّة المكررة التي ستكون لدينا إذا قمنا بذلك مع كلّ كائن بيانات؟ حسنًا، هذه ليست مشكلة كبيرة بالنسبة للمشاريع الصغيرة، ولكنها أمر سيّء بالنسبة للمشاريع الكبيرة. الآن، وبعد أن عرّفنا العمليات الشائعة، يمكننا إنشاء واجهة: interface RepositoryInterface { public function all($columns = array('*')); public function paginate($perPage = 15, $columns = array('*')); public function create(array $data); public function update(array $data, $id); public function delete($id); public function find($id, $columns = array('*')); public function findBy($field, $value, $columns = array('*')); }هيكليّة الدليل Directory Structureقبل أن نتابع إنشاء فئة مستودع concrete التي ستنفّذ هذه الواجهة، لنفكّر قليلًا كيف نريد أن ننظّم النصوص البرمجيّة لدينا. عادة، عندما أنشئ شيئًا ما، أفضّل أن أفكر بطريقة تقسيمه إلى مكوّنات، حيث أرغب بأن أكون قادرًا على إعادة استخدام ذاك النصّ البرمجيّ لمشاريع أخرى. إن هيكليّة الدليل البسيطة التي أتبعها لمكونات المستودع تبدو كما يلي: ولكنها يمكن أن تكون مختلفة، كأن يكون للمكونات خيارات ضبط مثلًا. لدي في داخل الدليل src ثلاثة أدلّة أخرى، هي: Contracts وEloquent وexceptions. كما ترى، أسماء المجلدات مناسبة للغاية لما نريد وضعه فيها. ففي Contracts نضع الواجهات، أو الاتفاقيات – كما سميناها أعلاه - . ويحوي مجلد Eloquent مستودعي abstract و concrete تنفّذ الاتفاقات. ونضع في المجيد Exceptions فئات الاستثناءات. وحيث أننا ننشئ حزم، فعلينا أن ننشئ ملف composer.json، حيث نجد فيه ربطًا مكانيًّا لنطاق الأسماء (namespaces) لأدلّة معيّنة، واعتماديات حزم، وبيانات وصفيّة أخرى للحزم. فيما يلي محتوى composer.json لهذه الحزمة: { "name": "bosnadev/repositories", "description": "Laravel Repositories", "keywords": [ "laravel", "repository", "repositories", "eloquent", "database" ], "licence": "MIT", "authors": [ { "name": "Mirza Pasic", "email": "mirza.pasic@edu.fit.ba" } ], "require": { "php": ">=5.4.0", "illuminate/support": "5.*", "illuminate/database": "5.*" }, "autoload": { "psr-4": { "Bosnadev\\Repositories\\": "src/" } }, "autoload-dev": { "psr-4": { "Bosnadev\\Tests\\Repositories\\": "tests/" } }, "extra": { "branch-alias": { "dev-master": "0.x-dev" } }, "minimum-stability": "dev", "prefer-stable": true } كما ترى، فقد ربطنا Bosnadev\Repository إلى الدليل src. وهناك شيء آخر علينا أخذه بعين الاعتبار قبل البدء بتنفيذ RepositoryInterface، فحيث أنها موجودة في المجلد Contracts، فعلينا أن نحدّد نطاق الاسم لها: <?php namespace Bosnadev\Repositories\Contracts; interface RepositoryInterface {...}نحن الآن مستعدون لبدء تنفيذ المحتوى. تنفيذ المستودععلى كل مستودع فرعيّ في concrete أن يزيد من مستودع abstract لدينا، مما يعني تنفيذ اتفاقية RepositoryInterface. أما الآن، فكيف تنفّذ هذه الاتفاقيّة؟ ألقِ نظرة على method الأولى. ما الذي يمكنك قوله عنها بمجرد النظر إليها؟ تُسمى method الأولى في اتفاقيتنا بالاسم ()all للتوضيح. مهمتها هي جلب كلّ السجلات لهيئة concrete. تستقبل معامِلاً واحدًا، وهو columns$ الذي يجب أن يكون مصفوفة (array). يُستخدّم هذا المعامِل – كما هو واضح من اسمه – لتحديد الأعمدة التي ترغب بجلبها من مصدر البيانات، ومبدئيًّا نقوم بجلبها كلّها. لهئيات محدّدة، يمكن أن تبدو كالتالي: <?php namespace Bosnadev\Repositories\Contracts; interface RepositoryInterface {...}لكننا نريدها أن تكون عامّة أكثر لكي نتمكن من استخدامها متى أردنا: public function all($columns = array('*')) { return $this->model->get($columns); } في هذه الحالة this->model$ هي نسخة من Bosnadev\Models\Actor، ولهذا، فعلينا إنشاء نسخة جديدة للنموذج الموجود في مكان ما في المستودع. هنا أحد الحلول التي يمكنك استخدامها لتنفيذ هذا الأمر: <?php namespace Bosnadev\Repositories\Eloquent; use Bosnadev\Repositories\Contracts\RepositoryInterface; use Bosnadev\Repositories\Exceptions\RepositoryException; use Illuminate\Database\Eloquent\Model; use Illuminate\Container\Container as App; /** * Class Repository * @package Bosnadev\Repositories\Eloquent */ abstract class Repository implements RepositoryInterface { /** * @var App */ private $app; /** * @var */ protected $model; /** * @param App $app * @throws \Bosnadev\Repositories\Exceptions\RepositoryException */ public function __construct(App $app) { $this->app = $app; $this->makeModel(); } /** * Specify Model class name * * @return mixed */ abstract function model(); /** * @return Model * @throws RepositoryException */ public function makeModel() { $model = $this->app->make($this->model()); if (!$model instanceof Model) throw new RepositoryException("Class {$this->model()} must be an instance of Illuminate\\Database\\Eloquent\\Model"); return $this->model = $model; } } وحيث أننا عرفنا الفئة كـ abstract، فهذا يعني أن علينا زيادتها باستخدام فئة متفرّعة عن concrete. فعند تعريف method المسمّى ()model كـ abstract، فإننا نجبر المستخدم على تنفيذ هذه الطريقة (method) في فئة متفرعة عن concrete. فمثلًا: <?php namespace App\Repositories; use Bosnadev\Repositories\Contracts\RepositoryInterface; use Bosnadev\Repositories\Eloquent\Repository; class ActorRepository extends Repository { /** * Specify Model class name * * @return mixed */ function model(){ return 'Bosnadev\Models\Actor'; } }يمكننا الآن أن ننفذ بقية طرق (methods) الاتفاقات: <?php namespace Bosnadev\Repositories\Eloquent; use Bosnadev\Repositories\Contracts\RepositoryInterface; use Bosnadev\Repositories\Exceptions\RepositoryException; use Illuminate\Database\Eloquent\Model; use Illuminate\Container\Container as App; /** * Class Repository * @package Bosnadev\Repositories\Eloquent */ abstract class Repository implements RepositoryInterface { /** * @var App */ private $app; /** * @var */ protected $model; /** * @param App $app * @throws \Bosnadev\Repositories\Exceptions\RepositoryException */ public function __construct(App $app) { $this->app = $app; $this->makeModel(); } /** * Specify Model class name * * @return mixed */ abstract function model(); /** * @param array $columns * @return mixed */ public function all($columns = array('*')) { return $this->model->get($columns); } /** * @param int $perPage * @param array $columns * @return mixed */ public function paginate($perPage = 15, $columns = array('*')) { return $this->model->paginate($perPage, $columns); } /** * @param array $data * @return mixed */ public function create(array $data) { return $this->model->create($data); } /** * @param array $data * @param $id * @param string $attribute * @return mixed */ public function update(array $data, $id, $attribute="id") { return $this->model->where($attribute, '=', $id)->update($data); } /** * @param $id * @return mixed */ public function delete($id) { return $this->model->destroy($id); } /** * @param $id * @param array $columns * @return mixed */ public function find($id, $columns = array('*')) { return $this->model->find($id, $columns); } /** * @param $attribute * @param $value * @param array $columns * @return mixed */ public function findBy($attribute, $value, $columns = array('*')) { return $this->model->where($attribute, '=', $value)->first($columns); } /** * @return \Illuminate\Database\Eloquent\Builder * @throws RepositoryException */ public function makeModel() { $model = $this->app->make($this->model()); if (!$model instanceof Model) throw new RepositoryException("Class {$this->model()} must be an instance of Illuminate\\Database\\Eloquent\\Model"); return $this->model = $model->newQuery(); } } الأمر سهل للغاية، صح؟ الشيء الوحيد المتبقي الآن هو حقن ActorRepository في ActorsController، أو الجزء المتعلق بالأعمال (business logic) من تطبيقنا. <?php namespace App\Http\Controllers; use App\Repositories\ActorRepository as Actor; class ActorsController extends Controller { /** * @var Actor */ private $actor; public function __construct(Actor $actor) { $this->actor = $actor; } public function index() { return \Response::json($this->actor->all()); } }استعلامات المعايير Criteria Queriesكما يمكنك أن تتخيل، فهذه الأعمال البسيطة كافية فقط لاستعلامات البسيطة. أما بالنسبة للتطبيقات الكبيرة، فستحتاج بالتأكيد لعمل بعض الاستعلامات الخاصّة لاستدعاء مجموعة بيانات محدّدة أكثر باستخدام معايير محدّدة. للقيام بذلك، نبدأ بتحديد ما على معايير الفروع (child ، أو العميل client) القيام به. وبعبارة أخرى، سننشئ فئة abstract non-instantiable باستخدام method واحدة فقط : <?php namespace Bosnadev\Repositories\Criteria; use Bosnadev\Repositories\Contracts\RepositoryInterface as Repository; use Bosnadev\Repositories\Contracts\RepositoryInterface; abstract class Criteria { /** * @param $model * @param RepositoryInterface $repository * @return mixed */ public abstract function apply($model, Repository $repository); }ستحوي هذه الطريقة (method) استعلام معايير يتم تطبيقه في فئة Repository ضمن هيئة concrete. علينا أيضًا أن نزيد على فئة Repository قليلًا لتغطية استعلامات المعايير. لكن قبل ذلك، هيّا ننشئ اتفاقية أخرى لفئة Repository. <?php namespace Bosnadev\Repositories\Contracts; use Bosnadev\Repositories\Criteria\Criteria; /** * Interface CriteriaInterface * @package Bosnadev\Repositories\Contracts */ interface CriteriaInterface { /*** @param bool $status * @return $this */ public function skipCriteria($status = true); /*** @return mixed*/public function getCriteria(); /*** @param Criteria $criteria* @return $this*/public function getByCriteria(Criteria $criteria); /*** @param Criteria $criteria* @return $this*/public function pushCriteria(Criteria $criteria); /*** @return $this*/public function applyCriteria(); }يمكننا أن نزيد فاعلية فئة Repository بتنفيذ اتفاقية CriteriaInterface: <?php namespace Bosnadev\Repositories\Eloquent; use Bosnadev\Repositories\Contracts\CriteriaInterface; use Bosnadev\Repositories\Criteria\Criteria; use Bosnadev\Repositories\Contracts\RepositoryInterface; use Bosnadev\Repositories\Exceptions\RepositoryException; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Collection; use Illuminate\Container\Container as App; /** * Class Repository * @package Bosnadev\Repositories\Eloquent */ abstract class Repository implements RepositoryInterface, CriteriaInterface { /** * @var App */ private $app; /** * @var */ protected $model; /** * @var Collection */ protected $criteria; /** * @var bool */ protected $skipCriteria = false; /** * @param App $app * @param Collection $collection * @throws \Bosnadev\Repositories\Exceptions\RepositoryException */ public function __construct(App $app, Collection $collection) { $this->app = $app; $this->criteria = $collection; $this->resetScope(); $this->makeModel(); } /** * Specify Model class name * * @return mixed */ public abstract function model(); /** * @param array $columns * @return mixed */ public function all($columns = array('*')) { $this->applyCriteria(); return $this->model->get($columns); } /** * @param int $perPage * @param array $columns * @return mixed */ public function paginate($perPage = 1, $columns = array('*')) { $this->applyCriteria(); return $this->model->paginate($perPage, $columns); } /** * @param array $data * @return mixed */ public function create(array $data) { return $this->model->create($data); } /** * @param array $data * @param $id * @param string $attribute * @return mixed */ public function update(array $data, $id, $attribute="id") { return $this->model->where($attribute, '=', $id)->update($data); } /** * @param $id * @return mixed */ public function delete($id) { return $this->model->destroy($id); } /** * @param $id * @param array $columns * @return mixed */ public function find($id, $columns = array('*')) { $this->applyCriteria(); return $this->model->find($id, $columns); } /** * @param $attribute * @param $value * @param array $columns * @return mixed */ public function findBy($attribute, $value, $columns = array('*')) { $this->applyCriteria(); return $this->model->where($attribute, '=', $value)->first($columns); } /** * @return \Illuminate\Database\Eloquent\Builder * @throws RepositoryException */ public function makeModel() { $model = $this->app->make($this->model()); if (!$model instanceof Model) throw new RepositoryException("Class {$this->model()} must be an instance of Illuminate\\Database\\Eloquent\\Model"); return $this->model = $model->newQuery(); } /** * @return $this */ public function resetScope() { $this->skipCriteria(false); return $this; } /** * @param bool $status * @return $this */ public function skipCriteria($status = true){ $this->skipCriteria = $status; return $this; } /** * @return mixed */ public function getCriteria() { return $this->criteria; } /** * @param Criteria $criteria * @return $this */ public function getByCriteria(Criteria $criteria) { $this->model = $criteria->apply($this->model, $this); return $this; } /** * @param Criteria $criteria * @return $this */ public function pushCriteria(Criteria $criteria) { $this->criteria->push($criteria); return $this; } /** * @return $this */ public function applyCriteria() { if($this->skipCriteria === true) return $this; foreach($this->getCriteria() as $criteria) { if($criteria instanceof Criteria) $this->model = $criteria->apply($this->model, $this); } return $this; } } إنشاء معايير جديدة، حيث يمكنك الآن أن ترتب مستودعاتك بسهولة أكبر. لست بحاجة لأن تكون مستودعاتك مكونة من آلاف الأسطر. يمكن أن تبدو فئة المعايير لديك كالآتي: <?php namespace App\Repositories\Criteria\Films; use Bosnadev\Repositories\Contracts\CriteriaInterface; use Bosnadev\Repositories\Contracts\RepositoryInterface as Repository; use Bosnadev\Repositories\Contracts\RepositoryInterface; class LengthOverTwoHours implements CriteriaInterface { /** * @param $model * @param RepositoryInterface $repository * @return mixed */ public function apply($model, Repository $repository){ $query = $model->where('length', '>', 120); return $query; } }استخدام للمعايير (Criteria) في المتحكّمات (Controllers)الآن وقد صارت لدينا معايير بسيطة، لنرَ كيف يمكننا استخدامها. هناك طريقتان يمكنك اتباعهما لتطبيق المعايير على مستودع. الأولى باستخدام طريقة ()pushCriteria: <?php namespace App\Http\Controllers; use App\Repositories\Criteria\Films\LengthOverTwoHours; use App\Repositories\FilmRepository as Film; class FilmsController extends Controller { /*** @var Film*/private $film; public function __construct(Film $film) { $this->film = $film; } public function index() { $this->film->pushCriteria(new LengthOverTwoHours()); return \Response::json($this->film->all()); } }هذه الطريقة مفيدة إذا كنت ترغب بتطبيق أكثر من معيار في نفس الوقت، حيث يمكنك رصّها معًا كما تشاء، ولكن إذا رغب بتطبيق معيار واحد فقط، فيمكنك استخدام طريقة ()getByCriteria: <?php namespace App\Http\Controllers; use App\Repositories\Criteria\Films\LengthOverTwoHours; use App\Repositories\FilmRepository as Film; class FilmsController extends Controller { /** * @var Film */ private $film; public function __construct(Film $film) { $this->film = $film; } public function index() { $criteria = new LengthOverTwoHours(); return \Response::json($this->film->getByCriteria($criteria)->all()); } }تثبيت الحزميمكنك تثبيت هذه الحزمة بإضافة الاعتمادية التالية إلى قسم require في ملف composer لديك: "bosnadev/repositories": "0.*"وتنفيذ composer update بعد ذلك، الخلاصةلاستخدام المستودعات في تطبيقك العديد من الفوائد. من أشياء بسيطة كتقليل تكرار الأكواد ومنعك من ارتكاب أخطاء برمجيّة وجعل تطبيقك أسهل في التكبير والتوسعة، والاختبار، والصيانة. من وجهة نظر هندسيّة، فقد تمكنت من عزل الاهتمامات. المتحكّم (controller) لديك ليس بحاجة لأن يعرف كيف وأين تخزَّن البيانات. بسيط وجميل. تجريديّ (Abstract). ترجمة وبتصرف للمقال: Using Repository Pattern in Laravel 5.