اذهب إلى المحتوى

Adnane Kadri

الأعضاء
  • المساهمات

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

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

  • عدد الأيام التي تصدر بها

    52

كل منشورات العضو Adnane Kadri

  1. المشكلة الوحيدة لديك هي أن قاعدة البيانات التي تحاول القراءة من عليها فارغة , فمن المنطقي أن لا يتم الإنطلاق في الدور foreach و القراءة من عليه . أي أن أي شيفرة بداخل الدور كالتالي : foreach($stnt as $val) { echo 'يوجد بيانات في قاعدة البيانات'; سيتم تجاهلها في حالة ما كان المعاد من تنفيذ الإستعلام stnt مصفوفة فارغة . في حين أن نفس الشيفرة سيتم تنفيذها في حالة كان المعاد من تنفيذ الإستعلام مصفوفة بعناصر يمكن القراءة من عليها عن طريق الدور . و لذلك تأكد من أن الجدول data الذي تحاول القراءة من عليه يمتلك على الأقل قيمة أو أكثر افتراضيا حتى يكون ما داخل الدور foreach قابلا للوصول . و لكن أفهم أن الفكرة التي تحاول تطبيقها هي : و لكن ما هو عيب هاته الطريقة ؟ => هو أنه لن يتم تسجيل أي عضو في حالة ما لم يتم تسجيل على الأقل عضو أو أكثر يدويا من قبل , فهذه هي الطريقة الوحيدة للوصول إلى ما داخل الدور . و لتجاوز هاته المشكلة سنقوم بتطبيق المنطق التالي : يتم قبول أي عضو تلقائيا ما ان لم تكن بياناته مطابقة لعضو اخر مسجل بالفعل . و ذلك عوضا عن : التحقق من أن كامل الأعضاء لا يطابقون بيانات هذا العضو كشرط لتسجيله , بحيث يقوم هذا بإغفال احتمال فراغ قاعدة البيانات . لنقم بالتالي : تعريف متغير isAlreadyRegistered كمتغير يحمل قيمة false افتراضيا . يتم تغيير قيمة isAlreadyRegistered إلى true في حالة مطابقة بيانات أحد الأعضاء المسجلين لبيانات العضو الذي يحاول التسجيل . يتم في مرحلة أخيرة التحقق من قيمة isAlreadyRegistered و التصرف بناء عليها . فإن كانت isAlreadyRegistered تحمل القيمة false تم تسجيل العضو و إلا فلن يمكن ذلك و ستظهر رسالة الخطأ . ستتبع الشيفرة نحوا مشابها : $isAlreadyRegistered = false; foreach($stnt as $val) { if ($val["emailw"] == $this-> mail) { // لاحظ شرط التطابق $isAlreadyRegistered = true; // إعادة تعيين قيمة المتغير break; } } if (!$isAlreadyRegistered) { // return $val["emailw"]; $conn-> setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // send data to database .. $stmt = $conn - > prepare("INSERT INTO data (namew, emailw, passwordw, datew) VALUES (:name, :mail, :pass, :date)"); $stmt-> bindParam(':name', $namep); $stmt-> bindParam(':mail', $mailp); $stmt-> bindParam(':pass', $passp); $stmt-> bindParam(':date', $datep); $namep = $this-> name; $mailp = $this-> mail; $passp = $this-> pass; $datep = $this-> date; $stmt-> execute(); return "Successfully connected .."; } else { return 'this email has created ..'; } ثم سيمكنك إنشاء أعضاء في كامل الحالات و وفقا لأي احتمالات . ملاحظة : الشيفرة لديك قد تحتاج بعض التنظيم و التنظيف حتى تصبح مفهومة و سهلة التشخيص و القراءة , و قد كان ذلك ضروريا حتى أستطيع التوغل في شيفرتك و أفهم مرادك منها . يمكنك التحصل على نسخة أكثر تنظيما و تنسيقا لملفك هنا : index.php .
  2. هلا تأكدت من أن مزود الاستضافة الخاص بك يسمح لك بإرسال رسائل البريد الإلكتروني ولا يحد من إرسال رسائل البريد الإلكتروني ؟ و ذلك لأن العديد من مضيفات الويب المشتركة ، وخاصة موفري الاستضافة المجانية ، إما لا تسمح بإرسال رسائل البريد الإلكتروني من خوادمها أو تحد من العدد الذي يمكن إرساله خلال أي فترة زمنية معينة . و قد تحتاج إلى التواصل مع فريق دعمهم للتحقق مما إذا كانت هناك أي قيود مفروضة على إرسال رسائل البريد الإلكتروني أو لا . في مثل هاته الحالات يلحظ سلوك مشابه لدالة mail , إذا تقوم بإعادة قيمة صحيحة و تعتبر أن إرسال الرسالة تام , في حين أنه لا يتم إستقبال أي رسالة في صندوق البريد . شيء اخر قد تحتاج التأكد منه و هو أن القيمة الممررة في : From قيمة صحيحة أي أن عنوان البريد الإلكتروني التالي : info@yhyasyrian.cf موجود بالفعل . ان كان كل شيء صحيحا , قد تحتاج تمكين سجل الأخطاء الخاصة بالوظيفة أو الدالة mail , و لنتأكد أن نقوم بوضع هذا قبل الشيفرة لديك : ini_set("mail.log", "/tmp/mail.log"); ini_set("mail.add_x_header", TRUE); و قد تحتاج في حالات أخرى إستعمال خدمات أو موفرات SMTP أخرى لإرسال و إدارة البريد الإلكتروني , و ذلك لأنها توفر مميزات إضافية و خيارات إرسال أخرى . يقترح أحد التالي : PHPMailer (شائع إستعماله) swiftmailer pearMailer يمكنك التعرف على كيفية إرسال رسالة بإستعمال PHPMailler و إرسال بريد إلكتروني باستخدام php. كما يمكنك القراءة أكثر عن طريقة إرسال بريد إلكتروني في PHP بإستخدام الوظيفة mail .
  3. في الحقيقة فكرة حماية عرض مكون في تطيبقات الصفحة الواحدة أو تطبيقات الويب التقدمية فكرة تعاب فيها هاته التطبيقات , إذ أن اقصى حماية لأي مكون أو أية صفحة عرض هي عن طريق طبقات وسيطة تستعمل قيما و شروطا مهما تم التلاعب فيها وتطبيق الأفكار عليها ستبقى مخزنة أو متحقق منها محليا و على المتصفح . ولكنه يجب الإنتباه إلى أن عرض مكون دون بيانات ليس له معنى فسيكون مجرد قالب , و لذلك التركيز الحقيقي لا يكون على حماية صفحات العرض و المكونات و إنما على البيانات التي تستجلب من الواجهة الخلفية . كما أن التوجه إلى تطبيقات الـ ssr أو الـ server side rendering سيقوم بتجاوز هذا الإشكال و حله بشكل نهائي , و كونك تستخدم vueJS فسيكون إستعمال NuxtJs موافقا و سهلا .
  4. يجب أولا فهم الغرض من القنوات الخاصة أساسا , فالقنوات الخاصة في الويب سوكيت وجدت للتحقق ما ان كان سيسمح للمستخدمين بالاستماع إلى هاته القنوات الخاصة أو لا , و ذلك بالإعتماد على شروط تتوفر فيمن يقوم بطلب الإستماع مثلا أو أية شروط أخرى . يتم تحديد قواعد ترخيص القناة الخاصة هاته بنا في ملف : route / channels.php الخاص بتطبيقنا . تحتاج أولا تعريف القناة بالملف المذكور كالتالي : Broadcast::channel('new-notification.{postUserId}' , function ($postUserId) { }); كما أنها ستحمل شيفرة ما يتم التحقق ما إن كان للمستخدم الحالي الحق في الإستماع لأحداث هاته القناة , كأن يتم مطابقة معرف القناة الخاصة و التأكد من امتلاك المستخدم لمنشور بهذا الإسم , أو أية طريقة تحقق كانت . يمكنك صياغة منطق الشيفرة وفق حاجتك للقناة الخاصة من الأساس . مثلا : Broadcast::channel('new-notification.{postUserId}' , function ($postUserId) { if(auth()->user()->can_view_notfs_of($postUserId)){ return true; } }); بحيث سيكون هذا المسار نقطة تحقق قبل الإشتراك في القناة بالنسبة لمستخدم معين أو تحقق شرط معين . و هذا من جانب الواجهة الخلفية , أما من جانب الواجهة الأمامية فسيتم تحديد نقطة الوصول هاته -نقطة التحقق- بإستعمال الخاصية authEndpoint مرفقا بـ csrf_token : var pusher = new Pusher("4fb4fa908d87ec86abf9", { cluster: '{{ env('PUSHER_APP_CLUSTER') }}', authEndpoint: '/broadcasting/auth', auth: { headers: { 'X-CSRF-TOKEN': '{{ csrf_token() }}', } } }); بعد ذلك ستعمل القنوات الخاصة وفق المطلوب .
  5. القناة بالإسم : 'new-notification.'.$this->post_user_id بالفعل قناة خاصة Private Channel لديك , هلا قمت بتوضيح سؤالك أكثر ؟ يمكنك الإستغناء عن laravel ehco إن كنت تستخدم pusher كويب سوكيت دريفر , و لا علاقة لذلك بنوع القناة .
  6. بإنهائك لدورة تطوير واجهات المستخدم ستصبح مطور واجهات مستخدم , و هذا لأنك ستتعرف على كل من : مفهوم الواجهة الأماميّة للموقع front-end وكيفية عملها . أساسيات لغات تطوير واجهات المستخدم: HTML و CSS و JavaScript . استخدام أحدث أدوات التطوير: Bootstrap 4 و jQuery و Sass . التعامل مع خدمة استضافة المشاريع البرمجية GitHub و GitLab . نشر صفحات الموقع على الإنترنت . إعداد هيكل الموقع ووضع خطة العمل . التصميم المتجاوب مع مختلف الأجهزة والشاشات . و مطور واجهة المستخدم هو المسمى الوظيفي لمن يكون مسؤولا عن بناء الموقع من الجانب الشكلي العام من هيكلية و تصميم و تفاعلية . يشمل هذا المسمى الوظيفي الفروع التالية : البرمجة بإستخدام الجافاسكربت و أدواتها , و تصميم الويب بإستخدام الـ css و أدواتها . أي أن التعبير مطور واجهات مستخدم وفق المقدم في دورة واجهات المستخدم يتضمن صفتي " مبرمج " و " مصمم " معا . و للتفصيل أكثر يمكن تعريف كل منهم وفق التعريفات التالية : مصمم الويب : يهتم بالتصميم الخارجي للموقع . مبرمج الويب : الشخص المفوض إليه عمليات التكويد و كتابة الشيفرة , له إمكانية كتابة أكواد و خوارزميات أكثر تعقيدا , لا يعير إهتمام لشخص المستخدم أو تجربته . مطور الويب : إهتمامه أكثر بتجربة المستخدم نحو التطبيق المفوض إليه , معرفته بالمستخدم أكثر و بالتكويد و التعقيد أقل عادة .
  7. مثلما ذكر المدرب حسن حيدر فإن الخاصية ليست قياسية أو على المسار القياسي الذي تحترمه كامل متصفحات الويب كما أنها تابعة لمجموعة الإضافات ذات السابقة webkit- . و يقصد بغير قياسي: أن استخدامها لا يكون في مواقع الإنتاج التي تستهدف كامل المتصفحات و الأجهزة , فالخاصية قد لن تعمل مع كل مستخدم . كما أنه قد يكون هناك أيضا عدم توافق كبير بين عمليات التنفيذ , أو ربما قد يتغير السلوك في المستقبل . يمكنك التعرف على قائمة المتصفحات التي تدعم مجموعات الإضافات webkit هنا . و بشكل عام , تحدد الخاصية -webkit-text-fill-color لون " تعبئة أحرف النص" . فإذا لم يتم تعيين هذه الخاصية ، يتم استخدام خاصية اللون color لها . أي أن : يتم تأويل إستعمال -webkit-text-fill-color على إستعمال الخاصية color إن تم إعطاء لكل منهما قيمة مخالفة للثانية : مثال 1 . الخاصية -webkit-text-fill-color تقبل إستعمال القيمة transparent لها على عكس الخاصية color مما يسمح بتطبيق العديد من الأفكار عليها : مثال 2 . يتم إستعمال الخاصية -webkit-text-fill-color عادة بمحاذاة الخاصيتين -webkit-text-stroke-color و -webkit-text-stroke-width , على عكس الخاصية color التي تستعمل وحدها . الخاصية -webkit-text-fill-color هي خاصية ليست قياسية أي أنها ليست مدعومة من قبل كامل المتصفحات على عكس الخاصية color . إليك قائمة المتصفحات الداعمة : المصدر : https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-text-fill-color
  8. يمكنك إستخدام ما يعرف بالتعليقات الشرطية لحجب المحتوى أو عرض رسالة معينة بناءا على متصفح المستخدم في أي صفحة أو مكون , و كونك تستخدم تطبيق nuxt.js يمكنك مباشرة إضافة هاته الأسطر : <div> <!--[if IE]> <p>عذرا, Internet Explorer غير مدعوم.<p> إستعمل <a href="http://google.com">Chrome</a> بدلا. <![endif]--> <![if !IE]> <Nuxt /> <![endif]> </div> إلى ملف default.vue قبل إخراج ملف dist عن طريق الأمر : npm run generate كما يمكنك إنشاء مكون كامل ليتم عرضه بدلا من رسالة بسيطة : <template> <div> <!--[if IE]> <IENotSupportedComponent /> <![endif]--> <![if !IE]> <Nuxt /> <![endif]> </div> </template> <script> import IENotSupportedComponent from '@/components/IENotSupportedComponent'; export default defineComponent({ name: 'Default', components: { IENotSupportedComponent }, }); </script>
  9. قبل التحدث عن وسم div ينبغي معرفة أن وسوم HTML تنقسم إلى ثلاث فئات رئيسية : Block level Tags : تشمل كل العناصر و الوسوم التي تأخذ كامل عرض حاوياتها افتراضيا . Inline Level Tags : تشمل كل العناصر و الوسوم التي توضع تلقائيا في خط أفقي افتراضيا ، و عندما تنفد المساحة على المحور الأفقي ، فإنها تلتف في السطر التالي وهكذا . Meta Level tags : تشمل كل العناصر و الوسوم التي لا تظهر في الصفحة كعناصر أو تقسيمات أو حاويات , و لكنها تستعمل لمهام أخرى (مثل توجيه محركات البحث نحوها) . وسم div هو وسم Block level tag , و يعبر عن اختصار division بالإنجليزية أو تقسيمة بالعربية . و يستخدم لإنشاء تقسيمات أو أقسام حاوية منفصلة للمحتوى في صفحة الويب مثل حاويات لكل من : استمارة أو نموذج , نص ، صور ، رأس ، تذييل ، شريط تنقل ، إلخ . و بالتالي فإن الغرض من استخدام علامة div هو تقسيم صفحة ويب ليس إلا . إذا نظرت إلى موقع ويب نموذجي ، يمكنك تقسيمه إلى أقسام منطقية. على سبيل المثال ، إذا نظرت إلى صفحة أسئلة البرمجة في أكاديمية حسوب ، يمكنك تقسيمه إلى شريط التنقل أعلى الشاشة ، وقسم المحتوى أي ينقسم إلى شريط تصفح علوي و اخر سفلي و قسم للمحتوى الرئيسي . يمكن تقسيم قسم المحتوى الرئيسي إلى سلسلة من الأسئلة والشريط الجانبي الأيسر . كل عنصر من هذه العناصر موضوع في وسم div لفصله كتقسيمة و عنصر . رغم أن هنالك العديد من الوسوم الأخررى التي يمكن لها أن تقسم صفحة الويب إلى حاويات و أقسام إلا أن إستعمال div شائع لعدة أسباب نذكر من بينها : أن وسم div يحتوي كل من علامة الفتح (<div> ) وعلامة الإغلاق (</div>) بشكل إلزامي لإغلاق الحاوية . يجعلنا هذا نتعرف بسهولة على بداية و نهاية كل حاوية . وسم div هو Block level tag , أي أنه يقوم بالنزول سطرا تلقائيا عند كل وسم فتح له , على عكس وسوم من مثل span أو label التي هي وسوم سطرية inline level tags . وسم div هو عنصر حاوية عامة , على عكس وسم body مثلا الذي يجب أن يتوفر مرة واحدة في هيكلية الـ HTML لكل صفحة . داخل وسم div يمكننا وضع أكثر من عنصر HTML و يمكن تجميعها معًا ويمكننا تطبيق CSS عليها على خلاف لو قمنا مثلا بتعريف هاته العناصر بشكل مباشر في وسم الـ body . كما أنه يقبل أية أنواع من العناصر و الوسوم داخله على عكس وسوم أخرى تشترط امتلاك عناصر محدودة . مثلا : يمكن استخدام وسم div لإنشاء تخطيط لصفحات الويب أو Layouts بالإنجليزية بسهولة و مرونة . لاحظ التخطيط : يمكن تخصيص عرضه و إرتفاعه بشكل مرن جدا , على عكس وسوم أخرى تشترط أن لا تتجاوز عرضا معينا . و هذا في الغالب ما جعل صاحب الفيديو يعتمد تقسيم الصفحة وفق وسوم div , كونه يريد أقصى مرونة و سهولة في تحكمه بعناصر صفحته . ومع ذلك ، اتجاه تطوير الويب في الاونة الأخيرة أصبح يتحرك نحو الاتجاه الدلالي أو الـ semantic direction . فبدلاً من عناصر div العامة ، من المفترض أن تستخدم العلامات الدلالية الأحدث التي تشير بشكل أفضل إلى أي شيفرة تبحث عنها في الصفحة . لذلك أصبح لدينا علامات التنقل nav والقسم section والرأس headere ،و غيرها . و لذلك فمن الناحية النظرية , ستجد البعض يطالبون أو يعتبرون أن من المفترض أن تتم إزالة الحاجة إلى علامات div العامة بشكل نهائي . أما من الناحية العملية ، فلا يزال معظم الأشخاص يستخدمون divs فقط . كونها طريقة مهيمنة و شائعة جدا , أو على الأقل أفضل من الأيام التي كان فيها يتم تصميم الصفحات باستخدام الجداول و الإطارات ! و طبعا هذا بجانب إمتلاكها للمميزات التي تم ذكرها سابقا . تعرف أكثر عن : و أيضا : توثيق وسم div : هنا .
  10. قد يكون سؤالك متشعب كثيرا كونك لم تحدد ما تقصده بالضبط بـ "التعديل على الكود و بناء المواقع " , فعملية بناء المواقع عملية متسلسة و تتفرع إلى كثير من التخصصات و يستعمل فيها الكثير من اللغات . فالمواقع و تطبيقات الويب بصفحة عامة تنقسم إلى قسمين : واجهات خلفية Backend : تمثل كل العمليات و المنطق الحاصل على مستوى الخادم , يتم في هاته المرحلة عادة بناء واجهات تطبيق برمجية لتسهيل ربطها مع الواجهة أو الواجهات الأمامية . واجهات أمامية Frontend : تمثل مجموع التصاميم و التفاعليات و بنى العناصر , يتم في مرحلة بناء الواجهات الأمامية بناء شاشات و قوالب قد تكون تفاعلية و قد تكون صماء . و كون أغلب العمليات تبتدئ من الخادم فسيكون المسؤول الأول عن عرض الصفحة لديك بشكل أو بتنسيق أو تفاعلية معينة , و لذلك نجد صفحات بلواحق php. تحمل شيفرات واجهات أمامية من مثل HTML أو جافاسكربت أو CSS . و لذلك فإن التعديل على هاته الصفحات يكون موجها و مقيدا بالهدف من التعديل : فإن كان الهدف التعديل على الواجهات الأمامية , مثل القوالب , فسيمكن مباشرة التعديل على بنية الـ HTML أو الملفات أو الأسطر المسؤولة عن تنسيق العناصر أو عن تفاعليتها , دون المساس بأي منطق داخل محددي الـ PHP : <?php // PHP هنا شيفرة ?> <div class="container-fluid"> .. </div> <?php // PHP هنا شيفرة ?> أما ان كان الهدف تغيير طريقة تعامل الخادم مع الطلبات و كل عمليات الخادم فسيتطلب التعديل على ما داخل المحددين , و إن لزم خارجهما . فاﻷول هو ما يقوم به مطورو الواجهة الأمامية , و الثاني هو ما يقوم به مطورو الواجهة الخلفية . نعم يمكن و هو المفترض أن يكون , فلا دخل لما يحدث على الواجهة الخلفية في الواجهة الأمامية . و قد لا تتطلب الكثير من التطبيقات و المواقع واجهة خلفية أصلا , مثل قوالب الـ HTML أو صفحات العرض أو أي من المواقع ثابتة المحتوى . و لذلك فإن إستعمال HTML , JS , CSS كاف جدا بهذا الخصوص . التعديل على واجهات المستخدم لا يلزم في الغالب أي معرفة مسبقة بخصوص الواجهات الخلفية في التطبيقات و المواقع المبنية بتنظيم , و لكن كون الواجهة الخلفية هي المحرك الأول فسيكفي - في الغالب - امتلاك معرفة سطحية لسهولة التصفح و التنقل بين الملفات و فهم الشيفرة , قبل على التعديل على ما يخص الواجهة الأمامية منها .
  11. الكود النظيف بشكل عام هو أمر شخصي و ذاتي و لكل مطور وجهة نظر شخصية بخصوصه . ولكن هناك بعض الأفكار التي يتفق عليها الأغلب و يعتبرونها أفضل الممارسات لما يشكل شيفرة نظيفة , مفهومة , قابلة للقراءة و التعديل على نطاق واسع . أي باختصار : و لنأخذ المفهوم بشكل أعمق كالتالي : سهولة الفهم تعني سهولة قراءة الكود ، سواء كان ذلك القارئ هو المؤلف الأصلي للكود أو أي شخص آخر . لذا فهو يقلل من الحاجة إلى التخمين وإمكانية حدوث سوء فهم . من السهل أن نفهمه على كل المستويات ، وعلى وجه التحديد : منطق العمل للتطبيق بأكمله . كيفية تعاون الكائنات و الأصناف المختلفة مع بعضها البعض . دور ومسؤولية كل فئة و كائن . ما تفعله كل دالة و تابع . الغرض من كل تعبير يتم تعريفه , سواء متغير , ثابت أو خاصية . أما سهولة التعديل تعني أن الشيفرة سهلة التوسيع , التطوير وإعادة البناء . كما أنه من السهل إصلاح و تشخيص الأخطاء فيها , و نفس الأمر ينطبق أيضا : سواء كان ذلك القارئ هو المؤلف الأصلي للكود أو أي شخص آخر . و يشمل ذلك النقاط : الكائنات و التوابع صغيرة الحجم ولا تتحمل سوى مسؤولية واحدة (و ذلك احتراما لمبدأ فصل المهام ) . تتوفر الكائنات على واجهات برمجية عامة واضحة و موجزة . يمكن التنبؤ بكبفية عمل الكائنات و توابعها . الكود قابل للاختبار بسهولة ولديه اختبارات وحدة (أو على الأقل من السهل كتابة الاختبارات) . الاختبارات - إن توفرت- فهي سهلة الفهم والتغيير بسهولة . و لكن لما نحتاج الإهتمام أصلا بالكود النظيف ؟ لنقم بتلخيص أهمية الأمر في النقاط التالية : يجب أن تهتم لأن الشفرة (في الغالب ) لا تتم كتابتها مرة واحدة و فقط . و إنما في معظم الأوقات ، تحتاج أنت أو أي شخص آخر إلى العمل على الكود. ولكي تكون قادرًا على العمل عليه بكفاءة ، فأنت بحاجة إلى فهمه بالدرجة الأولى . مبادئ كتابة كود نظيف تمنحك الثقة أثناء كتابة شيفرتك . تكون الأخطاء أسهل للتشخيص و الحل . تجعل الكود سهل للصيانة و التطوير . و قد يتعلق مفهوم الكود النظيف بكثير من المفاهيم الأخرى , من مثل أنماط التصميم و مبادئه و غيرها . و لذلك قد تجد بعضهم يستعير بعض مبادئ تصميم البرمجيات ليستعملها كمبادئ للكود النظيف , من مثل مجموعة مبادئ SOLID (سنأتي لإرفاق سلسلة مقالات تخص كل مبدأ) أو DRY (اختصارا لترجمة لا تكرر نفسك Don't Repeat Yourself ) أو YAGNI (اختصارا لترجمة أنت لن تحتاجها You Aren't Gonna Need It) أو غيرها من المبادئ . و لذلك فهي عملية متشعبة جدا . و لعل الكود النظيف هو ما يصنع الفارق بين مبرمجي نفس اللغة و نفس إطار العمل , فإن كنت لا توليه أهمية أو أولوية فسيجب عليك ذلك . لأنك لن تلتفت إليه بداية كونك بكامل تركيزك وإستيعابك للكود, و لكنك ستجد نفسك في حاجة إليه عندما تبتعد قليلا عن تطوير تطبيقك ثم تقرر العودة إليه . و أتذكر جيدا إضطرار أحد فرق التطوير لنسف الكود بأكمله (الذي كان بحجم 400 ميغا) و إعادته لمجرد أن مطوريه نفسهم عجزوا عن فهمه بعد أقل من سنة من إنطلاقه . يمكنك التعرف أكثر على مبادئ SOLID لتصميم البرمجيات في سلسلة المقالات التالية : S O L I D تعرف أكثر على الشيفرة النظيفة هنا و هنا . كما يمكنك التعرف أكثر على مفهوم الكود النظيف في HTML , CSS و Sass هنا .
  12. لنقم بتناول الأمر بشكل عام على هذا النحو : الـ devDependencies : هي مجموع التبعيات و الحزم المطلوبة للتشغيل على بيئة التطوير , أي تلك التي يتم إستعمالها فقط أثناء التطوير أو الإصدار ، على سبيل المثال : الحزم المستعملة في اختبارات الوحدة . مثل حزمة jest في تطبيقات الـ node , react , angular و الـ vueJS , عناصر و حزم webpack . بحيث تشكل مجموع الأدوات التي تساعدك على إدارة كيفية تطوير التطبيق لديك . هاته الحزم يتم تثبيتها عن طريق مدير الحزم npm مثلا بإضافة الخيار save-dev-- : npm install pakcageName --save-dev الـ Dependencies : و هي مجموع التبعيات و الحزم التي يحتاجها مشروعك ليكون قادرًا على العمل في بيئة الإنتاج . على سبيل المثال : الحزم المستعملة في عرض أو تنسيق أو تفاعلية جزء من أجزاء الصفحة , فلو احتجنا إلى حزمة للحصول على اليوم و الشهر . فسنجد أننا نقوم مثلا باستيراد {getDate} من الحزمة "date-fns" في مكون ما , و بالتالي فإنه بدون هذه الحزمة لن يعمل كودنا . هاته الحزم يتم تثبيتها عن طريق مدير الحزم npm مثلا بدون إضافة أي خيار : npm install myPackage الـ PeerDependencies : هي مجموع الحزم التي تحتاجها حزمة ما لكي تشتغل بشكل طبيعي , و نادرا ما يتم التعامل مع هاته التبعيات و الحزم . و يقصد بها أي حزمة B تتطلبه حزمة معينة A ما ولكنها لا تشمله مع نفسه عند تثبيته . على سبيل المثال : حزمة popper.js مع بوتستراب , فهي حزمة يستعملها Bootstrap 4 لإظهار النوافذ المنبثقة و بعض التأثيرات و التفاعليات في بوتستراب , و لكن بوتستراب لا يتضمنها عند التثبيت و قد نغفل في كثير من الحالات عن تثبيتها فتسبب بعض المشاكل من مثل : مثال 2 : . يطلق هنا على popper.js و مثلها من الحزم لفظ peer dependecies لـ bootstrap . يمكن نمذجة طريقة التعامل معها عن طريق مدير الحزم npm كالتالي : ./node_modules/ | | | +- devDependency | | | | +- dependency/node_modules/ | | | +- peerDependency-1/ | | +- dependency | | | | | +- devDependency/node_modules/ | +- peerDependency-2/ كما يوجد أيضا : الـ bundledDependencies و الـ OptionalDependencies . قد لا يختلف الأمر كثيرا مع بيئات و لغات مختلفة , كما قد تختلف طريقة التعامل مع كل منها مع مدراء الحزم من مثل npm , composer , yarn , pip . سواء في أسماءها أو اللواحق أو الخيارات التي إضافتها لتحديد نوع الحزمة أو التبعية , أو ربما في طريقة تنسيق بنية ملفات الحزم و التبعيات أو أيضا في ملف مدير الحزمة (مثل composer.json و package.json) . و لكن يبقى منطقها و تقسيمها سواءا مهما اختلف كل ذلك .
  13. التعريفات و الأفكار حول هندسة البرمجيات متفاوتة و كثيرة . و قد يصب مجملها و سياقها العام في مفهوم واحد جامع . بحيث يمكن القول على أنها فرع من فروع الهندسة حيث يقوم على مجموعة من الأسس التي تهدف إلى تصميم و تطوير البرامج بجودة عالية تلبي بالدرجة الأولى احتياجات المُستخدمين . يمكن أيضا الاستعانة ببعض المراجع و المصادر العالمية في التعريف بها : و أيضا : مما سبق يمكن فهم أن هندسة البرمجيات ليست كتابة شيفرة أو تكويدا و فقط , فمثل ما شرح المدرب عبد الحميد في تعليق سابق هي جزء من مرحلة التنفيذ . يمكن من هذا إستنتاج أن هندسة البرمجيات عملية إنتاجية لها عدة مراحل أساسية وضرورية للحصول على المُنتج البرمجي النهائي . بحيث يكون الهدف النهائي من أي برنامج هو الحصول على أفضل كفاءة ممكنة بأقل كلفة ممكنة. بحيث يطلق على مجمل هذه المراحل اسم دورة حياة تطوير النظام البرمجي أو بالإنجليزية : Software Development Life Cycle , و اختصارها " SDLC " . و كون فلسفاتها و نماذج بناءها كثيرة و متفاوتة سنأخذ أحد النماذج الكلاسيكية مثالا , هذا النموذج يعرف بنموذج الشلال أو Waterfall Model . لنقل أن هندسة البرمجيات وفق هذا النموذج تتبع بشكل عام المراحل التالية : تحليل المتطلبات Requirement Analysis : في هاته المرحلة يتم تجميع المتطلبات لفهم المشكلة و متطلبات الزبون أو المشروع ، هنا أيضا يتم تقسيم أو توزيع كل موضوع كبير أو معقد إلى اجزاء اصغر من اجل اكتساب فهم أفضل وأسهل للمشكلة والاهداف . التصميم Design : في هاته المرحلة يتم تصميم حلول للمتطلبات التي جمعناها، عملية تحتوي على انشطة معينة مثل صياغة وترتيب، لتحقيق الاهداف , و لا يعنى بالتصميم التصميم بمعناه و إنما تصميم حلول افتراضية أو منمذجة في مخططات لأفضل ما يليق أو ما قد يليق بالمشروع . التنفيذ Implementation : بعد تحديد المتطلبات والأهداف والاتفاق على لغة البرمجة ووضع الخطوط الأساسية للبرنامج وبنيته، يقوم الفريق بتنفيذ “ نموذج أولي Prototype ” للبرنامج . الاختبار Testing : في مرحلة الاختبار، يقوم الفريق باختبار البرنامج والتأكد من أنه يقوم بكافة الوظائف المطلوبة أو يتناسب مع المتطلبات التي تم تحديدها مسبقا . التطوير Evolution : يقوم الفريق بالبحث عن سبل تطوير البرنامج أو تطويره إنطلاقا من MVP (الحد الأدنى للمنتج أو Minimum Viable Product) . يتم اقتراح و إضافة ميزات جديدة له، كما تتم ماقشة إمكانية أو سبل جعله أكثر فاعلية وأكثر كفاءة . المخطط التالي يشرح خطوات النموذج : و بالتالي فإنها عملية منهجية و منظمة للغاية , و في الغالب تتم بالتنسيق بين فرق من مجالات مختلفة . يمكنك الاستزادة عن طرييق قراءة المقالات التالية : ما هي هندسة البرمجيات . مراحل تطوير البرامج في هندسة البرمجيات . دورة حياة البرنامج أو المشروع في هندسة البرمجيات .
  14. يبدوا أن لديك خطأ بسياق تعليمة الـ SQL , و لذلك سيجب إعادة صياغتها على نحو أدق و أصح . هلا قمت بتجربة هاته التعليمة بدلا من تلك التي لديك : CREATE TRIGGER `on_comment` BEFORE INSERT ON `article_likers` FOR EACH ROW BEGIN UPDATE `articles` SET `likes` = `likes` + 1 WHERE `id` = new.`article_id` END ( فلا حاجة لتحديد الجدول في aricles.id عند التعليمة WHERE ) يرجى التأكد أيضا من أن لديك جدولا بإسم article_likers
  15. ستحتاج في هذا إنشاء : ملف عرض : لعرض نموذج الإدخال و رفع الصورة و تقديمها إلى الواجهة الخلفية . متحكم : ليحتوي تابعا يتولى مهمة التحقق من المدخلات و فحص ملف الصورة و تخزينه . مسارين : واحد لعرض نموذج رفع الصورة أو الملف و واحد لإستهداف تابع المتحكم الخاص بالرفع . تعريف المسارين بداخل ملف web.php سيكون كالتالي : Route::view('/upload-image' ,'upload-image'); # إعادة ملف عرض Route::post('/upload-image' , 'UploadImageController@store'); # store إستهداف تابع المتحكم ملف العرض upload-image.blade.php سيحتوي بنية HTML مشابهة للتالي : <form action="/upload-image" method="POST" enctype="multipart/form-data" > <!-- يجب تحديد نوع التشفير --> @method('POST') @csrf <div class="form-group"> <label>صورة</label> <input type="file" name="image" required> </div> <button type="submit">رفع الملف</button> </form> المتحكم UploadImageController.php سيحتوي التابع store بالشيفرة التالية : public function store(Request $request) { // التأكد من أن الطلب يمتلك بالفعل ملف if ($request->hasFile('image')) { // التحقق من الملف صورة بلاحقة ملف صورة $request->validate([ 'image' => 'mimes:jpeg,bmp,png' ]); // images في مجلد app/storage/public تخزين الملف محليا في مجلد $request->image->store('images', 'public'); } return back(); } بعد تقديم النموذج و نجاح التحميل سيمكنك الوصول إلى الملفات المخزنة عن طريق التصفح إلى المسار : app/storage/public/images و ستلاحظ وجود ملف الصورة المرفوعة حديثا . هذا لن يكفي لجعل الصورة قابلة للوصول , فلا يزال عليك إنشاء الرابط الرمزي بين المسارين : storage/public/ و : public/storage سيمكن هذا عن طريق تنفيذ الأمر : php artisan storage:link ستلاحظ إنشاء رابط رمزي بين المجلد storage داخل public و public داخل app/storage . و يمكنك الوصول إلى رابط هاته الصور عن طريق التابع hashName في : // images في مجلد app/storage/public تخزين الملف محليا في مجلد $request->image->store('images', 'public'); $filePath = '/storage' .$request->file->hashName();
  16. يمكنك إستخدام الكائن Date في جافاسكربت و توابعه لطباعة التاريخ المرافق بشكل dd-mm-yy . نحتاج أولا تعريف نموذج عن الكائن Date , مع تمرير التاريخ المراد إلى الدالة البانية الخاصية بالكائن : var date = new Date("Tue Aug 31 2021 14:29:37 GMT+0200 (EET)"); ثم لنقم بقراءة اليوم و الشهر و السنة عن طريق التوابع على الترتيب : getDate و getMonth و getFullYear على هذا النحو : var date = new Date("Tue Aug 31 2021 14:29:37 GMT+0200 (EET)"); var year = date.getFullYear(); var month = date.getMonth() + 1 // لأن عد الأشهر في الكائن يبدأ من الصفر var day = date.getDate(); ثم لنقم بتركيب سلسلة نصية تجمع كل منهم : var date = new Date("Tue Aug 31 2021 14:29:37 GMT+0200 (EET)"); var year = date.getFullYear(); var month = date.getMonth() + 1; // لأن عد الأشهر في الكائن يبدأ من الصفر var day = date.getDate(); var formatted = year + "-" + month + "-" + day; و أخيرا طباعة السلسلة الناتجة : var date = new Date("Tue Aug 31 2021 14:29:37 GMT+0200 (EET)"); var year = date.getFullYear(); var month = date.getMonth() + 1; // لأن عد الأشهر في الكائن يبدأ من الصفر var day = date.getDate(); var formatted = year + "-" + month + "-" + day; console.log(formatted); // "2021-8-31" يمكنك الإطلاع على مثال حي من هنا . كما يمكنك التعرف أكثر على الكائن Date في جافاسكربت هنا و هنا .
  17. تحتاج في هذا إستعمال واجهة خلفية و قاعدة بيانات و هذا لأنك تحتاج حفظ بيانات المستخدمين لإستعمالها مستقبلا في عمليات تسجيل الدخول و الخروج . يعني هذا أن الذاكرة المحلية للتخزين في الجافاسكربت لن تكفي و ستحتاج إلى واجهة خلفية و قاعدة بيانات , و لن يكفي إستعمال الجافاسكربت وحدها لتحقيق ذلك . إن كانت برمجة الواجهة الخلفية مألوفة لديك , فيمكنك تحديد لغة أو إطار عمل الواجهة الخلفية و نظام إدارة قواعد البيانات و سيحاول مدربوا الأكاديمية تبسيط العملية لك . أما إن لم يكن إستعمالها مألوفا لديك و كان جديدا عليك فأنصحك أولا بتعلم برمجة الواجهة الخلفية و التعامل مع قواعد البيانات و من ثم نمذجة نظم مثل الذي تريده بشكل برمجي . و عموما , سيكون منطق العملية كالتالي : يتم إرسال بيانات العضو المراد تسجيله إلى الواجهة الخلفية للتطبيق عن طريق نموذج form . تقوم الواجهة الخلفية بإستقبال البيانات , معالجتها و فحصها . تقوم الواجهة الخلفية بكتابة إستعلام قواعد بيانات ﻹدراج بيانات العضو في قواعد البيانات . يتم تسجيل العضو و حفظ بياناته كعضو غير مؤكد التسجيل . يتم في نفس الوقت عن طريق الواجهة الخلفية توليد سلسلة نصية أو token يتم حفظه في قواعد البيانات و أيضا إرساله إلى البريد الإلكتروني للمستخدم أو بشكل رسالة نصية SMS . يتم لاحقا التقاط و إستقبال الكود أو السلسلة النصية التي يقوم المستخدم بإدخالها محاولا تأكيد حسابه , ثم مطابقتها مع المخزنة في قواعد البيانات . و إن تم التطابق يتم تأكيد حساب المستخدم و يتم تسجيله كعضو مؤكد التسجيل . و قد تختلف بعض التفاصيل بشأن كل خطوة بين لغة برمجية و أخرى .
  18. بجانب ما قام المدربان شرف و أبو عواد بشرحه , فإنه يتم الإعتماد عليها في الإستعلامات أيضا لأن استخدام المفاتيح الرئيسية شائع أيضا كطريقة لتقييد أو ربط البيانات ذات الصلة ببعضها في قاعدة بيانات . و هذا يضمن أن تظل البيانات متسقة وأن قاعدة البيانات لا تحتوي على أية بيانات زائدة عن الحاجة . على سبيل المثال ، إذا قمت بحذف جدول (أو حتى صف في جدول) تعتمد عليه الجداول الأخرى ، فستتم إزالة البيانات المكررة . و يساعد هذا في منع مشاكل تكامل البيانات التي يمكن أن تسبب مشاكل مع التطبيقات التي تستخدم مثل هذه البيانات . و يعني هذا أنه بجانب أن المفاتيح الرئيسية سهلة في الإستعلام و فريدة و غير مكررة هي ضرورية جدا أيضا إذا كان الجدول مؤهلاً كجدول علائقي , أي أن يكون جدول يرتبط بغيره من الجداول بعلاقة ما . أي أنه له دورا علائقيا أيضا . كما أن هناك مشكلة أخرى تتعلق باستخدام السلاسل النصية مثلا كمفتاح أساسي وهي : نظرًا لأنه يتم وضع الفهرس في ترتيب تسلسلي باستمرار ، فإنه عند إنشاء مفتاح جديد يكون في منتصف الترتيب ، يتطلب علينا إعادة ترتيب الفهرس ... لكن إستخدامنا لمفتاح رئيسي يكون تلقائيا كعدد صحيح مرتب يجعلنا نضيف المفتاح الجديد فقط إلى نهاية الفهرس دون أي تداخل . أي أنه له بجانب دوره التبسيطي , و العلائقي دورا ترتيبيا و منطقيا أيضا . و لنأخذ على سبيل المثال , تطبيقات السجلات . فمن غير المنطقي طبعا أن يتغير ترتيب السجل كل مرة يتم فيها إدخال حقل جديد . فكل هذا يؤول إستخدام المفاتيح الرئيسية في أغلب الإستعلامات . و طبعا كل هذا لا يعني هذا الإستغناء عن باقي الأعمدة و الإكتفاء بالإستعلام عن البيانات بمفتاح رئيسي , فقد نحتاج في كثير من الحالات الإستعلام عن البيانات عن طريق أعمدة أخرى من مثل الإستعلامات التي يتم كتابتها في عمليات البحث و الفهرسة .
  19. يبدوا أن مشكلة تثبيت webpack قد تم تجاوزها . يظهر من الخطأ أنه هنالك مشكلة بتثبيت حزمة popper.js و popperjs core أيضا . و ليكن في العلم أن حزمة popper.js هي حزمة يستعملها Bootstrap 4 لإظهار النوافذ المنبثقة و بعض التأثيرات و التفاعليات في بوتستراب. يطلق على popper.js و مثلها من الحزم لفظ peer dependecies , و يقصد بها أي حزمة B تتطلبه حزمة معينة A ما ولكنها لا تشمله مع نفسه عند تثبيته . و بسبب عدم تثبيتها أو تثبيتها على نحو خاطئ , نواجه مثل هاته الأخطاء التي تخبرنا أن واحدة من الحزم تتطلب حزمة أخرى . و لذلك سنحتاج أيضا تثبيت popper.js بشكل مستقل , لنقم بتنفيذ الأمر : npm install popper.js --save أما ان كنت تستعملين نسخ x.5 من بوتستراب فسيتطلب عليك تثبيت popperjs core عوضا عن popper.js , لنتأكد عندها من تثبيت الحزمة عن طريق الأمر : npm install @popperjs/core --save ثم سيكون عليك أخيرا تشغيل أمر البناء مجددا : npm run build إن استمرت مشكلتك بالظهور يرجى إخباري بشأن أيضا أخطاء تظهر .
  20. لتفادي المشكل يمكننا تكبير المهلة الزمنية التي يتم فيها التحقق ما إن كان المستخدم قد قام بتحريك الشريط الجانبي إلى مهلة تراها مناسبة أكثر ، يمكننا بهذا إعطاء المستخدم وقتا أكثر لقراءة الرسائل بدون عمل أي إنزلاق للشريط الجانبي .
  21. تشير في الغالب الخطوط و الألوان الحمراء في vs code إلى أخطاء بالسياق في ملف web.php و ملف ForgetPasswordController.php , هلا تأكدت من عدم وجود أية أخطاء بهما ؟ كما أنه سيكون من المساعد لنا في تعقب المشكل و حله لو قمت بإرفاق الملفين أو نسخهما بشكل أكواد بإستعمال محرر الأكواد .
  22. يحدث هذا لأنك تقوم بتغيير قيمة num في كل دورة من التكرار . فإن كانت قيمة num في الدورة الأولى 5 فهي أقل من القيمة الأولى لـ i و هي الصفر , و بالتالي فإن 5 أقل أيضا من 1 و 6 من 2 و 7 من 3 و هكذا . أي أن الشرط لن يتوقف . و لتدارك هاته المشكلة لنقم بتخزين القيمة الافتراضية لـ num في متغير ثان لا يكون له تأثير في ما داخل الدور سوى تحديده : function addUp(num) { var myNum = num; for (i = 0; i < myNum; i++) { num += i } return num } سيقوم هذا بإصلاح المشكلة و تحديد الدور من الأعلى في حالة بلوغ i قيمة num الإفتراضية . يمكنك الإطلاع على هذا المثال التجريبي .
  23. بالإضافة إلى ما قام المدرب سامح بشرحه يمكننا تلخيص الوظيفة في دالة مستقلة تماما يتم إستدعاءها عند الحاجة لها . لنقم أولا بتعريف الدالة المسؤولة عن العملية : function scrollToBottom() { var div = $('#div1'); div.scrollTop(div.prop("scrollHeight")); } لنقم الان بإستدعاء الدالة في كل من الوظائف المعبر عنها بهاته الحالات : أي أن الدالة تستدعى عند كل تحميل لموارد الصفحة : scrollToBottom(); نحتاج في هذا تعقب الوظيفة المسؤولة عن عرض رسائل جديدة و إستدعاء الدالة المسؤولة عن تعديل الاسكرول عند كل عرض لرسالة جديدة . أي : <script> $(document) .ready(function(e) { function getMessages(letter) { var message = $('#div_show_message_groups'); $.get('show_pages/display_messages_groups.php', function(data) { message.html(data); // ضبط الشريط الجانبي بعد كل تضمين لرسالة جديدة scrollToBottom(); }); } }); </script> هاته العملية هي خطوتان مركبتان , نحتاج أولا تعقب إرسال رسائل جديدة و إستدعاء الدالة عند كل تضمين (وهو ما تم عمله بالفعل في الخطوة الثانية) , و ثانيا : التحقق من أن المستخدم لا يقوم بتحريك الشريط للأعلى . لنقم بتعريف متغير يحمل قيمة بوليانية تعبر عن ما إن كان المستخدم يقوم بتحريك الشريط أو لا , و لنقم بربط هذا بفترة تقوم بالتحقق ما إن توقف المستخدم بتحريك الشريط أو لا : var myDiv = $('#div1'); var userIsScrolling = false; /*يتم تغيير فيمة المتغير عند كل تحريك للشريط الجانبي*/ myDiv.scroll(function(){ userIsScrolling = true; }); /*ان توقفف المستخدم عن تحريك الشريط الجانبي لأكثر من ثلاث ثواني سيتم تغيير قيمة المتغير*/ setInterval(function(){ userIsScrolling = false; }, 3000); ثم سيتم إستعمال قيمة هذا التغير في التحقق قبل ضبط الشريط الجانبي عند كل إرسال لرسالة جديدة : <script> $(document) .ready(function(e) { function getMessages(letter) { var message = $('#div_show_message_groups'); $.get('show_pages/display_messages_groups.php', function(data) { message.html(data); // ضبط الشريط الجانبي للأسفل عند كل رسالة جديدة , و في حالة عدم تحريكه من طرفف المستخدم if(! userIsScrolling){ scrollToBottom(); } }); } }); </script> ثم لنقم بدمج الشيفرة كاملة , لتكون : $(document) .ready(function(e) { /*الوظيفة المسؤولة عن عمل الاسكرول*/ function scrollToBottom() { var div = $('#div1'); div.scrollTop(div.prop("scrollHeight")); } /*استدعاء الدالة عند كل تحميل جديد للصفحة*/ scrollToBottom(); var userIsScrolling = false; /*يتم تغيير فيمة المتغير عند كل تحريك للشريط الجانبي*/ $('#div1') .scroll(function() { userIsScrolling = true; }); /*ان توقفف المستخدم عن تحريك الشريط الجانبي لأكثر من ثلاث ثواني سيتم تغيير قيمة المتغير*/ setInterval(function() { userIsScrolling = false; }, 3000); /*الدالة المسؤولة عن جلب الرسائل من الواجهة الخلفية*/ function getMessages(letter) { var message = $('#div_show_message_groups'); $.get('show_pages/display_messages_groups.php', function(data) { message.html(data); // ضبط الشريط الجانبي للأسفل عند كل رسالة جديدة , و في حالة عدم تحريكه من طرفف المستخدم if (!userIsScrolling) { scrollToBottom(); } }); } setInterval(function() { getMessages("letter"); }, 100) /*إرسال طلب إلى الواجهة الخلفية بتسجيل رسالة جديدة*/ $("#messages_form_groups") .on('submit', function(e) { e.preventDefault(); $.ajax({ type: 'POST', url: 'send_pages/send_messages_form_groups.php', data: new FormData(this), dataType: 'json', contentType: false, cache: false, processData: false, success: function(response) { if (response.status == 1) { $('#messages_form_groups')[0].reset(); } } }); }); }); و بالطبع فإنه يمكنك التعديل من سلوك التحريك كإضافة بعض الأنيميشن أو ربما تغيير المهلة التي يتم فيها إعتبار أن المستخدم توقف عن السكرول . تأكد فقط أن الكثير من الأفكار قد تحتاج تجزئة و تبسيط لها ,و لما لا نمذجة في سبورة أو ورقة , فهو صحيح أن البرمجة ومفاهيمها أشياء تجريدية , و لكنها تنطلق من الواقع و تحاول نمذجته و تبسيطه و لذلك حاول دوما تقسيم الفكرة إلى أفكار جزئية و لا يجب عليك أبدا النظر إلى أية فكرة بمجملها كفكرة واحدة موحدة غير قابلة للتقسيم .
  24. ستحتاج أولا التسجيل بحساب تجاري في بيبال .ثم سنحتاج الدخول إلى لوحة تحكم المطور , أين يمكنك التعرف على نمطين و حسابين : حساب تجريبي , أو حساب sandbox (لأغراض الإختبار و التطوير). حساب حي , أو حساب live (لأغراض الدفع و التعامل الحقيقي) . سنقوم بتثبيت مكتبة paypal/rest-api-sdk-php عن طريق مدير الحزم composer : composer install paypal/rest-api-sdk-php 2. لنقم بإنشاء ملف إعداد paypal.php في مجلد config , و لنتأكد من إمتلاكه المحتوى التالي : <?php return [ 'client_id' => env('PAYPAL_CLIENT_ID',''), 'secret' => env('PAYPAL_SECRET',''), 'settings' => array( 'mode' => env('PAYPAL_MODE','sandbox'), 'http.ConnectionTimeOut' => 30, 'log.LogEnabled' => true, 'log.FileName' => storage_path() . '/logs/paypal.log', 'log.LogLevel' => 'ERROR' ), ]; 3. لنتوجه إلى ملف البيئة env. و لنتأكد من تعريف المتغيرات التالية : PAYPAL_CLIENT_ID=xxxxxxxxx PAYPAL_SECRET=xxxxxxxxx PAYPAL_MODE=xxxx حيث أن المتغيرين PAYPAL_CLIENT_ID و PAYPAL_SECRET يجلبان من لوحة تحكم المطور . 4. لنقم بإنشاء متحكم PaymentController يلخص بعض العمليات و لنتأكد من إمتلاكه المحتوى التالي : <?php namespace App\Http\Controllers; use App\Http\Controllers\Controller; use Illuminate\Http\Request; use PayPal\Rest\ApiContext; use PayPal\Auth\OAuthTokenCredential; use PayPal\Api\Agreement; use PayPal\Api\Payer; use PayPal\Api\Plan; use PayPal\Api\PaymentDefinition; use PayPal\Api\PayerInfo; use PayPal\Api\Item; use PayPal\Api\ItemList; use PayPal\Api\Amount; use PayPal\Api\Transaction; use PayPal\Api\RedirectUrls; use PayPal\Api\Payment; use PayPal\Api\PaymentExecution; use Illuminate\Support\Facades\Input; use Redirect; use URL; class PaymentController extends Controller { public function __construct() { /** إعداد سياق العملية و بدءها **/ $paypal_conf = \Config::get('paypal'); $this->_api_context = new ApiContext(new OAuthTokenCredential( $paypal_conf['client_id'], $paypal_conf['secret']) ); // حفظ الاعدادات $this->_api_context->setConfig($paypal_conf['settings']); } public function payWithpaypal() { $amountToBePaid = 100; // قيمة افتراضية يفترض أن تستجلب من الطلب $payer = new Payer(); $payer->setPaymentMethod('paypal'); /*تعريف عنصر*/ $item_1 = new Item(); $item_1->setName('هاتف نقال') /** اسم العنصر **/ ->setCurrency('USD') ->setQuantity(1) ->setPrice($amountToBePaid); /** سعر العنصر **/ /*تعريف قائمة عناصر و إضافة العنصر المعرف سابقا*/ $item_list = new ItemList(); $item_list->setItems(array($item_1)); /*تعريف مقدار*/ $amount = new Amount(); $amount->setCurrency('USD') ->setTotal($amountToBePaid); /** تخصيص روابط إعادة **/ $redirect_urls = new RedirectUrls(); $redirect_urls->setReturnUrl(URL::route('status')) ->setCancelUrl(URL::route('status')); /*تعريف تحويل جديد*/ $transaction = new Transaction(); $transaction->setAmount($amount) ->setItemList($item_list) ->setDescription('وصف التحويل'); /*تعريف مدفوعة و إضافة عملية الدفع إليها*/ $payment = new Payment(); $payment->setIntent('Sale') ->setPayer($payer) ->setRedirectUrls($redirect_urls) ->setTransactions(array($transaction)); /*تنفيذ العملية*/ try { $payment->create($this->_api_context); } catch (\PayPal\Exception\PPConnectionException $ex) { if (\Config::get('app.debug')) { \Session::put('error', 'فشل الاتصال تحقق من الاتصال'); return Redirect::route('/'); } else { \Session::put('error', 'حدث خطأ'); return Redirect::route('/'); } } foreach ($payment->getLinks() as $link) { if ($link->getRel() == 'approval_url') { $redirect_url = $link->getHref(); break; } } /** إضافة معرف الدفع إلى الجلسة **/ \Session::put('paypal_payment_id', $payment->getId()); if (isset($redirect_url)) { /** redirect to paypal **/ return Redirect::away($redirect_url); } \Session::put('error', 'حدث خطأ'); return Redirect::route('/'); } public function getPaymentStatus(Request $request) { /** جلب معرف العملية من الجلسة **/ $payment_id = Session::get('paypal_payment_id'); /** حذف المعرف من الجلسة **/ Session::forget('paypal_payment_id'); if (empty($request->PayerID) || empty($request->token)) { session()->flash('error', 'فشل الدفع'); return Redirect::route('/'); } $payment = Payment::get($payment_id, $this->_api_context); $execution = new PaymentExecution(); $execution->setPayerId($request->PayerID); /**تنفيذ العملية **/ $result = $payment->execute($execution, $this->_api_context); if ($result->getState() == 'approved') { session()->flash('success', 'نجح الدفع'); return Redirect::route('/'); } session()->flash('error', 'فشل الدفع'); return Redirect::route('/'); } } و بالطبع فإننا سنحتاج تعريف مسارين : واحد للدفع . واحد لتتبع مسار أو حالة الدفع . و ليكن : Route::post('/payment', ['as' => 'payment', 'uses' => 'PaymentController@payWithpaypal']); Route::get('/payment/status',['as' => 'status', 'uses' => 'PaymentController@getPaymentStatus']); حيث يتم إرسال طلب POST بمعلومات العنصر + تفاصيل عملية الدفع , ثم طلب GET لجلب حالة الدفع . و هذا هو الشكل الأبسط لعملية الدفع عبر بيبال , و بالطبع فإنه يمكن تخصيصها على نطاق واسع و إضافة الكثير من التفاصيل , كأن نقوم بحفظ تفاصيل العملية في قواعد البيانات لدينا و هكذا . كما يمكن أيضا إستخدام أحد الحزم التي تلخص العملية أكثر , من مثل حزمة srmklive/laravel-paypal .
  25. يمكنك للتخلص من المشكلة تثبيت webpack بشكل global , تأكد أن تقوم بطباعة الأمر التالي : npm install -g webpack أما إذا كان لديك Webpack مثبت محليًا ، ففي حالات خاصة جدا سنحتاج تحديد المكان الذي يجب أن يبحث فيه موجه الأوامر بشكل صريح للعثور عليه ، مثل : node_modules\.bin\webpack يكون هذا في ملف package.json أين يتم تعريف الأوامر ضمن الكائن script (هذا بافتراض أنك داخل دليل المشروع بالفعل وأنك قمت بالفعل بتسطيب الحزمة عن طريق الأمر webpack ) .
×
×
  • أضف...