محمد أحمد العيل

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

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

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

  • Days Won

    22

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

101 Excellent

المعلومات الشخصية

  • النبذة الشخصية مهندس نظم معلومات، أكتب في مدونتي (تلميحات تقنية) عن البرامج الحرة ومفتوحة المصدر. لدي خبرة في التطوير بلغة جافا وإدارة قواعد بيانات MySQL و Oracle، إضافة إلى إدارة أنظمة لينوكس (دبيان ومشتقاتها).
  • الموقع الالكتروني

6 متابعين

آخر الزُوّار

3,677 زيارة للملف الشّخصي
  1. يحدُث كثيراً أن ينقسم مجتمع المطوّرين حول تقنيّات يراها بعضهم غير ذات جدوى أو على الأقل ليست بالأهميّة التي يروّج لها مؤيّدوها. تنطبق هذه الملاحظة على أطر العمل التي تعمل على ربط العلاقات بالكائنات Object relational mapping (أو ORM اختصارا)، ومن بينها Eloquent الذي يُستخدَم مبدئيًّا في Laravel؛ دون أن يعني ذلك عدم إمكانيّة استخدامه خارج Laravel كما سنرى في هذه السّلسلة. ليست أطر عمل ORM بالتقنيّة الجديدة، فهي توجد منذ سنين وأصبحت جزءًا من عمليّة التطوير بالنسبة للكثير من المبرمجين؛ كما أنها حسّنت من أدائها وتغلّبت على الكثير من النواقص مع الزمن. يتناول هذا الدّرس، الأوّل من سلسلة دروس عن الإطار Eloquent ORM، الأسباب التي أدّت إلى ظهور أطر عمل ORM والهدف من ورائها. التصوّر البرمجي Programming paradigm وإدارة البيانات العلاقيّة تُكتَب أغلب تطبيقات PHP الحديثة بأسلوب ذي تصوّر Paradigm كائنيّ التوجّه Object oriented، بينما تستخدم قواعدَ بيانات علاقيّة لإدارة البيانات وتخزينها. دور أُطُر عمل ORM هو تسهيل الانتقال بين هذيْن العالميْن أثناء تشغيل التطبيق. البرمجة كائنية التوجّه تعتمد لغات البرمجة على أفكار ونُظُم تحدّد طريقة عملها وأساليب كتابة البرامج التي تعمل بها. تُسمّى هذه الأساليب والقواعد بالتصورات البرمجيّة. يمكن للغةِ برمجة أن تُصنَّف في أكثر من تصوّر؛ إلّا أن كل لغة تركّز غالبًا على بضعة تصوّرات، ممّا يجعل كتابة برامج وفقَ هذه التصوّر ات أسهل بالنسبة لمبرمجي اللغة. تدعم لغة PHP البرمجيّة ميزات من تصوّرات برمجيّة عدّة؛ إلا أنها تُركّز على تصوّريْن: التصوّر الإجرائي Procedural الذي يمكن عدّه التصوّر الأساسي للغة منذ بداياتها. التصوّر كائنيّ التوجه Object oriented الذي اعتمده مصمّمو لغة البرمجة - فعليًّا - في الإصدار الخامس (سنة 2004). يُؤسَّس التصوّر الإجرائي على التصريح بكيفيّة إجراء العمليّات، خطوة بخطوة، واستخدام الحلقات Loops، العبارات الشرطيّة Conditions وتجميع الأوامر في سياقات محليّة Local context أو ضمن وحدات Modules يمكن استدعاءها عند الحاجة. أما التصوّر كائنيّ التوجه فيقوم على جمع البيانات والإجراءات (الدوالّ) ضمن كائن Object واحد يمكنه تطبيق الإجراءات المضمَّنة فيه على البيانات التي تخصّه. أنظمة قواعد بيانات العلاقيّة يعتمد النموذج العلاقي على نظريّة المجموعات Set theory والمنطق الرياضي من أجل تجميع البيانات ضمن جداول Tables؛ هي في الواقع علاقات Relations وفق التعريف الرياضي، ومنه التسميّة. يهدف النموذج العلاقي إلى تقديم طريقة تمكّن المستخدمين من تحديد البيانات المُعدّة للتخزين في قاعدة البيانات وتلك التي يريدون الحصول عليها من القاعدة، دون الاهتمام بالكيفيّة التي يستخدمها نظام الإدارة للإجابة على هذه الطلبات. يسمّى التصوّر البرمجي الذي يهتمّ بماهيّة المهمّة دون التركيز كثيرًا على كيفيّة أدائها بالتصوّر التقريري Declarative paradigm. يمكن عدّ لغة الاستعلام SQL تطبيقًا عمليًّا للنموذج العلاقي، وإن كانت تنحرف أحيانًا عن هذا النموذج. ربط العلاقات بالكائنات Object relational mapping تنبني شفرة التطبيقات التي تعتمد على تصوّر كائنيّ التوجه على أسس نظريّة تختلف عن تلك التي تُبنى عليها قواعد البيانات (النموذج العلاقي)؛ وهو ما يعني أنه على المطورين إيجاد طريقة للتخاطب مع قاعدة البيانات انطلاقًا من الشفرة البرمجيّة. يستخدم المبرمجون عادةً كائنًا مخصّصًا - يُسمّى كائن الوصول إلى البيانات Data access object، أو DAO اختصارا - لتخزين البيانات في القاعدة أو جلبها منها ثم تقديمها لبقيّة التطبيق بصيغة تناسبه. تُكتَب في هذا الصّنف استعلامات SQL المطلوب تنفيذها للتخاطب مع قاعدة البيانات، إما مباشرة أو عبر مكتبة وسيطة. يُساعد استخدام كائن DAO على الفصل بين أجزاء التطبيق واحترام مبادئ نمط التطوير Model-Vue-Controller (أو MVC اختصارا)؛ إلا أنّ كائنات DAO تتطلّب الكثير من الوقت، إذ يتحتّم على المطوِّر كتابة الكثير من التعليمات لتخزين البيانات في القاعدة أو جلبها منها. تأتي أطُر عمل ORM - ومن بينها Eloquent - للتخفيف من عبء هذه المهمّة. إطار العمل Eloquent ORM يسعى Laravel إلى أن يكون إطار عمل سهلَ الاستخدام يوفّر على المطوّرين الوقت ويرفع من إنتاجيّتهم؛ لذا جعل من Eloquent إطار عمل ORM الذي يستخدمه مبدئيًّا، لما يتميّز به من سهولة الاستخدام ووضوح آليّة العمل. طُوِّر Eloquent وفقا لنمط التسجيلة النشطة Active record الذي يقوم على وجود صنف Class لكلّ جدول في قاعدة البيانات، بحيث يُنشَأ كائن من هذا الصّنف لكل تسجيلة (سطر في الجدول). يعني هذا أن بيانات الكائن تحمل نسخة طبق الأصل من تسجيل الجدوَل. يُسمِّى Eloquent الصّنفَ الذي يطابق الجدول بالنموذج Model. يحوي كلّ كائن من هذا الصنف خاصيّات Attributes بعدد وأسماء توافق حقول (أعمدة) الجدول؛ وتمكّن الدوالّ التي يوفّرها من القيام بعمليّات من قبيل إدراج التسجيلة، التعديل عليها أو حذفها. إذا كان لدينا - مثلا - جدول في قاعدة البيانات باسم Address نحتفظ فيه بعناوين أشخاص، فإن إدراج عنوان جديد في قاعدة البيانات سيكافئ إنشاء كائن من الصنف (النموذج) Address وملئه بالبيانات التي نريد ثم استدعاء دالة التخزين في هذا الكائن. يضيف Eloquent وظائف مساعدة مثل التحقّق من أن الكائنات تحترم قواعد معيّنة قبل تخزينها في الجدول؛ وهو ما يعني التقليل من الأخطاء على مستوى قاعدة البيانات. قد تجد نفسك أمام حالات معقّدة لا تؤدّي فيها هذه الطريقة (تطبيق الدوالّ على الصنف المماثل لجدول قاعدة البيانات) النتيجة المطلوبة؛ يمكنك في هذه الحالة استخدام منشئ الاستعلامات Query builder وكتابة استعلامات SQL مباشرة. يُنصَح بتجنّب Eloquent عند تنفيذ سلسلة طويلة من المهامّ تلقائيًّا، دون تدخّل يدوي. يُناسب Eloquent المعاملات Transactions، وهي مجموعة إجراءات تُنفَّذ في قاعدة البيانات على أنّها وِحدة لا تتجزّأ: إما أن تُطبَّق جميع الإجراءات الموجودة في المعاملة أو لا تُطبَّق أي منها.
  2. لطالما وفّر إطار العمل Laravel آليات سهلة للاستيثاق Authentication بالطرق التقليدية مثل استمارات Forms تسجيل الدخول في واجهة الوِب؛ إلا أن الأمر لم يكن سهلا بما فيه الكفاية بالنسبة للاستيثاق من الطلبات القادمة عبر واجهة تطبيقات برمجية. يأتي الإصدار 5.3 بحزمة جديدة، تُسمّى Passport، تهتم بتيسير استخدام Laravel لضبط خادوم Oauth وإعداده. يتناول هذا الدرس كيفية إعداد Laravel ليوفّر وظيفة خادوم Oauth اعتمادا على حزمة Passport. خادوم Oauth من المحتمل جدّا أنه سبق لك التعامل مع خادوم Oauth؛ مثلا عند استخدام حسابات على الشبكات الاجتماعية (فيس بوك، تويتر، … إلخ) للتسجيل في موقع على الشبكة. الفكرة التي تنبني عليها آلية الاستيثاق هذه هي تفويض عمليّة الاستيثاق إلى خادوم Oauth. نفترض مثلا أن لديك موقعا يستخدم هذه الآلية؛ عند تسجيل دخول الموقع عن طريق الحساب على فيس بوك فإن ما يحدُث هو التالي: يلج المستخدم إلى الموقع ويختار طريقة تسجيل الدخول (فيس بوك). يُنقَل المستخدم إلى صفحة تسجيل الدخول في فيس بوك حيثُ يُطلَب منه تسجيل الدخول إن لم يكن سبق له ذلك ثم يؤكّد سماحه لموقعك باستخدام حسابه على فيس بوك. يُعاد توجيه المستخدم إلى الموقع مع تسجيل دخوله إليه. يفترض هذا الدرس أن لديك إلماما بكيفية عمل خواديم Oauth للتصريح بالدخول. يمكن الاطّلاع على مقال مدخل إلى OAuth 2 للمزيد عن هذا الموضوع. إن كنتَ تبحث عن كيفية تسجيل الدخول إلى تطبيقك باستخدام حساب على موقع يوفّر وظيفة Oauth فراجع درس تسجيل الدخول عبر الشبكات الاجتماعية في Laravel باستخدام Socialite. إعداد خادوم Oauth على Laravel 5.3 يحتاج إعداد Passport على Laravel لتنفيذ بضعة أوامر وتحرير ملفات إعداد؛ إلا أن العملية عموما سهلة. ابدأ بتثبيت الإصدار 5.3 من Laravel: composer create-project --prefer-dist laravel/laravel laravel53oauth "5.3.*" نستخدم composer لإضافة حزمة Passport إلى متطلبات المشروع: composer require laravel/passport نسجّل مزوّد الخدمة Passport في ملفّ الإعداد config/app.php. افتح الملف ثم أضف السطر التالي إلى مصفوفة providers: Laravel\Passport\PassportServiceProvider::class, تأكّد من إعدادات قاعدة البيانات ثمّ نفّذ أمر التهجير Migration التالي: php artisan migrate ستلاحظ تنفيذ تهجيرات إضافية علاوة على تلك التي تأتي مبدئيّا مع Laravel: Migration table created successfully. Migrated: 2014_10_12_000000_create_users_table Migrated: 2014_10_12_100000_create_password_resets_table Migrated: 2016_06_01_000001_create_oauth_auth_codes_table Migrated: 2016_06_01_000002_create_oauth_access_tokens_table Migrated: 2016_06_01_000003_create_oauth_refresh_tokens_table Migrated: 2016_06_01_000004_create_oauth_clients_table Migrated: 2016_06_01_000005_create_oauth_personal_access_clients_table يأتي مزوّد الخدمة Passport مع تهجيرات لإنشاء جداول تخزّن عملاء Client التطبيق ومعلومات رموز الوصول Access tokens. ثم ننفّذ الأمر التالي الذي سيولّد مفاتيح التعميّة Encryption keys الخاصّة بمشروع كما أنه ينشئ رموز الوصول ويخزّنها في جدول البيانات المناسب: php artisan passport:install ننتقل الآن إلى نموذج المستخدم User ونضيف إليه السّمة Trait المسمّاة HasApiTokens. يُصبح النموذج على النحو التالي (بعد نزع التعليقات للاختصار): <?php namespace App; use Illuminate\Notifications\Notifiable; use Illuminate\Foundation\Auth\User as Authenticatable; // استيراد السّمة use Laravel\Passport\HasApiTokens; class User extends Authenticatable { // استخدام السمة use HasApiTokens, Notifiable; protected $fillable = [ 'name', 'email', 'password', ]; protected $hidden = [ 'password', 'remember_token', ]; } توفّر هذه السّمة مجموعة من الدوال المساعدة التي تتيح الحصول على رموز الوصول الخاصّة بالمستخدمين ومدى الصلاحيّات Permissions scope المتاح للعميل. الخطوة المواليّة هي استدعاء الدالة Passport::routes ضمن الدالة boot الموجودة في مزوّد خدمة الاستيثاق app/Providers/AuthServiceProvider.php: public function boot() { $this->registerPolicies(); Passport::routes(); } لا تنس استدعاء الصنف Laravel\Passport\Passport في مزوّد خدمة الاستيثاق: use Laravel\Passport\Passport; يمكنك ملاحظة المسارات الجديدة التي أضافتها الدالة Passport::routes بتنفيذ أمر Artisan التالي الذي يسرُد لائحة بالمسارات الموجودة في المشروع: php artisan route:list الخطوة الأخيرة هي تعديل ملف الإعداد config/auth.php وتحديد قيمة driver في المصفوفة api لتصبح passport بدلا من token: 'api' => [ 'driver' => 'passport', 'provider' => 'users', ], يعتمد Laravel مبدئيا على رموز للاستيثاق من الطلبات القادمة عبر واجهة التطبيقات البرمجية؛ إلا أن هذه الرموز المبدئية لا تتيح إلا القليل من الخيارات، عكس ما يوفّره Passport. بهذا نكون أنهينا إعداد Laravel لأداء وظيفة خادوم Oauth. اختبار خادوم Oauth المرحلة المواليّة بعد إعداد Laravel ليعمل خادوم Oauth هي اختبار ما أعددناه. سنحتاج لعميل نجرّب عن طريقه الاستيثاق من الخادوم. نعيد النظر، قبل الانتقال للجانب التطبيقي من الاختبار، في كيفية الاستيثاق من مستخدم في خادوم Oauth. تضمّ العمليّة ثلاثة أطراف: المستخدِم، الخادوم والعميل. يريد المستخدِم تسجيل الدخول إلى العميل بالاعتماد على معلوماته الموجودة في الخادوم: ينقُل العميل المستخدِم إلى صفحة تسجيل الدخول على الخادوم. يُسجّل المستخدم الدخول إلى حسابه على الخادوم عن طريق بريده الإلكتروني وكلمة السّر. يتحقّق الخادوم من بيانات المستخدِم، ثم ينشئ - إذا كانت البيانات صحيحة - رمز وصول خاصًّا بالمستخدِم ويعيده إلى العميل. يستعمل العميل رمز الوصول الذي أرسله إليه الخادوم في طلباته المقبلة للتدليل على أن المستخدم فوّضه التعامل باسمه مع الخادوم. إنشاء مستخدمين للاختبار نبدأ بإنشاء مستخدمين لتجربة الاستيثاق. ننفذ أمر Artisan التالي لإنشاء صنف بذر Seed جديد: php artisan make:seeder UsersTableSeeder ثم نعدّل الدالة run لإنشاء مستخدمَيْن جديديْن: public function run() { $user1 = [ 'name' => 'Med Ahmed Eyil', 'email' => 'medeyil@laravel.com', 'password' => Hash::make('1234'), ]; $user2 = [ 'name' => 'Fatima Benziane', 'email' => 'fbenziane@laravel.com', 'password' => Hash::make('4567'), ]; User::create($user1); User::create($user2); } لا تنس استيراد النموذج User: use App\User; ثم نعتمد صنف البذر بتعديل الدالة run في الصنف DatabaseSeeder: public function run() { $this->call(UsersTableSeeder::class); } ثم ننفذ أمر البذر: php artisan db:seed جهّزنا الآن طرفيْن من الأطراف الثلاثة التي تحدثنا عنها، بقي العميل. إعداد العميل يوفّر Laravel بواسطة Passport مسارات من بينها المسار oauth/token/ الذي يمكّن من الحصول على رمز وصول. ينتظر المسار المعطيَات التاليّة: طريقة منح الصلاحيّة grant_type. معرّف المستخدم client_id. عبارة سر العميل client_secret. يحدّد المعطى grant_type طريقةَ توليد رموز الوصول. بما أننا نريد أن يطلُب الخادوم من المستخدم بريده الإلكتروني وكلمة السّر لكي يولّد رمز وصول للتطبيق العميل، فإن هذا المعطى سيأخذ القيمة password. يمكن لهذا المعطى أن يأخذ قيما أخرى حسب الطريقة التي تريد للتطبيقات العميلة أن تصل بها إلى موارد محميّة على خادومك. يعيّن المعطى الثاني معرّف العميل الذي نريد الاستيثاق منه؛ أما عبارة السّر فمهمتها إثبات أن العميل لديه ترخيص للاستيثاق بخادوم Oauth الذي أعددناه. سنحتاج أيضا لتمرير بريد المستخدم الإلكتروني وكلمة سره، بما أننا اخترنا طريقة password لتوليد الرموز. نعود لمشروع Laravel حيث يوجد الخادوم ثم ننفذ الأمر التالي: php artisan passport:client --password What should we name the password grant client? [Laravel Password Grant Client]: > Academy Password Client Password grant client created successfully. Client ID: 3 Client Secret: 17ZNjWn5A1I3mYOhGXZQFNACTdyt9Nwxup9jXG4M يطلُب الأمر أعلاه إنشاء تصريح لعميل للاستيثاق بخادوم Oauth ويحدّد طريقة توليد رموز الوصول بـpassword. يطلُب الأمر اسمًا للعميل ثم بعد كتابته وتأكيده يمنحنا معرّفا للعميل وعبارة سرّ. ملحوظة: رمز الوصول المولَّد فريد Unique، القيمة التي ستظهر لديك مختلفة حتما عن القيمة الظاهرة هنا. نعرّج قليلا، قبل الانتقال إلى العميل، على قاعدة البيانات وننظر في الجدول oauth_clients. لاحظ في الجدول وجود العميل الذي أنشأناه للتو، إضافة إلى عميليْن آخرين؛ أنشأهما الأمر passport:install عندما نفّذناه. تمكن ملاحظة أن قيمة العمود password_client بالنسبة للعميل الذي أنشأناه هي 1 للدلالة على أنه من نوع password. نأتي الآن للعميل، الذي يمكن أن يكون تطبيق وِب، تطبيقا للأجهزة المحمولة أو غيره. بالنسبة لي سأختار إضافة Postman على متصفح Chrome، وهي إضافة تمكّن من اختبار واجهات التطبيقات البرمجية. إرسال الطلبات نشغّل خادوم Passport بتنفيذ الأمر: php artisan serve ثم نفتح واجهة Postman ونعطيه المسار oauth/token/ ونمرّر له المعطيات التي تحدّثنا عنها سابقا. نحدّد نوع الإجراء بـ POST، ونختار تبويب Body لأننا في صدد ذكر بيانات جسم الطلب، ونختار x-www-form-urlencode للترميز. يؤدي استخدام الترميز x-www-form-urlencode إلى إرسال البيانات بنفس الطريقة التي تُرسَل بها بيانات استمارة Form على موقع وِب باستخدام الإجراء POST. إن كنتَ تُفضّل سطر الأوامر فبإمكانك تنفيذ أمر curl التالي والذي يؤدي نفس المهمّة (الخيار d- لاستخدام الترميز x-www-form-urlencode): curl -d "grant_type=password&client_id=3&client_secret=17ZNjWn5A1I3mYOhGXZQFNACTdyt9Nwxup9jXG4M&username=medeyil@laravel.com&password=1234" http://localhost:8000/oauth/token نحصُل بعد ذكر المعطيات الصحيحة وتنفيذ الطلب على الرد التالي: { "token_type": "Bearer", "expires_in": 31536000, "access_token":"رمز طويل هنا" , "refresh_token": "رمز طويل هنا" } تتضمن الإجابة أربعة حقول: نوعية الرمز token_type. يعني النوع bearer أن بإمكان الرمز منح الوصول إلى الخادوم دون الحاجة لذكر معلومات إضافية (مثل مفاتيح التعمية). مدة صلاحيّة الرمز expires_in. يحدّد مدّة صلاحيّة الرمز، بالثواني. يحدّد Laravel مبدئيا مدة طويلة (حوالي سنة)؛ إلا أنه يمكن تغيير هذه المدة. رمز الوصول access_token. رمز التحديث refresh_token. يُستخدَم لطلب رمز وصول جديد عند انتهاء مدة صلاحية رمز الوصول الحالي. حصلنا على رمز الوصول؛ سنرى الآن كيفية استخدامه للوصول إلى موارد على الخادوم. يُرسِل العميل من الآن فصاعدا رمزَ الوصول الذي حصل عليه مع كل طلب موجّه للخادوم يحتاج للاستيثاق. إن نظرت في ملفّ مسارات واجهة التطبيقات البرمجية routes/api.php فستجد أن المسار user/ محميّ بـ auth:api: Route::get('/user', function (Request $request) { return $request->user(); })->middleware('auth:api'); يعني هذا أنه يتوجّب على المستخدم تقديم بيانات الاستيثاق الخاصّة بواجهة التطبيقات، التي جعلناها تعتمد على التعريف passport، حتى يتمكن من الحصول على المورِد (الذي هو في المثال أعلاه بيانات المستخدم الذي أرسل الطلب). نعود لـPostman لطلب المسار api/user/ مع تقديم رمز الوصول الذي حصلنا عليه سابقا. ملحوظة: تنبغي إضافة api/ إلى جميع المسارات المعرّفة في الملف routes/api.php. نحدّد في خانة الترويسة Headers طبيعة المعلومات التي نريد تقديمها إلى المسار والإجراء GET المعرَّف في المسار user/ الخاص بواجهة التطبيقات. الترويسة التي نريد تحديد قيمتها هي Authorization (طلب استيثاق) وقيمتها هي: Bearer access_token حيث access_token رمز الوصول الذي حصلنا عليه في الخطوة السابقة، مسبوقا بنوع الرمز (Bearer). نحصُل على معلومات المستخدِم الذي قدّم الطلب. يمكن الحصول على نفس النتيجة بأمر curl التالي: curl -H "Authorization: Bearer access_token" http://localhost:8000/api/user يعني الخيار H- أننا نريد تضمين الترويسة التاليّة له في الطلب. نلاحظ أن الإجابات التي نحصُل عليها من خادوم Passport مهيّأة بصيغة JSON، وهو ما يجعل التعامل معها سهلا في التطبيقات العميلة على الأجهزة المحمولة والمتصفّحات. الخلاصة هي أننا استخدمنا بيانات المستخدم (البريد وكلمة السر) للحصول على رمز وصول من خادوم Passport ثم استخدمنا رمز الوصول هذا للاطلاع على موارد محميّة على الخادوم.
  3. يتوفّر إطار العمل Laravel على واجهة تطبيقات برمجيّة API موّحّدة للتعامل مع الملفات. تأتي واجهة التطبيقات هذه مجهّزة مبدئيا بتعريفات Drivers تمكّن من إدارة الملفات على نظام الملفات المحلّي، خادوم FTP أو على خدمتي Amazon S3 وRackspace السّحابيتين. سنعرض في هذا الدرس لأساسيّات إدارة الملفات: رفعها Upload، تخزينها والعثور عليها في الإصدار 5.3 من إطار العمل Laravel. أقراص التخزين في Laravel تُضبَط إعدادات تخزين الملفات ضمن الملف config/filesystems.php عن طريق ما يُسمّيه Laravel الأقراص Disks. يُمثّل كل قرص تعريفا، مسارا للتخزين وإعدادات خاصّة بالقرص. يعرّف ملفّ الإعداد مبدئيا ثلاثة أقراص public، local وs3: 'disks' => [ 'local' => [ 'driver' => 'local', 'root' => storage_path('app'), ], 'public' => [ 'driver' => 'local', 'root' => storage_path('app/public'), 'visibility' => 'public', ], 's3' => [ 'driver' => 's3', 'key' => 'your-key', 'secret' => 'your-secret', 'region' => 'your-region', 'bucket' => 'your-bucket', ], ], يستخدم القرصُ local في المثال المبدئي أعلاه التعريفَ local (نظام الملفات المحلّي) ومسار التخزين storage/app. بالنسبة للقرص s3 فهو يستخدم التعريف s3 (خدمة Amazon S3) ويتطلّب قيما ضرورية للولوج إلى الخدمة. يشبه القرص public القرصَ local؛ إلا أنّ بينهما فرقًا جوهريًّا: هذا القرص مهيّأ للملفات التي نريد إتاحتها للعموم. تُخزَّن ملفات هذا القرص على المسار storage/app/public. يمكن ملاحظة أن المسار storage/app/public يوجد خارج المجلّد public الذي يحوي ملفّات المشروع المتاحة للعموم. نستخدم أمر Artisan التالي لجعل ملفات القرص public متاحة على الوِب: php artisan storage:link ينشئ الأمر وصلة رمزيّة على المسار public/storage ويجعلها تحيل إلىstorage/app/public الذي هو مسار تخزين القرص public. سنرى بعد قليل كيف نصل إلى الملفات الموجودة في هذا القرص. ملحوظة: يتطلّب استخدام التعريفيْن s3 وrackspace تثبيت الحزمتييْن التاليّتيْن على التوالي (عن طريق composer): league/flysystem-aws-s3-v3 ~1.0 league/flysystem-rackspace ~1.0 رفع ملفات وعرض روابطها في Laravel 5.3 سنهيّئ في بقيّة الدرس مشروع Laravel 5.3 للعمل عليه. سيكون هدفنا رفع صورة في المتصفّح ثم عرض هذه الصورة في صفحة الوِب. نضيف مسارين إلى ملف مسارات الوِب routes/web.php: Route::get('image-upload','ImageController@imageUpload'); Route::post('image-upload','ImageController@imageUploadPost'); يتلقّى الإجراء get طلبات عرض الصفحة، في ما نستخدم الإجراء post لتخزين الصّورة المحمَّلة في الصفحة التي سننشئها بعد قليل. الخطوة التاليّة هي إنشاء المتحكّم ImageController وكتابة الدالتين imageUpload وimageUploadPost: php artisan make:controller ImageController كلّ ما تفعله الدالة imageUpload هو استدعاء العرض image-upload: public function imageUpload() { return view('image-upload'); } بالنسبة للدالة imageUploadPost فستستقبل الصورة المحمّلة من المتصفّح، تخزّنها ثم ترسلها إلى image-upload الذي يعرضها: public function imageUploadPost(Request $request) { // TODO: return view('image-upload') ->with('message', "Image uploaded successfully") ->with('path', $imagePath); } الدالة غير مكتملة لحد الساعة، فكل ما يظهر منها هو استدعاء القالب وتمرير رسالة إليه تفيد بنجاح رفع الصورة، إضافة إلى متغيّر يمثّل رابط الصورة. استقبال الصورة، تخزينها والحصول على رابط تخزينها سيكون محلّ التعليق TODO. ننشئ القالب image-upload قبل العودة إلى الدالة imageUploadPost. ننشئ الملف image-upload.blade.php على المسار resources/views ونضع فيه المحتوى التالي: <!DOCTYPE html> <html> <head> <title>Laravel 5.3 Image Upload example</title> </head> <body> <div> @if (isset($path)) <p>{{ $message }}</p> <img src="{{ url($path) }}"> @endif <form action="{{ url('image-upload') }}" enctype="multipart/form-data" method="POST"> {{ csrf_field() }} <div> <div> <input type="file" name="image" /> </div> <div> <button type="submit">Upload</button> </div> </div> </form> </div> </body> </html> نتحقّق أولا، عن طريق الدالة isset، من وجود متغيّر باسم path في المعطيات الممرّرة إلى القالب. يمثّل المتغيّر path مسار الصورة التي نعرضها في حال وجود المتغيّر path. ثم يأتي دور استمارة الرّفع التي ترسل طلبا بإجراء POST إلى المسار image-upload. نحدّد نوع المُدخَل input الذي نريد استقبال الصورة عن طريقه بالنوع file ونحدّد اسمه بـimage. نستطيع الآن العودة إلى الدالة imageUploadPost لإكمالها: public function imageUploadPost(Request $request) { // TODO: return view('image-upload') ->with('message', "Image uploaded successfully") ->with('path', $imagePath); } نستقبل الطلب في المعطى request؛ حيث يمكننا تطبيق الدالة file للحصول على الصورة التي حمّلها المتصفّح بتحديد اسم المُدخَل input الذي استقبلها في صفحة الوٍب: $request->file('image'); نطبّق على الملف الدالة store لتخزينه: $image = $request->file('image')->store('images', 'public'); تأخذ الدالة store معطَيَيْن، الأول منهما هو اسم المجلّد حيثُ نريد تخزين الملفّ والثاني اسم القرص الذي نريد استخدامه. إن لم نحدّد اسمَ القرص فسيُستخدَم القرص المبدئي المعرّف بالتعليمة default ضمن ملف الإعداد config/filesystems (تأخذ التعليمة مبدئيا القيمة local). نريد أن تكون الصورة متاحة للعموم ضمن مجلد خاصّ بالصور اسمه images، لذا نمرّر القيمتيْن images وpublic للمعطييْن الأول والثاني على التوالي. خزّنا الآن الصورة على القرص المتاح للعموم. الخطوة التاليّة هي الحصول على مسارها من أجل إرساله إلى القالب image-upload لعرضه. نستخدم الدالة url ضمن الصنف Storage والتي يمكن تطبيقها على أقراص تستخدم أحد التعريفيْن local أو s3: $imagePath = Storage::url($image); لا ننسى استيراد الصّنف Storage: use Illuminate\Support\Facades\Storage; أصبحت دالة المتحكّم مكتملة على النحو التالي: public function imageUploadPost(Request $request) { $image = $request->file('image')->store('images','public'); $imagePath = Storage::url($image); return view('image-upload') ->with('message', "Image uploaded successfully") ->with('path', $imagePath); } لا ننسى إنشاء الوصلة الرمزية: php artisan storage:link نشغّل خادوم التطوير المضمَّن في Laravel: php artisan serve ثم نفتح المتصفّح لزيارة المسار http://localhost:8000/image-upload. نختار صورة لرفعها ثم نضغط على الزّر Upload. يخزّن Laravel الصّورة في المجلّد storage/app/public، ثم يطلُب عرض القالب image-upload مع تمرير رابط الصّورة في المتغيّر path، إضافة إلى رسالة في المتغيّر message تفيد بنجاح الرّفع. ملحوظة: يختار Laravel عند استخدام الدالة store اسما مميّزا للملف بتطبيق دالة تجزئة Hash عليه. إن أردت اختيار الاسم الذي يُخزَّن به الملف فيمكنك ذلك بالدالة storeAs: تخزين الملف في القرص المبدئي: $image = $request->file('image')->storeAs('images','fileName'); تخزين الملف مع تحديد القرص: $image = $request->file('image')->storeAs('images','fileName','public'); العمليّات على الملفات المرفوعة يوفّر Laravel الصّنفَ Storage للتعامل مع الملفات والأقراص. إضافة ملفات إلى التخزين تُستخدَم الدالة put في الصّنف Storage لتخزين ملفات على النحو التالي: Storage::put('images', $fileContents); يُمثّل المعطى الأوّل الممرَّر إلى الدالة اسمَ المجلّد الذي تريد حفظ الملفّ فيه والثاني محتوى الملفّ. تستخدم الدالة أعلاه القرص المبدئي (التعليمة default في ملف الإعداد). إن أردت استخدام قرص مغاير فيمكنك الاستعانة بالدالة disk: Storage::disk('public')->put('images', $fileContents); يُحدَّد المجلّد المُمرَّر إلى الدالة put (المعطَى الأول) اعتمادا على المسار الجذر للقرص المستخدَم؛ أي أن المقصود بالمجلّد images في المثال السابق هو المجلد storage/app/public/images؛ نظرا لكون storage/app/public هو المسار الجذر للقرص public. يُعيَّن المسار الجذر بالتعليمة root أثناء إعداد الأقراص في ملف الإعداد config/filesystems. استرجاع ملف من التخزين يتيح الصّنف Storage الدالة get لاسترجاع محتوى ملف مخزّن في القرص المبدئي: $contents = Storage::get('images/file.jpg'); يمكن تحديد القرص المستهدَف باستدعاء الدالة disk قبل تطبيق get. يتوفّر الصّنف Storage على الدالة exists التي تتيح التأكد من وجود الملفّ: $exists = Storage::disk('s3')->exists('file.jpg'); الحصول على بيانات ملفّ استخدم الدالة size على النحو التالي لمعرفة حجم ملفّ موجود في القرص المبدئي: $size = Storage::size('file1.jpg'); أو بتحديد القرص المستهدَف: $size = Storage::disk('s3')->size('file1.jpg'); يمكن على نفس المنوال معرفة تاريخ آخر تعديل على الملف على صيغة ختم زمني Timestamp: $time = Storage::lastModified('file1.jpg');
  4. يضيف الإصدار 5.3 من Laravel نظاما للإشعارات Notifications يمكن من خلاله إخطار المستخدم بحدوث شيء ما. تُستخدَم الإشعارات، التي تكون عادة على هيئة بلاغات قصيرة، لإعلام مستخدم التطبيق بوقوع أمر ما؛ على سبيل المثال “صدور فاتورة”، “تسجيل مستخدم”، “إعادة تعيين كلمة السّر” أو “نشر درس”؛ حسب طبيعة التطبيق الذي تعمل عليه. يتيح نظام الإشعارات في Laravel 5.3 وسائط عدّة (تُسمّى قنوات Channels) لتوصيل الإشعار: البريد الإلكتروني، الرسائل النصية القصيرة SMS (بالاعتماد على خدمة Nexmo) وخدمة Slack. يمكن أيضا تخزين الإشعارات في قاعدة البيانات من أجل عرضها في واجهة الوِب. سنرى في هذا الدرس كيفية عمل الإشعارات، بأخذ مثال لإشعار يُرسَل إلى بريد المستخدم عند إضافة منشور جديد. تهيئة المشروع نبدأ أولا بتهيئة المشروع الذي سنعمل عليه. أنشئ مشروع Laravel 5.3 جديدا، إن لم يكن لديك واحد جاهز واضبط إعدادات قاعدة البيانات. نستخدم نفس الإعدادات المذكورة في فقرة تهيئة بيئة التطوير من درس أساسيات التخبئة في Laravel؛ مع التعديل عليها قليلا. سنضيف صنفا لبذر المستخدمين: php artisan make:seeder UsersTableSeeder ونعدّل صنف البذر على النحو التالي لإضافة مستخدمَيْن إلى قاعدة البيانات: public function run() { $user1 = [ 'name' => 'Med Ahmed Eyil', 'email' => 'medeyil@laravel.com', 'password' => Hash::make('1234'), ]; $user2 = [ 'name' => 'Fatima Benziane', 'email' => 'fbenziane@laravel.com', 'password' => Hash::make('4567'), ]; User::create($user1); User::create($user2); } ثم ننفّذ البذر: php artisan db:seed يصبح لدينا الآن نموذجان، واحد للمستخدمين وآخر للمنشورات. ننتقل إلى ملف المسارات routes/web.php ونضيف المسار التالي: Route::get('/notify/{user_id}/{post_id}', 'PostsController@notify'); يعني هذا أنه عند زيارة الرابط notify/user_id/post_id/ (حيث user_id وpost_id معرّفا المستخدم والمنشور على التوالي) سيطلُب Laravel تنفيذ الدالة notify في المتحكم PostsController. نعدّل المتحكّم PostsController لإضافة هذه الدالة: public function notify($user_id,$post_id) { $user = User::find($user_id); $post = Post::find($post_id); // todo } سنعود لاحقا لإكمال الدالة notify التي تكتفي لحد الساعة بالعثور على المستخدم والمنشور الممررَيْن إليها في المعطيات. الإشعارات هدفنا هو التالي: يطلُب المتصفح الرابط notify/1/2/ (مثلا) فيُرسَل إشعار عبر البريد إلى المستخدم ذي المعرف 1 يخبره أن المنشور ذا المعرف 2 قد أُضيف إلى الموقع. سنحتاج لبلوغ هذا الهدف لإشعار “إضافة منشور”. ننفذ الأمر التالي لإنشاء هذا الإشعار: php artisan make:notification NewPost ستلاحظ بعد تنفيذ الأمر أعلاه ظهور مجلد جديد باسم Notifications (إن لم يكن موجودا سلفا) متفرع عن المجلد app وبداخله صنف باسم NewPost.php (نزعنا التعليقات للاختصار): <?php namespace App\Notifications; use Illuminate\Bus\Queueable; use Illuminate\Notifications\Notification; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Notifications\Messages\MailMessage; class NewPost extends Notification { use Queueable; public function __construct() { } public function via($notifiable) { return ['mail']; } public function toMail($notifiable) { return (new MailMessage) ->line('The introduction to the notification.') ->action('Notification Action', 'https://laravel.com') ->line('Thank you for using our application!'); } public function toArray($notifiable) { return [ ]; } } يمدّد الصنف NewPost الصنفَ Notification الذي يحوي التعليمات الضرورية للتعامل مع الإشعارات. تظهر الدالة المشيّدة Constructor وثلاث دوال أخرى: via: تعرّف القنوات التي نريد إرسال الإشعارات عبرها. توجد قناة البريد mail مبدئيا. تمكن إضافة قناة أخرى من تلك المتوفرة. يستخدم Laravel جميع القنوات المذكورة في المصفوفة المرجَعة، بمعنى؛ إن حددت مثلا القناتين mail وdatabase في المصفوفة فإن الإشعار سيُرسَل عبر البريد ويُخزَّن أيضا في قاعدة البيانات. toMail: هذه الدالة خاصّة بالتعامل مع قناة البريد الإلكتروني. تنشئ الدالة toMail كائنا من صنف MailMessage لتخصيص بريد إلكتروني. سنعود لهذا الصنف بعد قليل. toArray: إن حدّدت قناة للإشعارات في الدالة via دون أن تنشئ دالة خاصّة للتعامل معها فسيستدعي Laravel الدالة toArray. تجدر الإشارة إلى أن الدوال المخصّصة للتعامل مع قنوات الإشعارات تبدأ بـto متبوعة باسم القناة. مثلا toMail للبريد الإلكتروني (تأتي مبدئيا) وtoDatabase لتخصيص الإشعار عبر قناة قاعدة البيانات. نعدّل الصنف NewPost قليلا بحيث نمرّر للمشيّد معطى من نوع Post (نزعنا أجزاء الصنف التي لم يطرأ عليها تعديل): <?php // استدعاء الأصناف، أضفنا إليه الصنف Post use App\Post; class NewPost extends Notification { // أضفنا خاصيّة post إلى الصنف protected $post; public function __construct(Post $post) { $this->post = $post; } // بقية شفرة الصّنف } نعود إلى الدالة notify في المتحكم ونعدّلها لتصبح على النحو التالي: public function notify($user_id,$post_id) { $user = User::find($user_id); $post = Post::find($post_id); $user->notify(new NewPost($post)); } لا تنس استدعاء الصنف NewPost في المتحكم: use App\Notifications\NewPost; استدعينا الدالة notify من الكائن user$ ومررنا إليها صنفا جديدا من الإشعار NewPost. سنحتاج قبل تجربة إرسال الإشعار إلى ضبط البريد الإلكتروني في ملف النظام env.. ستجد في الملف عند فتحه الإعدادات التالية: MAIL_DRIVER=smtp MAIL_HOST=mailtrap.io MAIL_PORT=2525 MAIL_USERNAME=null MAIL_PASSWORD=null MAIL_ENCRYPTION=null يقترح Laravel مبدئيا موقع https://mailtrap.io الذي يوفر خدمة لمحاكاة إرسال بريد إلكتروني وعرضه. نحصُل بالتسجيل في الموقع على صندوق بريد للاختبار. ندخل إلى صندوق البريد (Demo inbox). ننسخ قيم Username وPassword ونضيفها إلى الإعدادات في ملف env. على النحو التالي: MAIL_DRIVER=smtp MAIL_HOST=mailtrap.io MAIL_PORT=2525 MAIL_USERNAME="be7c7d3972da27" MAIL_PASSWORD="5f445b7bdca027" MAIL_ENCRYPTION=null يمكننا الآن اختبار الإشعار بتشغيل خادوم التطبيق والذهاب إلى المسار http://localhost:8000/notify/1/2. إن نظرت في صندوق البريد على موقع mailtrap فستجد رسالة جديدة: لاحظ العنوان البريدي الذي يُوجّه إليه الإشعار medeyil@laravel.com. إن عدلت المسار الذي تطلُبه إلى التالي http://localhost:8000/notify/2/2 ونظرت في الإشعار الذي يصل إلى البريد فستجد أن المرسَل إليه To تبدّل إلى fbenziane@laravel.com الذي هو بريد المستخدم ذي المعرف 2. هل تساءلت كيف يعرف نظام الإشعارات البريد الإلكتروني الذي يجب إرسال الإشعار إليه، أي البريد الإلكتروني الخاصّ بالمستخدِم؟ تخصيص الإشعار عبر البريد الإلكتروني يمكن تطبيق الإشعارات على أي نموذج Eloquent في النظام، بشرط أن يستخدم النموذجُ السّمةَ Trait المسمّاة Notifiable. إن نظرنا إلى النموذج User الذي يأتي مبدئيا مع المشروع فسنلاحظ التالي: use Illuminate\Notifications\Notifiable; و use Notifiable; يتيح استخدام هذه السمة في النموذج إمكانية استدعاء الدالة notify: $user->notify(new NewPost($post)); التي تأخذ معطى عبارة عن كائن من صنف Notification. بالنسبة لكيفية معرفة نظام الإشعارات ببريد المستخدم فالإجابة هي أن كل قناة إشعار تتطلّب وجود خاصيّات Properties معيّنة في النموذج الذي يستدعي الدالة notify. في حالة قناة البريد الإلكتروني فإن الخاصيّة المنتظرة هي - منطقيا - عنوان البريد email. بالعودة إلى الصنف NewPost لاحظ دالة التعامل مع البريد الإلكتروني: public function toMail($notifiable) { return (new MailMessage) ->line('The introduction to the notification.') ->action('Notification Action', 'https://laravel.com') ->line('Thank you for using our application!'); } ترجع هذه الدالة كائنا من صنف MailMessage. إن تنبهت جيدا فستدرك أن العبارات الواردة في الشفرة أعلاه هي نفسها الموجودة في البريد المرسَل إلى المستخدم: الدالة line تدرج فقرة في بريد الإشعار. الدالة action تدرج زرًّا وتحدّد النص الذي يظهر على الزرّ ورابط الصفحة التي تزورها عند النقر على هذا الزرّ. توجد دوال أخرى لتخصيص الإشعار أكثر: subject لتخصيص موضوع البريد الإلكتروني. from لتحديد بريد المرسِل. attach لإضافة ملف مرفَق. priority لتحديد الأولوية (1 تعني أولوية قصوى). نخصّص في ما يلي البريد السّابق: public function toMail($notifiable) { return (new MailMessage) ->subject('New post: '. $this->post->title) ->line('Don\'t miss our new post ' .$this->post->title) ->action('View the post', 'https://laravel.com') ->line('Thank you for your visit!') ->from('hsoub@academy.com'); } نحصل بزيارة الرابط http://localhost:8000/notify/1/2 على النتيجة أدناه. بالنسبة لاسم التطبيق الذي يظهر في الإشعار فيمكن تعديله عن طريق التعليمة name في ملف الإعداد config/app.php. تأخذ التعليمة مبدئيا القيمة Laravel. إن أردت تخصيصا أكبر للبريد ومظهره فيمكنك ذلك بنشر قالب Blade الخاص بالبريد الإلكتروني للإشعار ومن ثم تخصيصه. نفذ الأمر التالي لنشر القالب: php artisan vendor:publish --tag=laravel-notifications ستجد القالب على المسار resources/views/vendor/notifications/email.blade.php؛ حيث يمكنك التعديل عليه بما تراه مناسبا.
  5. يأتي Laravel بإطار العمل Eloquent مضمّنا مبدئيا؛ وهو ما يتيح بناء نماذج تُعالّج فيها البيانات وتُطبّق عليها القواعد التي تحكُم سير التطبيق. يُيسّر Eloquent كثيرا من الأمور على المطوِّر؛ إلا أنّه يمكن أن يكون أحيانا غير مناسب لتعقيد المشروع الذي تعمل عليه. يوفّر Laravel آلية للتعامل مع قواعد البيانات مباشرة دون الحاجة لربط كائنات التطبيق بجداول قاعدة البيانات. تُسمى هذه الآلية منشئ الاستعلامات Query builder. كانت الاستعلامات المعدّة بمنشئ الاستعلامات ترجع النتائج، في الإصدارات السابقة (قبل الإصدار 5.3) من Laravel، على هيئة مصفوفة Array؛ إلا أن الأمر تغيّر في الإصدار 5.3 وأصبحت أغلب دوالّ بناء الاستعلامات ترجع نتيجة من نوع Collection (مجموعة). سنرى في هذا المقال أمثلة تفصيلية حول هذا التغيير، ولكن قبل ذلك سنعرّج على مقارنة بين Eloquent ومنشئ الاستعلامات ومتى يكون استخدام أحدهما أنسب. أيهما أستخدم.. Eloquent أم منشئ الاستعلامات؟ يخضع تطوير البرمجيّات لإكراهات كثيرة، وهو ما يعني أن عليك أحيانا كتابة استعلامات SQL للتخاطب مع قاعدة البيانات مباشرة. يتيح Eloquent آلية أنيقة للتعامل مع قاعدة البيانات بربطها بأصناف التطبيق ممّا يقلّل كثيرا من الحاجة لتدخّل المطوّر في تفاصيل تنفيذ العمليّات. تشمل التسهيلات التي يتيحها Eloquent: التحقق المُستَتِر Dirty check: تحديث الحقول التي تغيّرت قيمتها فقط دون غيرها، عند تنفيذ استعلامات UPDATE. ربط النماذج بأحداث Events؛ مثلا لإشعار المستخدمين أو تحديث إحصائيات عند إنشاء مستخدم جديد. إدارة الحذف اللطيف Soft delete: تعليم تسجيلة على أنها محذوفة بدلا من حذفها فعلا. تنفيذ آليات التحميل الحثيث Lazy loading (طلب البيانات عند الحاجة إليها). يمكن - بالطبع - تطبيق هذه الآليات بنفسك دون الاعتماد على Eloquent؛ إلا أن ذلك سيأخذ وقتا. من الجيد أن يكون لديك سبب مقنع للوقت الذي ستمضيه في هذه المهمة بدلا من التركيز على أمور *قد* تكون أهم. سيكون مثاليّا جدا الحصولُ على كلّ هذه التسجيلات بدون دفع ثمن، إلا أن الأمور في الواقع ليست مثالية. الثمن الذي تدفعه مقابل التسهيلات التي يتضمّنها Eloquent - وأطر عمل ORM الأخرى، بغضّ النظر عن لغة البرمجة - هو سرعة تنفيذ الاستعلامات. على الجهة الأخرى، تمكّن كتابة الاستعلامات من الوصول إلى أقصى سرعة ممكنة في التنفيذ، مقابل التضحية بالجهد والوقت أثناء التطوير. لا يوجد مانع من مزج جميع الوسائل المتاحة أمامك؛ بل إن الأمر محبّذ. مثلا؛ استخدم Eloquent عندما تتعامل مع كيانات منفردة (استمارة تسجيل على موقع مثلا)، الجأ لمنشئ الاستعلامات في المهامّ التي تتطلب وقتا للتنفيذ (كيانات بالجملة Batch processing أو إعداد تقارير على سبيل المثال) واكتب استعلامات SQL مباشرة عندما تكون أمام استعلام شديد التعقيد. الجديد في Laravel 5.3 نستخدم منشئ الاستعلامات في المثال التالي للحصول على كافة المستخدمين (جدول users في قاعدة البيانات): DB::table('users')->get(); تكافئ التعليمة السابقة في نتيجتها تنفيذ الاستعلام التالي: SELECT * FROM users; كانت النتيجة، في الإصدارات السابقة من Laravel، من نوع Array (مصفوفة)؛ إلا أن هذا الأمر تغيّر في الإصدار 5.3 لتصبح النتيجة من نوع Collection (مجموعة). يعني هذا أنه للوصول إلى العنصر الأول في مجموعة المستخدمين السابقة فإننا يمكن أن نكتب: DB::table('users')->get()->first(); بدلا من: DB::table('users')->get()[0]; يجعل هذا التغيير من منشئ الاستعلامات متناسقا في نوع النتيجة مع Eloquent الذي يرجع مجموعة كائنات؛ كما أنه يمكّن من استخدام الدوال الموجودة مبدئيا للتعامل مع المجموعات؛ ومن بينها الدالة all التي ترجع مصفوفة مكافئة للمجموعة؛ مثلا ترجع التعليمة التالية مصفوفة بالمستخدمين: DB::table('users')->get()->all(); إن كان لديك تطبيق مبني على اعتبار أن منشئ الاستعلامات يُرجع النتائج على هيئة مصفوفات، فأسهل طريقة لتعديل الشفرة المصدرية للتوافق مع تغيير نوع النتيجة إلى مجموعة بدلا من مصفوفة؛ هي استخدام الدالة all. ملحوظة 1: لا ينطبق التغيير المذكور أعلاه (إرجاع مجموعة بدلا من مصفوفة) على دوال الصنف DB التي تتعامل مع استعلامات SQL مباشرة. مثلا؛ ترجع التعليمة التالية مصفوفة: DB::select('SELECT name FROM users WHERE id = ?', [1]); ملحوظة 2: انتبه عند استخدام استعلامات SQL مباشرة من ثغرات الحَقن SQL injection. استخدم ربط المتغيّرات Binding (كما في الملحوظة 1) بدلا من تمرير المتغيّر مباشرة كما في الاستعلام التالي: DB::select("SELECT name FROM users WHERE id = '$id' "]); قد يمثل الاستعلام الأخير خطرا إن لم يُتحقّق من المتغيّرid$ قبل استخدامه في الاستعلام. يتحقّق Laravel تلقائيا من المُدخلات عند ربط المتغيّرات كما في استعلام الملحوظة 1. راجع مقال استخدام Eloquent ORM للتعامل مع قاعدة البيانات في Laravel 5.
  6. يعدّ التصفيح Pagination (إعداد الصفحات) إحدى المهامّ الشائعة في مواقع الوِب، وهو ما دعا Laravel لتضمين هذه الوظيفة مبدئيا في إطار العمل. كانت تخصيص التصفيح في الإصدارات الأولى من Laravel بسهولة إعداد قالب Blade؛ إلا أن الأمر تغيّر في الإصدار 5.0، إذ عمل إطار العمل على تسهيل نظام التصفيح الذي يستخدمه لتمكن إعادة استخدامه في مشاريع لا تعتمد على إطار العمل Laravel. صاحب هذه العمليّة تعقيد في إمكانيّة تخصيص التصفيح. يأتي الإصدار 5.3 لتصحيح هذه الوضعية وإعادة الأمور إلى نصابها. تهيئة المشروع سنهيّئ مشروع Laravel 5.3 للعمل عليه. نبدأ بالتثبيت: composer create-project --prefer-dist laravel/laravel laravel53pagination "5.3.*" يأتي المشروع مبدئيا بنموذج للمستخدم User، سنعمل من أجل الشرح على هذا النموذج. نبدأ بإعداد مسار للمستخدمين في ملف المسارات الخاصّ بالوِب routes/web.php: Route::get('users', function () { return view('users.index') ->with('users', User::paginate(5)); }); يطلُب المسار users/ عند زيارته عرضَ القالب index.blade.php الموجود في المجلّد users ضمن مجلد القوالب resources/views. نمرّر للقالب المتغيّر users الذي يتلقّى نتيجة استدعاء الدالة paginate للحصول على المستخدمين المسجلين في قاعدة البيانات. ما يميّز هذه الدالة عن بقيّة الدوال الأخرى التي يمكن استدعاؤها في النموذج هي أنها توفّر إمكانية تطبيق الدالة links في قالب Blade من أجل إنشاء الصفحات، كما سنرى الآن. ليس لدينا لحد الساعة المجلد users ولا الملف index.blade.php، ننشئ المجلد والملف بحيث يصبح مسار ملف القالب resources/views/users/index.blade.php ثم نفتحه ونضيف إليه المحتوى التالي: <!DOCTYPE html> <html> <head> <meta charset="utf-8" > <title> Document </title> <link rel="stylesheet" href="/css/app.css"> <style type="text/css"> body { padding: 10em;}</style> </head> <body> @foreach ($users as $user) <li>{{ $user->name }}</li> @endforeach {{ $users->links() }} </body> </html> لدينا في أعلى الملف بضعة تعليمات للتنسيق، ثم تأتي في جسم المستند body التعليمة التكرارية foreach التي تمرّ على المستخدمين وتعرض أسماءهم، وفي الأخير الدالة links التي تحدثنا عنها أعلاه. يمكننا استدعاء هذه الدالة على المتغيّر users نظرا لأننا استخدمنا الدالة paginate في ملف المسارات للحصول على المستخدمين. تتكفّل الدالة links بإنشاء الصفحات عبر تقسيم المستخدمين على مجموعة من خمسة عناصر (مرّرنا المعطى 5 للدالة paginate في ملف المسارات). بقي لنا بذر جدول المستخدمين حتى يمكننا تجربة ما أنجزناه لحد الآن: php artisan make:seeder UsersTableSeeder نستعين بمكتبة Faker لتوليد مستخدمين وهميين وبذر الجدول: public function run() { User::unguard(); // نستخدم Faker لتوليد البيانات $faker = Faker\Factory::create(); // إنشاء 20 مستخدما foreach(range(1, 20) as $index) { User::create([ 'name' => $faker->name, 'email' => $faker->safeEmail, 'password' => $faker->password, ]); } User::reguard(); } لا تنس استيراد النموذج: use App\User; ثم نعدّل DatabaseSeeder لإضافة ملف البذر الذي أنشأناه للتو: $this->call(UsersTableSeeder::class); يمكننا الآن تنفيذ التهجيرات (تهجير المستخدم يأتي مبدئيا مع المشروع): php artisan migrate ثم بذر قاعدة البيانات: php artisan db:seed ثم تشغيل خادوم التطوير: php artisan serve تظهر عند زيارة الرابط http://localhost:8000/users قائمة بالمستخدمين مقسّمة على صفحات من خمسة مستخدمين. عند النقر على رقم الصّفحة ينقلك إلى مجموعة جديدة من المستخدمين. تخصيص التصفيح في Laravel يوجد القالب المبدئي لروابط الصفحات على المسار vendor/laravel/framework/src/Illuminate/Pagination/resources/views إن أردت تخصيصه فسيتوجّب عليك نشره بالأمر التالي: php artisan vendor:publish --tag=laravel-pagination وستجد بضعة قوالب على المسار ressources/views/vendor/pagination. يستخدم اثنان من هذه القوالب (bootstrap-4.blade.php وsimple-boostrap-4.blade.php) الإصدار 4 من Bootstrap والذي ما زال قيد التطوير؛ أما الآخران فأحدهما هو القالب المبدئي default.blade.php والآخر (simple-default.blade.php) نسخة مبسّطة منه لا تحوي سوى أزرار سابق ولاحق للتنقل بين الصفحات. أدناه الشفرة الخاصّة بالقالب المبدئي: @if ($paginator->hasPages()) <ul class="pagination"> {{-- Previous Page Link --}} @if ($paginator->onFirstPage()) <li class="disabled"><span>«</span></li> @else <li><a href="{{ $paginator->previousPageUrl() }}" rel="prev">«</a></li> @endif {{-- Pagination Elements --}} @foreach ($elements as $element) {{-- "Three Dots" Separator --}} @if (is_string($element)) <li class="disabled"><span>{{ $element }}</span></li> @endif {{-- Array Of Links --}} @if (is_array($element)) @foreach ($element as $page => $url) @if ($page == $paginator->currentPage()) <li class="active"><span>{{ $page }}</span></li> @else <li><a href="{{ $url }}">{{ $page }}</a></li> @endif @endforeach @endif @endforeach {{-- Next Page Link --}} @if ($paginator->hasMorePages()) <li><a href="{{ $paginator->nextPageUrl() }}" rel="next">»</a></li> @else <li class="disabled"><span>»</span></li> @endif </ul> @endif تتيح الدالة hasPages التحقق من وجود صفحات لعرضها. نتحقّق بالدالة onFirstPage من أننا على الصفحة الأولى. يمكن بالدالة hasMorePages معرفة ما إذا كان لدينا مزيد من الصفحات لعرضها. تطبع الدالتان previousPageUrl وnextPageUrlعلى التوالي رابطي الصفحة السابقة واللاحقة للصفحة الحالية التي نحصل عليها بالدالة currentPage. سنعدّل الآن القالب بحيث نضع كلمة Previous (السابق) مكان الرمز »، على ألا تظهر عندما نكون على الصفحة الأولى. نضع بنفس الطريقة كلمة Next (التالي) مكان «، على ألا تظهر عندما نكون على الصفحة الأخيرة. نحصُل على الشفرة التاليّة: @if ($paginator->hasPages()) <ul class="pagination"> {{-- Previous Page Link --}} @if (!$paginator->onFirstPage()) <li><a href="{{ $paginator->previousPageUrl() }}" rel="prev">Previous</a></li> @endif {{-- Pagination Elements --}} @foreach ($elements as $element) {{-- "Three Dots" Separator --}} @if (is_string($element)) <li class="disabled"><span>{{ $element }}</span></li> @endif {{-- Array Of Links --}} @if (is_array($element)) @foreach ($element as $page => $url) @if ($page == $paginator->currentPage()) <li class="active"><span>{{ $page }}</span></li> @else <li><a href="{{ $url }}">{{ $page }}</a></li> @endif @endforeach @endif @endforeach {{-- Next Page Link --}} @if ($paginator->hasMorePages()) <li><a href="{{ $paginator->nextPageUrl() }}" rel="next">Next</a></li> @endif </ul> @endif احفظ التعديلات ثم أعد تنزيل الصفحة للحصول على نتيجة مشابهة لتلك الظاهرة في الصورتين أعلاه.
  7. يوفّر Laravel واجهة تطبيقات برمجية موّحدة للتعامل مع أنظمة تخبئة Cache مختلفة مثل Memcached وRedis. سنعرض في هذا المقال لكيفية إعداد التخبئة في Laravel ومبدأ عملها ثم نتطرق للدالة المساعدة cache التي أضيفت إلى إطار العمل بداية من الإصدار 5.3. تهيئة بيئة التطوير يُحدَّد اسم النظام المستخدَم في التعليمة CACHE_DRIVER ضمن ملف env.. تأخذ التعليمة CACHE_DRIVER مبدئيا القيمة file التي تعني أن الكائنات Objects المخبَّأة ستخزن ضمن ملف ضمن نظام التشغيل. تُضبَط إعدادات نظام التخبئة المستخدَم في الملف config/cache.php، بعد تعيينه بالتعليمة السابقة. نفترض أنك لديك مشروع Laravel بالإصدار 5.3 للتطبيق عليه. ننفذ الخطوات التالية لتهيئة مثال نعتمد عليه في ما بعد للشرح. إنشاء نموذج والتهجير المصاحب له وبذره نستخدم artisan لإنشاء نموذج Eloquent مع التهجير المصاحب له على النحو التالي: php artisan make:model Post -m نفتح ملف التهجير لضبط الحقول في جدول البيانات بإضافة حقل جديد يُسمّىtitle (العنوان): public function up() { Schema::create('posts', function (Blueprint $table) { $table->increments('id'); $table->string("title"); $table->timestamps(); }); } يمكننا الآن تنفيذ التهجير: php artisan migrate الخطوة التالية هي بذر الجدول الحصول على بيانات للتطبيق عليها. ننشئ ملف البذر: php artisan make:seeder PostsTableSeeder ثم نعدل عليه: class PostsTableSeeder extends Seeder { public function run() { Model::unguard(); // نستورد faker لبذر الجدول $faker = Faker\Factory::create(); // ننشئ 10 تسجيلات في الجدول foreach(range(1, 10) as $index) { Post::create([ 'title' => $faker->sentence(5), ]); } Model::reguard(); } } نعدّل ملف بذر قاعدة البيانات لإضافة ملف بذر الجدول posts إليه: public function run() { $this->call(PostsTableSeeder::class); } ثم ننفذ البذر: php artisan db:seed --class=PostsTableSeeder لدينا الآن جدول بيانات بـ10 تسجيلات. أكملنا إعداد النموذج الذي نريد العمل عليه، الخطوة الموالية هي إعداد المتحكم والمسارات. المتحكم والمسارات نستعين بأداة artisan لإنشاء متحكم على النحو التالي: php artisan make:controller PostsController ننشئ في المتحكم PostsController دالة باسم index نستخدمها للحصول على جميع المنشورات الموجودة في جدول البيانات posts بصيغة JSON: public function index() { $posts = Post::all(); return response()->json($posts); } لا تنس استيراد النموذج Post: use App\Post; ثم ننتقل إلى إعداد المسارات. سنضيف مسارا باسم posts/ إلى ملف مسارات واجهةالوب routes/web.php: Route::get('/posts', 'PostsController@index'); يمكننا الآن تنفيذ الأمر php artisan serve من مجلد المشروع لتشغيل الخادوم الخاص ببيئة التطوير ثم الذهاب إلى الرابط http://localhost:8000/posts لرؤية جميع المنشورات المحفوظة في الجدول. إعداد التخبئة واستخدامها تُضبَط التخبئة - كما أسلفنا - في الملفين env. وconfig/cache.php. يحوي الصنف Illuminate\Support\Facades\Cache الكثير من الدوال الثابتة Static التي تتعامل مع التخبئة. إضافة قيمة إلى التخبئة تُستخدَم الدالة Cache::put لإضافة قيمة إلى التخبئة. تأخذ الدالة ثلاثة معطيات: Cache::put('key', 'value', 10); مفتاح key يمثل اسم المتغير. لا يمكن أن تحمل قيمتان في النظام نفس المفتاح. قيمة المفتاح value. مدة زمنية معبَّر عنها بالدقائق (10 في المثال أعلاه)، وتمثل المدة التي سيحتفظ النظام فيها بقيمة المفتاح ضمن التخبئة. تمكن أتمتة إضافة المفاتيح إلى التخبئة بالدالة remember. تبحث هذه الدالة أولا عن قيمة المفتاح في التخبئة، وترجعها إن وجدته؛ فإن لم تجده تنشئ المفتاح وتعطيه القيمة التي ترجعها دالة الإغلاق Closure الممررة في المعطى الثالث. سنستخدم الدالة remember في دالة index التي أضفناها سابقا إلى المتحكم. نعدّل الدالة index على النحو التالي: public function index() { $posts = Cache::remember('posts', 5, function() { return Post::all(); }); return response()->json($posts); } لا ننسى استدعاء الصنف Cache: use Illuminate\Support\Facades\Cache; تعني الشفرة أعلاه أننا عند طلب الدالة index فإنها ستبحث عن المنشورات الموجودة في التخبئة، فإن وجدتها ترجعها وإلا فإنها تنشئها باستدعاء الدالة all من النموذج Post؛ وتحتفظ بها في التخبئة لمدة خمس دقائق؛ ثم ترجع النتيجة. بهذا تكون جميع الطلبات التي ترد إلى المسار posts/ تمر عبر التخبئة؛ سنضيف لأغراض التوضيح مسارا جديدا (ضمن routes/web.php) لنعدل عن طريقه على إحدى التسجيلات الموجودة في جدول المنشورات: Route::get('/edit/post/{id}', 'PostsController@edit'); ونعدّل المتحكم بإضافة الدالة edit التالية: public function edit($id) { $post = Post::find($id); $post->title = "New title for ".$id; $post->save(); $posts = Post::all(); return response()->json($posts); } ما يحدث هو التالي: تظهر عند زيارة الرابط posts/ المنشورات مع المرور على نظام التخبئة؛ أما عند زيارة المسار edit/post/id/ فسنعدّل على المنشور ذي المعرف id، ثم نعرض المنشورات المخزنة في الجدول مباشرة، دون البحث عنها في التخبئة. يظهر في الصورة المتحركة التالية الفرق بين استخدام التخبئة وطلب المنشورات مباشرة من الجدول: تكون التخبئة فارغة عند زيارة المسار posts/ لأول مرة. في هذه الحالة يطلب Laravel المنشورات مباشرة من جدول البيانات، يعرضها ثم يضيفها إلى التخبئة. نطلب الرابط edit/post/1/ الذي يغيّر عنوان المنشور ذي المعرف 1 ليصبح New title for 1؛ ثم يعرض المنشورات بطلبها مباشرة من جدول قاعدة البيانات. لاحظ تغير عنوان المنشور. نعود للرابط posts/ قبل انقضاء المدة المحددة في دالة التخبئة (5 دقائق)، نلاحظ أن التعديل الذي أجريناه لا يظهر في المنشورات المعروضة. ملحوظة: يجب التأكد في التطبيقات الموجهة لبيئة الإنتاج من تناسق عمليات التعديل على قاعدة البيانات والتخبئة. بمعنى أنه يجب أن ينعكس التعديل على قيمة في جدول البيانات مباشرة على القيمة المحفوظة في التخبئة. العمليات على التخبئة الحصول على قيمة من التخبئة: Cache::get('key'); يمكن إرجاع قيمة مبدئية في حال عدم وجود المفتاح في التخبئة: Cache::get('key', 'default'); تبحث الدالة أعلاه عن قيمة المفتاح key في التخبئة، فإن لم تجدها ترجع القيمة المبدئية default. التحقق من وجود مفتاح في التخبئة: if (Cache::has('key')){ Cache::get('key'); } else { Cache::put('key', $values, 10); } نتحقق في الشفرة أعلاه من وجود قيمة للمفتاح key ونرجعها إن وجدت؛ وإلا نضيف قيمة جديدة للمفتاح في التخبئة. نزع مفتاح من التخبئة: Cache::forget('key'); كما يمكن استخدام الدالة pull التي تبحث عن قيمة في التخبئة، ثم تحذفها بعد الحصول عليها: Cache::pull('key'); يشبه الأمر تنفيذ الدالتين get وforget بالتتالي. توجد طريقة أخرى لحذف محتوى التخبئة قبل انتهاء المدة المحددة له بتنفيذ أمر artisan التالي: php artisan cache:clear الدالة المساعدة cache يحتوي إطار العمل Laravel على الكثير من دوال PHP المساعدة؛ بعضها مستخدَم في وظائف إطار العمل نفسه. تعمل الدوال المساعدة Helper functions على الرفع من الإنتاجية والابتعاد عن تكرار الشفرات البرمجية بتجميع وظائف اعتيادية وإتاحة الوصول إليها حيث دعت الحاجة. يضيف الإصدار 5.3 دالة مساعدة جديدة باسم cache يمكن استخدامها في أي جزء من المشروع من أجل تسهيل التخاطب مع نظام التخبئة، بدلا من الصّنف Illuminate\Support\Facades\Cache. نحصل على قيمة المتغير key (المفتاح) باستخدام الدالة المساعدة cache على النحو التالي: cache('key'); تكافئ التعليمة أعلاه استخدام الصنف Cache على النحو التالي: Cache::get('key'); يمكن أيضا تمرير مصفوفة بالمفاتيح وقيمها إلى الدالة cache؛ ونحدّد المدة الزمنية التي نريد أن تستغرقها التخبئة: cache(['title' => 'Hsoub Academy'], 5); إن طلبنا في الخمس دقائق القادمة قيمة المفتاح title في الدالة cache فسنجد القيمة Hsoub Academy. نستخدم الدوال الموجودة في الصنف Cache في الدالة المساعدة cache بنفس الطريقة: إضافة مفتاح إلى التخبئة: cache()->put('key', 'value', 10); أو cache(['key' => 'value'],10); البحث عن قيمة المفتاح في التخبئة، وإضافته إليها إن لم يوجد بها: cache()->remember('key', 5, function() { // }); الحصول على قيمة مفتاح من التخبئة ثم حذفه منها بعد ذلك: cache()->pull('key'); حذف مفتاح من التخبئة: cache()->forget('key'); نعيد كتابة الدالة index في المتحكم PostsController بالدالة المساعدة cache بدلا من الصنف Cache: public function index() { $posts = cache()->remember('posts', 5, function() { return Post::all(); }); return response()->json($posts); } يمكن ملاحظة أنك لا تحتاج لاستيرد صنف جديد؛ فالدالة المساعدة متوفرة في جميع المشروع.
  8. سنعرض في هذا الدرس، الرابع من سلسلة دروس الحوسبة الافتراضية باستخدام KVM، كيفية إدارة الآلات الافتراضية عن طريق أدوات تعمل على سطر الأوامر؛ بدلا من واجهة virt-manager الرسومية. تفيد أدوات سطر الأوامر خصوصا في كتابة السكربتات لأتمتة Automating المهام. سنرى الأدوات التاليّة: virt-install: لإنشاء الآلات الافتراضية وضبطها. virsh: لإنشاء مجامع التخزين وإعدادها. qemu-img: لإدارة تجزئات التخزين الافتراضية. إعداد مجمع تخزين تُعد أداة virsh على سطر الأوامر واجهةً لإدارة الآلات الافتراضية وإعدادها. سنستخدم هذه الأداة في الفقرات المقبلة لإنشاء مجمع تخزين لبيئة KVM. ننفذ الأمر pool-define-as مع virsh لتعريف مجمع تخزين جديد. نمرّر للأمر اسم المجمع، نوعه ومعطيات النوع. يبلغ عدد معطيات نوع المجمع خمسة، وتختلف معطيات نوع المجمع التي يجب تمريرها إلى الأمر من نوع إلى أخر. إن أردنا - مثلا - إنشاء مجمع تخزين مماثل للمجمع SPool1 الذي أنشأناه في الدرس الماضي ، والذي هو من نوع dir فسنحتاج لذكر المعطى target (الأخير من المعطيات الخمسة، ويشير إلى مسار مجلّد على النظام المضيف) فقط ونعوّض المعطيات الأربعة المتبقية بعلامة -: $ sudo virsh pool-define-as SPool1CL dir - - - - "/mnt/spools/Spool1New" Pool SPool1CL defined تظهر بعد تنفيذ الأمر رسالة توضّح أن مجمع التخزين قد عُرِّف. ملحوظة: أسمينا مجمع التخزين SPool1CL لتمييزه عن مجمع التخزين الذي أنشأناه في الدرس السابق. يمكننا سرد جميع مجامع التخزين المتوفّرة على النظام بتمرير المعطى all-- إلى الأمر virsh pool-list: $ sudo virsh pool-list --all Name State Autostart ------------------------------------------- default active yes SPool1 active yes SPool1CL inactive no SPool2 active yes ستلاحظ ظهور مجمع التخزين SPool1CL إلى جانب مجامع التخزين التي أنشأناها في الدرس الماضي؛ لكنّه يختلف عنها في الحالة State والتفعيل التلقائي (Autostart). في الواقع، الأمر السابق يعرّف مجمع تخزين، ولكنه لا ينشئه فعليا. ننفّذ الأمر virsh pool-build لإنشاء مجمع التخزين SPool1CL المعرَّف أعلاه: $ sudo virsh pool-build SPool1CL Pool SPool1CL built تظهر بعد تنفيذ الأمر رسالة تخبرنا بإنشاء المجمع SPool1CL. يمكننا الآن تفعيل SPool1CL (تغيير الحالة من خامل inactive إلى نشِط active): $ sudo virsh pool-start SPool1CL Pool SPool1CL started إن أعدنا سرد جميع مجامع التخزين في النظام فسنلاحظ تغيّر حالة SPool1CL من inactive إلى active: $ sudo virsh pool-list --all Name State Autostart ------------------------------------------- default active yes SPool1 active yes SPool1CL active no SPool2 active yes بقي لنا تفعيل التفعيل التلقائي لـSPool1CL بتنفيذ الأمر التالي: $ sudo virsh pool-autostart SPool1CL Pool SPool1CL marked as autostarted فلنعرض معلومات مفصّلة عن مجمع التخزين الذي أكملنا إنشاءه للتو: $ sudo virsh pool-info SPool1CL Name: SPool1CL UUID: 120247f3-669c-4868-8c1e-bbd134077f55 State: running Persistent: yes Autostart: yes Capacity: 184.33 GiB Allocation: 17.90 GiB Available: 166.43 GiB مجمع التخزين SPool1CL جاهز الآن للاستخدام. يُضبَط مجمع تخزين جديد إذن على خطوات: التعريف pool-define-as، الإنشاء pool-build، التفعيل pool-start والتفعيل التلقائي pool-autostart. إعداد تجزئات تخزين افتراضية حان الآن دور التجزئات الافتراضية التي نستخدم الأداة qemu-img لإعدادها. سننشئ بهذه الأداة تجزئة افتراضية جديدة على مجمع التخزين SPool1CL. نحدّد للأداة الأمر الذي نريد تنفيذه (create بالنسبة للإنشاء)، صيغة تجزئة التخزين، مسار الملفّ الخاصّ بالتجزئة وحجمها: $ sudo qemu-img create -f raw /mnt/spools/Spool1New/SVol1.img 10G Formatting '/mnt/spools/Spool1New/SVol1.img', fmt=raw size=10737418240 لاحظ أن مسار الملفّ الخاصّ بالتجزئة mnt/spools/Spool1New/SVol1.img/ يوجد ضمن المجلّد mnt/spools/Spool1New/ الخاصّ بمجمع التخزين الذي أنشأناه أعلاه (تذكّر أننا حدّدنا النوع dir، الذي يحفظ التجزئات الافتراضية ضمن مجلد يوجد في نظام التشغيل). يمكن التحقّق من خاصيّات التجزئة بتنفيذ الأمر qemu-img info: $ sudo qemu-img info /mnt/spools/Spool1New/SVol1.img image: /mnt/spools/Spool1New/SVol1.img file format: raw virtual size: 10G (10737418240 bytes) disk size: 0 تحذير: تجنّب استخدام الأمر qemu-img للتعديل على تجزئات تستخدمها آلالات افتراضية نشطة، أو أي عمليّة Process أخرى؛ فقد يؤدي ذلك إلى إفساد التجزئة. إنشاء آلات افتراضية الخطوة الأخيرة هي إنشاء آلة افتراضية وتثبيت نظام تشغيل عليها؛ نستخدم الأداة virt-install لهذا الغرض. يتطلّب إنشاء آلة افتراضية جديدة تمرير المعطيات التاليّة إلى الأمر virt-install: اسم الآلة الافتراضية name--. مسار التجزئة التي نريد استخدامها مع الآلة الافتراضية disk path--. كيفية الاتصال بالآلة الافتراضية graphics--. تكون هذه القيمة عادة spice (اسم برمجية للتخاطب مع أسطح المكتب الافتراضية، موجودة في حزمة libvirt-bin). عدد المعالجات الافتراضية vcpu--. حجم الذاكرة العشوائية بالميغابايت ram--. مسار ملفّ ISO المستخدَم لتثبيت نظام التشغيل الضيف على الآلة الافتراضية cdrom--. واجهة الشبكة الافتراضية network--. $ sudo virt-install --name=win7 --disk path=/mnt/spools/Spool1New/SVol1.img --graphics spice --vcpu=2 --ram=4096 --cdrom=/home/zeine77/Documents/ISOs/GRMCULFRER_FR_DVD.iso --network bridge=virbr0 يبدأ بعد تنفيذ الأمر إنشاءُ الآلة الافتراضية. ثم تظهر نافذة منبثقة جديدة لبدء تثبيت نظام التشغيل الضيف. ترجمة - بتصرّف - لمقال How to Manage KVM Virtual Environment using Commandline Tools in Linux لصاحبه Mohammad Dosoukey.
  9. رأينا في الدرسين السابقين من هذه السلسلة ( تثبيت KVM وإنشاء آلات افتراضية Virtual machines باستخدامه على أوبونتو و إنشاء آلات افتراضية في بيئة KVM بوسائط تثبيت شبكية) كيفية استخدام virt-manager لإنشاء آلات افتراضيّة وتثبيت أنظمة تشغيل (ضيفة) عليها، انطلاقا من وسيط تخزين محلي أو عبر الشبكة. سنتابع في هذا الدرس مع virt-manager ونرى كيفية إدارة وسائط تخزين افتراضيّة Virtual storage devices. تستخدَم أنظمة التشغيل يوميا وسائط تخزين بنظم ملفات File systems مختلفة، بعضها محلّي Local وآخر يعمل عبر الشبكة. لا توجد عموما اختلافات كبيرة بين مبادئ عمل وسائط التخزين الافتراضية وتلك الملموسة Physical. تُنشأ الأقراص الخاصّة بالآلات الافتراضية في بيئة KVM اعتمادا على أقراص التخزين الملموسة التي يتخاطب معها النظام المُضيف. يتعامل نظام التشغيل مع أقراص التخزين حسب تجزئات Partitions (أو Volumes)، ويتكوّن كل قرص صلب من تجزئة أو أكثر. بالنسبة لبيئة KVM فإنها تتعامل مع التجزئات الافتراضية ضمن مجموعات تُسمّى مجامع (أو أحواض) التخزين Storage pools. يجب أولا إنشاء مَجمع تخزين ثم ننشئ به تجزئة جديدة لقرص افتراضي. إنشاء مجامع تخزين Storage pools في بيئة KVM نبدأ أولا بعرض مجامع التخزين المتوفّرة بالنقر على قائمة Edit (تعديل) ضمن برنامج virt-manager، ثم Connection Details (تفاصيل الاتصال) ونختار تبويب Storage (تخزين). يوجد مبدئيًّا مجمع تخزين باسم default يحفظ التجزئات الافتراضية على المسار var/lib/libvirt/images/. ستلاحظ وجودَ التجزئة الخاصة بنظام التشغيل الضيف الذي أنشأناه في الدرس الأول (اخترنا عند إعداد التخزين في الخطوة 4 مساحة تخزين افتراضية موجودة سلفا). سننشئ في الخطوات التاليّة مجمع تخزين جديدا، بالنقر على زر + في الجانب السفلي الأيسر من النافذة. تظهر نافذة جديدة لإعطاء اسم لمجمع التخزين واختيار نوعه. يدعم KVM ثمانية أنواع لمجامع التخزين: dir: يحفظ التجزئات الافتراضية ضمن مجلد يوجد في نظام الملفات الذي يستخدمه نظام التشغيل . disk: يستخدم أقراصا صلبة ملموسة لحفظ التجزئات الافتراضية بداخلها. fs: يستخدم تجزئات تخزين مهيّأة سلفا لتخزين التجزئات الافتراضية. netfs: يستخدم وسائط تخزين متشاركة عبر الشبكة، مثل NFS، من أجل حفظ التجزئات الافتراضية. gluster: يحتفظ بالتجزئات الافتراضية ضمن وسيط تخزين ملموس يعمل بنظام الملفات GlusterFS. iscsi: يحفظ التجزئات الافتراضية في وسائط تخزين متوفّرة على الشبكة ببروتوكول ISCSI. scsi: يستخدم وسيط تخزين محلي يعمل بنظام SCSI. lvm: يعتمد على مدير التجزئات المنطقية لتخزين التجزئات الافتراضية. سنختار في هذه الخطوة استخدام مجمع تخزين من نوع dir (مجلد في نظام التشغيل المُضيف)؛ فهو النوع الأكثر شيوعا ولا يحتاج للتعديل على بنية نظام الملفات المحلية. لا توجد تقييدات على مكان وجود المجلد الخاص بمجمع التخزين، ولكن من المستحسَن أن يكون على تجزئة منفصلة؛ مع التأكد من الأذون والملكية الخاصة بالمجلّد حتى يمكن ل KVM التعامل معه. تأكد من أن التجزئة التي تريد إنشاء المجمع عليها فارغة، ثم ركّبها على النحو التالي: sudo mount -t ext4 /dev/sdb4 /mnt/spools/SPool1 تحذير: يمكن أن يؤدي استخدام تجزئة غير فارغة إلى مسح محتوياتها. حيث dev/sdb4/ التجزئة الملموسة وmnt/spools/SPool1/ مجلد التركيب. استخدم الأمر lsblk لعرض التجزئات المتوفرة لديك (أو الموجودة على قرص خارجي)؛ كما يمكنك استخدام الأداة الرسومية GParted لنفس الغرض. يظهر في النافذة التالية المسار الذي تريد حفظ تجزئة التخزين الافتراضية فيه. إن كنت تستخدم تجزئة منفصلة فتأكد من ذكر مسار مجلد التركيب (مثلا mnt/spools/SPool1/) هنا. يظهر مجمع التخزين SPool1 بعد إنشائه في القائمة ضمن تبويب Storage السابق. أنشأنا مجمع تخزين ويمكننا الآن إنشاء تجزئة تخزين افتراضية عليه؛ ولكن قبل ذلك سنرى نوعا آخر من أنواع مجامع التخزين، وهو fs. يشير النوع fs إلى أننا نريد استخدام تجزئة قرص ملموسة كاملة، بدلا من مجلد فقط. ستحتاج لإعداد تجزئة قرص ملموس جديدة. تأكد من اسم التجزئة الجديدة (وأنها فارغة) ثم أعدّها لاستخدام نظام الملفات ext4 (على سبيل المثال) بالأمر التالي أو ببرنامج إدارة الأقراص المفضّل لديك: sudo mkfs.ext4 /dev/sdb6 أنشئ أيضا مجلدا جديدا (مثلا SPool2) في النظام المُضيف لتركيب القرص عليه. ملحوظة: اكتف بتهيئة التجزئة وإنشاء المجلد دون تركيبها عليه. سيتولى KVM هذه المهمة. يمكننا الآن إنشاء مجمع تخزين جديد وتسميته مع تحديد نوع المجمع بـfs. حدد في الخطوة التالية من إنشاء المجمع مسار مجلد التركيب (مثلا mnt/spools/SPool2/) في الخانة Target Path (المسار المستهدَف) ومسار التجزئة التي أعددناها سابقا (dev/sdb6/ في المثال) في الخانة Source Path (المسار المصدَر). يظهر مجمع التخزين الجديد في قائمة وسائط التخزين سنناقش في الدرس الموالي إنشاء نوع آخر من مجامع التخزين (عن طريق سطر الأوامر). ننتقل الآن لإنشاء تجزئة جديدة. إضافة تجزئة تخزين افتراضية إلى مجمع تخزين في بيئة KVM توجد صيغ عدّة لتجزئات التخزين. تمكّن هذه الصّيغ من استخدام التجزئة مع مراقبين Hypervisors مختلفين: QEMU، VMware، VirtualBox أو Hyper-V. حدّد مجمع التخزين الذي تريد إضافة التجزئة إليه (SPool2 مثلا) ثم انقر على زر + الموجود تحت بيانات مجمع التخزين أمام عبارة Volumes (التجزئات) لتظهر نافذة إضافة تجزئة. حدّد اسم التجزئة الجديدة والصيغة Format التي تريد استخدامها لإنشاء التجزئة ومساحتها ثم انقر على زر Finish (إنهاء). إن كنت تنوي استخدام التجزئة على مراقب آخر غير KVM في ما بعد فتأكد من اختيار صيغة يدعمها ذلك المراقب. تظهر التجزئة الجديدة ضمن قائمة التجزئات الموجودة في مجمع التخزين SPool2. ترجمة - بتصرّف - لمقال How to Manage KVM Storage Volumes and Pools for Virtual Machines – Part 3 لصاحبه.
  10. يتناول هذا المقال، الثاني من سلسلة من أربعة أجزاء، كيفيّة توزيع آلات افتراضيّة عبر وسائط شبكة في بيئة تستخدم KVM. تأكد - قبل تنفيذ الخطوات المشروحة هنا - من تثبيت المتطلّبات المذكورة في الجزء الأول من السلسلة. تثبيت نظام تشغيل عن طريق خادوم FTP في بيئة KVM سنثبّت لأغراض هذا الدرس خادوم FTP على النظام المُضيف؛ وذلك باتباع الخطوات التالية (راجع درس كيفية تنصيب وإعداد خادوم FTP على أوبنتو لتفاصيل أكثر عن إعداد خادوم FTP). سنكتفي هنا بالأوامر الأساسية للحصول على الخادوم وتثبيت نظام ضيف على آلة افتراضية عن طريقه. للتثبيت: $ sudo apt install vsftpd ثم نعدّل ملف الإعداد etc/vsftpd.conf/ للسماح بالوصول المجهول لخادوم FTP: $ sudo nano /etc/vsftpd.conf نغيّر قيمة التعليمة anonymous_enable من NO إلى YES؛ ثم نعيد تشغيل الخادوم: $ sudo systemctl restart vsftpd.service إن أردنا مشاركة ملفات عن طريق خادوم FTP فيمكننا وضعها على المسار ‎/srv/ftp. جهّزنا خادوم FTP لمشاركة الملفات؛ حان الآن وقت اختيار ملف ISO الخاص بنظام التشغيل الذي نريد تثبيته على الآلة الافتراضية (النظام الضّيف)؛ ونحدّد نقطة تركيب Mount point ليُركَّب عليها هذا الملف: $ sudo mount -t iso9660 -o ro ISO_FILE MOUNT_POINT تشير ISO_FILE إلى المسار الذي يوجد عليه ملف ISO وMOUNT_POINT إلى مسار مجلد التركيب. ملحوظة: تمثّل ملفات ISO نسخا خامة من أقراص صلبة ويجب تركيبها على نظام الملفات File system حتى يمكن لنظام التشغيل التعامل معها. يتعامل نظام التشغيل مع المجلَّد الذي رُكِّب عليه المحتوى كما لو كان يقرأ البيانات من قرص مًدمَج (CD أو DVD). نحدّد أثناء التركيب نظام الملفات المناسب للتعامل مع ملف ISO وهو iso9660. لا يمكن لحدّ الساعة الوصول إلى محتويات الملفّ أعلاه عن طريق FTP؛ إذ أنها لا توجد في المسار الذي يمكن لخادوم تشارك الملفات القراءة منه. ننشئ مجلدا جديدا على المسار ‎/srv/ftp: $ sudo mkdir /srv/ftp/ubuntu-14.04 ثم ننقل إليه محتويات ملفّ ISO انطلاقا من نقطة التركيب: $ sudo cp -r /mnt/ubuntu-14.04/* /srv/ftp/ubuntu-14.04/ حيثُ mnt/ubuntu-14.04/ هي نقطة التركيب (MOUNT_POINT) السابقة. نتأكد من أن مجموعة المستخدمين ftp هي المالكة للمجلد ubuntu-14.04 الذي أنشأناه للتو حتى يمكن لخادوم FTP قراءة محتوياته: $ sudo chown -R :ftp /srv/ftp/ubuntu-14.04/ يمكننا الآن استخدام أداة virt-manager لإنشاء آلات افتراضية تستخدم وسيط التثبيت الموجود على خادوم تشارك الملفات. تظهر عند طلب إنشاء آلة افتراضية جديدة النافذة التالية. نحدّد خيار التثبيت عبر الشبكة Network Install وننتقل للخطوة الموالية. نحدّد في هذه الخطوة المسار URL الذي يوجد عليه وسيط التثبيت. بما أننا نريد الوصول إلى خادوم FTP فالمسار سيكون حسب الصيغة التالية: ftp://ip_or_host/folder حيثُ ip_or_host عنوان IP الخاص بخادوم تشارك الملفات أو اسم المضيف Host الخاص به؛ وfolder المجلّد حيث توجد ملفات نظام التشغيل الذي نريد تثبيته على الآلة الافتراضية (مجلد ubuntu-14.04 الموجود على خادوم تشارك الملفات). تذكّر أننا في الدرس السابق ثبّتنا حزمة bridge-utils وذكرنا أنها تنشئ جسرا يعمل بين الآلات الافتراضية (النظام الضّيف) والنظام المُضيف. ستلاحظ بعد تثبيت الحزمة ظهور واجهة شبكة جديدة لديك على النظام المُضيف (حيثُ ثبتتنا KVM وأدواته) باسم virbr0. يمكن للأجهزة الافتراضية التواصل مع النظام المضيف عن طريق هذه الواجهة؛ لذا سنحصُل على عنوانها بتنفيذ الأمر التالي: $ ip -4 a show 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever 4: virbr0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default qlen 1000 inet 192.168.122.1/24 brd 192.168.122.255 scope global virbr0 valid_lft forever preferred_lft forever يظهر من نتيجة الأمر أعلاه أن الواجهة vibr0 لديها العنوان 192.168.122.1 (قد يختلف العنوان لديك). يصبح عنوان المجلّد حيث توجد ملفات التثبيت على النحو التالي: ftp://192.168.122.1/ubuntu-14.04 وهو العنوان الذي أعطيناه للأداة virt-manager كما في الصورة أعلاه. تشبه الخطوات الموالية (من 3 إلى 5) الخطوات المذكورة في الجزء الأول من هذه السلسلة. تثبيت نظام تشغيل عن طريق خادوم وِب في بيئة KVM لا تقتصر إمكانيات التثبيت عبر الشبكة على الوسائط الموجودة على خواديم تشارك ملفات، بل يمكن استخدام خواديم أخرى مثل خواديم الوِب (Apache أو Nginx على سبيل المثال). سنأخذ في هذه الفقرة مثالا لإنشاء آلة افتراضية عبر الأداة virt-manager وتثبيت نظام تشغيل ضيف عن طريق وسيط تثبيت موجود على خادوم وِب. راجع درس كيف تثبت حزم MySQL ،Apache ،Linux :LAMP و PHP على أوبنتو 14.04 للمزيد عن تفاصيل تثبيت خادوم وِب Apache؛ أو مقال كيف تثبت حزم MySQL ،nginx ،Linux :LEMP وPHP على أوبنتو 14.04 إن كنت تفضّل خادوم Nginx. يمكن الاكتفاء - لأغراض هذا الدرس - بالجزء الأول من أحد المقالين المذكوريْن سابقا (تثبيت Apache أو Nginx دون بقية الحزم). لا يختلف تثبيت النظام الضّيف انطلاقا من وسيط تثبيت على خادوم وِب عن التثبيت عبر خادوم تشارك ملفات سوى في الخطوة الثانية عند تحديد العنوان. في حالة خادوم الوِب يُحدّد العنوان على النحو التالي: http://192.168.122.1/ubuntu-14.04 رأينا في هذا الدرس والدرس السابق كيفية استخدام الأداة الرسومية virt-manager لإنشاء آلات افتراضيّة في بيئة تستخدم KVM للحوسبة الافتراضية؛ سنرى في الدرس الموالي كيفية إدارة الأقراص الافتراضية في هذه البيئة. ترجمة - بتصرّف لمقال How to Deploy Multiple Virtual Machines using Network Install (HTTP, FTP and NFS) under KVM Environment – Part 2 لصاحبه Mohammad Dosoukey.
  11. تقدّم تقنية KVM (اختصار Kernel-based Virtual Machine؛ الأجهزة الافتراضيّة المعتمدة على نواة لينكس) آلية لتشغيل الحوسبة الافتراضيّة (التخيّليّة أو الوهميّة) Virtualization على الأنظمة العاملة بلينكس. تمكّن هذه الآلية - التي تتوفّر على هيئة وحدة Module في نواة لينكس منذ الإصدار 2.6.20 - من استخدام أنظمة تشغيل مستقلّة ومنفصلة في وقت واحد على نفس العتاد Hardware. يتولّى نظامٌ يُعرَف بالمضيف Host تشغيل العتاد Hardware عن طريق برنامج يُعرَف بالمراقِب Hypervisor. يوفّر المراقب موارد (معالج، ذاكرة عشوائية، أجهزة طرفية وغيرها) للآلات الافتراضيّة. يُسمّى النظام الذي يُشغّل الآلة الافتراضيّة بالنظام الضيف Guest. يقدّم KVM الميزات التاليّة: التعهّد الزائد Over-commiting: يعني تخصيص موارد افتراضيّة (المعالجات والذاكرة العشوائية) أكثر من تلك التي يوفّرها العتاد. التموين السريع Thin provisionning: يسمح بالتوزيع المرن للمساحة التخزينية المتوفّرة والبحث عن أمثل طريقة لمشاركتها بين الآلات الافتراضية. تحجيم الإدخال/الإخراج من القرص Disk I/O throttling: يوفّر إمكانية تعريف حد لطلبات الإدخال والإخراج بالنسبة لكل آلة افتراضيّة. إمكانيّة زيادة قدرات المعالجة الخاصّة بالآلات الافتراضية حسب الطّلب دون الحاجة لإيقافها عن العمل. يتطرّق هذا المقال، الأوّل في سلسلة من أربعة أجزاء، لكيفية استخدام KVM والأدوات التي يوفّرها على توزيعة Ubuntu 16.04. تشترك توزيعات لينكس عموما في الخطوط العريضة، وكثير من الخطوات الواردة في هذه السلسلة. المتطلّبات يجب أن تكون لدى العتاد الذي يعمل عليه النظام المُضيف القدرةُ على تنفيذ طلبات المُراقب عن طريق ما يُعرَف بامتدادات العتاد الخاصّة بالحوسبة الافتراضية Hardware virtualization extensions؛ وهو ما توفّره أغلب الحواسيب الحديثة. يمكّن الأمر kvm-ok من التأكد من جاهزيّة جهازك لاستخدام KVM: $ kvm-ok INFO: /dev/kvm exists KVM acceleration can be used يظهر من نتيجة الأمر أعلاه أن الجهاز قادر على استخدام التقنيّة. تأكّد، إن كانت النتيجة مخالفة، من أن امتدادات العتاد موجودة ومفعّلة في محمّل الإقلاع BIOS (أو UEFI) الخاص بجهازك. تثبيت أدوات KVM ونشر الآلات الافتراضية سنحتاج لبضعة حزم قبل أن يمكننا البدء في إنشاء آلات افتراضية باستخدام KVM. يُثبّت الأمر التالي هذه الحزم: $ sudo apt install qemu-kvm libvirt-bin bridge-utils virt-manager تعمل حزمة أدوات kvm-qemu على تشغيل وحدة KVM في النواة لتنفيذ الشفرة البرمجية الخاصّة بالآلة الافتراضية. كما أن هذه الأدوات تحجز الذاكرة العشوائية والموارد التي تحتاجها الآلات الافتراضيّة للعمل؛ زيادة على كونها تعمل على محاكاة عمل الأجهزة الطرفية Peripherals الملموسة التي يتوفّر عليها النظام المُضيف من أجل إتاحة أجهزة طرفية افتراضية للنظام الضيف. تتيح الحزمة libvirt-bin مجموعة من الأدوات للتخاطب مع قدرات الحوسبة الافتراضية التي يتوفّر عليها الجهاز المضيف. نذكر مثلا الأداة virsh التي يمكن من خلالها إنشاء آلات افتراضية، تعليقها (توقيف مؤقّت) أو إيقافها عن العمل. تحوي الحزمة bridge-utils الأدوات الضرورية لإنشاء أجهزة طرفية تعمل جسورا Bridge بين واجهات شبكة عدّة، ولإدارة هذه الأجهزة. من المهمّ توفّر هذه الميزة حتى يمكننا ضبط شبكة حواسيب تشمل الآلات الافتراضية. توفّر حزمة virt-manage واجهة رسومية لإدارة الآلات الافتراضية. الخطوة التاليّة هي إضافة المستخدم الحالي إلى مجموعة المستخدمين libvirtd حتى يمكنه استخدام الآلات الافتراضيّة التي ننشئها عن طريق KVM؛ إذ لا تُتاح هذه الوظيفة لغير أعضاء هذه المجموعة والحساب الجذر: $ sudo adduser zeine77 libvirtd نطلُب، من أجل التأكد من أن كل شيء على ما يُرام، سردَ الآلات الافتراضية المتوفّرة؛ وذلك باستخدام الأمر virsh على النحو التالي: $ virsh -c qemu:///system list Id Name State ---------------------------------------------------- لا توجد آلات افتراضية لحد الساعة، لذا لن تظهر معلومات عن هذه الآلات؛ وسيكتفي الأمر بعرض أسماء أعمدة (المعرّف Id، الاسم Name والحالة State) تصف معلومات تتعلّق بالآلات الافتراضية. تشير هذه النتيجة إلى أننا مستعدون الآن للبدء في استخدام KVM. إنشاء آلات افتراضية باستخدام KVM سنستخدم في هذا الجزء من المقال الأداة virt-manager للتخاطب مع KVM من أجل إنشاء آلات افتراضية. يمكن تشغيل virt-manager من قوائم سطح المكتب الذي تستخدمه (Gnome، KDE أو غيرهما) أو بتنفيذ الأمر virt-manager في سطر الأوامر. تظهر النافذة التاليّة بعد تشغيل الأداة. يظهر في واجهة البرنامج أنه متّصل بمضيف محلي يستخدم المُراقب QEMU/KVM. يمكن من هذه الواجهة الاتّصال بمضيفات Hosts بعيدة (توجد على أجهزة مغايرة للجهاز الذي تعمل عليه الأداة virt-manager)، وذلك عن طريق القائمة …File -> Add Connection ثم تحديد الخانة Connect to remote host وذكر البيانات المطلوبة. ننقر على زر Create a new virtual machine في واجهة virt-manager لإنشاء آلة افتراضية جديدة. تظهر نافذة جديدة تطلُب تحديد طريقة لتثبيت نظام التشغيل الضّيف على الآلة الافتراضية. توجد أكثر من طريقة كما يظهر في لقطة الشاشة أعلاه. سنحدّد خيار Local install media (وسيط تثبيت محليّ). تظهر النافذة التاليّة. يمكن عبر الواجهة الظاهرة في الصورة أعلاه الاختيار بين قرص مُدمَج (CD أو DVD) أو ملفّ ISO. سنحدّد الخيار الثاني (ملف ISO) وننقر على الزّر Browse. تظهر النافذة التالية. نستطيع عبر هذه النافذة إنشاء مساحة تخزين Storage volume افتراضية لاستخدامها مع الآلة؛ إلا أننا لن نفعل ذلك الآن وسننقر على الزّر Browse Local لتحديد المسار Path الذي يوجد عليه ملفّ ISO. يعود البرنامج بعد تحديد المسار إلى واجهة الاختيار الخاصة بوسيط التثبيت. نلاحظ أن البرنامج تعرّف تلقائيا على نوعية نظام التشغيل وإصداره اعتمادا على اسم وسيط التثبيت. ثم يطلب منا البرنامج ضبط إعدادات المعالج والذاكرة العشوائية الخاصّين بالآلة الافتراضيّة. نختار القيم المناسبة اعتمادا على العتاد المتوفّر على المُضيف ثم ننتقل لإعدادات التخزين. يمكن تحديد أحد خيارين؛ إما إنشاء مساحة تخزين افتراضية جديدة أو اختيار مساحة تخزين افتراضية موجودة سلفا. بما أننا لم نضبُط مساحة تخزين قبلًا فسنحدّد الخيار الأول. ثم ننتقل للخطوة الأخيرة وهي تحديد اسم للآلة الافتراضية. إن أردت تخصيص عمليّة التثبيت فبإمكانك تحديد الخيار Customize configuration before install (تخصيص الإعدادات قبل التثبيت). ثم ننقر على زر Finish. تبدأ الآلة الافتراضية بالعمل مباشرة وتظهر في واجهة برنامج virt-manager شاشةُ التثبيت الخاصّة بنظام التشغيل الضيف على الآلة الافتراضية. توجد في واجهة virt-manager أزرار للتحكّم في الآلة الافتراضية: إعادة التشغيل Reboot، الإيقاف Shut Down، فرض إعادة التعيين Force reset، فرض الإيقاف Force off (يشبه فصل حاسوب عن الطاقة دون إيقاف نظام التشغيل) وحفظ حالة الآلة Save. كما يمكن عن طريق زرّ Open فتح نافذة خاصّة للآلة الافتراضية يمكن عبرها أخذ لقطات سريعة Snapshots. تخزّن اللقطات السريعة جميع البيانات الموجودة على الآلة ويمكن الرجوع إلى هذه اللقطات في حالات مثل حدوث مشكل مع الآلة الافتراضية، توقفها عن العمل أو لإنشاء آلة افتراضية مطابقة لها تماما. ترجمة - بتصرّف - لمقال How to Create Virtual Machines in Linux Using KVM (Kernel-based Virtual Machine) – Part 1 لصاحبه Mohammad Dosoukey.
  12. استخدم اسم المشروع الذي خصّصه لك Heroku بعد تنفيذ الأمر create (راجع مخرجات الأمر heroku create)؛ nameless-chamber-90421 هو اسم المشروع الذي أعطاه Heroku لي عند كتابة الدرس وليست لديك صلاحيات الوصول إليه.
  13. يتناول هذا المقال المفاهيم الأساسيّة للاستيثاق باستخدام LDAP: ماهيّته، متى يُستخدَم ولماذا؛ وكيفيةَ ضبط خادوم LDAP وإعداد عميل للاستيثاق عن طريقه اعتمادا على Red Hat Enterprise Linux 7. سنركّز في هذا الدّرس على الاستيثاق المعتمد على LDAP رغم وجود طرق أخرى للاستيثاق. بيئة المختبر سنفترض وجود بيئة اختبار مكوّنة من جهازيْن تشغّلهما توزيعة RHEL 7: الخادوم: 192.168.2.100 واسم نطاقه المعرَّف بالكامل academy1.virtuallab.dev. العميل: 192.168.2.200 واسم نطاقه المعرَّف بالكامل ldapclient.virtuallab.dev. راجع مقال إنشاء شبكة داخلية افتراضية باستخدام Oracle VirtualBox للمزيد حول إنشاء هذه البيئة باستخدام Oracle VirtualBox، ومقال حول إعداد أسماء المضيفات. ما هو LDAP؟ يعرفّ LDAP (اختصار لـLightweight Directory Access Protocol، الميثاق الخفيف للوصول إلى الدّليل) مجموعة من البروتوكولات التي تسمح لعميل بالوصول إلى معلومات مخزّنة مركزيًّا عبر الشبكة. يتعلّق الأمر بمعلومات يُتاح الوصول إليها من أماكن مختلفة أو لمستخدمين كثر؛ مثل صدفات الدّخول (الولوج) Login shells، مسارات الوصول إلى المجلّدات الشخصيّة للمستخدمين أو معلومات أخرى - عناوين أشخاص، أرقام هواتف - عن مستخدمي النظام، على سبيل المثال لا الحصر. جعلُ هذه المعلومات في مكان مركزي يسهّل من صيانتها وتحديثها ويعني أنه يمكن لأي شخص الوصول إليها إذا كان لديه التصريح بذلك. يدور LDAP حول مفاهيم أساسية ثلاثة: مَدخَل Entry: يُمثّل المَدخَل في LDAP معلومة أو وحدة معلومات، ويُعرَّف باسم مُميَّز Distinguished name. خاصيّة Attribute: وهي بيانات تتعلّق بالمَدخَل. قيم Values: تُسنَد لكلّ خاصيّة قيمة أو مجموعة قيم تفصِل بينها مسافة. يُشار إلى القيمة الوحيدة في المَدخَل (أي التي لا توجد إلا مرة واحدة في خاصيّات المدخل) بالاسم المُميَّز النسبي Relative Distinguished Name. سنرى في الخطوة التاليّة كيفية تثبيت LDAP على كلّ من الخادوم والعميل. تثبيت LDAP وإعداده على الخادوم والعميل يُنفّذ برنامج OpenLDAP مبادئ LDAP على RHEL 7؛ يمكن تثبيته على الخادوم والعميل على التوالي بالأمرين التاليّين: # yum update && yum install openldap openldap-clients openldap-servers # yum update && yum install openldap openldap-clients nss-pam-ldapd تحوي الحزمة nss-pam-ldapd التي نثبّتها على العميل مجموعة دوال مهمّتها البحث عن معلومات في دليل LDAP والاستيثاق من المستخدمين بناءً على المعلومات المتوفّرة على خادوم LDAP. يجب أن نعدّ بضعة أمور بعد اكتمال التثبيت. تأكّد من SELinux يسمح لـLDAP بالعمل عن طريق تفعيل الإعدادات التاليّة على كلّ من الخادوم والعميل. لا تنس استخدام الخيار P- حتى تستمر التعديلات لما بعد إعادة تشغيل النّظام: # setsebool -P allow_ypbind=1 authlogin_nsswitch_use_ldap=1 يتطلّب الاستيثاق عبر LDAP تفعيل الإعداد allow_ypbind، بينما تحتاج برامج أخرى تفعيل الإعداد authlogin_nsswitch_use_ldap حتى يمكنها استخدام LDAP للاستيثاق. ثم نفعّل خدمة slapd التي تنصِت للاتصالات القادمة إلى خادوم LDAP، مبدئيّا على المنفذ 389 وتجيب على الطلبات التي تتلقّاها عبر هذه الاتصالات: # systemctl enable slapd.service # systemctl start slapd.service تذكّر أن نظام التمهيد SystemD يمكّنك من تعطيل الخدمة، إعادة تشغيلها أو إيقافها: # systemctl disable slapd.service # systemctl restart slapd.service # systemctl stop slapd.service تعمل خدمة slapd بصلاحيّات الحساب ldap (وهو ما يمكن التأكد منه بتنفيذ الأمر ps -e -o pid,uname,comm | grep slapd)، الأمر الذي يعني أن هذا الحساب يجب أن يكون مالكَ المجلّد var/lib/ldap/ من أجل أن تستطيع خدمة slapd تعديل مداخل LDAP التي تُنشئها الأدوات الإداريّة، التي لا تعمل إلا بصلاحيّات الحساب الجذر، في المجلّد المذكور. تأكّد قبل تغيير ملكيّة المجلّد var/lib/ldap/ من نسخ نموذج ملفّ الإعداد لقاعدة بيانات LDAP: # cp /usr/share/openldap-servers/DB_CONFIG.example /var/lib/ldap/DB_CONFIG # chown -R ldap:ldap /var/lib/ldap ضبط الحساب الإداري لـOpenLDAP وتعيين كلمة سرّ له: # slappasswd مثل ما يظهر في الصّورة التالية: ثم ننشئ ملفّ LDIF باسم ldaprootpasswd.ldif. ملفّات LDIF (اختصار لـ LDAP Data Interchange Format، صيغة تبادل البيانات في LDAP) هي تمثيل للبيانات الموجودة في خادوم LDAP والتغيرات التي تطرأ عليها. نضع البيانات التاليّة في الملفّ ldaprootpasswd.ldif: dn: olcDatabase={0}config,cn=config changetype: modify add: olcRootPW olcRootPW: {SSHA}PASSWORD حيث: PASSWORD: كلمة السّر التي حصلنا عليها أعلاه. cn=config: تشير إلى خيار إعداد عامّ. olcDatabase: اسم قاعادة بيانات معيّنة، توجد عادة على المسار etc/openldap/slapd.d/cn=config/. يُضيف ملفّ ldaprootpasswd.ldif مدخلا إلى دليل LDAP. يشير كلّ سطر في هذا المدخل إلى خاصيّة وقيمتها اللتين تفصل بينهما نقطتان عموديّتان :؛ أي أن changetype، add، dn وolcRootPW خاصيّات توجد قيمها على نفس السّطر، يمين النقطتين. تنبغي المحافظة في الخطوات المواليّة على نفس الأسماء الشائعة =Common Names, cn المستخدمة أعلاه. نضيف الآن مدخلا إلى دليل LDAP باستخدام الأمر ldapadd وتحديد عنوان URI الخاصّ بالخادوم وملفّ LDIF الخاصّ بالمدخل على النحو التالي: # ldapadd -Y EXTERNAL -H ldapi:/// -f ldaprootpasswd.ldif ثم نضيف تعريفات للدليل باستيراد الأمثلة الموجودة في المجلّد etc/openldap/schema/: # for def in cosine.ldif nis.ldif inetorgperson.ldif; do ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/openldap/schema/$def; done جعل LDAP يستخدم اسم النطاق في قاعدة بياناته. dn:olcDatabase={1}monitor,cn=config changetype:modify replace: olcAccess olcAccess: {0}to * by dn.base="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" read by dn.base="cn=Manager,dc=virtuallab,dc=dev" read by * none dn:olcDatabase={2}hdb,cn=config changetype: modify replace: olcSuffix olcSuffix: dc=virtuallab,dc=dev dn: olcDatabase={2}hdb,cn=config changetype: modify replace: olcRootDN olcRootDN: cn=Manager,dc=virtuallab,dc=dev dn: olcDatabase={2}hdb,cn=config changetype: modify add: olcRootPW olcRootPW: {SSHA}07/TOeaEPDlpTscePfA5kyij9mmepdr0 dn: olcDatabase={2}hdb,cn=config changetype: modify add: olcAccess olcAccess: {0}to attrs=userPassword,shadowLastChange by dn="cn=Manager,dc=virtuallab,dc=dev" write by anonymous auth by self write by * none olcAccess: {1}to dn.base="" by * read olcAccess: {2}to * by dn="cn=Manager,dc=virtuallab,dc=dev" write by * read نستخدم الأمر ldapmodify للتعديل على مَداخل الدليل: # ldapmodify -Y EXTERNAL -H ldapi:/// -f ldapdomain.ldif ننشئ ملفّ LDIF باسم baseldapdomain.ldif ونحدّد فيه مداخل لإضافتها إلى الدليل: dn: dc=virtuallab,dc=dev objectClass: top objectClass: dcObject objectclass: organization o: virtuallab dev dc: virtuallab dn: cn=Manager,dc=virtuallab,dc=dev objectClass: organizationalRole cn: Manager description: Directory Manager dn: ou=People,dc=virtuallab,dc=dev objectClass: organizationalUnit ou: People dn: ou=Group,dc=virtuallab,dc=dev objectClass: organizationalUnit ou: Group ثم نضيفها إلى الدليل بتنفيذ الأمر ldapadd (مع تحديد اسم النطاق المناسب): # ldapadd -x -D cn=Manager,dc=virtuallab,dc=dev -W -f baseldapdomain.ldif ملحوظة: أدخل كلمة السّر التي أنشأتها سابقا عندما تُطلب منك. ننشئ حسابَ مستخدم لإضافته إلى دليل LDAP: # adduser ldapuser نبدأ بإنشاء ملفّ LDIF باسم ldapgroup.ldif سنستخدمه لإضافة المجموعة التي ينتمي إليها المستخدم السّابق إلى الدّليل: # nano ldapgroup.ldif نضيف المحتوى التالي إلى الملفّ: dn: cn=Manager,ou=Group,dc=virtuallab,dc=dev objectClass: top objectClass: posixGroup gidNumber: 1012 قيمة الخاصيّة gidNumber هي معرّف المجموعة التي ينتمي إليها المستخدم ldapuserالسابق. يمكن الحصول عليه بتنفيذ الأمر id -g ldapuser. ننفذ الأمر ldapaddعلى النحو التالي لإضافة مدخل لمجموعة المستخدم إلى الدليل: # ldapadd -x -W -D "cn=Manager,dc=virtuallab,dc=dev" -f ldapgroup.ldif نعرّف في ملفّ LDIF جديد باسم ldapuser.ldif بيانات المستخدم ldapuser (استخدم الأمر slappasswd لإنشاء كلمة سرّ للمستخدم وإضافتها للملف): dn: uid=ldapuser,ou=People,dc=virtuallab,dc=dev objectClass: top objectClass: account objectClass: posixAccount objectClass: shadowAccount cn: ldapuser uid: ldapuser uidNumber: 1009 gidNumber: 1012 homeDirectory: /home/ldapuser userPassword: {SSHA}9A93CS4LCUx9L8s6HPw1PB3ClH5JEI0U loginShell: /bin/bash gecos: ldapuser shadowLastChange: 0 shadowMax: 0 shadowWarning: 0 ثم ننفّذ الأمر: # ldapadd -x -D cn=Manager,dc=virtuallab,dc=dev -W -f ldapuser.ldif يمكن باستخدام الأمر ldapdelete حذفُ نفس المستخدم من الدليل على النحو التالي (لن نفعل ذلك هنا): # ldapdelete -x -W -D cn=Manager,dc=virtuallab,dc=dev "uid=ldapuser,ou=People,dc=virtuallab,dc=dev" نطلُب من الجدار الناري السماح لخدمة LDAP بالعمل على المنفذ 389: # firewall-cmd --add-service=ldap # firewall-cmd --permanent --add-port=389/tcp # firewall-cmd reload ننتقل إلى العميل لتفعيل إمكانيّة التسجيل لمستخدمي LDAP. تأكّد من أن العميل يمكنه الوصول إلى الخادوم (والعكس). غيّر - إن لزم الأمر - ملفّ etc/hosts/ لإضافة عنوان الخادوم على العميل (والعكس، العميل على الخادوم). ننفّذ الأمر التالي - على العميل - لضبط طرق الاستيثاق عليه: # authoconfig-tui تظهر الواجهة التالية: تأكد من تحديد خياري Use LDAP وUse LDAP authentication بوضع علامة * أمامهما. استخدم الأسهم للانتقال بين الخيارات، والمسافة لتفعيل خيّار أو تعطيله؛ ثم Enter عند الوصول إلى زرّ Next. أضف في الشاشة المواليّة عنوان خادوم LDAP بالصّيغة ldap://academy1.virtuallab.dev ثم الاسم المُميّز القاعدي DN. لم نستخدم شهادة TLS لذا اترك الخيّار TLS غير محدّد. يمكننا الآن اختبار عمل LDAP على العميل بالبحث في الدليل عن المستخدم ldapuser: # ldapsearch -x cn=ldapuser -b dc=virtuallab,dc=dev يجب أن تظهر جميع معلومات المستخدم ldapuser في الدليل: # extended LDIF # # LDAPv3 # base <dc=virtuallab,dc=dev> with scope subtree # filter: cn=ldapuser # requesting: ALL # # ldapuser, People, virtuallab.dev dn: uid=ldapuser,ou=People,dc=virtuallab,dc=dev objectClass: top objectClass: account objectClass: posixAccount objectClass: shadowAccount cn: ldapuser uid: ldapuser uidNumber: 1009 gidNumber: 1012 homeDirectory: /home/ldapuser userPassword:: e1NTSEF9OUE5M0NTNExDVXg5TDhzNkhQdzFQQjNDbEg1SkVJMFU= (...) نفس الشيء عند استخدام الأمر getent على العميل: # getent passwd ldapuser ldapuser:x:1009:1012:ldapuser:/home/ldapuser:/bin/bash ترجمة - يتصرّف - لمقال RHCSA Series: Setting Up LDAP-based Authentication in RHEL 7 – Part 14 لصاحبه Gabriel Cánepa.
  14. تستخدم بعض توزيعات لينكس، خصوصا تلك المعتمدة على Fedora مثل CentOS وRed Hat Enterprise Linux، حزم RPM لتثبيت البرامج وإدارتها. ملف rpm. هو عبارة عن تجميع لبرامج ومكتبات تحتاجها هذه البرامج في حزمة تُستخدَم أداة باسم rpm لتثبيتها. الأداة rpm هي أداة مفتوحة المصدر تحتفظ ببيانات الحزم المثبّتة على النظام في المجلّد var/lib/rpm/. من المهم الانتباه إلى أن أداة rpm لا تستطيع التعامل مع البرامج التي ثُبِّتت انطلاقا من المصدر Source. يحتوي ملفّ rpm. على معلومات من قبيل ماهية الحزمة، من أين تأتي، الاعتماديات التي تحتاجها للعمل، الإصدار… إلخ. توجد خمسة أوضاع أساسية لأداة rpm: التثبيت i-: يُستخدَم لتثبيت حزم RPM. الحذف e-: يُستخدَم لحذف حزمة وإلغاء تثبيتها. الترقية U-: يُحدّث حزمة مثبّتة (تثبيت إصدار جديد من الحزمة). التحقق V-: يُستخدَم للتحقّق من حزمة RPM. الاستعلام q-: يُستخدَم للاستعلام عن حزم RPM. حزم RPM هي حزم قائمة بذاتها؛ تمكن الاستفادة من المواقع التالية للحصول على حزم RPM الخاصّة ببرنامج تريده: redhat.com rpmfind.net. rpm.pbone.net rpmseek.com يقدّم هذا المقال أمثلة لأوامر RPM من أجل المساعدة في تثبيت البرامج، تحديثها أو حذفها على توزيعة تستخدم RPM للتحزيم. ملحوظة: يجب أن تكون لديك صلاحيات الجذر حتى تستطيع إدارة الحزم. التحقق من توقيع حزمة يجب أولا التحقّق من مصدر الحزمة وموثوقيتها قبل تثبيتها. يُستخدم الخيار checksig-- مع الأمر rpm لهذا الغرض. لكي نستطيع التحقق من حزمة فإنه يجب أولا استيراد المفاتيح العمومية Public keys الخاصّة بالنظام الذي نعمل عليه (Fedora، CentOS، RHEL أو غيرها). بالنسبة لتوزيعة Red Hat Enterprise Linux فالأمر على النحو التالي: rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-* نطلب في الأمر التالي التحقق من الحزمة pidgin-1.5.1-6.el3.x86_64.rpm: [root@academy1 ~]# rpm --checksig pidgin-1.5.1-6.el3.x86_64.rpm pidgin-1.5.1-6.el3.x86_64.rpm: (sha1) dsa sha1 md5 gpg OK لاحظ أن نتيجة التحقق هي OK. تثبيت حزمة يطلُب الأمر التالي تثبيت الحزمة pidgin-1.5.1-6.el3.x86_64.rpm التي تحققنا من موثوقيتها أعلاه: [root@academy1 ~]# rpm -ivh pidgin-1.5.1-6.el3.x86_64.rpm Preparing... ########################################### [100%] 1:pidgin ########################################### [100%] تظهر في الأمر أعلاه الخيارات التالية: i-: وضع التثبيت، v-: طلب إظهار رسائل بتقدم عملية التثبيت، h-: طلب إظهار علامات # للدلالة على التقدم الحاصل في تنفيذ خطوات التثبيت. الخياران الأخيران v- وh- ليسا ضروريين. التحقق من اعتماديات حزمة قبل تثبيتها تتيح أداة rpm التحقق من اعتماديات حزمة قبل تثبيتها، مثلا للتحقق من اعتماديات الحزمة pidgin-2.11.0-1.fc24.x86_64.rpm: [root@academy1 ~]# rpm -qpR pidgin-1.5.1-6.el3.x86_64.rpm /bin/sh /bin/sh glib2 >= 2.2 htmlview libICE.so.6()(64bit) libSM.so.6()(64bit) libX11.so.6()(64bit) libXext.so.6()(64bit) libao.so.2()(64bit) libatk-1.0.so.0()(64bit) libaudiofile.so.0()(64bit) (...) يشير الخيار q- إلى أننا في وضع الاستعلام عن الحزمة. يطلب الخيار p- معلومات عن حزمة غير مثبّتة ويسرُد الخيار R- اعتماديّات الحزمة. تثبيت حزمة بغض النظر عن اعتمادياته لا يقبل rpm في الحالة العادية تثبيت حزمة إلا إذا كانت جميع اعتمادياتها مثبتة؛ إلا أنه يمكن إجباره على ذلك باستخدام الخيار nodeps--: [root@academy1 ~]# rpm -ivh --nodeps pidgin-1.5.1-6.el3.x86_64.rpm Preparing... ################################# [100%] Updating / installing... 1:pidgin-1.5.1-6.el3 ################################# [100%] يُثبّت الأمر أعلاه الحزمة بتجاهل الأخطاء المتعلقة باعتماديّاتها؛ إلا أن البرنامج لن يعمل إلا بعد تثبيت الاعتماديات المطلوبة. التحقق من أن حزمة مثبّتة استخدم وضع الاستعلام q- للتأكد من أن حزمة مّا مثبتة على النظام: [root@academy1 ~]# rpm -q BitTorrent package BitTorrent is not installed [root@academy1 ~]# rpm -q pidgin pidgin-1.5.1-6.el3.x86_64.rpm سرد قائمة بجميع ملفات حزمة مثبتة أضف خيار السرد l- إلى وضع الاستعلام q- للحصول على قائمة بملفات حزمة مثبتة على النظام: [root@academy1 ~]# rpm -ql pidgin /etc/gconf/schemas/purple.schemas /usr/bin/gaim /usr/bin/pidgin /usr/lib64/pidgin /usr/lib64/pidgin/convcolors.so /usr/lib64/pidgin/extplacement.so /usr/lib64/pidgin/gestures.so /usr/lib64/pidgin/gtkbuddynote.so (...) سرد الحزم المُثبتة يسرُد الخيار a- عند استخدامه في وضع الاستعلام قائمة بأسماء جميع الحزم المثبتة: [root@academy1 ~]# rpm -qa libsss_nss_idmap-1.13.0-40.el7_2.9.x86_64 pygobject3-base-3.14.0-3.el7.x86_64 libfprint-0.5.0-3.el7.x86_64 man-pages-3.53-5.el7.noarch redhat-release-server-7.2-9.el7.x86_64 rsyslog-7.4.7-12.el7.x86_64 libsane-hpaio-3.13.7-6.el7_2.1.x86_64 (...) يمكن ترتيب القائمة لتظهر الحزم المثبتة مؤخرا في الأعلى باستخدام الخيار last--: [root@academy1 ~]# rpm -qa --last pidgin-1.5.1-6.el3.x86_64.rpm Sun 04 Sep 2016 09:47:32 PM GMT glibc-devel-2.17-106.el7_2.8.x86_64 Tue 02 Aug 2016 11:42:29 PM GMT nss-pam-ldapd-0.8.13-8.el7.x86_64 Tue 02 Aug 2016 11:42:23 PM GMT nscd-2.17-106.el7_2.8.x86_64 Tue 02 Aug 2016 11:42:17 PM GMT glibc-headers-2.17-106.el7_2.8.x86_64 Tue 02 Aug 2016 11:42:14 PM GMT glibc-common-2.17-106.el7_2.8.x86_64 Tue 02 Aug 2016 11:42:01 PM GMT (...) تحديث حزمة يُستخدَم وضع الترقية U- لتحديث إصدار حزمة مثبتة. يحتفظ أمر rpm عند تحديث حزمة بنسخة احتياطية من الإصدار المثبت للعودة إليها في حال لم تعمل الحزمة حسب المطلوب بعد تحديثها: [root@academy1 ~]# rpm -Uvh pidgin-1.5.1-6.el3.x86_64.rpm Preparing... ########################################### [100%] 1:pidgin ########################################### [100%] حذف حزمة استخدم الخيار e- مع الأمر rpm لحذف حزمة بذكر اسمها. مثلا؛ لحذف حزمة pidgin السابقة: [root@academy1 ~]# rpm -e pidgin تمكن أيضا إضافة الخيار v إلى الأمر لعرض رسائل بما يحدُث: [root@academy1 ~]# rpm -ev pidgin Preparing packages... pidgin-1.5.1-6.el3.x86_64.rpm كما يمكن الإبقاء على اعتماديات الحزمة باستخدام الخيار nodeps-- مع أمر الحذف وهو ما ينتج عنه حذف الحزمة فقط دون اعتمادياتها: [root@academy1 ~]# rpm -ev --nodeps pidgin انتبه إلى أن حذف حزمة من النظام يمكن أن يؤدي إلى عدم استقراره؛ لذا تأكد من حاجتك فعلا لحذف الحزمة. معرفة الحزمة التي ينتمي إليها ملف استخدم الخيار f- في وضع الاستعلام لمعرفة الحزمة التي ينتمي إليها ملف. مثلا؛ تخبرنا نتيجة الأمر التالي أن الملف usr/bin/htpasswd/ ينتمي للحزمة httpd-tools-2.4.6-40.el7_2.4.x86_64. [root@academy1 ~]# rpm -qf /usr/bin/htpasswd httpd-tools-2.4.6-40.el7_2.4.x86_64 الحصول على معلومات عن حزمة يعرض الخيار i- عند استخدامه في وضع الاستعلام معلومات عن حزمة مثبتة؛ مثلا بالنسبة لحزمة vsftpd: [root@academy1 ~]# rpm -qi vsftpd Name : vsftpd Version : 3.0.2 Release : 11.el7_2 Architecture: x86_64 Install Date: Thu 21 Jul 2016 03:29:37 PM GMT Group : System Environment/Daemons Size : 355788 License : GPLv2 with exceptions Signature : RSA/SHA256, Fri 18 Mar 2016 01:08:35 AM GMT, Key ID 199e2f91fd431d51 Source RPM : vsftpd-3.0.2-11.el7_2.src.rpm Build Date : Wed 24 Feb 2016 11:06:41 AM GMT Build Host : x86-019.build.eng.bos.redhat.com Relocations : (not relocatable) Packager : Red Hat, Inc. <http://bugzilla.redhat.com/bugzilla> Vendor : Red Hat, Inc. URL : https://security.appspot.com/vsftpd.html Summary : Very Secure Ftp Daemon Description : vsftpd is a Very Secure FTP daemon. It was written completely from scratch. أما إن أردت الحصول على معلومات عن حزمة قبل تثبيتها فالأمر هو التالي: [root@academy1 ~]# rpm -qip pidgin-1.5.1-6.el3.x86_64.rpm Name : pidgin Version : 1.5.1 Release : 6.el3 Architecture: x86_64 Install Date: (not installed) Group : Applications/Internet Size : 11168813 License : GPL Signature : DSA/SHA1, Thu 29 Oct 2009 08:14:44 AM GMT, Key ID 219180cddb42a60e Source RPM : pidgin-1.5.1-6.el3.src.rpm Build Date : Wed 28 Oct 2009 03:52:51 PM GMT Build Host : x86-003.build.bos.redhat.com Relocations : (not relocatable) Packager : Red Hat, Inc. <http://bugzilla.redhat.com/bugzilla> Vendor : Red Hat, Inc. URL : http://www.pidgin/im/ Summary : A Gtk+ based multiprotocol instant messaging client Description : Pidgin allows you to talk to anyone using a variety of messaging protocols, including AIM, ICQ, IRC, Yahoo!, Novell Groupwise, MSN Messenger, Jabber, Gadu-Gadu, Napster, and Zephyr. These protocols are implemented using a modular, easy to use design. To use a protocol, just add an account using the account editor. Pidgin supports many common features of other clients, as well as many unique features, such as perl scripting, TCL scripting and C plugins. Pidgin is NOT affiliated with or endorsed by America Online, Inc., Microsoft Corporation, Yahoo! Inc., or ICQ Inc. [root@academy1 ~]# الاستعلام عن التوثيق Documentation الخاص بحزمة مثبتة يمكن الحصول على مستندات التوثيق الخاصّة بحزمة باستخدام الخيارين df- في وضع الاستعلام. يأخذ الأمر معطى عبارة عن مسار الحزمة؛ وهو ما يمكن الحصول عليه عن طريق الأمر which. نطلب في المثال التالي معرفة مسار الحزمة vsftpd ثم نستعلم عن مستدات التوثيق الخاصّة بها: [root@academy1 ~]# which vsftpd /sbin/vsftpd [root@academy1 ~]# rpm -qdf /sbin/vsftpd /usr/share/doc/vsftpd-3.0.2/AUDIT /usr/share/doc/vsftpd-3.0.2/BENCHMARKS /usr/share/doc/vsftpd-3.0.2/BUGS /usr/share/doc/vsftpd-3.0.2/COPYING /usr/share/doc/vsftpd-3.0.2/Changelog (...) التحقق من حزمة تقارن أداة rpm عند طلب التحقق من حزمة المعلومات المستقاة من الملفات المثبتة مع تلك الموجودة في قاعدة بيانات rpm. يُستخدم الخيار V- لوضع التحقق وp لتمرير حزمة إلى الأمر: [root@academy1 ~]# rpm -Vp vsftpd-3.0.3-2.fc24.x86_64.rpm warning: vsftpd-3.0.3-2.fc24.x86_64.rpm: Header V3 RSA/SHA256 Signature, key ID 81b46521: NOKEY .......T. c /etc/logrotate.d/vsftpd .......T. c /etc/pam.d/vsftpd .......T. c /etc/vsftpd/ftpusers .......T. c /etc/vsftpd/user_list .......T. c /etc/vsftpd/vsftpd.conf .......T. /etc/vsftpd/vsftpd_conf_migrate.sh .......T. /usr/lib/systemd/system-generators/vsftpd-generator .......T. /usr/lib/systemd/system/vsftpd.service .......T. /usr/lib/systemd/system/vsftpd.target .......T. /usr/lib/systemd/system/vsftpd@.service S.5....T. /usr/sbin/vsftpd missing /usr/share/doc/vsftpd missing d /usr/share/doc/vsftpd/AUDIT missing d /usr/share/doc/vsftpd/BENCHMARKS missing d /usr/share/doc/vsftpd/BUGS (...) يمكن طلب التحقق من جميع الحزم في النظام بالخيار a في وضع التحقق على النحو التالي: [root@academy1 ~]# rpm -Va S.5....T. c /etc/os-release S.5....T. /usr/src/kernels/3.10.0-327.22.2.el7.x86_64/include/config/auto.conf S.5....T. /usr/src/kernels/3.10.0-327.22.2.el7.x86_64/include/config/auto.conf.cmd S.5....T. /usr/src/kernels/3.10.0-327.22.2.el7.x86_64/include/config/tristate.conf S.5....T. /usr/src/kernels/3.10.0-327.22.2.el7.x86_64/include/generated/autoconf.h (...) سرد جميع المفاتيح العمومية المستوردة يمكنك إن أردت سرد جميع المفاتيح العمومية للحزم المثبتة على نظامك، وذلك على النحو التالي: [root@academy1 ~]# rpm -qa gpg-pubkey* gpg-pubkey-2fa658e0-45700c69 gpg-pubkey-37017186-45761324 gpg-pubkey-897da07a-3c979a7f gpg-pubkey-f21541eb-4a5233e7 gpg-pubkey-42193e6b-4624eff2 gpg-pubkey-fd431d51-4ae0493b gpg-pubkey-db42a60e-37ea5438 تصحيح قاعدة بيانات RPM معطوبة يحدُث أن تُصاب قاعدة البيانات الخاصّة بحزم RPM بعُطب مما ينتُج عنه غياب وظائف من أداة rpm. يمكنك في هذه الحالة محاولة بناء قاعدة البيانات من جديد بالأوامر التالية: [root@academy1 ~]# cd /var/lib/rpm [root@academy1 rpm]# rm __db* [root@academy1 rpm]# rpm --rebuilddb [root@academy1 rpm]# /usr/lib/rpm/rpmdb_verify Packages BDB5105 Verification of Packages succeeded. ترجمة - وبتصرف - لمقال 20 Practical Examples of RPM Commands in Linux لصاحبه Ravi Saive.
  15. يتغيّر إطار العمل Laravel باستمرار بإدراج تحديثات جديدة على كلّ إصدار من أجل تحسين تجربة التطوير وإضافة ميزات جديدة تسهّل عمل المطوّر. سنتناول في هذا الدرس، الأول من سلسلة مكونة من ستة أجزاء، التغييرات الحاصلة في هيكلة مشاريع في الإصدار 5.3 من إطار العمل Laravel. سنستخدم لأغراض الشرح مشروعي Laravel؛ الأول يعمل بالإصدار 5.2 والثاني بالإصدار 5.3 (الأخير أثناء كتابة هذا المقال). ننشئ المشروعين بمدير الاعتمادات composer على النحو التالي: بالنسبة للإصدار 5.2: composer create-project --prefer-dist laravel/laravel laravel52 "5.2.*" بالنسبة للإصدار 5.3: composer create-project --prefer-dist laravel/laravel laravel53 "5.3.*" يظهر الإصدار المستهدَف في كل من الأمرين أعلاه (5.2 و5.3 على التوالي). ملف المسارات Routes تعدّ المسارات الخطوة الأولى في معالجة الطلبات التي يتلقاها التطبيق. في الإصدارات السابقة من Laravel (قبل 5.3) كانت المسارات تُضبَط في الملف app/Http/routes.php بغضّ النظر عن طبيعة الطلبات التي تتعامل معها. إن بحثت عن هذا الملف في الإصدار الجديد فلن تجده؛ إذ أنه اختفى مع إنشاء مجلّد جديد باسم routes يوجد في جذر المشروع ويحوي ملفّين: ملف web.php: يحوي هذا الملف المسارات الخاصّة بواجهة الوِب. تعتمد المسارات الموجودة في هذا الملفّ على وسيط برمجي Middleware يحمل نفس الاسم (web) ويوفّر خاصيّات مثل حالة الجلسة Session state (الاحتفاظ ببيانات العميل لاستخدامها في الطلبات الواردة منه أثناء صلاحية الجلسة) والحماية من هجمات تزوير الطلب عبر الموقع Cross-site request forgery, CSRF. ملف api.php: يتوجّه هذا الملف للتطبيقات التي توفّر واجهة تطبيقات برمجية Application programming interface. تعتمد المسارات الموجودة في الملف api.php على وسيط برمجي أخف من السابق (يُسمّى هذا الوسيط api)؛ إذ لا يحتوي على الخاصيّات الموجودة في الوسيط web والتي لا تحتاجها واجهات التطبيقات البرمجية. يمكن القول إن الملف web.php معدّ للتعامل مع الطلبات ذات الحالة Stateful، بينما الملف api.php معدّ للطلبات عديمة الحالة Stateless. من الجيد الانتباه إلى أن جميع المسارات المذكورة في الملف api تُطبَّق عليها لاحقة api. لو نظرنا إلى الملف api.php المبدئي فسنجد أن محتواه على النحو التالي: <?php use Illuminate\Http\Request; Route::get('/user', function (Request $request) { return $request->user(); })->middleware('auth:api'); وبالنسبة للملف web.php: <?php Route::get('/', function () { return view('welcome'); }); إن نفذنا أمر سرد مسارات المشروع فستكون على النحو التالي: لاحظ كيف أن Laravel أضاف تلقائيا اللاحقة api إلى المسار user/ مع تحديد الوسيط البرمجي المستخدم بـ api, auth:api. فلنضف مسارا باسم user/ إلى الملف web.php: Route::get('/user', function () { return "1,2,3,4"; }); ثم نسرُد المسارات المتوفرة: يظهر المسار الجديد في القائمة، ولكن بدون اللاحقة api، ومع تحديد الوسيط بـweb. تغيير الهيكلة المبدئية للمجلدات تخَلّص الإصدار 5.3 من Laravel من مجلدات كانت تأتي مبدئيا مع إطار العمل. يمكن ملاحظة هذا الأمرحيث تظهر، في الصورة أدناه، المجلدات الفرعية من المجلد app لكل من الإصدارين 5.2 (في اليسار) و5.3 (في اليمين). نلاحظ إخفاء المجلدات Listeners، Jobs، Events و Policies. لا يعني الإخفاء المبدئي لهذه الملفات أن الوظائف المرتبطة بها لم تعد موجودة؛ بل يتعلّق الأمر بالتخفيف من المجلدات الموجودة مبدئيا وإنشائها فقط عند الحاجة لذلك. مثلا؛ إن أردنا إنشاء حدث Event باسم ContactHandlerAction فسنفّذ الأمر التالي: php artisan make:event ContactHandlerAction إن نفذت الأمر أعلاه فستجد أن Laravel أنشأ مجلدا فرعيا في app باسم Events وأنشأ داخله الملف ContactHandlerAction.php. نفس الشيء بالنسبة للمستمعات Listeners: php artisan make:listener EmailContactListener --event="ContactHandlerAction" يظهر جليا أن Laravel 5.3 تخفّف من مجلداتٍ مبدئيا؛ إلا أنه ينشئها إن احتاج إليها.