المحتوى عن 'api'.



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

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

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

نوع المُحتوى


التصنيفات

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

التصنيفات

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

التصنيفات

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

التصنيفات

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

التصنيفات

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

التصنيفات

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

التصنيفات

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

التصنيفات

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

أسئلة وأجوبة

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

التصنيفات

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

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

  1. إن كنت مطور ويب أو أحد المهتمين بتطوير الأنظمة المعلوماتية المختلفة في بيئة الويب، فإنك تعلم حجم التنوع الكبير للغات البرمجة المستخدمة في تطوير مواقع الويب وخدماته، ولربما كنت أحد مطوري هذه الخدمات وترغب بأن تكون خدماتك سريعة الانتشار وسهلة الاستخدام من قبل التطبيقات المختلفة ، وكما تعلم فإن كثرة لغات البرمجة المستخدمة في تطوير خدمات الويب تجعل من الصعب التواصل والتكامل بين هذه التطبيقات. إن المصطلح REST وهو اختصار لـ Representational state transfer يعبر عن المعمارية المستخدمة في تطوير خدمات الويب، التي تهدف إلى وضع معايير تضبط إدارة موارد الأنظمة resources وتحدد كيفية عنونتها ونقلها عبر بروتوكل HTTP إلى طيف واسع من التطبيقات المختلفة بغض النظر عن لغات البرمجة التي طورت بها تلك التطبيقات، وتعد معمارية REST أكثر معماريات تصميم الويب هيمنة خلال السنوات الماضية وذلك لسهولة استخدامها والتعامل معها. بعد التعرف على RESTful ستكون قادر على الانطلاق لتصميم الخدمة الخاصة بك دون القلق بشأن من سيستخدم الخدمة الخاصة بك.
  2. اكتشفت مؤخرًا المتعة الكبيرة في استخدام مكتبة [Fractal المقدّمة من PHP League] لإنشاء استجابات للواجهات البرمجية التي تعطي مخرجات من نوع JSON وذلك عند العمل على نماذج Eloquent في إطاري العمل Laravel و Lumen للحصول على المخرجات التي أرغب بها. هذه الحزمة مشابهة إلى حدٍّ كبير لمكتبة ActiveModel Serializer في إطار العمل Rails، إذ إنّها تتيح لك التحكّم الدقيق بالمخرجات التي ترغب في وصولها إلى المستخدم، إضافة إلى أنّها تفصل الشيفرة المسؤولة عن إنشاء كائن JSON عن نموذج Eloquent وهذا أمر في غاية الأهمية. والآن بعد أن أتيح لي تطوير عدد من الواجهات البرمجية لفترة زمنية لا بأس بها، خطر لي أن أتحدّث عن الطرق التي اتبعتها في ربط Fractal مع تطبيقات Laravel و Lumen (تختلف طريقة الربط في إطار Lumen لأنّه لا يدعم ماكروات Response). المُسَلسِلات المخصّصة Costum Serializers بادئ ذي بدء، تدعم Fractal عددًا من المُسَلسِلات Serializers لتكوين بنية خاصة بالاستجابة، وتستخدم بصورة افتراضية المسَلسِل الذي يدعى بـ DataArraySerializer والذي يضيف مفتاح جذر root key يحمل الاسم data إلى الاستجابة، لذا إن كنت لا تمانع من استخدام هذا المُسَلسِل فيمكنك تجاوز هذه الخطوة. أما لو كنت ترغب في استخدام مُسَلسِل آخر فعليك تسجيل الصنف Manager مع حاوية IoC في مزوّد الخدمة AppServiceProvider أو في مزوّد خدمة آخر إن كنت ترى أنّه يناسبك أكثر، ولكن عليك الانتباه إلى أنّ دعم JSON-API غير مكتمل حتى الآن (المقالة الأصلية نشرت سنة 2015. (المترجم)). public function register() { $this->app->bind('League\Fractal\Manager', function($app) { $manager = new \League\Fractal\Manager; // Use the serializer of your choice. $manager->setSerializer(new \App\Http\Serializers\RootSerializer); return $manager; }); } إنشاء المحوّلات Transformers بعد ذلك سنحتاج إلى إنشاء بعض المحوّلات - محوّل لكل نوع من أنواع نماذج Eloquent التي ترغب في تخريجها بواسطة الواجهة البرمجية - وفي مثالنا هذا سأستخدم مدوّنة حيث يضمّ النموذج AppUser عددًا من نماذج AppPost وسنعرض المستخدم مع المقالات الخاصّة به. أنشأت مجلّدً للتحويلات داخل مجلّد Http: app/Http/Transoformers، والآن يمكنني إنشاء محوّلين، الأول لنموذج User والثاني لنموذج Post. محوّل المستخدمين User سيكون سهلًا، إذ أنّه سيُرجع الحقول المطلوبة من قبل واجهتي البرمجية. في هذا المثال، يمتلك النموذج User خاصّية is_admin وهي عبارة عن قيمة منطقية boolean. ويمكنني التعبير عن ذلك في المحوّل بواسطة العبارة (bool) $user->is_admin ولكن اعتبارًا من الإصدار 5.0 وما بعده من Laravel أصبح بالإمكان استخدام خاصّية $casts في النموذج وسيكون Eloquent قادرًا بعدها على معالجة عملية الوصف بالنيابة عنك. يستحسن معالجة مثل هذه الأمور ضمن النموذج لأنّها ستكون متاحة في التطبيق برمّته. <?php namespace App\Http\Transformers; use App\User; use League\Fractal\TransformerAbstract; class UserTransformer extends TransformerAbstract { /** * Turn this item object into a generic array. * * @param \App\User $user * @return array */ public function transform(User $user) { return [ 'id' => $user->id, 'first_name' => $user->first_name, 'last_name' => $user->last_name, 'email' => $user->email, 'is_admin' => $user->is_admin, 'created_at' => $post->created_at->toDateTimeString(), 'updated_at' => $post->updated_at->toDateTimeString() ]; } } بما أنّ خصائص التاريخ في نماذج Eloquent هي نسخ من مكتبة Carbon يمكن إذًا استخدام الدوال المساعدة التي تقدّمها هذه المكتبة لإرجاع التاريخ بالصيغة التي نرغب بها. وفي مثالنا سنستخدم الدالة toDateTimeString() وسيكون التاريخ بالصيغة التالية: Y-m-d H:i:s (السنة - الشهر - اليوم الساعة:الدقائق:الثواني). والآن سنحتاج إلى محوّل لأجل نموذج Post، وسيكون هذا المحوّل مختلفًا عن السابق لأنّنا نرغب في تضمين النموذج User المرتبط بنموذج Post مع هذه الاستجابة. <?php namespace App\Http\Transforers; use App\Post; use League\Fractal\TransformerAbstract; class PostTransformer extends TransformerAbstract { /** * List of resources possible to include * * @var array */ protected $availableIncludes = ['user']; /** * List of resources to automatically include. * * @var array */ protected $defaultIncludes = ['user']; /** * Turn this item object into a generic array. * * @param \App\Post $post * @return array */ public function transform(Post $post) { return [ 'id' => $post->id, 'title' => $post->title, 'content' => $post->content, 'created_at' => $post->created_at->toDateTimeString(), 'updated_at' => $post->updated_at->toDateTimeString(), 'published_at' => $post->published_at->toDateTimeString() ]; } /** * Include user. * * @param \App\Post $post * @return League\Fractal\ItemResource */ public function includeLevels(Post $post) { return $this->item($post->user, new UserTransformer); } } لاحظ أنّه بالإمكان إرجاع $this->collection داخل المحوّل إن كنت ترغب في ربط مجموعة بدل من عنصر واحد. إنشاء الاستجابات في Laravel عادة ما أنشئ مزوّد خدمة Fractal FractalServiceProvider والذي يضمّ التابع register() الذي قمت بوصفه سابقًا، ثم أسجّل بعض الماكروات في التابع boot() كما تلاحظ هنا. سيؤدي ذلك إلى إضافة تابعين إضافيين إلى معمل (factory) Response في Laravel وسيسهّل إرجاع الاستجابات على هيئة عناصر أو مجموعات. لاحظ أنّي حدّدت نوع (type hint) المحوّل ليكون نسخة من الصنف TransformerAbstract وذلك لأنّي أنشئ دائمًا المحوّلات من الاستجابات بدلًا من صيغة الدالة المغلقة closure لأنّي أرى بأنّ هذه الطريقة ستحافظ على ترتيب الشيفرات التي أكتبها. لكن إن كنت تفضل استخدام الدوال المغلقة بدلًا من المحوّلات فيمكنك بكل ساطة أن لا تحدد نوع المحوّل ضمن المعاملات. public function boot() { $fractal = $this->app->make('League\Fractal\Manager'); response()->macro('item', function ($item, \League\Fractal\TransformerAbstract $transformer, $status = 200, array $headers = []) use ($fractal) { $resource = new \League\Fractal\Resource\Item($item, $transformer); return response()->json( $fractal->createData($resource)->toArray(), $status, $headers ); }); response()->macro('collection', function ($collection, \League\Fractal\TransformerAbstract $transformer, $status = 200, array $headers = []) use ($fractal) { $resource = new \League\Fractal\Resource\Collection($collection, $transformer); return response()->json( $fractal->createData($resource)->toArray(), $status, $headers ); }); } والآن أصبح من السهل إنشاء استجابة واحدة (لمستخدم واحد مثلًا) ومجموعة من الاستجابات (لجميع مقالات المدوّنة مثلًا). /** * GET /users/1 * * @param int $userId * @return \Illuminate\Http\Response */ public function showUser($userId) { $user = \App\User::findOrFail($userId); return response()->item($user, new \App\Http\Transformers\UserTransformer); } /** * GET /posts * * @return \Illuminate\Http\Response */ public function showPosts() { $posts = \App\Post::with('user')->get(); return response()->collection($posts, new \App\Http\Transformers\PostTransformer); } إن كنت ترغب في استخدام حالة استجابة HTTP أخرى مثل 201 (تم الإنشاء) أو 204 (لا يوجد محتوى) يمكنك وبكل سهولة تمرير رمز الحالة كمعامل ثالث إلى الماكرو. إنشاء الاستجابات في Lumen يتطلّب إنشاء الاستجابات في Lumen اتباع أسلوب مختلف قليلًا؛ ذلك لأنّ Lumen لا يدعم الماكروات في معمل Response؛ لذا سأضيف هذه التوابع إلى المتحكّم الرئيسي في الواجهة البرمجية. /** * Create the response for an item. * * @param mixed $item * @param \League\Fractal\TransformerAbstract $transformer * @param int $status * @param array $headers * @return Response */ protected function buildItemResponse($item, \League\Fractal\TransformerAbstract $transformer, $status = 200, array $headers = []) { $resource = new \League\Fractal\Resource\Item($item, $transformer); return $this->buildResourceResponse($resource, $status, $headers); } /** * Create the response for a collection. * * @param mixed $collection * @param \League\Fractal\TransformerAbstract $transformer * @param int $status * @param array $headers * @return Response */ protected function buildCollectionResponse($collection, \League\Fractal\TransformerAbstract $transformer, $status = 200, array $headers = []) { $resource = new \League\Fractal\Resource\Collection($collection, $transformer); return $this->buildResourceResponse($resource, $status, $headers); } /** * Create the response for a resource. * * @param \League\Fractal\Resource\ResourceAbstract $resource * @param int $status * @param array $headers * @return Response */ protected function buildResourceResponse(\League\Fractal\Resource\ResourceAbstract $resource, $status = 200, array $headers = []) { $fractal = app('League\Fractal\Manager'); return response()->json( $fractal->createData($resource)->toArray(), $status, $headers ); } يمكنك الآن استدعاء أي تابع من هذه التوابع في أي متحكّم موروث من هذا المتحكّم لإنشاء نفس الاستجابات التي أنشأناها مع Laravel. return $this->buildItemResponse($user, new \App\Http\Transformers\UserTransformer); return $this->buildCollectionResponse($posts, new \App\Http\Transformers\PostTransformer); ترجمة - وبتصرّف - للمقال Using Fractal with Laravel and Lumen لصاحبه Dwight Conrad Watson.
  3. بدأ تطوير لغة البرمجة Go بتجربة من مهندسين يعملون في Google لتلافي بعض التعقيدات الموجودة في لغات برمجة أخرى مع الاستفادة من نقاط قوّتها. تُطوَّر لغة Go باستمرار بمشاركة مجتمع مفتوح المصدر يزداد باضطّراد. تهدف لغة البرمجة Go إلى أن تكون سهلة، إلا أن اصطلاحات كتابة الشفرة البرمجية في Go قد تكون صعبة الاستيعاب. سأريكم في هذا الدرس كيف أبدأ جميع مشاريعي البرمجية عندما أستخدم Go، وكيفية استخدام التعابير التي توفّرها هذه اللغة. سننشئ خدمة سند خلفي Backend لتطبيق وِب. إعداد بيئة العمل الخطوة الأولى هي - بالطبع - تثبيتُ Go. يمكن تثبيت Go من المستودعات الرسمية على توزيعات لينكس؛ مثلا بالنسبة لأوبونتو: sudo apt install golang-go إصدارات Go الموجودة في المستودعات الرسمية تكون في العادة أقدم قليلا من تلك الموجودة على الموقع الرسمي، إلا أنها تؤدي الغرض؛ علاوة على سهولة التثبيت. يمكنك تثبيت إصدارات أحدث على أوبونتو (هنا) وCentos (هنا). بالنسبة لمستخدمي نظام Mac OS فيمكنهم تثبيت اللغة عن طريق Homebrew: brew install go يحوي الموقع الرسمي كذلك الملفات التنفيذية لتثبيت اللغة على أغلب أنظمة التشغيل، بما في ذلك ويندوز. تأكّد من تثبيت Go بتنفيذ الأمر التالي: go version مثال لنتيجة اﻷمر أعلاه (على توزيعة أوبونتو): go version go1.6.2 linux/amd64 – روابط للتثبيت على وندوز وإعداد المسارات – توجد الكثير من محرّرات النصوص والإضافات المتاحة لكتابة شفرات Go. أفضّل شخصيّا محرّر الشفرات Sublime Text وإضافة GoSublime؛ إلا أن طريقة كتابة Go تتيح استخدام محرّرات نصوص عاديّة بسهولة خصوصا للمشاريع الصغيرة. أعمل مع محترفين يقضون كامل اليوم في البرمجة بلغة Go باستخدام محرّر النصوص Vim، دون أي إضافة لإبراز صيغة الشفرات البرمجية Syntax highlighting. بالتأكيد لن تحتاج لأكثر من محرّر نصوص بسيط للبدء في تعلّم Go. مشروع جديد إن لم تكن أنشأت مجلّدا للعمل أثناء تثبيت Go وإعداده فالوقت مناسب لذلك. تتوقّع أدوات Go أن توجد جميع الشفرات البرمجية على المسار GOPATH/src$، وبالتالي سيكون عملنا دائما في هذا المجلّد. يمكن لمجموعة أدوات Go كذلك أن تتخاطب مع مشاريع مُضيَّفة على مواقع مثل GitHub وBitbucket إن أُعدّت لذلك. سننشئ لأغراض هذا الدرس مستودعا جديدا فارغا على GitHub ولنسمّه “hello” (أو أي اسم يناسبك). ننشئ مجلّدا ضمن مجلّد GOPATH لاستقبال ملفات المستودع (أبدل your-username باسم المستخدم الخاصّ بك على GitHub): mkdir -p $GOPATH/src/github.com/your-username cd $GOPATH/src/github.com/your-username ننسخ المستودع ضمن المجلّد الذي أنشأناه أعلاه: git clone git@github.com:your-username/hello cd hello سننشئ الآن ملفا باسم main.go ليحوي برنامجا قصيرا بلغة Go: package main func main() { println("hello!") } نفّذ الأمر go build لتصريف جميع محتويات المجلّد الحالي. سينتُج ملف تنفيذي بنفس الاسم؛ يمكنك بعدها طلب تشغيله بذكر اسمه على النحو التالي: go build ./hello النتيجة: hello! ما زلت رغم سنوات من التطوير بلغة Go أبدأ مشاريعي بنفس الطريقة: مستودع Git فارغ، ملف main.go وبضعة أوامر. يصبح أي تطبيق يتبع الطرق المتعارف عليها لتنظيم شفرة Go قابلا للتثبيت بسهولة بالأمر go get. إن أودعت على سبيل المثال الملف أعلاه ودفعته إلى مستودع Git فإن أي شخص لديه بيئة عمل Go يمكنه تنفيذ الخطوتين التاليتين لتشغيل البرنامج: go get github.com/your-username/hello $GOPATH/bin/hello إنشاء خادوم وب فلنجعل برنامجنا البسيط السابق خادوم وب: package main import "net/http" func main() { http.HandleFunc("/", hello) http.ListenAndServe(":8080", nil) } func hello(w http.ResponseWriter, r *http.Request) { w.Write([]byte("hello!")) } هناك بضعة سطور تحتاج للشرح. نحتاج أولا لاستيراد الحزمة net/httpمن المكتبة المعيارية لـGo: import "net/http" ثم نثبّت دالة معالجة Handler function في المسار الجذر لخادوم الوِب. تتعامل http.HandleFunc مع الموجّه المبدئي لطلبات Http في Go، وهو ServeMux. http.HandleFunc("/", hello) الدالة hello هي من النوع http.HandlerFunc الذي يسمح باستخدام دوال عاديّة على أنها دوال معالجة لطلبات HTTP. للدوال من النوع http.HandlerFunc توقيع Signature خاص (توقيع الدالة هو المعطيات المُمرَّرة لها وأنواع البيانات التي تُرجعها هذه الدالة) ويمكن تمريرها في معطى إلى الدالة HandleFunc التي تسجّل الدالة المُمرَّرة في المُعطى لدى الموجِّه ServeMux، وبالتالي يُنشئ خادوم الوِب، في كلّ مرة يصله فيها طلب جديد يطابق المسار الجذر، يُنشئ نسخة جديدة من الدالة hello. تستقبل الدالة hello متغيّرا من النوع http.ResponseWriter الذي تستخدمه الدالة المُعالِجة لإنشاء إجابة HTTP وبالتالي إنشاء ردّ على طلب العميل عن طريق التابع Write الذي يوفّره النوع http.ResponseWriter. بما أن التابع http.ResponseWriter.Write يأخذ معطى عامًّا من النوع []byte أو byte-slice، فنحوّل السلسة النصيّة hello إلى النوع المناسب: func hello(w http.ResponseWriter, r *http.Request) { w.Write([]byte("hello!")) } في الأخير نشغّل خادوم وب على المنفذ 8080 عبر الدالة http.ListenAndServe التي تستقبل معطيَين، الأول هو المنفَذ Port والثاني دالة معالجة. إذا كانت قيمة المعطى الثاني هي nil فهذا يعني أننا نريد استخدام الموجّه المبدئي DefaultServeMux. هذا الاستدعاء متزامن Synchronous، أو معترِض Blocking، يبقي البرنامج قيد التشغيل إلى أن يُقطَع الاستدعاء. صرّف وشغّل البرنامج بنفس الطريقة السابقة: go build ./hello افتح سطر أوامر - طرفيّة - آخر وأرسل طلب HTTP إلى المنفذ 8080: curl http://localhost:8080 النتيجة: hello! الأمر بسيط. ليست هناك حاجة لتثبيت إطار عمل خارجي، أو تنزيل اعتماديات Dependencies أو إنشاء هياكل مشاريع. الملف التنفيذي نفسه هو شفرة أصيلة Native code بدون اعتمادات تشغيلية. علاوة على ذلك، المكتبة المعيارية لخادوم الوِب موجهة لبيئة الإنتاج مع دفاعات ضدّ الهجمات الإلكترونية الشائعة. يمكن لهذه الشفرة الإجابة على الطلبات عبر الشبكة مباشرة ودون وسائط. إضافة مسارات جديدة يمكننا فعل أمور أكثر أهمية من مجرّد قول مرحبا (hello). فليكن المُدخَل اسم مدينة نستخدمه لاستدعاء واجهة تطبيقات برمجيّة API لأحوال الطقس ونعيد توجيه الإجابة - درجة الحرارة - في الرد على الطلب. توفّر خدمة OpenWeatherMap واجهة تطبيقات برمجيّة مجانيّة وسهلة الاستخدام للحصول على توقّعات مناخية. سجّل في الموقع للحصول على مفتاح API. يمكن الاستعلام من OpenWeatherMap حسب المدن. تُرجع واجهة التطبيقات البرمجية إجابة على النحو التالي (عدّلنا قليلا على النتيجة): { "name": "Tokyo", "coord": { "lon": 139.69, "lat": 35.69 }, "weather": [ { "id": 803, "main": "Clouds", "description": "broken clouds", "icon": "04n" } ], "main": { "temp": 296.69, "pressure": 1014, "humidity": 83, "temp_min": 295.37, "temp_max": 298.15 } } المتغيّرات في Go ذات أنواع ثابتة Statical type، بمعنى أنه ينبغي التصريح بنوع البيانات التي تخزّنها المتغيّرات قبل استخدامها. لذا سيتوجّب علينا إنشاء بنية بيانات لمطابقة صيغة رد الواجهة البرمجية. لا نحتاج لحفظ جميبع المعلومات، بل يكفي أن نحتفظ بالبيانات التي نهتم بشأنها. سنكتفي الآن باسم المدينة ودرجة الحرارة المتوقّعة التي تأتي بوحدة الكيلفن Kelvin. سنعرّف بنية لتمثيل البيانات التي نحتاجها من خدمة التوقعات المناخية. type weatherData struct { Name string `json:"name"` Main struct { Kelvin float64 `json:"temp"` } `json:"main"` } تعرّف الكلمة المفتاحية type بنية بيانات جديدة نسمّيها weatherData ونصرّح بكونها من النوع struct. يحوي كلّ حقل في المتغيّرات من نوع struct اسما (مثلا Name أو Main)، نوع بيانات (string أو struct آخر مجهول الاسم) وما يُعرَف بالوسم Tag. تشبه الوسوم في Go البيانات الوصفية Metadata، وتمكّننا من استخدام الحزمة encoding/json لإعادة صفّ الإجابة التي تقدّمها خدمة OpenWeatherMap وحفظها في بنية البيانات التي أعددناها. يتطلّب الأمر كتابة شفرة برمجية أكثر ممّا عليه الحال في لغات برمجيّة ذات أنواع ديناميكية للبيانات (بمعنى أنه يمكن استخدام متغيّر فور احتياجنا له دون الحاجة للتصريح بنوع البيانات) مثل روبي وبايثون، إلا أنه يمنحنا خاصيّة الأمان في نوع البيانات. عرّفنا بنية البيانات، نحتاج الآن لطريقة تمكّننا من ملْء هذه البنية بالبيانات القادمة من واجهة التطبيقات البرمجية؛ سنكتُب دالة لهذا الغرض. func query(city string) (weatherData, error) { resp, err := http.Get("http://api.openweathermap.org/data/2.5/weather?APPID=YOUR_API_KEY&q=" + city) if err != nil { return weatherData{}, err } defer resp.Body.Close() var d weatherData if err := json.NewDecoder(resp.Body).Decode(&d); err != nil { return weatherData{}, err } return d, nil } تأخذ الدالة سلسلة محارف تمثّل المدينة وتُرجِع متغيّرا من بنية بيانات weatherData وخطأ. هذه هي الطريقة الأساسية للتعامل مع الأخطاء في Go. تغلّف الدوال سلوكا معيَّنا، ويمكن أن يخفق هذا السلوك. بالنسبة لمثالنا، يمكن أن يخفق طلب GET الذي نرسله لـOpenWeatherMap لأسباب عدّة، وقد تكون البيانات المُرجَعة غير تلك التي ننتظرها. نُرجِع في كلتا الحالتين خطأ غير فارغ Non-nil للعميل الذي يُنتظَر منه أن يتعامل مع هذا الخطأ بما يتناسب مع السياق الذي أرسل فيه الطلب. إن نجح الطلب http.Get نؤجّل طلبا لغلق متن الإجابة لننفّذه بعد الخروج من نطاق Scope الدالة (أي بعد الرجوع من دالة طلب HTTP)، وهي طريقة أنيقة لإدارة الموارد. في أثناء ذلك نحجز بنية weatherData ونستخدم json.Decoder لقراءة بيانات الإجابة وإدخالها مباشرة في بنيتنا. عندما تنجح إعادة صياغة بيانات الإجابة نعيد المتغيّر weatherData إلى المُستدعي مع خطأ فارغ للدلالة على نجاح العملية. ننتقل الآن إلى ربط تلك الدالة بالدالة المعالجة للطلب: http.HandleFunc("/weather/", func(w http.ResponseWriter, r *http.Request) { city := strings.SplitN(r.URL.Path, "/", 3)[2] data, err := query(city) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json; charset=utf-8") json.NewEncoder(w).Encode(data) }) نعرّف دالة معالجة على السطر In-line بدلا من تعريفها منفصلة. نستخدم الدالة strings.SplitN لأخذ كل ما يوجد بعد /weather/ في المسار والتعامل معه على أنه اسم مدينة. ننفّذ الطلب وإن صادفتنا أخطاء نعلم العميل بها باستخدام الدالة المساعدة http.Error، ونوقف تنفيذ الدالة للدلالة على اكتمال طلب HTTP. إن لم يوجد خطأ نخبر العميل بأننا بصدد إرسال بيانات JSON إليه ونستخدم الدالة json.NewEncode لترميز محتوى weatherData بصيغة JSON مباشرة. الشفرة لحدّ الساعة أنيقة، تعتمد أسلوبا إجرائيا Procedural ويسهل فهمها. لا مجال للخطأ في تفسيرها ولا يمكنها تجاوز الأخطاء الشائعة. إن نقلنا الدالة المعالجة لـ "hello, world" إلى المسار hello/ واستوردنا الحزم المطلوبة فسنحصُل على البرنامج المُكتمل التالي: package main import ( "encoding/json" "net/http" "strings" ) func main() { http.HandleFunc("/hello", hello) http.HandleFunc("/weather/", func(w http.ResponseWriter, r *http.Request) { city := strings.SplitN(r.URL.Path, "/", 3)[2] data, err := query(city) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json; charset=utf-8") json.NewEncoder(w).Encode(data) }) http.ListenAndServe(":8080", nil) } func hello(w http.ResponseWriter, r *http.Request) { w.Write([]byte("hello!")) } func query(city string) (weatherData, error) { resp, err := http.Get("http://api.openweathermap.org/data/2.5/weather?APPID=YOUR_API_KEY&q=" + city) if err != nil { return weatherData{}, err } defer resp.Body.Close() var d weatherData if err := json.NewDecoder(resp.Body).Decode(&d); err != nil { return weatherData{}, err } return d, nil } type weatherData struct { Name string `json:"name"` Main struct { Kelvin float64 `json:"temp"` } `json:"main"` } نصرّف البرنامج وننفّذه بنفس الطريقة التي شرحناها أعلاه: go build ./hello نفتح طرفيّة أخرى ونطلب المسار http://localhost:8080/weather/tokyo (الحرارة بمقياس كلفن): curl http://localhost:8080/weather/tokyo النتيجة: {"name":"Tokyo","main":{"temp":295.9}} الاستعلام من واجهات برمجية عدّة ربما من الممكن الحصول على درجات حرارة أكثر دقّة إن استعلمنا من خدمات طقس عدّة وحسبنا المتوسّط بينها. تتطلّب أغلب الواجهات البرمجية لخدمات الطقس التسجيل. سنضيف خدمة Weather Underground، لذا سجّل في هذه الخدمة واعثر على مفاتيح الاستيثاق الضرورية لاستخدام واجهة التطبيقات البرمجية. بما أننا نريد أن نحصُل على نفس السلوك من جميع الخدمات فسيكون من المجدي كتابة هذا السلوك في واجهة. type weatherProvider interface { temperature(city string) (float64, error) // in Kelvin, naturally } يمكننا الآن تحويل دالة الاستعلام من openWeatherMap السابقة إلى نوع بيانات يوافق الواجهة weatherProvider. بما أننا لا نحتاج لحفظ أي حالة لإجراء طلب HTTP GET فسنستخدم بنية struct فارغة، وسنضيف سطرا قصيرا في دالة الاستعلام الجديدة لتسجيل ما يحدُث عند الاتصال بالخدمات لمراجعته في ما بعد: type openWeatherMap struct{} func (w openWeatherMap) temperature(city string) (float64, error) { resp, err := http.Get("http://api.openweathermap.org/data/2.5/weather?APPID=YOUR_API_KEY&q=" + city) if err != nil { return 0, err } defer resp.Body.Close() var d struct { Main struct { Kelvin float64 `json:"temp"` } `json:"main"` } if err := json.NewDecoder(resp.Body).Decode(&d); err != nil { return 0, err } log.Printf("openWeatherMap: %s: %.2f", city, d.Main.Kelvin) return d.Main.Kelvin, nil } لا نريد سوى استخراج درجة الحرارة (بالكلفن) من الإجابة، لذا يمكننا تعريف بنية struct على السطر Inline. في ما عدا ذلك فإن الشفرة البرمجية مشابهة لدالة الاستعلام السابقة، ولكنّها معرَّفة على صيغة تابع Method لبنية openWeatherMap. تتيح لنا هذه الطريقة استخدام عيّنة Instance من openWeatherMap مكان الواجهة weatherProvider. سنفعل نفس الشيء بالنسبة لخدمة Weather Underground. الفرق الوحيد مع الخدمة السابقة هو أننا سنخزّن مفتاح الواجهة البرمجية في بنية struct ثم نستخدمه في التابع. يجدر ملاحظة أن Weather Underground لا تعالج أسماء المدن المتطابقة بنفس جودة تعامل Open WeatherMap، وهو ما ينبغي الانتباه إليه في التطبيقات الفعلية. لن نعالج هذا الأمر في مثالنا البسيط هذا. type weatherUnderground struct { apiKey string } func (w weatherUnderground) temperature(city string) (float64, error) { resp, err := http.Get("http://api.wunderground.com/api/" + w.apiKey + "/conditions/q/" + city + ".json") if err != nil { return 0, err } defer resp.Body.Close() var d struct { Observation struct { Celsius float64 `json:"temp_c"` } `json:"current_observation"` } if err := json.NewDecoder(resp.Body).Decode(&d); err != nil { return 0, err } kelvin := d.Observation.Celsius + 273.15 log.Printf("weatherUnderground: %s: %.2f", city, kelvin) return kelvin, nil } لدينا الآن مزوّدا خدمة طقس. فلنكتب دالّة تستعلم من الاثنين وتعيد متوسّط درجة الحرارة. سنكفّ - حفاظا على بساطة المثال - عن الاستعلام إذا واجهتنا مشكلة في الحصول على بيانات من الخدمتين. func temperature(city string, providers ...weatherProvider) (float64, error) { sum := 0.0 for _, provider := range providers { k, err := provider.temperature(city) if err != nil { return 0, err } sum += k } return sum / float64(len(providers)), nil } لاحظ أن تعريف الدالة قريب جدّا من تعريف التابع temperature المُعرَّف في الواجهة weatherProvider. إن جمعنا الواجهات weatherProvider في نوع بيانات ثم عرّفنا تابعا باسم temperature على هذا النوع فسيمكننا إنشاء نوع جديد يجمع الواجهات weatherProvider. type multiWeatherProvider []weatherProvider func (w multiWeatherProvider) temperature(city string) (float64, error) { sum := 0.0 for _, provider := range w { k, err := provider.temperature(city) if err != nil { return 0, err } sum += k } return sum / float64(len(w)), nil } رائع! سنتمكّن من تمرير multiWeatherProvider إلى أي دالة تقبل weatherProvider. نربُط الآن خادوم HTTP بدالة temperature للحصول على درجات الحرارة عند طلب مسار به اسم مدينة: func main() { mw := multiWeatherProvider{ openWeatherMap{}, weatherUnderground{apiKey: "your-key-here"}, } http.HandleFunc("/weather/", func(w http.ResponseWriter, r *http.Request) { begin := time.Now() city := strings.SplitN(r.URL.Path, "/", 3)[2] temp, err := mw.temperature(city) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json; charset=utf-8") json.NewEncoder(w).Encode(map[string]interface{}{ "city": city, "temp": temp, "took": time.Since(begin).String(), }) }) http.ListenAndServe(":8080", nil) } صرّف البرنامج، شغّله واطلب رابط خادوم الوب كما فعلنا سابقا. ستجد - علاوة على الإجابة بصيغة JSON في نافذة الطلب - مُخرجات قادمة من تسجيلات الخادوم التي أضفناها أعلاه في النافذة التي شغّلت منها البرنامج. ./hello 2015/01/01 13:14:15 openWeatherMap: tokyo: 295.46 2015/01/01 13:14:16 weatherUnderground: tokyo: 273.15 $ curl http://localhost:8080/weather/tokyo {"city":"tokyo","temp":284.30499999999995,"took":"821.665230ms"} جعل الاستعلامات تعمل بالتوازي نكتفي لحدّ الساعة بالاستعلام من الواجهات البرمجية بالتتالي، الواحدة تلو الأخرى. لا يوجد ما يمنعنا من الاستعلام من الواجهتيْن البرمجيّتين في نفس الوقت، وهو ما سياسهم في تقليل الوقت اللازم للإجابة. نستفيد من إمكانات Go في التشغيل المتزامن عبر وحدات Go الفرعية goroutines والقنوات Channels. سنضع كل استعلام في وحدة فرعية خاصّة به ثم نشغّلها بالتوازي. نجمع الإجابات بعد ذلك في قناة واحدة ثم نحسب المعدّلات عندما تكتمل جميع الاستعلامات. func (w multiWeatherProvider) temperature(city string) (float64, error) { // ننشئ قناتيْن، واحدة لدرجات الحرارة والأخرى للأخطاء // يُضيف كل مزوّد خدمة قيمة إلى إحدى القناتيْن فقط temps := make(chan float64, len(w)) errs := make(chan error, len(w)) // نطلق بالنسبة لكلّ مزوّد خدمة وحدة فرعية جديدة بدالة مجهولة الاسم. تستدعي الدالة مجهولة الاسم التابع temperature ثم تعيد توجيه النتيجة المتحصًّل عليها. for _, provider := range w { go func(p weatherProvider) { k, err := p.temperature(city) if err != nil { errs <- err return } temps <- k }(provider) } sum := 0.0 // نجمع درجات الحرارة - أو الأخطاء في حالة وجودها - من كل خِدمة for i := 0; i < len(w); i++ { select { case temp := <-temps: sum += temp case err := <-errs: return 0, err } } // نُرجع الحرارة كما في السابق return sum / float64(len(w)), nil } يساوي الوقت اللازم الآن لتنفيذ جميع الاستعلامات المدة الزمنية اللازمة للحصول على إجابة من أبطأ خدمة طقس؛ بدلا من مجموع المدد الزمنية لجميع الاستعلامات كما كان سابقا. كل ما احتجنا له هو تعديل سلوك multiWeatherProvider الذي ما زال مع ذلك يُرضي حاجات واجهة weatherProvider البسيطة وغير المتوازية. السهولة انتقلنا ببضع خطوات وبالاقتصار فقط على المكتبة المعيارية لـGo من مثال “hello world” إلى خادوم سند خلفي Backend server يحترم مبادئ REST. يمكن نشر الشفرة التي كتبناها على أي معمارية خواديم تقريبا. الملف التنفيذي الناتج سريع ويتضمّن جميع ما يحتاجه للعمل؛ والأهم من ذلك، الشفرة البرمجيّة واضحة لقراءتها وفهمها. كما تمكن صيانتها وتمديدها بسهولة حسب الحاجة. أنا مقتنع أن كلّ هذه الميزات هي نتيجة لتفاني Go في البساطة. ترجمة - بتصرّف - للمقال How I start Go لصاحبه Peter Bourgon.
  4. هنالك الكثير من الواجهات البرمجية المبنية على JSON التي توفر وصولًا عبر دوال من جهة الخادوم فقط، فلو أردنا استخدام عميل JavaScript مع تلك الواجهات البرمجية الخارجية، فسنحتاج إلى ضبط خادومنا للوصول إلى تلك الواجهات البرمجية الخارجية ثم تخديم تلك البيانات عبر Ajax أو عبر نقطة نهاية (endpoint) خاصة بك. ولحسن الحظ، فعل ذلك سهلٌ جدًا في ووردبريس. سأريك في هذا الدرس كيفية الحصول على أجور الشحن عبر Easy Post API باستخدام نقطة نهاية في ووردبريس. لتبسيط هذا الدرس (وجعله قصيرًا) فسنرسل ستة معاملات (وهو عدد كاف لواجهة Easy Post البرمجية لإعادة أجور الشحن): عرض الطرد ارتفاع الطرد طول الطرد وزن الطرد كود Zip للمرسل كود Zip للمستقبل ضبط نقطة نهاية JSON أوّل خطوة هي ضبط المسار الذي ستُخدَّم منه بيانات JSON، وفي هذه الحالة هو: /wp-json/easy-post/v1/rates/ الشيفرة: /** * Sets up a JSON endpoint at /wp-json/easy-post/v1/rates/ */ function easy_post_api_init() { $namespace = 'easy-post/v1'; register_rest_route( $namespace, '/rates/', array( 'methods' => 'GET', 'callback' => 'easy_post_return_rates', ) ); } add_action( 'rest_api_init', 'easy_post_api_init' ); عرض البيانات تتوقع نقطة النهاية إرسال ست قيم من البيانات كطلبية، وستستخدمها للحصول على البيانات من الواجهة البرمجية الخارجية، فلو لم تكن تلك القيم موجودةً فستستعمل القيم الافتراضية، وسنحرص أن تكون تلك القيم رقميةً فقط. مثال عن رابط URL مع قيم لكل المعاملات: /wp-json/easy-post/v1/rates/?zip=78701&width=10&length=10&height=10&weight=10&shipping_zip=78751 الشيفرة: /** * عرض مخرجات Easy Post عبر النهاية الطرفية * * نستخدم القيم المبدئية في حال عدم ذكر بيانات في الطلب، إلا أن إعادة خطأ ربما تكون فكرة أفضل. * Valid URL: /wp-json/easy-post/v1/rates/?zip=78701&width=10&length=10&height=10&weight=10&shipping_zip=78751 */ function easy_post_return_rates( WP_REST_Request $request ) { // الإعدادات المبدئية للشحن $defaults = array( 'zip' => 78701, 'width' => 10, 'length' => 10, 'height' => 10, 'weight' => 16, 'shipping_zip' => 94105 ); // الحصول على المعطيات من الطلب $params = $request->get_query_params(); // التأكد من توفر جميع المعطيات $params = array_replace( $defaults, $params ); // تنظيف المفاتيح $params = array_map( "absint", $params ); // إنشاء قيم مفاتيح فريدة لتحديد قيم البيانات المؤقتة transient أو العثور عليها $key = 'easy_post_' . implode( "_", $params ); /** * نحتفظ بالإجابة في قيمة مؤقتة transient ضمن قاعدة بيانات ووردبريس * لتجنب إرسال الكثير من الطلبات في وقت قصير * * تنتهي صلاحية هذه البيانات بعد عشر دقائق */ if ( false === ( $data = get_transient( $key ) ) ) { $data = easy_post_make_request( $params ); $response = new WP_REST_Response( $data ); // التخبئة لمدة عشر دقائق set_transient( $key, $response, 60 * 10 ); } else { // إرجاع القيمة المخبّأة return get_transient( $key ); } return $response; } الحصول على البيانات الدالة easy_post_make_request()‎ هي الدالة التي تطلب البيانات من الواجهة البرمجية الخارجية، والغرض من أغلبية الشيفرات التي هنا هي تجهيز الطلبية ضمن مصفوفة يمكن لواجهة Easy Post البرمجية أن تعالجها: /** *العثور على البيانات من الواجهة البرمجية لـ Easy Post باستخدام الدالة wp_remote_post */ function easy_post_make_request( $params ) { $to_address = array( 'zip' => $params['shipping_zip'], 'country' => 'US' ); $from_address = array( 'zip' => $params['zip'], 'country' => 'US' ); $parcel = array( 'length' => $params['length'], 'width' => $params['width'], 'height' => $params['height'], 'weight' => $params['weight'] ); $request = array( 'shipment' => array( 'to_address' => $to_address, 'from_address' => $from_address, 'parcel' => $parcel ) ); $args = array( 'method' => 'POST', 'blocking' => true, 'headers' => array( 'Content-Type' => 'application/json' ), 'timeout' => 15, 'sslverify' => false, 'body' => json_encode( $request ) ); // ضع مفتاح API الخاص بك مكان EASYPOST_TEST_API_KEY $api_url = 'https://' . EASYPOST_TEST_API_KEY . ':@api.easypost.com/v2/shipments'; $response = wp_remote_post( $api_url, $args ); if ( ! is_wp_error( $response ) ) { $response = json_decode( $response['body'] ); } return $response; } استخدام البيانات عندما نحاول الوصول إلى نقطة النهاية التي أنشأناها، فإن بيانات JSON التي جلبناها من Easy Post ستظهر لنا: يمكنك الآن الحصول على أجور الشحن ديناميكيًا في موقعك باستخدام نقطة النهاية التي أنشأناها في هذا الدرس. يمكنك الاطلاع على كامل الشيفرة هنا. ترجمة –وبتصرّف– للمقال How to Set JSON Endpoints in WordPress to Access an External API لصاحبه Devin Price حقوق الصورة البارزة محفوظة لـ Freepik
  5. قدّمنا في الدرس السابق جافاسكريبت وذكرنا تموضعها في الوِب بالنسبة لـHTML وCSS، كما تحدّثنا عن واجهات برمجة التطبيقات الخاصّة بالمتصفّح وتلك القادمة من طرف ثالث. سنبدأ في هذا الجزء من الدرس النظر في بعض التعليمات البرمجية، وأثناء ذلك، يمكنك استكشاف ما يحدث فعليًا عند تشغيل شفرة جافاسكريبت في صفحتك. ما الذي تفعله جافاسكريبت على صفحتك؟ لنلخص بإيجاز قصة ما يحدث عند تنزيل صفحة وِب في المتصفح. عندما يُنزّل المتصفّح الشفرة التي كتبتها، والمكوّنة من وسوم HTML، أنماط CSS وتعليمات جافاسكريبت فإنه يشغّلها داخل بيئة تنفيذ (علامة تبويب Tab في المتصفح). تشبه هذه العملية المصنع الذي يأخذ المواد الخام (الشفرة) ويُخرج المنتح (صفحة وِب). يُنفّذ معالج جافاسكريبت JavaScript engine الموجود في المتصفّح تعليمات جافاسكريبت بعد أن تُجمَّع HTML وCSS وتوضعان في صفحة وِب. يضمن انتظار التجميع أنّ بنية الصفحة وتنسيقها موجودان بالفعل عند بدء تشغيل جافاسكريبت، أي أنه عند تشغيل جافاسكريبت تكون جميع تعليمات HTML وCSS موجودة ونفّذها المتصفّح. يعدّ هذا الأمر جيّدًا، إذ أن استخدام جافاسكريبت الشائع هو تعديل HTML و CSS ديناميكًيا لتحديث واجهة المستخدم، من خلال واجهة برمجة التطبيقات DOM التي تحدّثنا عنها في الدرس السابق؛ فإن نُزِّلت تعليمات جافاسكريبت وحاول المتصفّح تشغيلها قبل أن يكون هناك HTML و CSS فستحدث أخطاء. أمان المتصفح تعدّ كل علامة تبويب Tab في المتصفح دلوًا منفصلًا لتشغيل الشفرة (تسمى هذه الدلاء “بيئات التنفيذ” Execution environments من الناحية التقنية). يعني هذا أنه في معظم الحالات تُشغَّل الشفرة في كل علامة تبويب منفصلة عن الشفرات في بقية التبويبات، ولا يمكن للتعليمات البرمجية في علامة تبويب واحدة أن تؤثر مباشرة على التعليمات البرمجية في علامة تبويب أخرى - أو على موقع وِب آخر. هذا إجراء أمني جيد. لو لم يكن هذا هو الحال، لكان بإمكان القراصنة كتابة صفحات وِب مهمّتها سرقة المعلومات من المواقع الأخرى التي يزورها المتصفّح، وغيرها من هذه الأشياء السيئة. ملاحظة: هناك طرق لإرسال التعليمات البرمجية والبيانات بين مواقع وِب / علامات تبويب مختلفة بطريقة آمنة، ولكن هذه تقنيات متقدمة لن نغطيها في هذا الدرس. جافاسكريبت قيد التشغيل عندما يواجه المتصفح كتلة تعليمات برمجية من جافاسكريبت، فإنه يعمل بالترتيب، من أعلى إلى أسفل. وهذا يعني أنك تحتاج إلى توخي الحذر في الترتيب الذي تضعه فيه. على سبيل المثال، في شفرة جافاسكريبت التي شاهدناها في المثال الأول: var para = document.querySelector('p'); para.addEventListener('click', updateName); function updateName() { var name = prompt('Enter a new name'); para.textContent = 'Player 1: ' + name; } قمنا باختيار فقرة نصية (السطر 1)، ثم استدعينا مستمع الحدث Event listener بتطبيق التابعaddEventListener على الفقرة النصية المُخزَّنة في المتغيّر para (السطر 3)؛ لذا عندما يُنقر على النص تُشغَّل كتلة التعليمات البرمجية في الدالة updateName(). تُسمّى كُتلة التعليمات البرمجية القابلة لإعادة الاستخدام باستدعاء اسمها “وظائف” أو “دوال” Functions. يُطلب من المستخدم اسم جديد ثم يُدرَج هذا الاسم في الفقرة لتحديث العرض. إذا بدّلت ترتيب أول سطرين من التعليمات البرمجية، فإن الشفرة البرمجية لن تعمل . ستحصُل بدلا من ذلك خطأ في وحدة تحكم المتصفح الخاصة بالمطورين Console: -TypeError: para is undefined. وهذا يعني أن الكائن para غير موجود بعد، لذلك لا يمكننا إضافة مستمع الحدث إليه. ملاحظة: هذا خطأ شائع جدًا. يجب أن تكون حذرًا أن الكائنات Objects المشار إليها في التعليمات البرمجية موجودة قبل محاولة استخدامها. الفرق بين التعليمات البرمجية المفسَّرة Interpreted و المُترجَمة Compiled لابد وأنك سمعت في سياق البرمجة بمصطلحيْ التفسير Interpretation و الترجمة Compilation. جافاسكريبت هي لغة مفسَّرة. تُشغَّل تعليمات جافاسكريبت البرمجية من أعلى إلى أسفل وتُرجَع نتيجة تشغيل التعليمات البرمجية على الفور. لست بحاجة لتحويل التعليمات البرمجية إلى شكل مختلف قبل أن يقوم المتصفح بتشغيلها. من ناحية أخرى، تُحوَّل الشفرة البرمجية للغات المترجمة إلى شكل آخر قبل أن يشغّلها الحاسوب. على سبيل المثال تُجمَّع C / C ++ في لغة Assembly التي يُشغّلها الحاسوب. لكلّ من المنهجيْن ميزات، لكنّها خارج نطاق هذا الدرس. جانب الخادوم Server Side و جانب العميل Client Side لابد وأنك سمعت بمصطلحي من جانب الخادوم وجانب العميل، خاصة في سياق تطوير الوِب: الشفرة من جانب العميل Client side هي شفرة تُشغَّل على حاسوب المستخدم. يُنزّل العميل التعليمات البرمجية التي ينفّذها المتصفّح ليعرض صفحة الوٍب. سنتحدّث في هذا الدرس عن جافاسكريبت من جانب العميل. من ناحية أخرى تُنفَّذ شفرة جانب الخادوم على الخادوم، ثم تُرسَل نتيجة التنفيذ إلى المتصفّح ليعرضها. مثال على لغات الوِب الشائعة من جانب الخادوم PHP وبايثون وروبي و ASP.NET. وجافاسكريبت! يمكن أيضا استخدام جافاسكريبت كلغة من جانب الخادوم، على سبيل المثال في بيئة Node.js الشائعة. يُستخدم مصطلح ديناميكي Dynamic لوصف كل من جافاسكريبت من جانب العميل واللغات من جانب الخادوم. تشير كلمة ديناميكي إلى القدرة على تحديث عرض صفحة الوِب أو التطبيق لإظهار أشياء مختلفة في ظروف مختلفة، وتوليد محتوى جديد حسب الطلب. تُنشِئ الشفرة من جانب الخادوم ديناميكيا محتوى جديدًا على الخادوم، سحب بيانات من قاعدة بيانات على سبيل المثال؛ في حين أن جافاسكريبت من جانب العميل تُنشِئ ديناميكيا محتوى جديدًا داخل المتصفح (العميل)، على سبيل المثال إنشاء جدول HTML جديد، وإدراج البيانات المطلوبة من الخادوم في ذلك الجدول، ثم عرض الجدول في صفحة وِب للمستخدم. يعمل المنهجان معًا عادة. يشار إلى صفحة الوِب التي لا يوجد بها محتوى ديناميكي بأنها ثابتة Static، حيث يظهر دائما نفس المحتوى بغضّ النظر عن الزائر. كيف تضيف جافاسكريبت إلى صفحتك؟ يتم تطبيق جافاسكريبت على صفحة HTML بطريقة مشابهة لـ CSS. في حين تستخدم CSS عناصر <link> لتطبيق الأنماط الخارجية وعناصر <style> لتطبيق الأنماط الداخلية على HTML، تحتاج جافاسكريبت إلى صديق واحد فقط في عالم HTML، العنصر <script>. دعونا نتعلم كيفية العمل. جافاسكريبت داخلي أنشئ مستند HTML بالمحتوى التالي <!DOCTYPE html> <html lang="ar" dir="rtl"> <head> <title>مثال تطبيقي لجافاسكريبت</title> </head> <body> <button>انقر هنا</button> </body> </html> </html> استعرض الملف في متصفح الوِب وفي محرر النصوص. سترى أن HTML أنشأت صفحة وِب بسيطة تحتوي على زر قابل للنقر. بعد ذلك، انتقل إلى محرر النصوص وأضف ما يلي مباشرة قبل وسم الإغلاق <body/>: <script> // سنضع تعليمات جافاسكريبت هنا </script> سنضيف الآن بعض جافاسكريبت داخل عنصر <script> لجعل الصفحة تفعل شيئا أكثر إثارة للاهتمام. أضف الشفرة التالية أسفل السطر // سنضع تعليمات جافاسكريبت هنا مباشرة: function createParagraph() { var para = document.createElement('p'); para.textContent = 'لقد نقرت على الزرّ!'; document.body.appendChild(para); } var buttons = document.querySelectorAll('button'); for (var i = 0; i < buttons.length ; i++) { buttons[i].addEventListener('click', createParagraph); } احفظ الملف في محرّر النصوص ثم حدّث الصفحة على المتصفح. يجب أن الآن أن تظهر فقرة جديدة أسفل الزّر عند النقر عليه. ملاحظة: إذا لم يعمل المثال الخاص بك، راجع الخطوات مرة أخرى وتحقق من أنك فعلت كل شيء بشكل صحيح. هل حفظت الملف بامتداد html (مثلا lesson1.html)؟ هل أضفت العنصر <script> بعد علامة <body/> مباشرة؟ هل أدخلت جافاسكريبت تماما كما هو موضح؟ جافاسكريبت حساسة لحالة الأحرف +(صغيرة Lowercase أو كبيرة Uppercase)، لذلك تحتاج إلى إدخال الشفرة تماما كما هو مبين، وإلا فلن تعمل. جافاسكريبت خارجي ماذا لو أردنا وضع جافاسكريبت لدينا في ملف خارجي؟ دعونا نستكشف هذا الآن. أولا، أنشئ ملفًا جديدا في نفس المجلّد الذي يوجد به ملف HTML وسمّه script.js. تأكد من أنه يحتوي على امتداد اسم الملفjs، وهذه هي الطريقة التي يُتعرَّف بها على أننا أمام ملف يحوي شفرات برمجية مكتوبة بجافاسكريبت. بعد ذلك، انسخ كل النص البرمجي من الوسم <script> في ملف HTML (تلك التعليمات الموجودة في الخطوة 4 من الفقرة السابقة) وألصقه في الملف script.js ثم احفظ هذا الملف. الآن استبدل العنصر الحالي <script> بما يلي. <script src="script.js"></script> يصبح محتوى الملف HTML على النحو التالي: <!DOCTYPE html> <html lang="ar" dir="rtl"> <head> <title>مثال تطبيقي لجافاسكريبت</title> </head> <body> <button>انقر هنا</button> <script src="script.js"></script> </body> </html> احفظ الملفّ ثم حدّث صفحة الوِب في المتصفح. ويجب أن تحصُل على نفس النتيجة تمامًا! ولكن الآن لدينا جافاسكريبت في ملف خارجي. يعدّ هذا عموما أمرا جيّدًا من حيث تنظيم التعليمات البرمجية الخاصة بك، وجعلها قابلة لإعادة الاستخدام عبر ملفات HTML متعددة. بالإضافة إلى HTML تصبح أسهل للقراءة دون وجود قطع ضخمة من السكربت ملقاة فيها. شفرات جافاسكريبت المضمنة في وسوم HTML تُكتَب شفرة جافاسكريبت أحيانا داخل HTML على النحو التالي: function createParagraph() { var para = document.createElement('p'); para.textContent = 'You clicked the button!'; document.body.appendChild(para); } <button onclick="createParagraph()">Click me!</button> استعرض المثال على Jsfiddle لهذا العرض التوضيحي نفس الوظائف التي عرضناها في الحالتيْن أعلاه، إلا أن عنصر <button> يتضمن خاصيّة onclick مضمنة لتشغيل وظيفة الدالة عند الضغط على زر. رجاءً لا تضمّن شفرات جافاسكريبت في وسوم HTML فهذه الطريقة من الممارسات السيئة (خلط HTML مع جافاسكريبت)، كما أنها غير فعالة لأن عليك أن تدرج الخاصيّة onclick="createParagraph()" على كل زر تريد تطبيق جافاسكريبت عليه. أما باستخدام شفرة جافاسكريبت منفصلة عن HTML فيمكنك تحديد كافة الأزرار باستخدام تعليمة واحدة. الشفرة التي استخدمناها أعلاه لهذا الغرض تبدو كما يلي: var buttons = document.querySelectorAll('button'); for (var i = 0; i < buttons.length ; i++) { buttons[i].addEventListener('click', createParagraph); } قد يبدو هذا الأمر أطول قليلا من الخاصيّة onclick، ولكنه سيعمل على جميع الأزرار بغض النظر عن عدد الأزرار الموجودة في الصفحة وعدد العناصر التي أُضيفت أو أزيلت، فلا يلزم تغيير جافاسكريبت. ملاحظة: جرب تعديل ملف HTML وإضافة بضعة أزرار أخرى في الملف. عند إعادة التحميل، يجب أن تجد أن جميع الأزرار عند النقر عليها سوف تُظهر فقرة. هذا رائع أليس كذلك؟ التعليقات Comments كما هو الحال مع HTML و CSS، يمكنك كتابة تعليقات في شفرة جافاسكريبت وسيتجاهلها المتصفح. تُستخدم التعليقات لتقديم توضيحات لزملائك المطورين عن كيفية عمل الشفرة، كذلك تمثل مرجعًا لك عن التعليمات البرمجية التي كتبتها عندما تعود مثلًا بعد 6 أشهر لتتذكر مالذي فعلته. التعليقات مفيدة جدًا، ويجب عليك استخدامها في كثير من الأحيان، وخاصة بالنسبة للتطبيقات الكبيرة. هناك نوعان من التعليقات: تعليقات من سطر واحد، وتُكتَب بعد عموديْن مائليْن؛ مثلا: // هذا تعليق تعليق متعدّد الأسطر. يُكتَب بين المحارف / * و * / على النحو التالي: /* هذا تعليق هو الآخر */ تطبيق التعليقات على تعليمات جافاسكريبت السابقة: // دالة تنشئ فقرة جديدة وتضيفها إلى نهاية متن الصفحة function createParagraph() { var para = document.createElement('p'); para.textContent = 'You clicked the button!'; document.body.appendChild(para); } /* 1. نحصُل على مراجع بجميع الأزرار الموجودة في الصفحة ونضعها في مصفوفة 2. نمرّ على جميع الأزرار ونضيف إلى كلّ منها مستمعًا لحدث النقر تُنفَّذ الدالة ()createParagraph عندما ينقر على زرّ في الصفحة. */ var buttons = document.querySelectorAll('button'); for (var i = 0; i < buttons.length ; i++) { buttons[i].addEventListener('click', createParagraph); } كانت هذه خطوتك الأولى في عالم جافاسكريبت. لقد بدأنا بالمفاهيم النظرية فقط لجعلك تفهم لماذا تُستخدم جافاسكربت وما الأشياء التي يمكنك القيام بها باستخدام هذه اللغة. تعرّفت في هذا المقال على بضعة أمثلة من التعليمات البرمجية وتعلّمت كيفية تتفاهم لغةُ جافاسكريبت مع بقية التعليمات البرمجية على موقع الوِب. قد تبدو الآن جافاسكريبت شاقة بعض الشيء، ولكن لا تقلق - في هذه السلسلة من الدروس سوف نأخذك في خطوات بسيطة تمكّنك من المضي قدمًا في عالم جافاسكربت. ترجمة - بتصرف - للمقال What is JavaScript? الذي اشترك في كتابته مساهمو موزيللا.
  6. سنتعرّف في هذا الدّرس على لغة البرمجة جافاسكربت من منظور عامّ وسنجيب على تساؤلاتك ماهي جافاسكربت؟ وما الذي تقوم بعمله في صفحات الوِب؟ و نتأكد من أن مفهوم جافاسكريبت قد أصبح واضحًا لديك. ملحوظة: يتوجَّه هذا الدرس، والدروس التي تليه - إلى من لديهم معرفة سابقة بأساسيّات التطوير للوِب: HTML وCSS. التعريف العام لجافاسكريبت تُعرَّف جافاسكريبت بأنّها لغة برمجة تسمح لك بتنفيذ أشياء معقدة على صفحات الوِب، تعدّ صفحات الوِب الحديثة أكثر من مجرد موقع يُظهر لك معلومات ثابتة تتصفحها، فهي - مثلًا - تعرض تحديثات المحتوى المباشرة، أو تعرض الخرائط التفاعلية، أو الرسومات المتحركة ثنائية أو ثلاثية الأبعاد، أو تمرّر ملفات الفيديو المصورة، وما إلى ذلك. تمثّل جافاسكريبت الطبقة الثالثة من طبقات تقنيات الوِب القياسية. الطبقتان الأوليان هما HTML وCSS. HTML هي لغة توصيف Markup نستخدمها لإنشاء محتوى الوِب وإضفاء معنى عليه، مثل تعريف الفقرات، العناوين وجداول البيانات أو تضمين الصور ومقاطع الفيديو في الصفحة. CSS هي لغة تعرّف قواعد الأنماط Style rules التي نستخدمها لتطبيق التصميم على المحتوى الذي تصفه HTML، على سبيل المثال، تعيين ألوان وخطوط الخلفية، ووضع محتوى ما في أعمدة متعددة. Javascript هي لغة برمجة تمكّنك من إنشاء محتوى حيوي وديناميكي، التحكم في الوسائط المتعددة، وتحريك الصور وأمورا وكل شيء آخر. حسنًا، ليس كل شيء، ولكن من المدهش ما يمكنك تحقيقه مع بضعة أسطر من شفرة جافاسكريبت. سنأخذ مثالًا يوضح كيفية بناء هذه الطبقات الثلاث فوق بعضها بشكل لطيف. لنأخذ تسمية نص بسيط كمثال. سنضع علامة عليه باستخدام HTML لإعطائه هيكلًا وغرضًا: <p>Player 1: Chris</p> النتيجة حتى الآن. سنضيف تحسينات على الشكل باستخدام CSS: p { font-family: 'helvetica neue', helvetica, sans-serif; letter-spacing: 1px; text-transform: uppercase; text-align: center; border: 2px solid rgba(0,0,200,0.6); background: rgba(0,0,200,0.3); color: rgba(0,0,200,0.6); box-shadow: 1px 1px 2px rgba(0,0,200,0.4); border-radius: 10px; padding: 3px 10px; display: inline-block; cursor:pointer; } أصبحت النتيجة الآن كالتالي. وأخيرًا، نضيف بضعة تعليمات جافاسكريبت لتنفيذ السلوك الديناميكي. var para = document.querySelector('p'); para.addEventListener('click', updateName); function updateName() { var name = prompt('Enter a new name'); para.textContent = 'Player 1: ' + name; } غرضنا الآن ليس شرح التعليمات السابقة (سنفعل ذلك لاحقا) ولكن توضيح ما يمكن أن تضيفه جافاسكريبت لـHTML وCSS. عند النقر على النص تظهر نافذة تطلُب إدخال اسم ثم عند إدخال الاسم في النافذة المنبثقة والنقر على زر OK تأخذ النص وتطبعه أمام الجملة Player 1. استعرض المثال على Jsfiddle. الشفرة المصدرية الكاملة للمثال. التشغيل المباشر. يمكن لجافاسكريبت أن تفعل أكثر من ذلك بكثير؛ دعونا نستكشف مزيدًا من التفاصيل. مالذي يمكن لجافاسكريبت فعله حقا؟ تتكون لغة جافاسكريبت الأساسية من بعض ميزات البرمجة الشائعة التي تسمح لك بفعل أشياء مثل: تخزين قيم مفيدة داخل متغيرات Variables. في المثال السابق على سبيل المثال، نطلب من المستخدم ادخال اسم جديد ثم يُخزَّن هذا الاسم في متغير يسمى name. العمليات على أجزاء من النص (المعروفة باسم سلاسل المحارف Strings في البرمجة). في المثال أعلاه، نأخذ سلسلة المحارف Player 1: ونضمها إلى متغير الاسم المذكور أعلاه لإنشاء الجملة الكاملة، على سبيل المثال Player 1: Chris. تشغيل التعليمات البرمجية ردًا على أحداث معينة تحدث على صفحة وِب. في المثال أعلاه استخدمنا الحدث click الذي يخبرنا أن المستخدم نقر على النصّ ثم نشغّل الشفرة التي تعمل على تحديث النص. والكثير! ولكن ما هو أكثر إثارة هو وظيفة بُنيت على لبّ لغة جافاسكريبت الأساسي. تسمى APIs وهي اختصار لـ Application Programming Interface واجهات برمجة التطبيقات. تتيح لك واجهات برمجة التطبيقات (APIs) المزيد من القوة لاستخدامها في شفرة جافاسكريبت. تُعرّف واجهات برمجة التطبيقات (APIs) على أنها مجموعات جاهزة من الشفرات البرمجيّة تسمح لمطور البرامج بتنفيذ برامج سيكون من الصعب أو المستحيل تنفيذها من دون استخدام واجهة برمجة التطبيقات. لأوضح لك الفكرة، تخيل أنك تريد عمل رف خشبي للكتب، سيكون الأمر أسهل بكثير لو توافرت لك الألواح الخشبية والمسامير بالمقاسات المناسبة وما عليك سوى تجميعها معًا لعمل رف الكتب. في حين سيكون الأمر أصعب بكثير لو توجّب عليك أنت العثور على الخشب المناسب وتقطيعه إلى الحجم والشكل الصحيح وإيجاد قياس المسامير المناسب ومن ثم تجميعها معًا لعمل رف الكتب. هذا ما تسهل عمله واجهات برمجة التطبيقات. تنقسم واجهات برمجة التطبيقات (APIs) عمومًا إلى فئتين. واجهات برمجة التطبيقات الخاصّة بالمتصفّح Browser APIs: تُدمَج واجهات برمجة تطبيقات في المتصفّح لإعطائه القدرة على عرض البيانات من بيئة الحاسوب المحيطة به، أو القيام بأشياء معقدة مفيدة. فمثلا: تسمح لك واجهة برمجة تطبيقات DOM وهي اختصار لـ Document Object Model بمعالجة HTML و CSS، إنشاء، إزالة وتغيير HTML ، تطبيق أنماط Styles جديدة ديناميكيًّا على صفحتك، وما إلى ذلك. في كل مرة تُعرَض فيها نافذة منبثقة تظهر على صفحة، أو يُعرَض محتوى جديد -كما رأينا أعلاه في مثالنا السابق- فواجهة DOM هي المسؤولة عن ذلك. تستعيد واجهة برمجة التطبيقات Geolocation API معلومات الموقع الجغرافي، ممّا يفسّر كيف يمكن لخرائط جوجل Google Maps أن تعثُر على موقعك الجغرافي وترسمه على الخريطة. تسمح لك واجهات برمجة تطبيقات اللوحة Canvas و WebGL بإنشاء رسومات متحركة ثنائية الأبعاد وثلاثية الأبعاد. يقوم الأشخاص بأشياء مدهشة باستخدام تقنيات الوِب هذه. تسمح لك واجهات برمجة التطبيقات الصوتية والمرئية مثل HTMLMediaElement و WebRTC بالقيام بأشياء مثيرة للاهتمام حقًا مع الوسائط المتعددة، مثل تشغيل الصوت والفيديو مباشرة في صفحة وِب أو التقاط الفيديو من كاميرا الوِب الخاصة بك وعرضها على جهاز كمبيوتر شخص آخر (جرب العرض التوضيحي snapshot) ملاحظة: لن تعمل العديد من العروض التوضيحية المذكورة أعلاه في متصفح أقدم. عند التجربة، من الأفضل استخدام متصفح حديث مثل فايرفوكس أو كروم أو Edge أو Opera لتشغيل الشفرة. واجهة برمجة التطبيقات الخاصّة بطرف ثالث Third party APIs. لا تُضمَّن هذه الواجهات في المتصفح مبدئيًّا. وعليك عمومًا الحصول على التعليمات البرمجية والمعلومات من مكان ما على الوِب. فمثلا: تتيح لك واجهة برمجة تطبيقات تويتر Twitter API إجراء أشياء مثل عرض أحدث التغريدات على موقعك الإلكتروني. تسمح لك واجهة برمجة التطبيقات لخرائط غوغل Google Maps API بتضمين الخرائط المخصصة في موقعك على الوِب، وغير ذلك من الوظائف. ملاحظة: واجهات برمجة التطبيقات هذه متقدمة، ولن يتم تغطية أي منها في هذه الدّروس، ولكن الروابط الواردة أعلاه توفّر وثائق شاملة إذا كنت مهتمًا بمعرفة المزيد. يوجد الكثير لتعلّمه، لا تفقد حماسك. دراسة جافاسكريبت لمدة 24 ساعة لن تعطيك القدرة على بناء فيسبوك، خرائط غوغل أو إنستغرام. هناك الكثير من الأساسيات التي يجب تغطيتها أولًا. وهذا هو السبب في أنك هنا. سنكمل في الدرس السابق حديثنا عن جافاسكريبت وستكون فرصة على للنظر الفعلي في شفرات برمجية. ترجمة - بتصرف - للمقال What is JavaScript? الذي اشترك في كتابته مساهمو موزيللا.
  7. لطالما وفّر إطار العمل 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 ثم استخدمنا رمز الوصول هذا للاطلاع على موارد محميّة على الخادوم.
  8. لا يكاد يخلو أي موقع ويب من وجود خرائط Google فمن خلال وضع خريطة على الموقع الخاص بك فإنك تعطي تأثيرًا غنيًا ومستوى أعلى للتفاعل بين المستخدمين. سأقوم في هذا المقال بشرح كيفيّة استخدام خرائط غوغل وإضافتها إلى صفحة الويب بالإضافة إلى كيفيّة تحديد أكثر من موقع بنفس الوقت على نفس الخريطة، وشرح بعض المزايا الـتي يمكنك إضافتها إلى خريطتك. يمكنك تحميل الملف المصدري للمثال الموضح في هذا الدرس. إستخدام مكتبة Google APIمكتبة Google Maps API هي عبارة عن مكتبة جافاسكربت ولنتمكن من إستخدام مكتبة Google API قم بإضافة الـسكربت الـخاص بذلك. <script src ="http://maps.google.com/maps/api/js?sensor=false" type="text/javascript"></script> إنشاء الخريطةقم الآن بإنشاء div بالأبعاد الـتي تريد أن تظهر بها الـخريطة وقم بإعطاءه "id="map. <div id="map" style="width: 600px; height: 400px;" ></div>الآن لنقم بإنشاء السكربت لإظهار الخريطة، يجب عليك عند وضع أي خريطة أن تقوم بتحديد الإحداثيات: خط الطول longitude وخط العرض latitude ضمن الخاصية center. يمكنك معرفة الإحداثيات لمنطقة ما بالدخول لموقع LatLong وكتابة اسم المدينة أو المنطقة. في هذا المثال ستعرض الخريطة موقع تركيا Turkey. لنقم الآن ضمن الدالة {} ()function initialize، بتحديد خصائص الـخريطة ضمن الـمتغير mapProp، مثل: الموقع الذي سيظهر عند تحميلها، نوعها، وحجم الـتكبير الذي نريده zoom. كما هو موضح في الـكود الـتالـي: <script type="text/javascript"> function initialize() { var mapProp = { center: new google.maps.LatLng(38.963745, 35.243322), zoom: 5, mapTypeId: google.maps.MapTypeId.ROADMAP }; } </script> نستخدم الخاصية zoom لتحديد البعد الذي نريده للمكان (قريب – بعيد).الخاصية mapTypeId تحدد نوع الخريطة الذي سيظهر:ROADMAP ( الخريطة العادية 2D map)SATELLITE (خريطة مصورة)HYBRID (خريطة مصورة بالإضافة الى أسماء الطرق والمدن)TERRAIN (خريطة تتضمن الجبال والأنهار…الـخ)لنقم الآن بتعريف متغير map وإعطاءه id الخاصة بالـ div: var map = new google.maps.Map(document.getElementById("map"), mapProp);ثم إضافة الحدث DOM listener الذي يقوم بتنفيذ الدالة ()initialize عند تحميل صفحة الويب: google.maps.event.addDomListener(window, 'load', initialize);ليصبح الكود على هذا الشكل: <script type="text/javascript"> function initialize() { var mapProp = { center: new google.maps.LatLng(38.963745, 35.243322), zoom: 5, mapTypeId: google.maps.MapTypeId.ROADMAP }; var map = new google.maps.Map(document.getElementById("map"), mapProp); } google.maps.event.addDomListener(window, 'load', initialize); </script>ستظهر لك الخريطة بهذا الشكل: وضع محددات Markersإضافة محدد واحدلنقم الآن بتحديد موقع معين ووضع محدد (marker). أولًا قم بتعريف مصفوفة تحتوي على الإحداثيات (خط الـطول وخط الـعرض) للمدينة أو البلد أو الموقع المعين الذي تريد تحديده. مثلًا مدينة اسطنبول Istanbul: var myLatLng = {lat: 41.008238, lng: 28.978359 };هنا سأقوم بإضافة محدد واحد ثم سأشرح كيف يمكننا إضافة أكثر من محدد. الآن نعطي للخاصية center الـمتغير myLatLng: var map = new google.maps.Map(document.getElementById('map'), { zoom: 8, center: myLatLng });ثم نقوم بتعريف الـمحدد وإعطاء للخاصية position الـمتغير myLatLng: var marker = new google.maps.Marker({ position: myLatLng, map: map, title: 'Hello World!' });سيظهر بهذا الشكل: إضافة أكثر من محدد على نفس الـخريطةقم بتعريف مصفوفة تحتوي على إحداثيات المواقع الـتي تود تحديدها، سأقوم بتحديد 6 مدن: Istanbul, Antalya, Ankara, Trabzon, Bursa, Adana كما هو موضح أدناه: var myLocations = [ ['Istanbul', 41.008238, 28.978359, 1], ['Antalya', 36.896891, 30.713323, 2], ['Ankara', 39.933363, 32.859742, 3], ['Trabzon', 41.002697, 39.716763, 4], ['Bursa', 40.188528, 29.060964, 5], ['Adana', 36.991419, 35.330829, 6] ];الآن عند تعريف المحدد قم بعمل for loop حلقة تكرار وتعريف متغير i ومن ثم إعطاء الـمصفوفة myLocations للخاصية position: var marker, i; for (i = 0; i < myLocations.length; i++) { marker = new google.maps.Marker({ position: new google.maps.LatLng(myLocations[i][1], myLocations[i][2]), map: map }); }وهذه هي المواقع التي قمنا بتحديدها: وضع محدد مع لصيقة (label) عند الـنقر على مكان معينسأقوم هنا بشرح فكرة عن الـevents في Google map ثم سأتطرق فيما بعد لشرح الـevents بالتفصيل في مقال مقبل إن شاء الله. الآن لنرى كيف يمكننا عند الضغط على مكان معين على الخريطة إظهار محدد مع لصيقة. مثال: marker A, marker B. أولاً يجب تعريف متغير Labels نضع فيه القيم الـتي نريد إظهارها في اللصيقة. مثال: الأحرف من A-Z. ونحتاج أيضًا إلى تعريف متغير labelIndex لتحديد الـقيمة الـتي نريد إظهارها. var labels = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; var labelIndex = 0;الآن قم بتحديد المكان الـذي ستفتح عنده الخريطة. مثال: تركيا. var Turkey = { lat: 38.963745 , lng: 35.243322 }; var map = new google.maps.Map(document.getElementById('map'), { zoom: 12, center: Turkey });الدالة ()addMarkerيقوم الحدث event.addListener باستدعاء التابع ()addMarker عند الضغط على الخريطة. كما في المثال التالي: google.maps.event.addListener(map, ‘click’, function(event) { addMarker(event.latLng, map); });وليكون هناك marker محدد عند فتح الخريطة قم بإضافة محدد إلى Turkey: addMarker(Turkey, map);لنكتب الآن الدالة ()addMarker: نحتاج لتعريف 2 parameters (معاملين) هما الـخريطة والـموقع map & location ثم نقوم بتعريف marker وإعطاء المعامل location للخاصية position. ولإظهار label نقوم بإعطاء الـمتغير labels الـذي يحتوي على الأحرف من A-Z للخاصية label مع تحديد ++index لكي ينتقل عند كل نقرة جديدة لحرف جديد وإظهاره على الـ marker كما هو موضح في الكود الـتالي: function addMarker(location, map) { var marker = new google.maps.Marker({ position: location, label: labels[labelIndex++ % labels.length], map: map }); }ستظهر لك الخريطة بهذا الشكل: وعند النقر على الخريطة: إضافة تأثير Marker Animationيمكنك إضافة تأثير حركي على المحدد ليبدو وكأنه يقفز ، فقط باستخدام خاصية animation:google.maps.Animation.BOUNCE كما هو موضح في الكود الـتالـي: var marker = new google.maps.Marker({ position: myCenter, animation: google.maps.Animation.BOUNCE });إضافة أيقونة للمحدد يمكنك تغيير الأيقونة الخاصة بالمحدد ووضع أيقونة من اختيارك عن طريق الخاصية icon، حيث تقوم بإعطاء مسار الصورة للخاصية icon. كما هو موضح في الكود التالي: var marker = new google.maps.Marker({ position: myCenter, icon:'pin.png' });ستظهر لك الخريطة بهذا الشكل: إضافة شكل متعدد الخطوط Polyline تتيح لك ميزة polyline رسم خط يصل بين سلسلة إحداثيات على الخريطة بشكل متسلسل، على سبيل المثال يمكنك رسم مسار رحلة عبر مجموعة من المدن ويمكنك التحكم بخصائص عديدة للخط مثل اللون والشفافية والحجم. خصائص الميزة Polyline: path - تحدد مجموعة الإحداثيات التي سيمر بها الخط.strokeColor - يحدد لون الخط بالـHex كود.strokeOpacity - يحدد شفافية الخط بين 0.0 و 1.0.strokeWeight - يحدد عرض الخط بالبكسل.Editable - تحدد ما إذا كان الخط قابل للتعديل من قبل المستخدم أم لا. لنقم الآن بتحديد مجموعة إحداثيات لمدن ونصل فيما بينها عن طريق polyline، سأستخدم نفس الإحداثيات التي استخدمتها في تحديد المدن عندما وضعت أكثر من محدد، سأقوم برسم خط بين ثلاث مدن: اسطنبول، انطالـيا، طرابزون. أولاً: تحديد إحداثيات الـمدن: var istanbul = new google.maps.LatLng(41.008238, 28.978359); var antalya = new google.maps.LatLng(36.896891, 30.713323); var trabzon = new google.maps.LatLng(41.002697, 39.716763);ثانيًا: نقوم بتعريف مصفوفة myTrip وتعبئتها بأسماء المتغيرات (المدن الـتي نريد تحديد المسار عليها ): var myTrip = [ istanbul, antalya, trabzon];وثم نعطي المصفوفة للخاصية path: path: myTrip,ثالثًا: نقوم بتعريف متغير flightPath لاختيار الـخاصية polyline: كما هو موضح في الكود الـتالي: var map = new google.maps.Map(document.getElementById(“map”),mapProp); var myTrip = [ istanbul, antalya, trabzon]; var flightPath = new google.maps.Polyline({ path:myTrip, strokeColor: "#0000FF", strokeOpacity: 0.8, strokeWeight: 2 }); flightPath.setMap(map); }ستظهر لك الخريطة بهذا الشكل: إضافة مضلع Polygonتتيح لك هذه الـميزة إنشاء مضلع تكون زواياه عبارة عن سلسلة الإحداثيات التي تريد الوصل بينها. لا تختلف ميزة polygon كثيراً عن polyline من حيث كتابة الـتعليمات فقط نقم باستبدالـ polyline بـpolygon. خصائص الـميزة polygon: path - تحدد مجموعة الإحداثيات التي سيمر بها الخط.strokeColor - يحدد لون الخط بالـHex كود.strokeOpacity - يحدد شفافية الخط بين 0.0 و 1.0.strokeWeight - يحدد عرض الخط بالبكسل.fillColor - يحدد لون المضلع بالـHex كود.fillOpacity - يحدد نسبة الشفافية للمضلع بين 0.0 و 1.0.Editable - تحدد ما إذا كان الخط قابل للتعديل من قبل المستخدم أم لا. كما هو موضح في الكود التالي: var map = new google.maps.Map(document.getElementById(“map”),mapProp); var myTrip = [istanbul, antalya, trabzon]; var flightPath = new google.maps.Polygon({ path: myTrip, strokeColor: "#0000FF", strokeOpacity: 0.8, strokeWeight: 2 }); flightPath.setMap(map); }ستظهر لك الـخريطة بهذا الـشكل: إضافة دائرة Circleتتيح لك هذه الميزة إحاطة منطقة معينة على الخريطة بدائرة، يمكنك التحكم بقطر الدائرة ولونها وشفافيتها. خصائص الميزة Circle: center - تحديد إحداثيات مركز الدائرة. radius - تحديد قطر الدائرة بالمتر المربع. strokeColor - يحدد لون الخط بالـHex كود.strokeOpacity - يحدد شفافية الخط بين 0.0 و 1.0.strokeWeight - يحدد عرض الخط بالبكسل.fillColor - يحدد لون المضلع بالـHex كود.fillOpacity - يحدد نسبة الشفافية للمضلع بين 0.0 و 1.0.Editable - تحدد ما إذا كان الخط قابل للتعديل من قبل المستخدم أم لا.كما هو موضح في الكود التالي: var map = new google.maps.Map(document.getElementById(“map”), mapProp); var flightPath = new google.maps.Circle({ center: istanbul, radius: 20000, strokeColor: "#0000FF", strokeOpacity: 0.8, strokeWeight: 2, fillColor: "#0000FF", fillOpacity: 0.4 }); flightPath.setMap(map); ستظهر لك الخريطة بهذا الشكل: إضافة نافذة نصية InfoWindowتتيح لك ميزة infoWindow إظهار نافذة على موقع معين محدد باسم بحيث يمكنك كتابة اسم الموقع المحدد بداخلها. كما هو موضح في الكود التالي: var map = new google.maps.Map(document.getElementById(“map”), mapProp); var marker = new google.maps.Marker({ position: new google.maps.LatLng(38.963745, 35.243322), }); marker.setMap(map); var infowindow = new google.maps.InfoWindow({ content:”Hello World!” }); infowindow.open(map, marker); }ستظهر لك الخريطة بهذا الشكل: هكذا نكون قد استعرضنا أهم النقاط والميزات التي تتيح لك استخدام خرائط Google Maps بسهولة في موقعك الخاص. هل أنت جاهز الآن لإضافة Google Maps إلى موقعك والإستفادة من مزاياها؟ الـمصدر: Google Developers.حقوق الصورة البارزة: Designed by Freepik.
  9. أصبحت واجهات REST البرمجية أكثر انتشارًا بين المطوّرين، نظرًا لأنها توفّر واجهة بسيطة، موحّدة وواضحة لخدمات الطرف الثالث third-party مثل Twitter، MailChimp وGitHub. ومع ازدياد شهرة واجهة ووردبريس البرمجية (المتوفرة عبر إضافة) فإن الوقت قد حان لنتعلم حول واجهة HTTP البرمجية في ووردبريس، وكيفية عملها واستخداماتها. ما هي واجهة HTTP في ووردبريس؟ لن يكون من المستغرب القول بأنها طريقة لإرسال واستقبال الرسائل بواسطة HTTP – لغة شبكة الويب. حيث يقوم المتصفح بإرسال واستقبال الرسائل طوال الوقت وبهذا الشكل يتم استقبال صفحات الويب. ويمكن من خلال واجهة REST البرمجية، يمكن باستخدام رسائل HTTP القيام بأشياء أكثر كتعديل منشور، حذف حساب مستخدم أو نشر وصفة جديدة على موقعك. لهذا السبب تعتبر واجهة ووردبريس البرمجية مهمة جدًا، فهي تسمح بفصل التطبيق من جهة المستخدم عن النواة البرمجية الخاصة بووردبريس. ولاستخدامها، تحتاج أن تكون معتادًا على إرسال طلبات HTTP واستقبال الأجوبة، وهذا هو أساس عمل واجهة HTTP البرمجية. وهناك العديد من الطرق لإرسال طلبات HTTP، حيث توفّر واجهة HTTP البرمجية طريقة موحّدة باستخدام مجموعة من التوابع المساعدة والتي سنطّلع عليها بعد قليل. طرق HTTP ومصادرها ترتكز HTTP حول الطرق methods (تسمّى أحيانًا بالأفعال verbs) والمصادر resources. تحدّد المصادر العنصر الذي سيتم تنفيذ العملية عليه، وتحدّد الطريقة نوع العملية التي سيتم تنفيذها. ويعتبر العنوان URL هو المصدر الذي يشير إلى الغرض على شبكة الويب، كمنشور على سبيل المثال. وتوجد العديد من الطرق، لكن الأهم فيها هي GET, POST, PUT و DELETE. ولا بد أن لديك الكثير من الخبرة مع GET فهذه الطريقة هي المستخدمة في الحصول على المصادر، فمثلًا عند استعراض مقال في المتصفح يتم إرسال طلب GET إلى عنوان المقال وليكن مثلًا https://premium.wpmudev.org/blog/using-the-wordpress-http-api/. أما طلبات PUT فتستخدم لتعديل المصادر، وتستخدم طلبات POST لإنشاء مصادر جديدة، وتستخدم طلبات DELETE لحذف مصادر موجودة مسبقًا. ولو كان لدى موقع WPMU DEV واجهة REST برمجية، فربما كان من الممكن أن يقوم المدير بإرسال طلب DELETE لحذف المقال على الرابط https://premium.wpmudev.org/blog/wordpress-http-api، وهذا الأمر مفيد جدًا للمواقع الكبيرة التي تملك برامج إدارة على الهواتف الذكية. مثال عن طلب HTTP بسيط للقيام بإرسال طلب GET بسيط على سبيل التجربة، سنقوم باستخدام التابع wp_remote_get() الذي يملك مُعاملين، المُعامل الأول هو رابط المصدر، والمُعامل الثاني هو مصفوفة اختيارية optional من الخيارات التي من الممكن استخدامها لتحديد بعض التفاصيل. $test = wp_remote_get( 'http://google.com' ); echo "<pre>"; var_dump($test); echo "</pre>"; يقوم المثال السابق بجلب الصفحة الرئيسية لموقع جوجل، ومن ثم يتم طباعة محتوى المتغير test الذي يحوي على جواب الطلب الذي أرسله موقع جوجل – حيث يمكن رؤية جميع العناصر التي يحتويها. تحتوي الترويسات headers على المزيد من المعلومات عن كل رسالة. وقد تطلب منك بعض واجهات REST البرمجية أن تقوم بإرسال معلومات محددة في الترويسات عندما ترسل الطلب. يحتوي الجواب response على رمز الحالة status code وعبارة قد تكون معروفة بالنسبة لك كأخطاء 404، أو خطأ في معالجة الطلب 500 أو تحويل من النوع 301 أو 302. يحتوي موقع W3.org على جميع رموز أخطاء HTTP المعرّفة والمشروحة، ويعتبر مصدرًا مفيدًا إن احتجت أن تعرف ما يعني أي خطأ. بإمكانك أيضا الاطّلاع على هذا المقال على أكاديمية حسوب لتعرف المزيد حول هذه الرّموز: رموز الإجابة في HTTP يحتوي الجسم body على الجواب وهو المكان الذي تحتاج أن تنظر إليه بحثًا عن النتيجة المطلوبة. في حالة المثال السابق، فإننا نحصل على وسوم HTML التي تشكّل الصفحة الرئيسية، ولكن عند التعامل مع واجهات REST البرمجية فمن الشائع أن نحصل على نص بصيغة JSON. وعادة ما تطلب الواجهات البرمجية APIs أن يتم إضافة نص محدّد إلى الجسم عند إرسال طلبات أيضًا. يحتوي قسم cookies على أي كعكات تم استلامها مع الرسالة. كما ترى، فإن إرسال طلب باستخدام واجهة HTTP البرمجية أمر بسيط جدًا. ما يجعل العمل مع HTTP معقّد نوعًا ما هو أن واجهات REST البرمجية قد تكون حساسة جدًا لصيغة البيانات المدخلة (وهو أمر جيّد) لذا فإن تجاوزت سطرًا عند دراسة توثيق الواجهة البرمجية قد ينتهي بك الأمر ببرمجية لا تعمل كما ترغب. العمل مع الواجهات البرمجية أعتقد بأنه من الآمن القول بأن معظمكم سيستخدم HTTP للتعامل مع واجهات REST البرمجية في شبكة الويب، وفي هذه الحالة سنحتاج لاستخدام المعامل الثاني لتحديد بعض الأمور، كالاستيثاق وتجنب بعض الأخطاء الشائعة. لنبدأ بمثال بسيط – استعادة البيانات من لوحة Pinterest. تتطلب جميع الواجهات البرمجية الجيّدة تنفيذ عملية استيثاق للتعريف عن المستخدم الذي يرسل الطلب، لكنّنا سنغش قليلًا في هذا المثال باستخدام مولّد رموز الأمان الخاص بـ Pinterest. بعد إكمال الاستيثاق ستحصل على رمز أمان يمكن استخدامه في المثال التالي، حيث سنقوم بإنشاء طلب لجلب وعرض قائمة بمنشورات Pinterest. $request = wp_remote_get( 'https://api.pinterest.com/v1/boards/marticz/home-office/pins/?access_token=<your access token>' ); $pins = json_decode( $request['body'], true ); if( !empty( $pins['data'] ) ) { echo '<ul>'; foreach( $pins['data'] as $pin ) { echo '<li><a href="' . $pin['url'] . '">' . $pin['note']. '</a></li>'; } echo '</ul>'; } لو قمنا بنسخ المثال السابق ولصقه في صفحة content-page.php في قالب Twenty Fifteen لتجربته فإن النتيجة هو الحصول على لائحة بالمنشورات من لوحة Pinterest حول المكاتب الشخصية في المنزل. لا تنس أن تستبدل <your access token> في المثال السابق برمز الأمان الذي حصلت عليه. يقوم السطر الثاني في المثال بفك ترميز جسم الجواب من JSON إلى شكله الأساسي كمصفوفة ونقوم بإنشاء حلقة loop تقوم بطباعة محتوى عنصر المصفوفة $pins['data']. الاستيثاق يقع العديد من الأشخاص في عثرات في هذه المرحلة لأنها تتطلب خطوة إضافية على الأقل وربما بعض الترويسات الإضافية أيضًا. ولننظر إلى واجهة تويتر البرمجية على سبيل المثال، وتحديدًا إلى الاستيثاق الخاص بالتطبيقات، الذي يمكنك استخدامه لاستيثاق تطبيقك مع تويتر. قراءة التوثيق إن أول خطأ قد يتم ارتكابه هو عدم قراءة التوثيق بشكل جيّد. ولو كنت مبرمجًا متمرسًا في واجهات REST البرمجية فيمكنك القفز مباشرة إلى الجزء حول الاستيثاق، ولو فعلت هذا فقد لا تنتبه إلى سطر يقول: وإهمال هذا الشرط سيؤدي إلى فشل حتمًا في الحصول على نتيجة، على الرغم من أن كل شيء آخر مطبّق بشكل كامل، لذا حتى توفّر على نفسك الحاجة لمراجعة شفرة برنامجك، تأكد من قراءة التوثيق بشكل جيّد وكامل. إضافة ترويسات ومعاملات أخرى بعد اتباع التوجيهات في التوثيق حرفيًّا، قمت بإنشاء طلب POST، ينبغي أن يؤدي إلى توليد رمز أمان للوصول من أجلي، ويبدو الكود على الشكل: $key = base64_encode( urlencode( "n8KP16uvGZA6xvFTtb8IAA:i4pmOV0duXJv7TyF5IvyFdh5wDIqfJOovKjs92ei878" ) ); $request = wp_remote_post('https://api.twitter.com/oauth2/token', array( 'headers' => array( 'Authorization' => 'Basic ' . $key, 'Content-Type' => 'application/x-www-form-urlencoded;charset=UTF-8' ), 'body' => 'grant_type=client_credentials', 'httpversion' => '1.1' )); $token = json_decode( $request['body'] ); echo "<pre>"; var_dump($token); echo "</pre>"; إن الخطوة الأولى هو ترميز رمز أمان الوصول والكلمة السرّية بصيغة URL encoding. قمت أيضًا بإضافة ترويستين، واحدة ترويسة استيثاق تحوي على بيانات الوصول. أما الترويسة الثانية فهي ترويسة نوع المحتوى content type، والتي يطلب توثيق تويتر إضافتها. إضافة لما سبق، قمت بملء جسم الطلب تمامًا كما ذكرت الملاحظة السابقة حول grant_type=client_credentials وتم إضافة إصدار HTTP كما يطلب توثيق تويتر أيضًا. سيحتوي الجواب بالإضافة للعديد من المعلومات الأخرى على رمز أمان الوصول في جسم الجواب، وسنحتاج لرمز الأمان هذا في جميع الطلبات اللاحقة المرسلة إلى الواجهة البرمجية الخاصة بتويتر. تخزين رمز أمان الوصول إن رمز الأمان صالح لبعض الوقت، ويعتبر طلبه مجدّدًا هدرًا غير ضروري عند تحميل كل صفحة أو عندما يحتاج تطبيقك أن يقوم بعملية ما وسيؤدي إلى استهلاك عدد الطلبات المسموح بسرعة. يمكن في ووردبريس استخدام عابرة transient لتخزين قيمة رمز الأمان ومن ثم استخدام العابرة عند كل طلب لاحق للواجهة البرمجية. $token = get_transient( 'twitter_access_token' ); $token = ( empty( $token ) ) ? get_twitter_access_token() : $token; $request = wp_remote_get('https://api.twitter.com/1.1/followers/ids.json?screen_name=danielpataki&count=5', array( 'headers' => array( 'Authorization' => 'Bearer ' . $token, 'Content-Type' => 'application/x-www-form-urlencoded;charset=UTF-8' ), 'httpversion' => '1.1' )); $token = json_decode( $request['body'] ); سيؤدي إرسال الطلب السابق إلى واجهة تويتر البرمجية إلى عرض 5 من متابعيّ على تويتر (قائمة بسيطة لأرقام حساباتهم Ids). وكما يظهر في المثال السابق، فإنني أقوم بجلب القيمة التي خزّنتها في العابرة فإن لم تكن موجودة سأقوم باستدعاء التابع get_twitter_access_token() للحصول على رمز أمان جديد ومن ثم إضافته إلى العابرة كي أستخدمه في الطلبات اللاحقة. تجدر الإشارة إلى أن هذه الطريقة تهدف فقط إلى إظهار آلية التنفيذ وليست أفضل طريقة للتنفيذ، وكسيناريو أبسط كنت لأقوم بوضع جميع العبارات الشرطية IFs داخل التابع get_twitter_access_token() الذي سيقوم بتنفيذ جميع الشيفرة السابقة ضمنيًا. التوابع المساعدة في واجهة HTTP البرمجية الآن وبعد أن حصلنا على لمحة جيدة حول هدف المقال، دعونا نلق نظرة على التوابع التي ستساعدك في واجهة HTTP البرمجية في ووردبريس. هناك 4 توابع يمكن بواسطتها تنفيذ طلبات: wp_remote_get() wp_remote_post() wp_remote_head() wp_remote_request() ومن الواضح تمامًا وظيفة كل تابع، أما التابع الأخير wp_remote_request() فهو تابع عام يمكنك استخدامه مع أي طريقة HTTP method. أما التوابع الخمسة التالية فتسمح لك بالحصول على جواب على الطلب بسهولة باستخدام توابع قياسية عوضًا عن الخوض في التعامل مع المصفوفات وعناصرها. wp_remote_retrieve_body() wp_remote_retrieve_header() wp_remote_retrieve_headers() wp_remote_retrieve_response_code() wp_remote_retrieve_response_message() أيضًا يبين اسم كل تابع الهدف الوظيفي له بسهولة، وينصح عند الإمكان باستخدام هذه التوابع عوضًا عن التعامل مع المصفوفات يدويًا، حيث ستسمح هذه الطريقة الموحّدة لمطوّرين آخرين بفهم البرمجية بسهولة واستخدام الخطافات hooks إن أصبحت متاحة في المستقبل. الخلاصة كما ترى فإن التعامل مع واجهة REST البرمجية سهل باستخدام واجهة HTTP البرمجية في ووردبريس وبعض توابع ووردبريس كالعبّارات. أنصح بشدّة تجربة هذا الأمر لأن عملية التطوير في ووردبريس تسير بثقة باتجاه العالم المُقاد باستخدام الواجهات البرمجية وعليك أن تقفز إلى الموكب قبل فوات الأوان. إن كان لديك المزيد من الأسئلة حول استخدام واجهة HTTP البرمجية أو لديك بعض الأفكار حول كيفية استخدامها، فلا تتردد بمناقشة الأمر في قسم التعليقات في الأسفل. ترجمة -وبتصرّف- للمقال How to Use the WordPress HTTP API لصاحبه Daniel Pataki.
  10. بدأنا في دروس سابقة بـإنشاء متجر إلكتروني على WooCommerce وقمنا بضبط إعدادات المتجر العامة General، إعدادات المنتجات Products، الضريبة Tax، الدفع Checkout، والشحن Shipping. سنغطي في هذا الدرس ما تبقّى من إعدادات المتجر والتي تتضمن إعدادات الحسابات Accounts، البريد الإلكتروني Email، والواجهة البرمجية API. إعدادات الحسابات من هذا التبويب تستطيع ضبط كيفيّة عمل حسابات عملائك. للوصول إلى هذه الإعدادات قم بتسجيل الدخول إلى موقعك ثم اذهب إلى: Dashboard > WooCommerce > Settings > Accounts يحتوي هذا التبويب على ثلاثة أقسام: Account Pages ومنه تقوم بإعداد صفحات الحسابات للعملاء: عندما تقوم بتثبيت WooCommerce سيقوم بإنشاء هذه الصفحة تلقائيًا، تأكّد من إبقاء الصفحة الافتراضية التي تم إنشاؤها، أو قم بإنشاء واحدة لكي يستطيع العميل استعراض الوظائف الخاصة بحسابه، كالطلبات، بالإضافة إلى تغيير تفاصيل الحساب عند الحاجة. My Account Endpoints Endpoints هي أجزاء من عنوان الصفحة URL، كصفحة My Account، تُلحق بذلك العنوان لمعالجة إجراءات معيّنة على الصفحة: إذا كنت تريد إنشاء Endpoints مخصّصة لعرض الطلب View Order، تعديل الحساب Edit Account، أو الإجراءات الأخرى، تأكّد من كون هذه الأجزاء فريدة ولا تظهر في عناوين الصفحات الأخرى على المتجر. وإلا ستعطل الروابط وستسبب مشاكل أخرى في صفحات حسابات العملاء. Registration Options من هذا القسم تتحكّم في خيارات التسجيل: بإمكانك تمكين التسجيل من على صفحة الدفع بتأشير الخيار Enable registration on the Checkout page، و/أو التسجيل من على صفحة "حسابي" Enable registration on the My Account page. هذه الخيارات مفيدة في حال كنت لا تسمح بإجراء الدفع من قبل زوار المتجر (غير المسجّلين)، ويتيح للعملاء الجدد سهولة التسجيل وإنشاء الحساب. من الخيارات الأخرى المفيدة هو Display returning customer login reminder on the Checkout page الذي يقوم بعرض تذكير بتسجيل الدخول للعملاء المتكررين على صفحة الدفع مما يتيح لهم سهولة الدخول إلى حساباتهم من على صفحة الدفع: وأخيرا من خيارات إنشاء الحساب Account Creation بإمكانك القيام بتوليد اسم مستخدم للعميل تلقائيًا من عنوان بريده الإلكتروني بتأشير الخيار Automatically generate username from customer email، وكذلك توليد كلمة سريّة لحساب العميل تلقائيًا بتأشير الخيار Automatically generate customer password، وسيتم إرسال تلك الكلمة السرية إلى عنوان البريد الإلكتروني الخاص بالعميل. انقر على Save Changes لحفظ التغييرات والانتقال إلى التبويب التالي. إعدادات البريد الإلكتروني توجد في هذا التبويب خيارات تعديل إعدادات البريد الإلكتروني والقوالب. للبدء بضبط الإعدادات اذهب إلى: Dashboard > WooCommerce > Settings > Emails يحتوي هذا التبويب أيضًا على ثلاثة أقسام: Email Notifications يحتوي هذا القسم على قائمة الإشعارات التي ستصلك (أو تُرسل للعميل) على البريد الإلكتروني والتي يمكنك تفعيلها أو إلغاء تفعيلها. ولكل رسالة إشعار، تستطيع تعديل بعض الخيارات مثل عنوان البريد الإلكتروني الذي سيُرسل إليه، عنوان الرسالة، إلخ: يمكن أن يقوم WooCommerce بإرسال إشعارات في الحالات التالية: الطلبات الجديدة New Order الطلبات الملغية Cancelled Order الطلبات الفاشلة Failed Order الطلبات الجارية Processing Order الطلبات المكتملة Completed Order الطلبات المستردّة Refunded Order فواتير العملاء Customer Invoice ملاحظات العملاء Customer Note استعادة الكلمة السرية Reset Password إنشاء حسابات جديدة New Account ملاحظة: الإشعارات الثلاثة الأولى تُرسل إلى عنوان البريد الإلكتروني الذي تحدده، أما الإشعارات المتبقية فتُرسل إلى العميل. يملك كل إشعار نفس الإعدادات تقريبًا، مع بعض الاختلافات التي سنأتي إلى ذكرها. بإمكانك الوصول إلى الإعدادات أما بالنقر على عنوان الإشعار في القائمة، أو النقر على أيقونة الترس المقابلة للعنوان في أقصى اليمين. لنبدأ بإعدادات إشعار New Order أولًا: الخيار الأول هو تفعيل/ إلغاء تفعيل هذا الإشعار بتأشير/إلغاء تأشير الخيار Enable this email notification. يفضّل معظم أصحاب المتاجر الإلكترونية استلام إشعار عن الحصول على طلب جديد، مع ذلك، الخيار راجع لك. في حقل (Recipient(s قم بإدخال عنوان/عناوين البريد الإلكتروني الذي تريد استلام الإشعار عليه. إذا قمت بإدخال أكثر من عنوان، تأكّد من فصلها بواسطة الفاصلة (,). في حقل Subject قم بإدخال عنوان الإشعار الذي سيصلك، بإمكانك استخدام العنوان الافتراضي الذي يحتوي على عنوان الموقع+ "New Customer Order"+ رقم الطلب+ تاريخ الطلب في نهايته: {site_title}] New customer order ({order_number}) - {order_date} أو بإمكانك إنشاء واحد مخصص بإدخال نص العنوان والتفاصيل الإضافية المرغوبة. من حقل Email Header يمكنك التحكم في العنوان الرئيسي لإشعار البريد الإلكتروني، ووظيفته تشبه وظيفة العنوان Subject. بإمكانك تركه فارغًا لاستخدام العنوان الرئيسي الافتراضي. لهذا الحقل المزيد من المعلومات التقنية التي سنتجاوزها لأنّها غير مهمة في هذا الدرس. من حقل Email Type يمكنك التحكّم في الصيغة التي سيرسل بها البريد الإلكتروني. الخيارات المتاحة هي Plain Text (نص عادي)، HTML، أو Multipart (أجزاء متعددة). أخيرًا، هناك خياران تحت حقل Email Type، وهما يعتمدان على صيغة البريد الإلكتروني التي اخترتها. الأول Copy file to theme والذي من خلاله تقوم بنسخ قالب البريد الإلكتروني الحالي إلى مجلد القوالب للقيام بتخصيصه أكثر ممّا توفّره هذه الإعدادات. إذا قمت بالفعل بنسخ مجلّد woocommerce/templates/emails بالكامل إلى مجلد القوالب، فلا حاجة إلى نسخ القوالب بشكل منفصل مجددا. وفي تلك الحالة ستجد زر Delete template file بدلًا من Copy file to theme. الخيار الثاني هو View template ويمكنك من خلاله عرض شيفرة قالب البريد الإلكتروني الحالي وستتمكّن من تعديل القالب إذا قمت بالفعل بنسخه إلى مجلد القوالب. وبالرغم من أننا لا ننصحك بتغيير شفرة القوالب مباشرة من هذه النافذة، لكن تأكّد من امتلاك نسخ احتياطية لهذه القوالب والنقر على Save Changes بعد الانتهاء من تخصيص الشفرة إذا قمت بتعديل القالب من ووردبريس. كما ذكرنا سابقًا، تحتوي بقية إعدادات إشعارات البريد الإلكتروني على نفس الحقول التي قم بشرحها في إعدادات New Order. هناك بعض الاستثناءات في إعدادات إشعار الطلبات المكتملة، الطلبات المستردّة، وفواتير العملاء. هناك حقلان إضافيان في إعدادات Completed Order: هذه الحقول خاصّة بعنوان الإشعار (Subject (downloadable والعنوان الرئيسي (Email Heading (downloadable للمنتجات الرقمية القابلة للتنزيل. وفي إعدادات Refunded Order، هناك حقلان للعنوان وحقلان للعنوان الرئيسي: الحقلان الأولان هما للطلبات المستردّة بشكل جزئي Partial Refund والآخران للطلبات المستردّة بشكل كلي Full Refund، مما يتيح لك مستوى تحكّم جيّد في النص المرسل إلى العميل في الحالتين، الاسترداد الجزئي، والاسترداد الكلي. أما إعدادات Customer Invoice فتحتوي على حقلين إضافيين، الأول لعنوان إشعار الفاتورة المدفوعة (Email Subject (paid والثاني للعنوان الرئيسي لإشعار الفاتورة المدفوعة (Email Heading (paid: بعد الانتهاء من ضبط إعدادات كل إشعار، انقر على Save Changes لحفظ التغييرات. Email Sender Options من خيارات مرسل البريد الإلكتروني يمكنك التحكّم في اسم المرسل من حقل "From" Name بالإضافة إلى عنوان البريد الإلكتروني للمرسل "From" Address. من المهم ضبط هذه الإعدادات لأنّها ستُعرض على العملاء في صندوق الوارد لبريدهم الإلكتروني: على سبيل المثال، من خلال الإعدادات أعلاه، سيستلم العميل بريد إلكتروني من المرسل "My Store" صاحب عنوان البريد الإلكتروني " example@gmail.com". Email Template من هذه الخيارات يمكنك تخصيص قالب البريد الإلكتروني الافتراضي الذي يستخدمه WooCommerce: بإمكانك إدخال رابط لصورة الترويسة في حقل Header Image، أو رفع صورة من خلال المسار: Dashboard > Media > Add New وكذلك بإمكانك تخصيص نص هامش القالب بإدخال النص المرغوب في حقل Footer Text وسيظهر هذا النص في الجزء السفلي من جميع رسائل البريد الإلكتروني التي ترسلها. بعد ذلك يمكنك اختيار لون الأساس Base Color، لون الخلفية Background Color، لون خلفية متن البريد الإلكتروني Body Background Color، وكذلك لون نص المتن Body Text Color. بإمكانك معاينة القالب أثناء تخصيصه بالنقر على رابط Click here to preview your email template. بإمكانك عمل المزيد من التخصيصات المعقّدة بنسخ قالب البريد الإلكتروني إلى مجلد القوالب. قم بنسخ مجلد emails من على الامتداد woocommerce/templates/emails ولصقه في مجلد قالبك على الامتداد "theme name"/woocommerce/emails. اعلم فقط أنّ ذلك سيؤدي إلى إلغاء أي تخصيص قمت به من إعدادات قالب البريد الإلكتروني Email Template المذكورة. بعد الانتهاء من ضبط جميع الاعدادات انقر على Save Changes لحفظ التغييرات. إعدادات API من هذا التبويب تستطيع تفعيل REST API، بالإضافة إلى إنشاء Keys و Webhooks لتطبيقاتك. للوصول إلى تبويب إعدادات API اذهب إلى Dashboard > WooCommerce > Settings > API يحتوي هذا التبويب على ثلاثة تبويبات فرعية: Settings من هذا التبويب تستطيع تفعيل/إلغاء تفعيل REST API بتأشير/إلغاء تأشير الخيار Enable the REST API: إنّ هذا الخيار ضروري لكي تعمل تطبيقات للهاتف المحمول في متجرك. فهو يتيح للتطبيقات الخارجية معرفة أشياء مثل حالة المخزون، الطلبات، معلومات المنتجات، وغيرها الكثير. يُعتبر RESU API من الأجزاء الفعّالة في WooCommerce ويمكن أن يحقق الكثير من الأشياء الرائعة. Keys/Apps يتيح لك هذا التبويب إدارة التحكّم في الوصول إلى REST API: ترتبط المفاتيح keys بالمستخدمين على موقعك. ولإنشاء مفتاح انقر على Add Key: في حقل User، قم بتحديد المستخدم الذي تريد توليد مفتاح له، ثم قم بإضافة وصف في حقل Description. من قائمة Permissions قم بتحديد مستوى الوصول لهذا المفتاح. الخيارات المتاحة هي Read (قراءة)، Write (كتابة)، أو Read/Write (قراءة/كتابة). بعد ذلك انقر على زر Generate API Key، وسيقوم WooCommerce بتوليد مفتاح API لذلك المستخدم. بعد توليد المفتاح ستلاحظ وجود مفتاح المستهلك Consumer Key، الرمز السري للمستهلك Consumer Secret، رمز QR، بالإضافة إلى زر إبطال المفتاح Revoke API Key: قم بوضع مفتاح المستهلك والرمز السري للمستهلك في التطبيق الذي يستخدم WooCommerce API. وكذلك يجب أن يطلب ذلك التطبيق عنوان URL لمتجرك. Webhooks Webhooks هي عبارة عن أداة يمكنك استخدامها لإرسال إشعار حول حدث ما إلى عنوان URL من اختيارك. على سبيل المثال عند إنشاء منتج جديد، أو إنشاء قسيمة جديدة. وهي توفّر طريقة سهلة للتكامل مع الخدمات الخارجية وAPI الخارجية الأخرى التي تدعم Webhooks: سنقوم بإنشاء Webhook وهمي لاستخدامه لنشر منشور على منصة تواصل اجتماعي من اختيارنا عندما نقوم بإنشاء منتج جديد. وهذه من الطرق البسيطة للترويج لمنتجاتنا الجديدة عبر الإنترنت بصورة تلقائية. انقر على زر Add Webhook: قم بإدخال عنوان مناسب في حقل Title ثم حدّد الحالة من قائمة Status حسب حاجتك. خيارات الحالة المتوفّرة هي Active (مفعّل)، Paused (متوقف مؤقتًا)، أو Disabled (غير مفعّل). إذا رغبت في تشغيل Webhook تأكّد من اختيار الحالة Active. الخطوة التالية هي تحديد الموضوع من قائمة Topic، ويقصد به الحدث الذي سيشغّل Webhook. تتضمن القائمة العديد من الخيارات، مثل إنشاء طلب Order Created، حذف منتج Product Deleted، أو تحديث عميل Customer Updated. وكذلك يوجد خيار تحديد موضوع مخصص Custom لغرض إنشاء Webhook أكثر تقدّمًا. سنحدد Product Created مثلا. الحقل التالي هو Delivery URL ويُستخدم لتحديد العنوان الذي سيتم توصيل حمل Webhook إليه. في مثالنا هذا سنبحث في توثيق المطوّرين لمنصة التواصل الاجتماعي التي اخترناها للعثور على Webhook يمكننا استخدامه للتوصيل. في حقل Secret قم بإعداد مفتاح سرّي يدويًا، أو اترك الحقل فارغًا وسيتم استخدام مفتاح Consumer Secret للمستخدم الحالي والذي يوجد في قسم API لحساب المستخدم على WooCommerce. بعد الانتهاء من ضبط الإعدادات أعلاه، انقر على Save Webhook لبدء تشغيله ورصد المنتجات الجديدة. عندما يُشغّل Webhook بإمكانك العودة إلى هذه النافذة لمشاهدة التسجيلات في قسم Webhook Logs. إذ ستكون هناك مدخلات تبيّن وقت تشغيله وما هي استجابات الخدمات الأخرى التي تتكامل معها. وهذه التسجيلات توفّر لك طريقة سهلة لاختبار Webhook وإصلاحه. لن نكون قادرين على تغطية جميع التفاصيل التقنية المتعلّقة بالتكامل مع الخدمات الأخرى، لأنها ستكون معقّدة جدًا بالنسبة لغرض هذا الدرس. بهذا تكون قد تمكّنت من ضبط جميع إعدادات متجرك الإلكتروني على WooCommerce. سنغطّي في الدرس القادم -إن شاء الله- كيفية إنشاء المنتجات وسنلقي نظرة عامة على أنواعها.
  11. تعرفنا في الدرسين الماضيين عن مفهوم حاويات لينكس (LXC) ومبدأ عملها ثم شرعنا في كيفية البدء في استعمالها. سنعرج في هذا الدرس إلى كيفية استغلال مختلف نقاط ومراحل دورة حياة حاويات لينكس (LXC) لإدراج بعض من الإضافات (hooks) التي تقوم بإجراء مهمة ما. إضافات إدارة دورة التشغيل بدءًا من أوبنتو 12.10، أصبح من الممكن تعريف إضافات (hooks) تُنفَّذ عند نقاط محددة من دورة تشغيل الحاوية: الإضافات التي تحدث قبل التشغيل تُنفَّذ من مجال أسماء المضيف قبل أن تُنشَأ طرفيات أو نقاط وصل الحاويات؛ إذا أُجري أي وصل في هذه الفترة، فيجب أن يُنظَّف في إضافة تحدث بعد إيقاف التشغيل. الإضافات التي تحدث قبل الوصل تُنفَّذ في مجال أسماء الحاوية، لكن قبل أن يوصل جذر نظام الملفات؛ سينظف أي وصل لنظام الملفات في هذه الفترة تلقائيًا عند إيقاف تشغيل الحاوية. إضافات الوصل هي إضافات تنفذ بعد وصل أنظمة ملفات الحاوية، لكن قبل أن تُنفِّذ الحاوية pivot_root لتغيير جذر نظام ملفاتها. الإضافات التي تحدث بعد إيقاف التشغيل ستنفَّذ بعد إيقاف تشغيل الحاوية. إذا أعادت أيّة إضافة خطأً، فسيلغى تشغيل الحاوية، لكن أي إضافة تحدث بعد إيقاف التشغيل ستنفَّذ، ستُسجَّل أيّة مخرجات تولد من السكربت بأولوية التنقيح (debug). رجاءً راجع صفحة دليل lxc.container.conf لصيغة ملف الضبط التي سيحدد الإضافات؛ يمكن أن تأتي بعض أمثلة الإضافات في الحزمة lxc لتخدم كمثال حول طريقة كتابة إحدى تلك الإضافات. سطر الأوامر لدى الحاويات عدد مضبوط من «أسطر الأوامر» (consoles)؛ أحدها موجودٌ دائمًا في ‎/dev/console؛ الذي يظهر في الطرفية عندما تُشغِّل lxc-start ما لم تحدد الخيار ‎-d؛ يمكن إعادة توجيه ناتج خرج ‎/dev/console إلى ملف باستخدام ‎-c console-file في الأمر lxc-start؛ يمكن تحديد عدد إضافي من أسطر الأوامر باستخدام المتغير lxc.tty المضبوط عادةً إلى 4؛ يمكن أن تظهر أسطر الأوامر تلك في ‎/dev/ttyN (حيث N أكبر أو تساوي 1، وأصغر أو تساوي 4)؛ ولتسجيل الدخول إلى console 3 من المضيف، فنفِّذ الأمر: sudo lxc-console -n container -t 3 إذا لم تحدد الخيار ‎-t N، فسيتم اختيار سطر أوامر غير مُستخدَم؛ للخروج منه، استخدام عبارة الخروج Ctrl-a q؛ لاحظ أن عبارة الخروج لا تعمل في سطر الأوامر الناتج عن lxc-start دون الخيار ‎-d. استكشاف الأخطاء التسجيل إذا حدث شيء ما خاطئ عند تشغيل حاوية، فإن أول خطوة هي الحصول على سجل كامل من LXC: sudo lxc-start -n C1 -l trace -o debug.out هذا سيؤدي إلى جعل lxc يسجل في أعلى درجة إسهاب، التي هي trace، وسيكون ملف التخزين هو ملف باسم «debug.out»، إذا كان الملف debug.out موجودًا مسبقًا، فستُضاف معلومات السجل الجديد إليه. مراقبة حالة الحاوية هنالك أمران متوفران لمراقبة تغيرات حالة الحاوية: lxc-monitor الذي يراقب حاويةً أو أكثر ﻷي تغيرات في الحالة، حيث يأخذ اسم الحاوية مع الخيار ‎-n كالعادة؛ لكن في هذا الحالة، يمكن أن يكون اسم الحاوية تعبيرًا نمطيًا من نمط POSIX للسماح بمراقبة مجموعة من الحاويات؛ يستمر lxc-monitor بالعمل ويعرض تغيرات حالات الحاويات؛ أما lxc-wait فينتظر تغيِّرًا محددًا في الحالة ثم ينتهي تنفيذه؛ على سبيل المثال: sudo lxc-monitor -n cont[0-5]* هذا سيعرض جميع تغيرات الحالة لأي حاوية تطابق التعبير النمطي؛ بينما: sudo lxc-wait -n cont1 -s 'STOPPED|FROZEN' سينتظر إلى أن تتغير حالة الحاوية cont1 إلى STOPPED أو FROZEN ثم ينتهي. الوصل من الممكن في أوبنتو 14.04 الوصل (attach) إلى مجال أسماء حاوية، أبسط طريقة هي تنفيذ: sudo lxc-attach -n C1 الذي سيبدأ صدفة موصولة لمجال الحاوية C1، أو داخل الحاوية؛ آلية عمل الوصل هي معقدة جدًا، مما يسمح بوصل مجموعة فرعية من مجالات أسماء (namespaces) الحاوية ونمط الحماية (security context)، راجع صفحة الدليل لمزيدٍ من المعلومات. درجة إسهاب init في الحاوية إذا أكمل LXC بدء تشغيل الحاوية، لكن فشل إكمال تنفيذ init فيها (على سبيل المثال، لم يُعرَض محث الدخول)، فمن المفيد طلب درجة إسهاب أكبر من عملية init، فلحاوية upstart: sudo lxc-start -n C1 /sbin/init loglevel=debug يمكنك أيضًا بدء تشغيل برامج مختلفة عن init، على سبيل المثال: sudo lxc-start -n C1 /bin/bash sudo lxc-start -n C1 /bin/sleep 100 sudo lxc-start -n C1 /bin/cat /proc/1/status واجهة LXC البرمجية API يمكن الوصول إلى غالبية وظائف LXC عبر واجهة برمجية (API) مُصدَّرة من liblxc التي تكون ارتباطاتها متوفرة لعدة لغات برمجية بما فيها بايثون، و lua، وروبي، و go. ما يلي هو مثال عن استخدام ربط بايثون (المتوفرة في حزمة python3-lxc)، التي تُنشِئ وتبدأ حاوية، ثم تنتظر إلى أن يوقف تشغيلها: # sudo python3 Python 3.2.3 (default, Aug 28 2012, 08:26:03) [GCC 4.7.1 20120814 (prerelease)] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import lxc __main__:1: Warning: The python-lxc API isn't yet stable and may change at any p oint in the future. >>> c=lxc.Container("C1") >>> c.create("ubuntu") True >>> c.start() True >>> c.wait("STOPPED") True الحماية يربط مجال الأسماء المعرفات (ids) إلى الموارد؛ لكنه لا يوفر للحاوية أي معرِّف يمكنه أن يشير إلى المورد، لذلك يمكن أن يُحمى المورد؛ وهذا هو أساس بعض الحماية الموفرة لمستخدمي الحاوية؛ على سبيل المثال، مجال أسماء IPC معزول تمامًا؛ لكن مجالات أسماء أخرى فيها بعض «التسربات» (leaks) التي تسمح للامتيازات بأن تُستخرَج بشكل غير ملائم من الحاوية إلى حاوية أخرى، أو إلى المضيف. افتراضيًا، تُشغَّل حاويات LXC بسياسة AppArmor التي تقيّد بعض الأفعال، تفاصيل دمج AppArmor مع LXC موجودة في قسم «AppArmor» في الدرس السابق، الحاويات دون امتيازات تربط الجذر في الحاوية إلى مستخدم دون امتيازات في المضيف، وهذا يمنع الوصول إلى ملفات ‎/proc و ‎/sys التي تمثل موارد المضيف، وغيرها من الملفات المملوكة من الجذر في المضيف. الثغرات في استدعاءات النظام ميزة أساسية من مزايا الحاويات أنها تشارك النواة مع المضيف؛ وهذا يعني أنه إذا حوت النواة على أيّة ثغرات في استدعاءات النظام (system calls)، فيمكن أن تستغلها الحاوية؛ وبعد أن تتحكم حاوية بالنواة، فيمكنها أن تسيطر سيطرةً كاملةً على أي مورد معروف للمضيف! بدءًا من أوبنتو 12.10، يمكن أن تقيَّد الحاوية من مرشِّح seccomp، إن Seccomp هو ميزة جديدة في النواة التي تُرشِّح استدعاءات النظام التي يمكن أن تُستخدَم من المهمة وأولادها؛ بينما يتوقع الوصول إلى إدارة سهلة ومحسنة للسياسة في المستقبل القريب، لكن تحتوي السياسة الحالية على قائمة بيضاء بسيطة لأرقام استدعاءات النظام؛ يبدأ ملف السياسة برقم الإصدار (الذي يجب أن يكون 1) في أول سطر ونوع السياسة (الذي يجب أن يكون whitelist) في ثاني سطر؛ وتُلحَق بقائمة أرقام، كل رقم في سطر. سنحتاج عادةً لتشغيل حاوية بتوزيعة كاملة إلى عدد كبير من استدعاءات النظام؛ لكن لحاويات البرامج، يمكن أن نقلل عدد استدعاءات النظام المتوفرة إلى رقم قليل؛ وحتى للحاويات التي تشغل توزيعات كاملة يمكن الحصول على فوائد أمنية إذا حذفت -على سبيل المثال- استدعاءات النظام المتوافقة مع 32 بت في حاوية 64 بت؛ راجع صفحة دليل lxc.container.conf للمزيد من التفاصيل حول كيفية ضبط الحاوية لتستخدم seccomp؛ لن تُحمَّل افتراضيًا سياسة seccomp. مصادر كتاب «Secure Containers Cookbook» يشرح كيفية استخدام أنماط الحماية لجعل الحاويات أكثر أمانًا. مشروع LXC مُستضاف في linuxcontainers.org. مشاكل LXC الأمنية مذكورة ومناقشة في صفحة وكي «LXC Security». ترجمة -وبتصرف- للمقال Ubuntu Server Guide: LXC.
  12. توفر الكثير من المواقع واجهات برمجية Application Programming Interface ،API بهدف إتاحة موارد الموقع لتطبيقات خارجية؛ قد تكون تطبيقات ٍللجوال، أجهزةً لوحية، أو أجهزةً مكتبية. قد نود مثلا إنشاء تطبيق للجوال نعرض فيه منتجات الموقع. نستخدم لغة البرمجة المناسبة للتطبيق (جافا مثلا لتطبيقات أندرويد) الذي يرسل طلبات لواجهتنا البرمجية يحصُل بموجبها على بيانات يتولى هو طريقة عرضها. نقول إن تطبيق الجوال في هذه الحالة يستهلك Consume الواجهة البرمجية. هذا الدرس جزء من سلسلة تعلم Laravel والتي تنتهج مبدأ "أفضل وسيلة للتعلم هي الممارسة"، حيث ستكون ممارستنا عبارة عن إنشاء تطبيق ويب للتسوق مع ميزة سلة المشتريات. يتكون فهرس السلسلة من التالي: مدخل إلى Laravel 5.تثبيت Laravel وإعداده على كلّ من Windows وUbuntu.أساسيات بناء تطبيق باستخدام Laravel.إنشاء روابط محسنة لمحركات البحث (SEO) في إطار عمل Laravel.نظام Blade للقوالب.تهجير قواعد البيانات في Laravel. استخدام Eloquent ORM لإدخال البيانات في قاعدة البيانات، تحديثها أو حذفها. إنشاء سلة مشتريات في Laravel.الاستيثاق في Laravel. إنشاء واجهة لبرمجة التطبيقات API في Laravel. (هذا الدرس)إنشاء مدوّنة باستخدام Laravel.استخدام AngularJS واجهةً أمامية Front end لتطبيق Laravel.الدوّال المساعدة المخصّصة في Laravel.استخدام مكتبة Faker في تطبيق Laravel لتوليد بيانات وهمية قصدَ الاختبار. نغطي في هذا الدرس المواضيع التالية: ماهي واجهات REST البرمجية؟الممارسات المنصوح بها في واجهات REST البرمجية.إنشاء واجهة برمجية لمشروع Larashop.ماهي واجهات REST البرمجية؟توصف الكثير من الواجهات البرمجية بأنها RESTful، فما المقصود بهذا الوصف؟ تختصر REST العبارة Representational State Transition (النقل التمثيلي للحالة) وهي طريقة لتصميم البرمجيات تعرِّف معاييرَ يجب على خدمات الويب اتباعها من أجل أداء أعلى وصيانة أسهل. تعتمد بنية التطبيقات REST على بروتوكول HTTP لإرسال الطلبات والحصول على إجابات؛ ومن أهم القيود التي يجب الالتزام بها في تطبيقات REST: العمل حسب مبدأ خادوم-عميل Server-Client، انعدام الحالة Stateless وتوحيد الواجهات (إضافة لقيود أخرى). مبدأ خادوم-عميل: يجب التفريق بين واجهة المستخدم والخادوم الذي يخزن البيانات ويطبق العمليات عليها.انعدام الحالة: يجب أن يحوي الطلب الموجّه من العميل إلى الخادوم كل المعلومات الضرورية ليستطيع الخادوم فهمه والإجابة عليه؛ دون الحاجة لسياق محفوظ على الخادوم (لفهم الطلب).توحيد الواجهات بمعنى أن كل مورد على الخادوم يمكن تعريفه فرديا واستغلاله عبر بيانات تمثله يحتفظ بها العميل. يجب أن تكون الطلبات واضحة يمكن فهمها والإجابة عنها دون الحاجة لمعلومات خارجة عنها. يدخل ضمن توحيد الواجهات أيضا افتراضُ العميل أن أي إجراء Action غيرُ متوفر على الخادوم، ما لم يصّرح هذا الأخير بتوفره.تساهم هذه القيود (والقيود الأخرى التي تعرفها بنية REST) في تسهيل عمل الواجهات، الرفع من أدائها، تيسير الصيانة وقابلية التمدد Scalability. سنرى في الفقرة التالية توصيات لبناء واجهات برمجية تساعد في احترام مبادئ REST. ملحوظة: يكثُر وصف الواجهات البرمجية بأنها RESTful (تلتزم بقيود REST) دون أن تلتزم بكامل القيود التي تعرِّفها بنية REST، وهو ما يجعلها أقرب لواجهات شبيهة لـREST منها لواجهات RESTful. الممارسات المنصوح بها في واجهات REST البرمجيةيُساعد الالتزام بالممارسات التالية في بناء واجهة تطبيقات برمجية ذات أداء عال وقابلية كبيرة للتمدد والصيانة. استخدام إجراءات HTTP لتحديد العمل الذي سيؤديه الخادوم: GET للحصول على مورد، POST لإنشاء مورد جديد، PUT لتحديث مورد وDELETE لحذفه.أَصْدَرَة Versioning الواجهة: يساعد استخدام إصدارات في عدم كسر التطبيقات التي تستهلك الواجهة البرمجية. يحدّد العميل إصدار واجهة التطبيق الذي يود العمل عليه مما يسمح بإحداث تغييرات على الخادوم تضمَّن في إصدار جديد دون أن يتوقف عملاء الواجهة البرمجية.من المتعارف عليه استخدام أسماء جموع لوصف الموارد، api.mysite/v1/products مثلا للمنتجات. ليس واجبا اتباع هذا العرف لكن الأهم هو تناسق تسمية الموارد: لا تخلط بين أسماء مفردة للموارد وجموع.استخدام الإجابات الجزئية: طلب العميل اسم المنتج؟ أرسل اسم المنتج فقط، وليس كامل بيانات المنتج، في الإجابة.استخدام رموز الحالة: تسهّل رموز الحالة في HTTP التخاطب مع العميل. عولج الطلب على النحو الأمثل؟ أرسِل الرمز 200 في الإجابة. طلب العميل إنشاء مورد وتم الأمر؟ أرسل الرمز 201 في الإجابة؛ وهكذا. راجع هذا الرابط للمزيد من رموز الحالة في HTTP.ضع حدًّا أقصى لعدد الطلبات القادمة من نفس عنوان IP في الواجهات المفتوحة للجميع. يساعد هذا الأمر في التصدي للعملاء الذي يفرطون في استخدام واجهة تطبيقاتك البرمجية. ينصح أيضا بحظر عناوين IP ذات السلوك المشبوه حتى لا يؤثر على بقية المستخدمين.قد يُختلف حول هذه التوصية، إلا أنه يُنصح باستخدام صيغة JSON لإرسال البيانات في الإجابة عن الطلب، ما لم يحدّد العميل عكس ذلك.خبِّئ Cache نتائج طلبات GET التي لا تتغير كثيرا. ربما تكون قائمة العلامات التجارية في مواقع التسوق مثالا جيدا للبيانات التي يجب تخبئتها (قد تمضي أشهر دون الحاجة لإضافة علامة تجارية جديدة).واجهة Larashop البرمجيةسننشئ في هذه الفقرة واجهة تطبيقات برمجية لمشروع Larashop. تشتمل الواجهة على المسارات أدناه. تستخدم جميع المسارات إجراء GET للحصول على المورد. التسلسل المورد الرابط الوصف رمز الحالة1Product/api/v1/productsسرد لائحة بالمنتجات وخاصياتها2002Product/api/v1/products/1سرد خاصيات المنتج رقم 12003Category/api/v1/categoriesسرد لائحة بتصنيفات المنتجات2004Category/api/v1/categories/1التصنيف ذو المعرّف 1200لاحظ أننا لم نتح إمكانية التعديل على الموارد عبر الواجهة. تشير v1 في المسارات إلى رقم الإصدار 1. نفتح ملف المسارات routes.php ونعدّله بإضافة المسارات التالية: // API routes... Route::get('/api/v1/products/{id?}', ['middleware' => 'auth.basic', function($id = null) { if ($id == null) { $products = App\Product::all(array('id', 'name', 'price')); } else { $products = App\Product::find($id, array('id', 'name', 'price')); } return Response::json(array( 'error' => false, 'products' => $products, 'status_code' => 200 )); }]); Route::get('/api/v1/categories/{id?}', ['middleware' => 'auth.basic', function($id = null) { if ($id == null) { $categories = App\Category::all(array('id', 'name')); } else { $categories = App\Category::find($id, array('id', 'name')); } return Response::json(array( 'error' => false, 'user' => $categories, 'status_code' => 200 )); }]);يعرف المسار Route::get('/api/v1/products/{id?}', ['middleware' => 'auth.basic', function($id = null)رابطًا يطلب المنتجات مع معرّف اختياري id. يُستخدم المعرف لطلب منتج واحد وفي حال عدم ذكره ترجِع واجهة التطبيقات جميع المنتجات. نحمي المورد بالتعليمة 'middleware' => 'auth.basic' التي تستوثق من العميل. حددنا نمط الاستيثاق بـauth.basic لاستخدام بريد المستخدِم (حقل email في جدول users) مع كلمة السر. يستدعي كل مسار النموذج المناسب للعثور على البيانات في القاعدة ثم نرسل الإجابة بصيغة JSON بالتعليمة Response::json. عند طلب الرابط http://larashop.dev/api/v1/products ستظهر نافذة تطلب إدخال بريد المستخدم وكلمة سره. استخدم الحساب الذي أنشأته في درس الاستيثاق وستظهر النتيجة التالية في المتصفح (بعد التنسيق) { "error":false, "products": [ { "id":"1", "name":"Mini skirt black edition", "price":"35" }, { "id":"2", "name":"T-shirt blue edition", "price":"64" }, { "id":"3", "name":"Sleeveless Colorblock Scuba", "price":"13" } ], "status_code":200 }حصلنا على إجابة بصيغة JSON للطلب الذي أرسلناه من أجل الحصول على منتجات الموقع. يشير الرمز 200 إلى أن معالجة الطلب تمّت دون مشاكل. يمكن للعميل الآن تنسيق الإجابة لعرضها بطريقة مناسبة. خاتمةوضعنا في هذا الدرس أساسا يمكن البناء عليه لإنشاء واجهات برمجية أكثر تطورا. يتلخص إنشاء واجهات برمجية في Laravel في تعريف المسارات، استخدام النماذج للحصول على البيانات المطلوبة ثم تهيئة الإجابة بصيغة JSON ثم إرسالها. ترجمة -وبتصرّف- للمقال Laravel 5 REST API لصاحبه Rodrick Kazembe.
  13. يهدف هذا الدرس إلى بناء تطبيق CRUD (إدراج بيانات في قاعدة بيانات، القراءة منها، تحديثها وحذفها؛ اختصار لـUpdate، Read، Create وDelete). التطبيق عبارة عن واجهة لإدارة لائحة من الموظفين: إضافة موظّف، تحرير بياناته أو حذفها. سنستخدم Laravel 5 للنهاية الخلفية Backend و AngularJS للنهاية الأمامية Frontend. سنستخدم أيضا Bootstrap لإضافة لمسة جمالية إلى التطبيق. هذا الدرس جزء من سلسلة تعلم Laravel والتي تنتهج مبدأ "أفضل وسيلة للتعلم هي الممارسة"، حيث ستكون ممارستنا عبارة عن إنشاء تطبيق ويب للتسوق مع ميزة سلة المشتريات. يتكون فهرس السلسلة من التالي: مدخل إلى Laravel 5.تثبيت Laravel وإعداده على كلّ من Windows وUbuntu.أساسيات بناء تطبيق باستخدام Laravel.إنشاء روابط محسنة لمحركات البحث (SEO) في إطار عمل Laravel.نظام Blade للقوالب.تهجير قواعد البيانات في Laravel.استخدام Eloquent ORM لإدخال البيانات في قاعدة البيانات، تحديثها أو حذفها.إنشاء سلة مشتريات في Laravel.الاستيثاق في Laravel.إنشاء واجهة لبرمجة التطبيقات API في Laravel.إنشاء مدوّنة باستخدام Laravel.استخدام AngularJS واجهةً أمامية Front end لتطبيق Laravel. (هذا الدرس)الدوّال المساعدة المخصّصة في Laravel.استخدام مكتبة Faker في تطبيق Laravel لتوليد بيانات وهمية قصدَ الاختبار. AngularJS هو إطار عمل جافاسكريبت قويّ وفعّال، يتبنى مبدأ النموذج-العرض-المتحكم MVC ويعمل لدى العميل (المتصفّح). للاطلاع على المزيد عن AngularJS تابع سلسلة دروس مدخل إلى تعلم AngularJS . يفترض هذا الدرس معرفة أساسيات Laravel 5، تثبيت كلّ من PHP، MySQL، ِApache وComposer. يغطي الدرس المواضيع التالية: إنشاء نهاية خلفية في Laravel 5، ستكون عبارة عن واجهة برمجية API.بنية تطبيق AngularJS.واجهة برمجية باستخدام Laravel 5ننشئ في هذه الفقرة النهاية الخلفية للتطبيق الذي نعمل عليه. سيتولى Laravel هذه المهمة. نبدأ بإنشاء تطبيق Laravel ثم نعدّه لتوفير الواجهة البرمجية. الخطوة الأولى: إنشاء تطبيق Laravelننفذ الأمر التالي في أصل المستند Document root لإنشاء مشروع Laravel باسم angulara: composer create-project laravel/laravel angularaراجع درس تثبيت Laravel 5 وإعداده على Windows وUbuntu للمزيد حول تثبيت Laravel وإعداده. سنفترض في بقية هذا الدرس تثبيت Laravel على المنصة المفضلة لديك وإنشاء مضيف افتراضي باسم angulara.dev. الخطوة التالية هي إعداد تهجير قاعدة البيانات. الخطوة الثانية: إعداد قاعدة البياناتستحتاج لقاعدة بيانات للعمل مع التطبيق. استخدم برنامج إدارة قاعدة البيانات المفضل لديك أو نفذ الأمر التالي في سطر أوامر MySQL لإنشاء قاعدة بيانات للمشروع: CREATE DATABASE `angulara`;ننتقل الآن لإعداد Laravel لكي يعمل مع قاعدة البيانات. افتح ملف env. في مجلد تطبيق Laravel وعدل القيم التالية بما يناسب: DB_HOST=localhost // اسم قاعدة البيانات DB_DATABASE=angulara // اسم مستخدم في MySQL لديه صلاحيات الكتابة والقراءة في القاعدة أعلاه DB_USERNAME=root // كلمة سر المستخدم DB_PASSWORD=melodyاحفظ التعديلات. نستغل أداة Artisan لإنشاء ملف تهجير خاص بجدول يحوي بيانات الموظفين employees. نفذ الأمر التالي لإنشاء ملف التهجير: php artisan make:migration create_employees_tableستحصُل على الرسالة التالية التي تعرض اسم الملف المنشأ: Created Migration: 2016_01_05_203637_create_employees_tableينشئ الأمر ملفا في المجلد database/migrations، نفتحه للتعديل عليه. عدّل المحتوى كالتالي: <?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateEmployeesTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('employees', function (Blueprint $table) { $table->increments('id'); $table->string('name')->unique(); $table->string('email')->unique(); $table->string('contact_number'); $table->string('position'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::drop('employees'); } }احفظ التعديلات ثم نفذ أمر ِArtisan التالي: php artisan migrateينفذ الأمر التهجيرات وينشئ جدول بيانات الموظفين. تظهر الرسائل التالية بعد تنفيذ الأمر: Migration table created successfully. Migrated: 2014_10_12_000000_create_users_table Migrated: 2014_10_12_100000_create_password_resets_table Migrated: 2016_01_05_203637_create_employees_tableتحقق من قاعدة بيانات MySQL، ستجد أن جدولا جديدا أُدرِج في القاعدة. الخطوة الثالثة: إنشاء واجهة برمجيةننشئ متحكما Controller للتطبيق، سنسميه Employees: php artisan make:controller Employeesثم يأتي دور النموذج Model: php artisan make:model Employeeسنضيف مصفوفة في النموذج لتفعيل الإسناد الشامل؛ افتح ملف النموذج وعدّله على النحو التالي: <?php namespace App; use Illuminate\Database\Eloquent\Model; class Employee extends Model { protected $fillable = array('id', 'name', 'email','contact_number','position'); }احفظ التعديلات. ننتقل للتعديل على المتحكم. افتح ملف المتحكم Employees وعدله ليصبح كالتالي <?php namespace App\Http\Controllers; use App\Employee; use Illuminate\Http\Request; use App\Http\Requests; use App\Http\Controllers\Controller; class Employees extends Controller { /** * Display a listing of the resource. * * @return Response */ public function index($id = null) { if ($id == null) { return Employee::orderBy('id', 'asc')->get(); } else { return $this->show($id); } } /** * Store a newly created resource in storage. * * @param Request $request * @return Response */ public function store(Request $request) { $employee = new Employee; $employee->name = $request->input('name'); $employee->email = $request->input('email'); $employee->contact_number = $request->input('contact_number'); $employee->position = $request->input('position'); $employee->save(); return 'Employee record successfully created with id ' . $employee->id; } /** * Display the specified resource. * * @param int $id * @return Response */ public function show($id) { return Employee::find($id); } /** * Update the specified resource in storage. * * @param Request $request * @param int $id * @return Response */ public function update(Request $request, $id) { $employee = Employee::find($id); $employee->name = $request->input('name'); $employee->email = $request->input('email'); $employee->contact_number = $request->input('contact_number'); $employee->position = $request->input('position'); $employee->save(); return "Sucess updating user #" . $employee->id; } /** * Remove the specified resource from storage. * * @param int $id * @return Response */ public function destroy($id = null) { if ($id == null) { } else { $employee = Employee::find($id); $employee->delete(); return "Employee record successfully deleted #" . $id; } } }يعرِّف المتحكم الدوالّ index لسرد قائمة بالموظفين، store لإضافة موظف جديد، show لعرض موظف بناء على معرّفه، update لتحديث بيانات موظّف وdestroy لحذف موظّف. تبقى لنا إعداد المسارات Routes في Laravel. افتح ملف المسارات routes.php وعدله كالتالي: <?php /* |-------------------------------------------------------------------------- | Application Routes |-------------------------------------------------------------------------- | | Here is where you can register all of the routes for an application. | It's a breeze. Simply tell Laravel the URIs it should respond to | and give it the controller to call when that URI is requested. | */ Route::get('/', function () { return view('index'); }); Route::get('/api/v1/employees/{id?}', 'Employees@index'); Route::post('/api/v1/employees', 'Employees@store'); Route::post('/api/v1/employees/{id}', 'Employees@update'); Route::delete('/api/v1/employees/{id}', 'Employees@destroy');لاحظ استخدام إجراءات HTTP وفقا للمبدأ المشروح في درس إنشاء واجهة برمجية API في Laravel 5. يختلف التعامل مع المسار api/v1/employees حسب إجراء HTTP المستخدم: get يُستخدم للحصول على معلومات موظف أو قائمة بالموظفين، post لإضافة موظف جديد، put لتحديث بيانات موظّف وdelete لحذف موظّف. أكملنا الآن جانب النهاية الخلفية بإنشاء واجهة برمجية. ننتقل للواجهة الأمامية التي ستستخدم الواجهة البرمجية للحصول على بيانات الموظفين وتنسيقها قبل عرضها في المتصفح. بنية تطبيق AngularJSسيأخذ مجلد public (الواجهة الأمامية لتطبيق Laravel) الشكل التالي: يحوي المجلّد الفرعي app ملفات جافاسكريبت التابعة لـAngularJS.توجد متحكمات AngularJS في المجلّد app/controllers.توجد ملفات نواة AngularJS في المجلّد app/lib. توجد أيضا إمكانية استخدام شبكة توصيل محتوى Content delivery network, CDN.توجد ملفات CSS في المجلد css.ملفات جافاسكريبت غير التابعة لـAngularJS توجد في المجلد js.توجد ملفات الواجهة الأمامية في الملف المرفق، سنشرح ما يحتاج لشرح منها. الملف app.jsسنستخدم الملف public/app/app.js لتعريف التطبيق الخاص بنا: var app = angular.module('employeeRecords', []) .constant('API_URL', 'http://angulara.dev/api/v1/');ننشئ وحدة Module في AngularJS ونسند الكائن إلى المتغير app. سيُستخدم هذا المتغير في جميع ملفات AngularJS؛ ثم نعرّف ثابتا Constant لرابط الواجهة البرمجية API_URL. ملحوظة: إن كنت اخترت اسما مختلفا للمضيف فضعه مكان angulara.dev. المتحكم Employees.jsسيكون هذا الملف مسؤولا عن التفاعل مع عروض تطبيق AngularJS: app.controller('employeesController', function($scope, $http, API_URL) { // الحصول على لائحة الموظفين $http.get(API_URL + "employees") .success(function(response) { $scope.employees = response; }); // عرض نافذة منبثقة $scope.toggle = function(modalstate, id) { $scope.modalstate = modalstate; switch (modalstate) { case 'add': $scope.form_title = "Add New Employee"; break; case 'edit': $scope.form_title = "Employee Detail"; $scope.id = id; $http.get(API_URL + 'employees/' + id) .success(function(response) { console.log(response); $scope.employee = response; }); break; default: break; } console.log(id); $('#myModal').modal('show'); } //save new record / update existing record $scope.save = function(modalstate, id) { var url = API_URL + "employees"; var request_method = 'POST'; //append employee id to the URL if the form is in edit mode if (modalstate === 'edit'){ url += "/" + id; request_method = 'PUT'; } $http({ method: request_method, url: url, data: $.param($scope.employee), headers: {'Content-Type': 'application/x-www-form-urlencoded'} }).success(function(response) { console.log(response); location.reload(); }).error(function(response) { console.log(response); alert('This is embarassing. An error has occured. Please check the log for details'); }); } //delete record $scope.confirmDelete = function(id) { var isConfirmDelete = confirm('Are you sure you want this record?'); if (isConfirmDelete) { $http({ method: 'DELETE', url: API_URL + 'employees/' + id }). success(function(data) { console.log(data); location.reload(); }). error(function(data) { console.log(data); alert('Unable to delete'); }); } else { return false; } } });نعرِّف متحكما باسم employeesController في المتغيّر app الذي أنشأناه في الملف app.js. حدّدنا الاعتماديات بـ $http، scope$ وAPI_URL. نستدعي خدمة http$ في AngularJS لإرسال طلب إلى الواجهة البرمجية، مع تمرير المعطى API_URL + "employees": $http.get(API_URL + "employees").success(function(response) {$scope.employees = response;});عند نجاح الطلب نُسنِد الإجابة إلى المتغير scope.employees$ الذي سيصبح متاحا في العرض View لإظهار محتواه. تعرض الدالة: $scope.toggle = function(modalstate, id) {…}نافذة منبثقة تحوي استمارة لإضافة موظّف جديد أو تعديل بيانات موظّف موجود حسب الحالة. نستخدم الدالة: $scope.save = function(modalstate, id){…}في حالتيْ الإضافة والتعديل. بالنسبة لإضافة موظّف نستخدم إجراء POST؛ وفي حالة التعديل نستخدم الإجراء PUT. تحذف الدالة: $scope.confirmDelete = function(id){…}بيانات موظّف من جدول الموظفين. عرض البيانات المتحصل عليها من الواجهة البرمجية بـAngularJSسننشئ عرضا لإظهار البيانات التي نتحصل عليها من الواجهة البرمجية؛ يستخدم كل من angularJS وBlade الحاضنات المزدوجة {{}} لعرض البيانات، لذا لن نحفظ عرض AngularJS بلاحقة blade.php حتى لا يعدّ نظام Blade العرض موجها له. محتوى العرض هو التالي، نحفظ الملف index.php في ملف العروض resources/views حتى يُحمّل عند طلبه من المسار / (الصفحة الرئيسية للموقع): <!DOCTYPE html> <html lang="en-US" ng-app="employeeRecords"> <head> <title>Laravel 5 AngularJS CRUD Example</title> <!-- Load Bootstrap CSS --> <link href="<?= asset('css/bootstrap.min.css') ?>" rel="stylesheet"> </head> <body> <h2>Employees Database</h2> <div ng-controller="employeesController"> <!-- Table-to-load-the-data Part --> <table class="table"> <thead> <tr> <th>ID</th> <th>Name</th> <th>Email</th> <th>Contact No</th> <th>Position</th> <th><button id="btn-add" class="btn btn-primary btn-xs" ng-click="toggle('add', 0)">Add New Employee</button></th> </tr> </thead> <tbody> <tr ng-repeat="employee in employees"> <td>{{ employee.id }}</td> <td>{{ employee.name }}</td> <td>{{ employee.email }}</td> <td>{{ employee.contact_number }}</td> <td>{{ employee.position }}</td> <td> <button class="btn btn-default btn-xs btn-detail" ng-click="toggle('edit', employee.id)">Edit</button> <button class="btn btn-danger btn-xs btn-delete" ng-click="confirmDelete(employee.id)">Delete</button> </td> </tr> </tbody> </table> <!-- End of Table-to-load-the-data Part --> <!-- Modal (Pop up when detail button clicked) --> <div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> <h4 class="modal-title" id="myModalLabel">{{form_title}}</h4> </div> <div class="modal-body"> <form name="frmEmployees" class="form-horizontal" novalidate=""> <div class="form-group error"> <label for="inputEmail3" class="col-sm-3 control-label">Name</label> <div class="col-sm-9"> <input type="text" class="form-control has-error" id="name" name="name" placeholder="Fullname" value="{{name}}" ng-model="employee.name" ng-required="true"> <span class="help-inline" ng-show="frmEmployees.name.$invalid && frmEmployees.name.$touched">Name field is required</span> </div> </div> <div class="form-group"> <label for="inputEmail3" class="col-sm-3 control-label">Email</label> <div class="col-sm-9"> <input type="email" class="form-control" id="email" name="email" placeholder="Email Address" value="{{email}}" ng-model="employee.email" ng-required="true"> <span class="help-inline" ng-show="frmEmployees.email.$invalid && frmEmployees.email.$touched">Valid Email field is required</span> </div> </div> <div class="form-group"> <label for="inputEmail3" class="col-sm-3 control-label">Contact Number</label> <div class="col-sm-9"> <input type="text" class="form-control" id="contact_number" name="contact_number" placeholder="Contact Number" value="{{contact_number}}" ng-model="employee.contact_number" ng-required="true"> <span class="help-inline" ng-show="frmEmployees.contact_number.$invalid && frmEmployees.contact_number.$touched">Contact number field is required</span> </div> </div> <div class="form-group"> <label for="inputEmail3" class="col-sm-3 control-label">Position</label> <div class="col-sm-9"> <input type="text" class="form-control" id="position" name="position" placeholder="Position" value="{{position}}" ng-model="employee.position" ng-required="true"> <span class="help-inline" ng-show="frmEmployees.position.$invalid && frmEmployees.position.$touched">Position field is required</span> </div> </div> </form> </div> <div class="modal-footer"> <button type="button" class="btn btn-primary" id="btn-save" ng-click="save(modalstate, id)" ng-disabled="frmEmployees.$invalid">Save changes</button> </div> </div> </div> </div> </div> <!-- Load Javascript Libraries (AngularJS, JQuery, Bootstrap) --> <script src="<?= asset('app/lib/angular/angular.min.js') ?>"></script> <script src="<?= asset('js/jquery.min.js') ?>"></script> <script src="<?= asset('js/bootstrap.min.js') ?>"></script> <!-- AngularJS Application Scripts --> <script src="<?= asset('app/app.js') ?>"></script> <script src="<?= asset('app/controllers/employees.js') ?>"></script> </body> </html>نربط تطبيق AngularJS بوسم html حتى يكون له كامل التحكّم في العناصر الموجودة في الصفحة: <html lang="en-US" ng-app="employeeRecords"> نفس الشيء بالنسبة للمتحكم employeesController الذي نربطه بعنصر div حتى نجعل جميع دوال المتحكم متاحة في هذا العنصر: <div ng-controller="employeesController">نستخدم تعليمة ng-repeat للمرو على جميع عناصر المجموعة employees: <tr ng-repeat="employee in employees">تعمل ng-repeat بطريقة مشابهة لعمل الحلقة التكرارية foreach. التحقق من بيانات استمارات AngularJSاستخدمنا في استمارات الموظفين طرقا للتحقق من المُدخلات. اعثر على الشفرات التالية في ملف العرض index.php ولاحظ استخدامها: <form name="frmEmployees" class="form-horizontal" novalidate="">تعرّف هذه الشفرة استمارة باسم frmEmployees وتضيف إليها خاصيّة novalidate للطلب من HTML 5 الامتناع عن تدقيق الاستمارة، سنتولى الأمر. <input type… ng-model="employee.name" ng-required="true">تستخدم تعليمة ng-model لربط حقول الاستمارة ببيانات النموذج Employee. ربطنا الحقل أعلاه بخاصية name في النموذج. بهذه الطريقة يكون محتوى الحقل متاحا للنموذج، والعكس أيضا. تدل التعليمة ng-required أن الحقل مطلوب. إن لم تذكر قيمة لهذا الحقل فلن يمكن إرسال الاستمارة. <span class="help-inline" ng-show="frmEmployees.name.$invalid && frmEmployees.name.$touched">لا يظهر هذا العنصُر إلا إذا أخفق التحقق من حقل name قبله. تظهر رسالة تفيد أن قيمة الحقل مطلوبة. ng-disabled="frmEmployees.$invalid" تعطّل هذه التعليمة إمكانية إرسال الاستمارة عند إخفاق التحقق من أحد الحقول المطلوبة. حان الآن وقت تجربة ما عملناه في الخطوات السابقة، افتح الموقع (بالنسبة لي اخترتُ إنشاء مضيف افتراضي angulara.dev): http://angulara.dev/ستحصُل، إن اتبعت الخطوات السابقة، على الواجهة التالية: اضغط على زر Add New Employee لإضافة موظّف. ستظهر نافذة منبثقة لإضافة بيانات الموظّف. تصبح الواجهة بعد إضافة موظفين على النحو التالي: تمكّن الأزرار edit و delete الموجودة في كل سطر تحديثَ أو حذف بيانات موظّف. الملف المرفق: مجلد public. ترجمة -وبتصرّف- لمقال Laravel 5 AngularJS Tutorial لصاحبه Rodrick Kazembe.
  14. OAuth 2 هو آليّة للتّرخيص تسمح للتطبيقات بطلب وصول محدود إلى حسابات المستخدمين في خدمات HTTP، مثل Facebook وGitHub وDigitalOcean. يعمل OAuth 2 بتوكيل الخدمة المستضيفة لحساب المستخدم باستيثاق هذا الحساب، ثمّ السّماح للتطبيقات الخارجيّة بالوصول إلى حساب المستخدم هذا. يوفّر OAuth 2 مسارًا لترخيص تطبيقات الويب وتطبيقات سطح المكتب والأجهزة المحمولة. هذا الدّرس موجّه لمطوّري التّطبيقات، وهو يُلقي الضّوء على أدوار OAuth 2 وأنواع الرُّخَص المتاحة، وكذلك يستعرض مجالات استخدامه وسير عمليّة التّرخيص. لنبدأ بالتّعرّف على أدوار OAuth. أدوار OAuthيُحدِّد OAuth أربعة أدوار: مالك المحتوىالعميلخادوم المحتوىخادوم التّرخيصسنُفصّل كلًّا من هذه الأدوار في الفقرات التّالية. مالك المحتوى: المستخدممالك المحتوى هو المستخدم الذي يُرخِّص لتطبيقٍ الوصول إلى حسابه. وصول التّطبيق إلى حساب المستخدم محدودٌ "بنطاق" (scope) الترخيص الممنوح (مثلاً: صلاحيّة القراءة والكتابة). خادوم المحتوى/التّرخيص: الواجهة البرمجيّة (API)يستضيف خادوم المحتوى حسابات المستخدمين المحميّة، ويتحرّى خادوم التّرخيص هويّة المستخدم ثمّ يمنح التّطبيق رمز وصول (access token). من وجهة نظر مطوّر التّطبيقات، فإنّ الواجهة البرمجيّة للخدمة تؤدّي كلا الدّورين، دور خادوم المحتوى ودور خادوم التّرخيص. سنُشير إلى هذين الدّورين مجتمعين على أنّهما دور الخدمة أو الواجهة البرمجيّة. العميل: التّطبيقالعميل هو التّطبيق الّذي يريد الوصول إلى حساب المستخدم، وقبل أن يستطيع ذلك، يجب أن يحصل على "ترخيص" المستخدم، وعلى هذا التّرخيص أن يُصادَق من الواجهة البرمجيّة. سير البروتوكول نظريًّابعد أن تعرّفنا على أدوار OAuth، دعونا نلقِ نظرةً على المخطّط التالي، والذي يبيّن كيف تتفاعل هذه الأدوار فيما بينها: وفيما يلي شرح أكثرُ تفصيلًا للخطوات المُبيّنة في المُخطّط: يطلب التطبيق رخصةً للوصول إلى الخدمة من المستخدمإن رخّص المستخدم الطّلب، فإنّ التطبيق يحصل على إذن بالتّرخيصيطلب بعدها التطبيق رمز وصول (access token) من خادوم التّرخيص (الواجهة البرمجيّة) مُقدّمًا ما يُثبت هوّيته مع إذن التّرخيص الّذي حصل عليه.إن كانت هويّة التّطبيق موثّقة وإذن التّرخيص سليمًا، أصدر خادوم التّرخيص (الواجهة البرمجيّة) رمز وصول (access token) يمنحه للتّطبيق، لتكتمل حينئذٍ عمليّة الترخيص.يطلب التّطبيق من خادوم المحتوى (الواجهة البرمجيّة) المحتوى المطلوب، مُقدّمًا رمز الوصول الّذي حصل عليه.إن كان رمز الوصول سليمًا، قدّم خادوم المحتوى (الواجهة البرمجيّة) المحتوى المقصود للتطبيققد يختلف مسار العمليّة الفعليّ بحسب نوع الرّخصة المُستخدمة، ولكن هذه هي الفكرة العامّة. سنستعرض أنواع الرُّخَص المُختلفة في فقرة لاحقة. تسجيل التّطبيققبل استخدام OAuth في تطبيقاتك، عليك تسجيل التّطبيق في الخدمة المعنيّة. يجري التسجيل عادةً من خلال نموذج في قسم المُطوّرين أو الواجهة البرمجيّة في موقع الخدمة على الويب، حيث ينبغي عليك تقديم البيانات التالية (وربّما معلومات أخرى عن تطبيقك): اسم التّطبيقموقع التّطبيقرابط إعادة الّتوجيه (Redirect URL) أو الاستدعاء الرّاجع (Callback URL)تُعيد الخدمة توجيه المستخدم إلى رابط إعادة التّوجيه الّذي توفّره بعد ترخيصه لتطبيق (أو رفضه)، وعليه فإنّ هذا الرابط هو المسؤول عن التّعامل مع رموز الترخيص أو رموز الوصول (access tokens). مُعرّف العميل وكلمة سرّ العميلستمنحك الخدمة بعد تسجيل تطبيقك "وثائق اعتماد العميل" المؤلّفة من معرّف العميل وكلمة سرّ العميل. معرّف العميل هو سلسلة من الحروف مكشوفة للعموم تستخدمها الواجهة البرمجيّة للخدمة لتحديد هويّة التّطبيق، ولبناء روابط التّرخيص المُقدّمة للمستخدمين. أمّا كلمة سر العميل فتُستخدم للاستيثاق من هويّة التّطبيق بالنّسبة للواجهة البرمجيّة للخدمة عندما يطلب التّطبيق الوصول إلى حساب المُستخدم، ويجب أن تبقى سرّيّة بين التّطبيق والواجهة البرمجيّة. إذن التّرخيصفي فقرة "سير البروتوكول نظريًّا"، تبيّن الخطوات الأربع الأولى كيفيّة الحصول على إذن بالتّرخيص ورمز للوصول. يعتمد نوع الإذن على طريقة طلب التّطبيق للتّرخيص، وأنواع الأذون الّتي تدعمها الواجهة البرمجيّة. يعرّف OAuth 2 أربعة أنواع من أذون التّرخيص، يمكن الاستفادة من كلّ منها في حالات مُختلفة: رمز التّرخيص (Authorization Code): تستخدمه التّطبيقات الّتي تعمل على الخواديمضمنيّ (Implicit): تستخدمه تطبيقات الويب وتطبيقات الأجهزة المحمولة (أي التّطبيقات الّتي تعمل على جهاز المُستخدم)كلمة مرور مالك المحتوى: تستخدمها التّطبيقات الموثوقة، كتلك الّتي تتبع للخدمة ذاتهاكلمة مرور العميل: تستخدم في حالة الوصول للواجهة البرمجيّة للتّطبيقاتسنشرح أنواع الأذون وحالات استخدامها بالتّفصيل في الفقرات التّالية. الإذن من نوع "رمز الترخيص":هذا النّوع من الأذون هو الأكثر استخدامًا لأنّه مُصمّم للتطبيقات الّتي تعمل على الخواديم، حيث لا يُكشف النّص المصدريّ للتّطبيق للعموم، وحيث يمكن الاحتفاظ بسرّيّة كلمة سرّ العميل بصورة تامّة. ويعتمد سير التّرخيص هنا على إعادة التّوجيه، ممّا يعني أنّه على التّطبيق أن يكون قادرًا على التّفاعل مع وكيل المستخدم (كمتصفّح الويب الّذي يستخدمه) وعلى استقبال رموز التّرخيص الّتي توفّرها الواجهة البرمجيّة والّتي تمرّ من خلال وكيل المستخدم. سنشرح الآن سير عمليّة التّرخيص في هذا النّوع من الأذون: سير التّرخيص بالرّمز الخطوة 1: رابط رمز الترخيص يُعطى المُستخدم في البداية رابطًا لرمز التّرخيص يُشبه هذا: https://cloud.digitalocean.com/v1/oauth/authorize?response_type=code&client_id=CLIENT_ID&redirect_uri=CALLBACK_URL&scope=readفيما يلي شرحٌ لمكوّنات الرابط: https://cloud.digitalocean.com/v1/oauth/authorize: نقطة الوصول إلى قسم التّرخيص في الواجهة البرمجيّةclient_id=client_id: مُعرّف العميل للّتطبيق (كيفيّة تحديد الواجهة البرمجيّة لهويّة التّطبيق)redirect_uri=CALLBACK_URL: المكان الّذي تعيد الخدمة توجيه وكيل المستخدم إليه بعد منح رمز الترخيصresponse_type=code: يُبيّن أنّ تطبيقك يطلب إذنًا بالحصول على رمز ترخيصscope=read: يُعيّن مستوى الوصول الّذي يطلبه المُستخدمالخطوة 2: يُرخّص المستخدم التّطبيق عندما ينقر المُستخدم الرّابط، يجب عليه أوّلًا تسجيل الدّخول إلى الخدمة، وذلك للتّحقق من هويّة المُستخدم (ما لم يكن قد سجّل دخوله من قبل). ثم تعرض عليه الخدمة ترخيص أو رفض وصول التّطبيق إلى حسابه. فيما يلي مثال عن صفحة ترخيص التّطبيق: هذه الصّورة مُلتقطة من صفحة ترخيص DigitalOcean، ونرى فيها التّطبيق "Thedropletbook App" يطلب إذنًا بقراءة حساب المُستخدم "manicas@digitalocean.com". الخطوة 3: يتلقّى التطبيق رمز التّرخيص إذا نقر المُستخدم "Authorize Application"، فإنّ الخدمة تُعيد تحويل وكيل المستخدم إلى رابط إعادة التّوجيه الّذي حدّده التّطبيق أثناء تسجيل المُطوِّر له، وتُرفق الخدمة مع الرّابط رمز التّرخيص. مثال على الرّابط (مُفترضين أنّ التّطبيق هو "dropletbook.com"): https://dropletbook.com/callback?code=AUTHORIZATION_CODEالخطوة 4: يطلب التّطبيق رمز الوصول (Access Token) يطلب التّطبيق من الخدمة رمز وصول، مُمرّرًا لها رمز التّرخيص مع تفاصيله، بما في ذلك كلمة سرّ العميل، والّتي تُرسل جميعها إلى رابط الحصول على رمز الوصول الخاصّ بالخدمة. فيما بلي مثال على طلب POST يُرسل إلى رابط رمز الوصول في DigitalOcean: https://cloud.digitalocean.com/v1/oauth/token?client_id=CLIENT_ID&client_secret=CLIENT_SECRET&grant_type=authorization_code&code=AUTHORIZATION_CODE&redirect_uri=CALLBACK_URLالخطوة 5: يتلقّى التّطبيق رمز الوصول إن كان التّرخيص سليمًا، فإنّ الواجهة البرمجيّة تردّ على الطّلب بجواب يحوي رمز الوصول (مع رمز إعادة تجديد الرّخصة، غير إلزاميّ) إلى التّطبيق. يبدو الجواب مثل هذا: {"access_token":"ACCESS_TOKEN","token_type":"bearer","expires_in":2592000,"refresh_token":"REFRESH_TOKEN","scope":"read","uid":100101,"info":{"name":"Mark E. Mark","email":"mark@thefunkybunch.com"}}أصبح التّطبيق الآن مُرخّصًا! وبإمكانه استخدام الّرمز للوصول إلى حساب المُستخدم عن طريق الواجهة البرمجيّة للخدمة، محدودًا بنطاق الوصول، إلى أن تنتهي مدّة الرّمز أو يُسحب التّرخيص. في حال أُصدر رمز إعادة تجديد الرّخصة (refresh token)، فبإمكان التّطبيق استخدامه للحصول على رمز وصول جديد في حال انتهى مدّة السّابق. الإذن الضّمنيّيُستخدم نوع الأذون الضّمنيّ في تطبيقات الويب (التي تعمل في المتصفح) وتطبيقات الأجهزة المحمولة، حيث يصعب ضمان سرّية كلمة سرّ العميل. يقوم هذا النّوع من الأذون على مبدأ إعادة التّوجيه أيضًا، إلّا أنّ رمز الوصول يُعطى لوكيل المُستخدم ليقوم بدفعه إلى التّطبيق، وبهذا قد يُكشف للمُستخدم وللتّطبيقات على جهازه. لا يتضمّن سير التّرخيص في هذا النّوع هوّيّة التّطبيق، بل يعتمد على رابط إعادة التّوجيه (الّذي سُجّل في الخدمة) للوصول إلى هذا الهدف. لا يدعم هذا النّوع من الأذون رموز إعادة تجديد التّرخيص. يسير التّرخيص في هذا النّوع كما يلي: يُطلب من المُستخدم ترخيص التّطبيق، ثمّ يُمرّر خادوم التّرخيص رمز الوصول إلى وكيل المُستخدم، الّذي ينقله بدوره إلى التّطبيق. إن كُنت مُهتمًّا بالتّفاصيل، فتابع القراءة. سير التّرخيص الضّمنيّ الخطوة 1: رابط التّرخيص الضّمني يُعرض على المُستخدم رابط التّرخيص، الّذي يطلب رمزًا من الواجهة البرمجيّة، يبدو هذا الرّابط مُشابهًا لرابط رمز التّرخيص، باستثناء أنّه يطلب رمز token بدلًا من code (لاحظ نوع الجواب المطلوب "token"): https://oauth.example.com/authorize?response_type=token&client_id=CLIENT_ID&redirect_uri=CALLBACK_URL&scope=readملاحظة: لا تدعم DigitalOcean حاليًا التّرخيص الضّمني، لذا ذكرنا رابطًا وهميًّا "oauth.example.com". الخطوة 2: يرخّص المُستخدم التّطبيق عندما ينقر المُستخدم الرّابط، يجب عليه أوّلًا تسجيل الدّخول إلى الخدمة، وذلك للتّحقق من هويّة المُستخدم (ما لم يكن قد سجّل دخوله من قبل). ثم تعرض عليه الخدمة ترخيص أو رفض وصول التّطبيق إلى حسابه. فيما يلي مثال عن صفحة ترخيص التّطبيق: نرى في الصّورة التّطبيق "Thedropletbook App" يطلب إذنًا بقراءة حساب المُستخدم "manicas@digitalocean.com". الخطوة 3: يتلقّى وكيل المُستخدم رمز الوصول مع رابط إعادة التّوجيه إذا نقر المُستخدم "Authorize Application"، فإنّ الخدمة تُعيد تحويل وكيل المستخدم إلى رابط إعادة التّوجيه الّذي حدّده التّطبيق أثناء تسجيل المُطوِّر له، وتُرفق الخدمة مع الرّابط رمز الوصول. مثال على الرّابط: https://dropletbook.com/callback#token=ACCESS_TOKENالخطوة 4: يتبع وكيل المُستخدم مسار إعادة التّوجيه يتبع وكيل المُستخدم رابط إعادة التّوجيه مع احتفاظه برمز الوصول. الخطوة 5: يُرسل التّطبيق نصًّا برمجيًّا لاستخراج رمز الوصول يُعيد التّطبيق صفحة ويب تحوي نصًّا برمجيًّا بإمكانه استخراج رمز الوصول من رابط إعادة التّوجيه الكامل الّذي احتفظ به وكيل المُستخدم. الخطوة 6: يُمرّر رمز الوصول إلى التّطبيق يُنفّذ وكيل المستخدم النّصّ البرمجيّ ويُمرّر رمز الوصول المُستخرَج إلى التّطبيق. أصبح التّطبيق الآن مُرخّصًا! وبإمكانه استخدام الّرمز للوصول إلى حساب المُستخدم عن طريق الواجهة البرمجيّة للخدمة، محدودًا بنطاق الوصول، إلى أن تنتهي مدّة الرّمز أو يُسحب التّرخيص. ملاحظة: لا تدعم DigitalOcean حاليًا التّرخيص الضّمني، لذا ذكرنا رابطًا وهميًّا "oauth.example.com". الإذن بالوصول إلى كلمة مرور مالك المُحتوىفي هذا النّوع من التّرخيص، يزوّد المستخدم التّطبيق مباشرةً باسم حسابه وكلمة مروره، ليستخدمها للحصول على رمز الوصول من الخدمة. يجب استخدام هذا النّوع من الأذون في الخوادم عندما لا تكون الأنواع الأخرى مُناسبة فقط. ويجب استخدامه فقط في حال كان التّطبيق موضع ثقة المُستخدم، كأن يكون تابعًا للخدمة ذاتها، أو أن يكون نظام التّشغيل على حاسوب المُستخدم هو ما يطلب الوصول. سير التّرخيص بالحصول على كلمة مرور المُستخدمبعد أن يُعطي المستخدم كلمة مروره للتّطبيق، يطلب التّطبيق رمز الوصول من خادوم التّرخيص. يُشبه طلب POST ما يلي: https://oauth.example.com/token?grant_type=password&username=USERNAME&password=PASSWORD&client_id=CLIENT_IDإن كان اسم المُستخدم وكلمة المرور صحيحين، يُعيد خادوم التّرخيص رمز وصول للتّطبيق ويُصبح التّطبيق مُرخّصًا! ملاحظة: لا تدعم DigitalOcean حاليًا التّرخيص بالحصول على كلمة المرور، لذا ذكرنا رابطًا وهميًّا "oauth.example.com". الإذن بالوصول إلى كلمة مرور العميلفي هذا النّوع من التّرخيص، يوفّر التّطبيق طريقة للوصول إلى حسابه الخاصّ على الخدمة. من الأمثلة الّتي يكون فيها استخدام هذا النّوع مُفيدًا أن يرغب التّطبيق بتحديث وصفه أو رابط إعادة التّوجيه المُسجّلين في الخدمة، أو أن يصل إلى بيانات أخرى حول حسابه على الخدمة عن طريق الواجهة البرمجيّة. سير التّرخيص بالحصول على كلمة مرور العميليطلب التّطبيق رمز الوصول مُرسلًا مُعرَّفه وكلمة مروره إلى خادوم التّرخيص، فيما يلي مثال عن طلب POST: https://oauth.example.com/token?grant_type=client_credentials&client_id=CLIENT_ID&client_secret=CLIENT_SECRETإن كان مُعرّف التّطبيق وكلمة مروره صحيحين، يُعيد خادوم التّرخيص رمز وصول للتّطبيق ويُصبح التّطبيق مُرخّصًا باستخدام حسابه الخاصّ! ملاحظة: لا تدعم DigitalOcean حاليًا التّرخيص بالحصول على كلمة مرور العميل، لذا ذكرنا رابطًا وهميًّا "oauth.example.com". مثال على استخدام رمز الوصولبعد أن يحصل التّطبيق على رمز الوصول، إمكانه استخدام هذا الّرمز للوصول إلى حساب المُستخدم عن طريق الواجهة البرمجيّة للخدمة، محدودًا بنطاق الوصول، إلى أن تنتهي مدّة الرّمز أو يُسحب التّرخيص. فيما يلي مثال عن طلب يُرسل للواجهة البرمجيّة للخدمة باستخدام curl، لاحظ أنّه يتضمّن رمز الوصول: curl -X POST -H "Authorization: Bearer ACCESS_TOKEN""https://api.digitalocean.com/v2/$OBJECT"على فرض أنّ رمز الوصول سليم، فإنّ الواجهة البرمجيّة تُعالج الطّلب حسب ما صُمِّمت؛ وإلّا أعادت الواجهة خطأ "invalid_request"، كما يحدث عند انتهاء مدّة التّرخيص أو استخدام رمز خاطئ. سير الحصول على رمز إعادة تجديد الرُخصةيؤدّي استخدام رمز وصول بعد انتهاء مدّة صلاحيّته إلى "خطأ رمز غير سليم (Invalid Token Error)". في هذه النّقطة، يمكن استخدام رمز إعادة تجديد الرّخصة في حال أُصدَر مع رمز الوصول للحصول على رمز وصول جديد من خادوم التّرخيص. فيما يلي مثال على طلب POST للحصول على رمز وصول جديد مُستخدمين رمز إعادة تجديد: https://cloud.digitalocean.com/v1/oauth/token?grant_type=refresh_token&client_id=CLIENT_ID&client_secret=CLIENT_SECRET&refresh_token=REFRESH_TOKENالخاتمةإلى هناك نكون قد وصلنا إلى ختام دليل OAuth. من المُفترض أن لديك الآن فكرة جيّدة عن البروتوكول وكيف يعمل، ومتى يمكن استخدام كلّ نوع من الأذون. إذا أردت تعلّم المزيد عن OAuth 2، اطّلع على هذه المصادر القيّمة (بالإنكليزيّة): How To Use OAuth Authentication with DigitalOcean as a User or DeveloperHow To Use the DigitalOcean API v2The OAuth 2.0 Authorization Framworkترجمة (بشيء من التّصرّف) لمقال Introduction to OAuth 2 لصاحبه Mitchell Anicas.