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

Adnane Kadri

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

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

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

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

    52

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

  1. الكود النظيف بشكل عام هو أمر شخصي و ذاتي و لكل مطور وجهة نظر شخصية بخصوصه . ولكن هناك بعض الأفكار التي يتفق عليها الأغلب و يعتبرونها أفضل الممارسات لما يشكل شيفرة نظيفة , مفهومة , قابلة للقراءة و التعديل على نطاق واسع . أي باختصار : و لنأخذ المفهوم بشكل أعمق كالتالي : سهولة الفهم تعني سهولة قراءة الكود ، سواء كان ذلك القارئ هو المؤلف الأصلي للكود أو أي شخص آخر . لذا فهو يقلل من الحاجة إلى التخمين وإمكانية حدوث سوء فهم . من السهل أن نفهمه على كل المستويات ، وعلى وجه التحديد : منطق العمل للتطبيق بأكمله . كيفية تعاون الكائنات و الأصناف المختلفة مع بعضها البعض . دور ومسؤولية كل فئة و كائن . ما تفعله كل دالة و تابع . الغرض من كل تعبير يتم تعريفه , سواء متغير , ثابت أو خاصية . أما سهولة التعديل تعني أن الشيفرة سهلة التوسيع , التطوير وإعادة البناء . كما أنه من السهل إصلاح و تشخيص الأخطاء فيها , و نفس الأمر ينطبق أيضا : سواء كان ذلك القارئ هو المؤلف الأصلي للكود أو أي شخص آخر . و يشمل ذلك النقاط : الكائنات و التوابع صغيرة الحجم ولا تتحمل سوى مسؤولية واحدة (و ذلك احتراما لمبدأ فصل المهام ) . تتوفر الكائنات على واجهات برمجية عامة واضحة و موجزة . يمكن التنبؤ بكبفية عمل الكائنات و توابعها . الكود قابل للاختبار بسهولة ولديه اختبارات وحدة (أو على الأقل من السهل كتابة الاختبارات) . الاختبارات - إن توفرت- فهي سهلة الفهم والتغيير بسهولة . و لكن لما نحتاج الإهتمام أصلا بالكود النظيف ؟ لنقم بتلخيص أهمية الأمر في النقاط التالية : يجب أن تهتم لأن الشفرة (في الغالب ) لا تتم كتابتها مرة واحدة و فقط . و إنما في معظم الأوقات ، تحتاج أنت أو أي شخص آخر إلى العمل على الكود. ولكي تكون قادرًا على العمل عليه بكفاءة ، فأنت بحاجة إلى فهمه بالدرجة الأولى . مبادئ كتابة كود نظيف تمنحك الثقة أثناء كتابة شيفرتك . تكون الأخطاء أسهل للتشخيص و الحل . تجعل الكود سهل للصيانة و التطوير . و قد يتعلق مفهوم الكود النظيف بكثير من المفاهيم الأخرى , من مثل أنماط التصميم و مبادئه و غيرها . و لذلك قد تجد بعضهم يستعير بعض مبادئ تصميم البرمجيات ليستعملها كمبادئ للكود النظيف , من مثل مجموعة مبادئ SOLID (سنأتي لإرفاق سلسلة مقالات تخص كل مبدأ) أو DRY (اختصارا لترجمة لا تكرر نفسك Don't Repeat Yourself ) أو YAGNI (اختصارا لترجمة أنت لن تحتاجها You Aren't Gonna Need It) أو غيرها من المبادئ . و لذلك فهي عملية متشعبة جدا . و لعل الكود النظيف هو ما يصنع الفارق بين مبرمجي نفس اللغة و نفس إطار العمل , فإن كنت لا توليه أهمية أو أولوية فسيجب عليك ذلك . لأنك لن تلتفت إليه بداية كونك بكامل تركيزك وإستيعابك للكود, و لكنك ستجد نفسك في حاجة إليه عندما تبتعد قليلا عن تطوير تطبيقك ثم تقرر العودة إليه . و أتذكر جيدا إضطرار أحد فرق التطوير لنسف الكود بأكمله (الذي كان بحجم 400 ميغا) و إعادته لمجرد أن مطوريه نفسهم عجزوا عن فهمه بعد أقل من سنة من إنطلاقه . يمكنك التعرف أكثر على مبادئ SOLID لتصميم البرمجيات في سلسلة المقالات التالية : S O L I D تعرف أكثر على الشيفرة النظيفة هنا و هنا . كما يمكنك التعرف أكثر على مفهوم الكود النظيف في HTML , CSS و Sass هنا .
  2. لنقم بتناول الأمر بشكل عام على هذا النحو : الـ 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) . و لكن يبقى منطقها و تقسيمها سواءا مهما اختلف كل ذلك .
  3. التعريفات و الأفكار حول هندسة البرمجيات متفاوتة و كثيرة . و قد يصب مجملها و سياقها العام في مفهوم واحد جامع . بحيث يمكن القول على أنها فرع من فروع الهندسة حيث يقوم على مجموعة من الأسس التي تهدف إلى تصميم و تطوير البرامج بجودة عالية تلبي بالدرجة الأولى احتياجات المُستخدمين . يمكن أيضا الاستعانة ببعض المراجع و المصادر العالمية في التعريف بها : و أيضا : مما سبق يمكن فهم أن هندسة البرمجيات ليست كتابة شيفرة أو تكويدا و فقط , فمثل ما شرح المدرب عبد الحميد في تعليق سابق هي جزء من مرحلة التنفيذ . يمكن من هذا إستنتاج أن هندسة البرمجيات عملية إنتاجية لها عدة مراحل أساسية وضرورية للحصول على المُنتج البرمجي النهائي . بحيث يكون الهدف النهائي من أي برنامج هو الحصول على أفضل كفاءة ممكنة بأقل كلفة ممكنة. بحيث يطلق على مجمل هذه المراحل اسم دورة حياة تطوير النظام البرمجي أو بالإنجليزية : Software Development Life Cycle , و اختصارها " SDLC " . و كون فلسفاتها و نماذج بناءها كثيرة و متفاوتة سنأخذ أحد النماذج الكلاسيكية مثالا , هذا النموذج يعرف بنموذج الشلال أو Waterfall Model . لنقل أن هندسة البرمجيات وفق هذا النموذج تتبع بشكل عام المراحل التالية : تحليل المتطلبات Requirement Analysis : في هاته المرحلة يتم تجميع المتطلبات لفهم المشكلة و متطلبات الزبون أو المشروع ، هنا أيضا يتم تقسيم أو توزيع كل موضوع كبير أو معقد إلى اجزاء اصغر من اجل اكتساب فهم أفضل وأسهل للمشكلة والاهداف . التصميم Design : في هاته المرحلة يتم تصميم حلول للمتطلبات التي جمعناها، عملية تحتوي على انشطة معينة مثل صياغة وترتيب، لتحقيق الاهداف , و لا يعنى بالتصميم التصميم بمعناه و إنما تصميم حلول افتراضية أو منمذجة في مخططات لأفضل ما يليق أو ما قد يليق بالمشروع . التنفيذ Implementation : بعد تحديد المتطلبات والأهداف والاتفاق على لغة البرمجة ووضع الخطوط الأساسية للبرنامج وبنيته، يقوم الفريق بتنفيذ “ نموذج أولي Prototype ” للبرنامج . الاختبار Testing : في مرحلة الاختبار، يقوم الفريق باختبار البرنامج والتأكد من أنه يقوم بكافة الوظائف المطلوبة أو يتناسب مع المتطلبات التي تم تحديدها مسبقا . التطوير Evolution : يقوم الفريق بالبحث عن سبل تطوير البرنامج أو تطويره إنطلاقا من MVP (الحد الأدنى للمنتج أو Minimum Viable Product) . يتم اقتراح و إضافة ميزات جديدة له، كما تتم ماقشة إمكانية أو سبل جعله أكثر فاعلية وأكثر كفاءة . المخطط التالي يشرح خطوات النموذج : و بالتالي فإنها عملية منهجية و منظمة للغاية , و في الغالب تتم بالتنسيق بين فرق من مجالات مختلفة . يمكنك الاستزادة عن طرييق قراءة المقالات التالية : ما هي هندسة البرمجيات . مراحل تطوير البرامج في هندسة البرمجيات . دورة حياة البرنامج أو المشروع في هندسة البرمجيات .
  4. يبدوا أن لديك خطأ بسياق تعليمة الـ 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
  5. ستحتاج في هذا إنشاء : ملف عرض : لعرض نموذج الإدخال و رفع الصورة و تقديمها إلى الواجهة الخلفية . متحكم : ليحتوي تابعا يتولى مهمة التحقق من المدخلات و فحص ملف الصورة و تخزينه . مسارين : واحد لعرض نموذج رفع الصورة أو الملف و واحد لإستهداف تابع المتحكم الخاص بالرفع . تعريف المسارين بداخل ملف 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();
  6. يمكنك إستخدام الكائن 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 في جافاسكربت هنا و هنا .
  7. تحتاج في هذا إستعمال واجهة خلفية و قاعدة بيانات و هذا لأنك تحتاج حفظ بيانات المستخدمين لإستعمالها مستقبلا في عمليات تسجيل الدخول و الخروج . يعني هذا أن الذاكرة المحلية للتخزين في الجافاسكربت لن تكفي و ستحتاج إلى واجهة خلفية و قاعدة بيانات , و لن يكفي إستعمال الجافاسكربت وحدها لتحقيق ذلك . إن كانت برمجة الواجهة الخلفية مألوفة لديك , فيمكنك تحديد لغة أو إطار عمل الواجهة الخلفية و نظام إدارة قواعد البيانات و سيحاول مدربوا الأكاديمية تبسيط العملية لك . أما إن لم يكن إستعمالها مألوفا لديك و كان جديدا عليك فأنصحك أولا بتعلم برمجة الواجهة الخلفية و التعامل مع قواعد البيانات و من ثم نمذجة نظم مثل الذي تريده بشكل برمجي . و عموما , سيكون منطق العملية كالتالي : يتم إرسال بيانات العضو المراد تسجيله إلى الواجهة الخلفية للتطبيق عن طريق نموذج form . تقوم الواجهة الخلفية بإستقبال البيانات , معالجتها و فحصها . تقوم الواجهة الخلفية بكتابة إستعلام قواعد بيانات ﻹدراج بيانات العضو في قواعد البيانات . يتم تسجيل العضو و حفظ بياناته كعضو غير مؤكد التسجيل . يتم في نفس الوقت عن طريق الواجهة الخلفية توليد سلسلة نصية أو token يتم حفظه في قواعد البيانات و أيضا إرساله إلى البريد الإلكتروني للمستخدم أو بشكل رسالة نصية SMS . يتم لاحقا التقاط و إستقبال الكود أو السلسلة النصية التي يقوم المستخدم بإدخالها محاولا تأكيد حسابه , ثم مطابقتها مع المخزنة في قواعد البيانات . و إن تم التطابق يتم تأكيد حساب المستخدم و يتم تسجيله كعضو مؤكد التسجيل . و قد تختلف بعض التفاصيل بشأن كل خطوة بين لغة برمجية و أخرى .
  8. بجانب ما قام المدربان شرف و أبو عواد بشرحه , فإنه يتم الإعتماد عليها في الإستعلامات أيضا لأن استخدام المفاتيح الرئيسية شائع أيضا كطريقة لتقييد أو ربط البيانات ذات الصلة ببعضها في قاعدة بيانات . و هذا يضمن أن تظل البيانات متسقة وأن قاعدة البيانات لا تحتوي على أية بيانات زائدة عن الحاجة . على سبيل المثال ، إذا قمت بحذف جدول (أو حتى صف في جدول) تعتمد عليه الجداول الأخرى ، فستتم إزالة البيانات المكررة . و يساعد هذا في منع مشاكل تكامل البيانات التي يمكن أن تسبب مشاكل مع التطبيقات التي تستخدم مثل هذه البيانات . و يعني هذا أنه بجانب أن المفاتيح الرئيسية سهلة في الإستعلام و فريدة و غير مكررة هي ضرورية جدا أيضا إذا كان الجدول مؤهلاً كجدول علائقي , أي أن يكون جدول يرتبط بغيره من الجداول بعلاقة ما . أي أنه له دورا علائقيا أيضا . كما أن هناك مشكلة أخرى تتعلق باستخدام السلاسل النصية مثلا كمفتاح أساسي وهي : نظرًا لأنه يتم وضع الفهرس في ترتيب تسلسلي باستمرار ، فإنه عند إنشاء مفتاح جديد يكون في منتصف الترتيب ، يتطلب علينا إعادة ترتيب الفهرس ... لكن إستخدامنا لمفتاح رئيسي يكون تلقائيا كعدد صحيح مرتب يجعلنا نضيف المفتاح الجديد فقط إلى نهاية الفهرس دون أي تداخل . أي أنه له بجانب دوره التبسيطي , و العلائقي دورا ترتيبيا و منطقيا أيضا . و لنأخذ على سبيل المثال , تطبيقات السجلات . فمن غير المنطقي طبعا أن يتغير ترتيب السجل كل مرة يتم فيها إدخال حقل جديد . فكل هذا يؤول إستخدام المفاتيح الرئيسية في أغلب الإستعلامات . و طبعا كل هذا لا يعني هذا الإستغناء عن باقي الأعمدة و الإكتفاء بالإستعلام عن البيانات بمفتاح رئيسي , فقد نحتاج في كثير من الحالات الإستعلام عن البيانات عن طريق أعمدة أخرى من مثل الإستعلامات التي يتم كتابتها في عمليات البحث و الفهرسة .
  9. يبدوا أن مشكلة تثبيت 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 إن استمرت مشكلتك بالظهور يرجى إخباري بشأن أيضا أخطاء تظهر .
  10. لتفادي المشكل يمكننا تكبير المهلة الزمنية التي يتم فيها التحقق ما إن كان المستخدم قد قام بتحريك الشريط الجانبي إلى مهلة تراها مناسبة أكثر ، يمكننا بهذا إعطاء المستخدم وقتا أكثر لقراءة الرسائل بدون عمل أي إنزلاق للشريط الجانبي .
  11. تشير في الغالب الخطوط و الألوان الحمراء في vs code إلى أخطاء بالسياق في ملف web.php و ملف ForgetPasswordController.php , هلا تأكدت من عدم وجود أية أخطاء بهما ؟ كما أنه سيكون من المساعد لنا في تعقب المشكل و حله لو قمت بإرفاق الملفين أو نسخهما بشكل أكواد بإستعمال محرر الأكواد .
  12. يحدث هذا لأنك تقوم بتغيير قيمة 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 الإفتراضية . يمكنك الإطلاع على هذا المثال التجريبي .
  13. بالإضافة إلى ما قام المدرب سامح بشرحه يمكننا تلخيص الوظيفة في دالة مستقلة تماما يتم إستدعاءها عند الحاجة لها . لنقم أولا بتعريف الدالة المسؤولة عن العملية : 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(); } } }); }); }); و بالطبع فإنه يمكنك التعديل من سلوك التحريك كإضافة بعض الأنيميشن أو ربما تغيير المهلة التي يتم فيها إعتبار أن المستخدم توقف عن السكرول . تأكد فقط أن الكثير من الأفكار قد تحتاج تجزئة و تبسيط لها ,و لما لا نمذجة في سبورة أو ورقة , فهو صحيح أن البرمجة ومفاهيمها أشياء تجريدية , و لكنها تنطلق من الواقع و تحاول نمذجته و تبسيطه و لذلك حاول دوما تقسيم الفكرة إلى أفكار جزئية و لا يجب عليك أبدا النظر إلى أية فكرة بمجملها كفكرة واحدة موحدة غير قابلة للتقسيم .
  14. ستحتاج أولا التسجيل بحساب تجاري في بيبال .ثم سنحتاج الدخول إلى لوحة تحكم المطور , أين يمكنك التعرف على نمطين و حسابين : حساب تجريبي , أو حساب 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 .
  15. يمكنك للتخلص من المشكلة تثبيت webpack بشكل global , تأكد أن تقوم بطباعة الأمر التالي : npm install -g webpack أما إذا كان لديك Webpack مثبت محليًا ، ففي حالات خاصة جدا سنحتاج تحديد المكان الذي يجب أن يبحث فيه موجه الأوامر بشكل صريح للعثور عليه ، مثل : node_modules\.bin\webpack يكون هذا في ملف package.json أين يتم تعريف الأوامر ضمن الكائن script (هذا بافتراض أنك داخل دليل المشروع بالفعل وأنك قمت بالفعل بتسطيب الحزمة عن طريق الأمر webpack ) .
  16. عذرا , قد ورد خطأ في الكود هنا بالضبط : $('#messages_form_groups').insertBefore(newEmptyImg); فالمفروض أن يقوم هذا بـ تضمين العنصر الجديد قبل العنصر بالمعرف messages_form_groups , و هو ما نحتاجه و صوابه : $(newEmptyImg).insertBefore('#messages_form_groups'); قم باستبدال الخطأ بصوابه و أخبرني بالنتيجة
  17. لا يؤدي تغيير خاصية src إلى "إلغاء تحميل" مورد الصورة - بل سيزيل فقط ارتباطها بعنصر <img /> هذا . أي أن التغييرات لن تظهر مباشرة , بل ستظهر في حالة إعادة تحميل عنصر الصورة مجددا . لأن إعادة التحميل ليس شيئًا يمكنك تحقيقه بالصور ، و في الغالب أنك لا تريد حقًا "تفريغه". بل تحاول إخفاءه أو إزالته أو ربما إعادة تحميله ، ولكن عليك الاحتفاظ به لأسباب معينة . و لذلك سيتبقى حل أن تقوم بحذف الصورة كليا ثم إعادة تضمين صورة جديدة بعد عرضها و إرسال طلب الأجاكس . و لذلك سيكون الكود الخاص بالعملية كاملة مشابها للتالي : <!-- لنتأكد من تعريف الصورة --> <img class="img-thumbnail" id="viewimg" style="display:none;"> <form id="messages_form_groups" enctype="multipart/form-data" class="form_msgs"> <label for="file" title="صورة"> <img src="images/photo_upload.png" /> </label> <input name="picname" id="putname" type="hidden"> <textarea class="textarea_msgs_groups" name="message" id="message" maxlength="350" placeholder="اكتب رسالة ...."> </textarea> <div class="myButton" title="ارسال"> <input type="submit" name="submit_profile" value=""> </div> <script> $("#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(); // إزالة العنصر كاملا $('#viewimg').remove(); // صورة جديدة فارغة var newEmptyImg = '<img class="img-thumbnail" id="viewimg" style="display:none;">'; // وضعها مكان السابقة $('#messages_form_groups').insertBefore(newEmptyImg); } } }); }); }); </script>
  18. لعل السؤال "من أين أبدأ" واحد من أكثر الأسئلة شيوعا و اﻷكثر طرحا في المجال و رغم أن السؤال يطرح وحده بالنسبة لكل جديد على المجال إلا أني أظن أنه لا يجب سؤالها ابدا لأن كل لغة لها تخصص معين ولها طريقة كتابة وسياق خاص بها و لربما لها طريقة خاصة بالتعلم ! و لذلك قبل طرح هذا السؤال يجب أولا : اختيار مجال معين من مثل : تطوير مواقع الويب , تطوير تطبيقات الأندرويد , تطوير الألعاب و غيرها من المجالات . البحث عن لغات هذا المجال الأكثر شيوعا و استعمالا وافهم وظيفة كل لغة تخص هذا المجال . يفضل تعلم مبادئ البرمجة و التفكير المنطقي قبل التوغل في أي مجال . أشياء من مثل هاته يمكن إكتسابها و تعلمها عن طريق تعلم الخوارزميات و بنية البيانات . يمكنك الان تعلم لغات هذا المجال عن طريق دورات أو معاهد تكوينية . يمكنك إلقاء نظرة على دورات الأكاديمية هنا . التطبيق العملي و الاختبار و التجربة و الكثير منه . يساعدك كل من المراحل السابقة على تحديد هدفك و توضيحه أكثر و أكثر , و هو ما ستحتاج إليه على طول مسيرتك في المجال . كما أن هذا سيولد لديك الكثير من الأسئلة التي ستوسع نظرتك للمجال و تطور من ثروتك النظرية مما سيدفعك أسرع و أسرع للتطبيق العملي و الإنتاج .
  19. لإستعمال خدمات zoom في تطبيق لارافيل يجدر بك فتح حساب مطور في zoom لإستعمال الواجهة البرمجية الخاصة بهم بشكل طبيعي . يمكنك قراءة توثيق الواجهة البرمجية كاملا هنا . كما يمكنك إستعمال الـ sdk الخاص بـ zoom للاستفادة من كامل المميزات و الخدمات التي توفرها . يمكنك قراءة التوثيق كاملا هنا . كما يمكنك إستعمال هاته الحزمة للإستفادة من بعض الخدمات بشكل أبسط . كما يمكنك الاستزادة بقراءة هاته المقالات حول نفس الموضوع : كيفية إضافة zoom API إلى تطبيق لارافيل كيفية تضمين الـ embeded code الخاص بأي ملتقى zoom (مقالة). كيفية تضمين الـ embeded code الخاص بأي ملتقى zoom (نقاش في منتديات zoom). إدارة ملتقيات زوم عن طريق تطبيق لارافيل .
  20. لتوسيط الصورة يمكنك تعديل محاذاة العنصر الحاوي لعنصر الصورة ليحمل قيمة المحاذاة إلى الوسط : .img-container{ text-align: center; } و لعمل عرض شرائح سنحتاج إنشاء حاوي لعناصر الشرائح و أزرار جانبية لكل من التالي و السابق و بعض عناصر التصفح تكون على شكل نقط و دوائر , ستكون بنية الـ html على نحو مشابه : <!-- حاوي عرض الشرائح --> <div class="slideshow-container"> <!-- الشرائح --> <div class="mySlides fade"> <img src="img1.jpg" style="width:100%"> </div> <div class="mySlides fade"> <img src="img2.jpg" style="width:100%"> </div> <div class="mySlides fade"> <img src="img3.jpg" style="width:100%"> </div> <!-- أزرار التالي و السسابق --> <a class="prev" onclick="plusSlides(-1)">❮</a> <a class="next" onclick="plusSlides(1)">❯</a> </div> <br> <!-- دوائر التصفح --> <div style="text-align:center"> <span class="dot" onclick="currentSlide(1)"></span> <span class="dot" onclick="currentSlide(2)"></span> <span class="dot" onclick="currentSlide(3)"></span> </div> سنحتاج لإضافة بعض الـتنسيقات : /* حاوي عرض الشرائح */ .slideshow-container { max-width: 1000px; position: relative; margin: auto; } /* نححتاج إخفاء الصور في الحالة الافتراضية */ .mySlides { display: none; } /* أزرار التالي و السابق */ .prev, .next { cursor: pointer; position: absolute; top: 50%; width: auto; margin-top: -22px; padding: 16px; color: white; font-weight: bold; font-size: 18px; transition: 0.6s ease; border-radius: 0 3px 3px 0; user-select: none; } /* تحديد زر التالي ليظهر بيمين الصفحة */ .next { right: 0; border-radius: 3px 0 0 3px; } /* المؤشرات */ .dot { cursor: pointer; height: 15px; width: 15px; margin: 0 2px; background-color: #bbb; border-radius: 50%; display: inline-block; transition: background-color 0.6s ease; } /* انيميشن */ .fade { -webkit-animation-name: fade; -webkit-animation-duration: 1.5s; animation-name: fade; animation-duration: 1.5s; } @-webkit-keyframes fade { from {opacity: .4} to {opacity: 1} } @keyframes fade { from {opacity: .4} to {opacity: 1} } الان لن يظهر أي شيء لأن كل الشرائح مخفية افتراضا , و لعمل التفاعلية سنحتاج تطبيق المنطق التالي وفق الجافاسكربت : عند الضغط عن زر التالي سنقوم بتحديد الشريحة الما بعد الشريحة الحالية و إظهارها . عند الضغط عن زر السابق سنقوم بتحديد الشريحة الما قبل الشريحة الحالية و إظهارها . و سنستعمل فيهما دالة plusSlide لنقوم بتمرير رقم 1 معبرا عن التالي , و 1- معبرا عن السابق . عند الضغط عن دائرة سنقوم بتحديد الشريحة المرافقة لها و إظهارها . و سنستعمل في هذا دالة currentSlide لنقوم بتمرير الرقم الترتيبي للشريحة المراد تفعيلها . و لإظهار الشريحة لن نحتاج إلا لـ : تحديد الرقم الترتيبي للشريحة المراد تفعيلها . إخفاء كامل الشرائح عن طريق إعطاءهم القيمة none في الخاصية display . إظهار الشريحة المفعلة عن طريق إعطاءها القيمة block في الخاصية display . فيكون كود الجافاسكربت كالتالي : var slideIndex = 1; // الشريحة الحالية showSlides(slideIndex); // إستدعاء الدالة المسؤولة عن عرض الشرائح // الدالة الخاصة بأزرار التالي و السابق function plusSlides(n) { showSlides(slideIndex += n); } // الدالة الخاصة برموز الاتجاهات function currentSlide(n) { showSlides(slideIndex = n); } // الدالة المسؤولة عن عرض الشرائح function showSlides(n) { var i; var slides = document.getElementsByClassName("mySlides"); // تحديد كامل الشرائح var dots = document.getElementsByClassName("dot"); // تحديد كامل الدوائر if (n > slides.length) {slideIndex = 1} // تحديد الشريحة الاولى كمفعلة if (n < 1) {slideIndex = slides.length} // تحديد الشريحة الأخيرة كمفعلة for (i = 0; i < slides.length; i++) { slides[i].style.display = "none"; // إخفاء كامل الشرائح } slides[slideIndex-1].style.display = "block"; // تعديل تنسيق الشريحة المفعلة } و بالطبع فإن هذا هو المنطق البسيط للعملية , يمكنك تخصيص بنية الـ html و تنسيقها و تفاعليتها بكل حرية .
  21. يمكنك إستعمال التابع where لتحديد شرط التساوي بين الرقم الوزاري للمستخدم الحالي و الموظفين من جدول الموظفين : $no_ministry = Auth::user()->no_ministry; $selectemployees = Employee::select('id' , 'id_no' , 'name' , 'no_ministry' , 'staff' , 'phone') ->where('no_ministry' ,$no_ministry) ->get();
  22. سنحتاج في هذا توليد token و تخزينه بقواعد البيانات و إخفاءه عن المستخدم . و إرساله مرة أخرى إلى البريد الالكتروني للشخص أو رقم هاتفه . إما كسلسلة نصية و التحقق من مطابقتها . أو إرسالها كرابط بمتغير و ليكن token يحمل token هذا المستخدم . و لارافيل تجعل العملية أبسط و أبسط عن طريق الخطوات التالية : تحضير النموذج User : لنتأكد أولا من أن النموذج ينفذ الصنف MustVerifyEmail : <?php namespace App; use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; class User extends Authenticatable implements MustVerifyEmail { use Notifiable; // ... } سيقوم هذا بإرسال رابط تحقق لكل بريد إلكتروني يخص عضو يقوم بالتسجيل حديثا . إعتبار قواعد البيانات : نتأكد من إمتلاك جدول المستخدمين users على عمود email_verified_at . ثم تنفيذ أمر التهجير : php artisan migrate تحضير المسارات : يتضمن Laravel صنف VerificationController الذي يحتوي على المنطق الضروري لإرسال روابط التحقق والتحقق من رسائل البريد الإلكتروني . و لتسجيل المسارات الضرورية لهذا المتحكم ، تأكد أن تقوم بتمرير خيار التحقق إلى التعريف Auth :: route : Auth::routes(['verify' => true]); حماية المسارات : يمكن استخدام طبقات وسيطة Middlewares جاهزة و معدة مسبقا من قبل لارافل للمسارات للسماح للمستخدمين الذين تم التحقق منهم فقط بالوصول إلى مسار معين . و كل ما عليك فعله هو إرفاق الطبقة الوسيطة verified بتعريف المسار : Route::get('profile', function () { // هذا المسار قابل للوصول من قبل المستخدمين المتحقق من بريدهم فقط })->middleware('verified'); ملفات العرض : لإنشاء كل ملفات العرض الضرورية للتحقق من البريد الإلكتروني ، يمكنك استخدام حزمة laravel/ui : composer require laravel/ui php artisan ui vue --auth يقوم هذا بوضع كل ملفات العرض الخاصة بالتحقق من البريد الإلكتروني في : Resources / views / auth / verification.blade.php. بحيث يمكنك تخصيصها كما تشاء .
  23. هل يمكنك وصف المشكلة بشكل أكثر دقة ؟ كما أنه قد يكون من الجيد تضمين بعض الشيفرات البرمجية و الأكواد التي تظن أنها مسببة للمشكلة . يمكنك القراءة أكثر عن إرشادات الاستخدام .
  24. صحيح .خدمات Pusher مدفوعة لكنها تقدم في عرضها المجاني sandbox إمكانية تمرير 200 ألف رسالة بشكل يومي . و هو رقم جد مناسب في مثل حالتك .
×
×
  • أضف...