-
المساهمات
5233 -
تاريخ الانضمام
-
تاريخ آخر زيارة
-
عدد الأيام التي تصدر بها
52
نوع المحتوى
ريادة الأعمال
البرمجة
التصميم
DevOps
التسويق والمبيعات
العمل الحر
البرامج والتطبيقات
آخر التحديثات
قصص نجاح
أسئلة وأجوبة
كتب
دورات
كل منشورات العضو Adnane Kadri
-
تأكد أن تقوم بحذف ملف index.lock داخل مجلد git. , يمكنك ذلك عن طريق الأمر : rm -f .git/index.lock و في حال ظهر معك خطأ على هذا النحو : 'rm' is not recognized as an internal or external command يمكنك طباعة الأمر : del -f .git/index.lock .. قد نتسبب في هذا المشكل عندما نحاول تطبيق أمري أو عمليتي git في نفس الوقت , و في الغالب تكون واحد عن طريق المحرر vsCode و الاخر عن طريق موجه الأوامر أو التارمنل . و حذف ملف index.lock سيقوم بإلغاء أي عمليات أخرى معدا الحالية و بالتالي حل المشكل.
-
يمكنك معاملة الصور و أيقونات كزر فتح المودل , سيتبع الكود منطقا مشابها : <button type="button" class="btn" data-toggle="modal" data-target="#mymodal"> <img src="img1.png" /> </button> <button type="button" class="btn" data-toggle="modal" data-target="#mymodal"> <img src="img2.png" /> </button> <button type="button" class="btn" data-toggle="modal" data-target="#mymodal"> <img src="img3.png" /> </button> <button type="button" class="btn" data-toggle="modal" data-target="#mymodal"> <img src="img4.png" /> </button> <!-- المودل يبدأ هنا --> و بالطبع ستحتاج إرسال طلب الأجاكس بشكل منفصل . سنحتاج أولا تعريف النموذج و إضافة حدث عن طريق الجافاسكربت : var myForm = document.querySelector('#myform'); myForm.addEventListener('submit' ,function(e){ e.preventDefault(); // منع تحديث الصفحة sendAjaxRequest(); }); function sendAjaxRequest() { // سيتم إرسال طلب الاجاكس هنا } حيث أن الدالة sendAjaxRequest ستكون المسؤولة عن إرسال هذا الطلب . function sendAjaxRequest() { // XMLHttpRequest تعريف نموذج عن الكائن var xhttp = new XMLHttpRequest(); // جلب البيانات من النموذج var formData = new FormData(document.querySelector('#myForm')) xhttp.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { alert('تم إرسال الطلب'); } }; xhttp.open("POST", "http://path.to/endPoint/example", true); // فتح طلب xhttp.send(formData); // إرسال طلب }
-
تعبر الخطوة السادسة عن طريقة القراءة بإستعمال هاته العلاقة فقط , ففي المثالين تم على الترتيب جلب : المنتجات التي تخص الفئة X . الفئات التي تخص المنتج Y . مثال : المنتجات التي تخص الفئة "ملابس رجالية" . الفئات التي تخص المنتج "تيشرت أزرق" .
-
يمكن تلخيص الية إنشاءها وفق الخطوات التالية , سنقوم بتعريف العلاقة كمثال بين النموذجين Category و Product : بداية نحتاج إنشاء الجداول و النماذج المراد إنشاء العلاقة بينهما : php artisan make:model Category -m php artisan make:model Product -m سنحتاج أيضا إنشاء جدول وسيط لتحقيق منطق العملية . بحيث يجب أن يتكون اسم الجدول الوسيط من أسماء فردية لكلا الجدولين ، مفصولة برموز شرطة سفلية ، ويجب ترتيب هذه الأسماء بترتيب أبجدي ، لذلك يجب أن يكون لدينا category_product ، وليس product_category : php artisan make:migration create_category_product_table بعد ذلك سنحتاج التأكد من تعريف الحقول اللازمة في ملف تهجير هذا الجدول الوسيط , بحيث يجب أن يمتلك حقلا أجنبيا يعبر عن عمود المعرف id في كلا الجدولين : products و categories : public function up() { Schema::create('category_product', function (Blueprint $table) { $table->increments('id'); $table->integer('category_id')->unsigned(); $table->integer('product_id')->unsigned(); }); } 4. تنفيذ أمر التهجير : php artisan migrate 5. تعريف العلاقات في ملفات النماذج : بداخل ملف النموذج Product.php : class Product extends Model { public function categories() { return $this->belongsToMany(Category::class); } } و أيضا بداخل ملف النموذج Category.php : class Category extends Model { public function products() { return $this->belongsToMany(Product::class); } } 6. ثم كخطوة أخيرة لن يكون علينا إلا احترام العلاقة عند إنشاء أي صفوف بقواعد البيانات و القراءة عن طريق العلاقتين products و categories . $productsOfXCategory = Category::find($ID)->products; $categoriesOfXProduct = Product::find($ID)->categories;
-
يبدوا أنك تقوم بإخراج الملفات إلى ملف واحد . بالطبع ستحتاج تعديل المخرج أيضا , سنحتاج إخراج الملفات منفصلة بحسب إسم الملف . فعوضا عن مثل هذا : output:{ path:path.join(__dirname,"/dist"), filename:"main.js" }, نحتاج إخراج الملفات على هذا النحو : output:{ path:path.join(__dirname,"/dist"), filename:"[name].js" }, حتى لا يحدث خطأ التعارض الذي ظهر معك , ثم يمكنك بعد ذلك تشغيل أمر البناء بشكل طبيعي : npm run build
- 3 اجابة
-
- 2
-
-
تأكد أنك تقوم بإعداد webpack ليقوم بعمل المطلوب على نحو صحيح , ففي الأرجح يتم بناء الملف الأصلي index.js لأنك تقوم بهذا في ملف webpack.config.js : entry: { main: path.resolve(__dirname, './src/index.js'), }, ثم تقوم بتضمينه في ملف الـ html : <script type="text/html" src="{required('index.js')}"></script> في حين أنك تحتاج تمرير عدة نقاط وصول لكائن المدخلات entry : entry: { main: path.resolve(__dirname, './src/scripts/index.js'), about: path.resolve(__dirname, './src/scripts/about.js'), contact: path.resolve(__dirname, './src/scripts/contact.js'), }, ثم يمكنك تضمين كل ملف على حدة في ملفات الـ html التي تقوم ببناءها . يمكنك القراءة أكثر عن الخاصية entry في دليل Webpack الشامل .
- 3 اجابة
-
- 1
-
-
في المتصفح , الإسم name لو تم تعريفه كمتغير بشكل عام globally يكون ذا معنى و قيمة خاصة , و مثلما أشار المدرب @عبدالباسط ابراهيم ينتهي الأمر بأي تعريف له إلى إكتشافه على أنه المتغير name الخاص بالكائن window . و كما هو شائع في توابع و متغيرات الكائن window , لا يجب إستدعاءها أو إستعمالها دوما عن طريق الكائن على هذا النحو : window.document.getElementById('#someID') بل يكفي قراءتها مباشرة . و ليكن في العلم أن المتغير window.name يحتوي على غرض خاص ، ويفترض أن يكون سلسلة نصية , لذلك فإن أية تعريفات على هذا النحو : var name = {}؛ ينتهي الأمر بها فعليًا بإعطاء اسم المتغير العام (أي window.name) قيمة . و هنا يحاول المدقق الخاص ببرنامج VsCode توضيح هذا الشأن عن طريق شطب أي إسناد قيمة للمتغير name بشكل عام global . و كما يمكنك رؤية أن الشطب يختفي إذا وضعت نفس الشيفرة داخل دالة و هذا دليل على أن هذا الأمر يقتصر على تعريف المتغير name بشكل عام فقط . و لذلك للتخلص من المشكل بشكل نهائي يمكن الإستعانة بأحد الحلين : تعريف المتغير و إسناد قيمة له داخل دالة , أي بشكل محلي locally . إختيار إسم اخر للمتغير عوضا عن name , و ليكن customerName مثلا .
- 2 اجابة
-
- 1
-
-
يجدر الإشارة أن الـ Greedy Algorithms أو الخوارزميات الجشعة ليست خوارزمية Algorithm و إنما هي أسلوب خوارزميات Algorithm Paradigm وهو الشكل العام لتصميم الخوارزمية و منطقها دون التوغل في تفاصيلها . و أصل التسمية نفسه يعود إلى أسلوبها , فهي الأسلوب الذي يفرض أن تجد حلول مثلى محلية localized optimum solution, والتي من الممكن فيما بعد أن تتحول الى حلول مثلى عامة globally optimized solutions. و لذلك سميت بالخوارزميات الجشعة, لأنها تختار الحلول المحلية المثلى , أي أنها تؤمن حلا للخطوة الحالية من الخوارزمية من دون الأخذ بالحسبان تأثير هذه الخطوة على تكملة الحل , فهي جشعة لا تنتظر أو تؤمن حلولا مثلى عامة . و يستعمل لشرحها عادة مسألة الرحالة التاجر الذي يريد أن يمر بعدد من الأحياء لبيع بضاعته. هدف هذا التاجر هو إيجاد المسار الأقصر الذي يمر بكل الأحياء , فيستهدف بذلك كل القرية أو المدينة . وفق طريقة الخوارزمية الجشعة، على التاجر أن ينظر كل مرة إلى خريطته ويسافر إلى أقرب حي لم يزره بعد . أي أن الهدف هنا هو الحل الأمثل المحلي و الخاص بالخطوة الحالية أي الـ localized optimum solution , و ذلك بغض النظر عن تأثير هاته الخطوة على تكملة الحل , فبتطبيق منطق الخوارزميات الجشعة لن نهتم إن كان التاجر سيضطر في نهاية الأمر إلى العودة إلى هذا الحي بنهاية المسار ويسلك طريقاً أطول . أي أننا لا نهتم بالدرجة الأولى بتوفير حلول مثلى عامة globally optimized solutions. يمكنك القراءة عن الكثير من الخوارزميات و المسائل التي تقوم بإستعمال هذا النهج من مثل : مسألة عد العملات counting coins . كما يمكنك التعرف أكثر على الخوارزميات الجشعة طبقا لويكي حسوب . و قد تحتاج أيضا التعرف أكثر الفرق بين الخوارزمية Algorithm و أسلوب الخوارزميات Algorithm Paradigm.
-
طريقة طرحك للسؤال تقتضي الفصل و توضيح الفرق بينهما . عن جافاسكربت : هي لغة برمجة نصية و تعتبر أفضل أداة لإنشاء صفحات ويب تفاعلية وسهلة الاستخدام. نظرًا لأنه يتم تنفيذ إستعلاماتها في المتصفح . فمن جانب العميل ، تتيح Javascript استجابة سريعة وتحميل أقل على الخادم . وقد اعتادت أن تكون لغة الواجهة الأمامية , و لكن تغير الأمر بعد ظهور Node.js , إذ أصبح يتم إستعمالها لتطوير كل من الواجهتين الخلفية و الأمامية . تعد أيضا جافاسكربت خيارًا مثاليًا لإنشاء أحدث الأنماط من تطبيقات الويب , و هي تطبيقات SPA ديناميكي (تطبيق صفحة واحدة) (يمكنك القراءة أكثر عن الـ SSR , SPA , PWA) . و تجعل أطر عملها و منصاتها الأمر سهلا من مثل AngularJS و ReactJS . و كذلك مع الكثير من تقنيات الخادم من مثل MongoDB و CouchDB و NoSQL. عن PHP : تمامًا مثل جافاسكربت ، تعد PHP لغة برمجة نصية . تم إنشاؤها لتطوير الويب و واجهته الخلفية , و تقوم بالتعامل مع قواعد البيانات و نظم إدارتها . و يتمثل الاختلاف الرئيسي عن جافاسكربت في أن PHP هي لغة من جانب الخادم تُستخدم للواجهة الخلفية فقط و يتم تنفيذ كل إستعلاماتها على الخادم . و يجدر القول أنها تحتوي مميزات و مكتبات الأكثر ثراءً وأمانًا أفضل مقارنةً بـجافاسكربت في هاته النقطة . كما أن أغلب نظم إدارة العمل المشهورة في الساحة تم بناءها عليها , وطبعا لا يجب إغفال حجم المجتمع الخاص بها مقارنة بحجم مجتمع NodeJS . و بالتالي فإن PHP أفضل خيار عندما تكون هناك حاجة للعمل مع نظم إدارة CMS (WordPress و Drupal و Joomla) أو LAMP stack أو تقنيات مثل MariaDB و MySQL و PostgreSQL . و على الرغم من أنه يمكن استخدام جافاسكربت لتطوير كل من الواجهة الأمامية والخلفية بمساعدة منصات مثل Node.js ، لا تزال PHP أداة أفضل للواجهة الخلفية . و لكن ,بحسب رأيي الشخصي, الأفضل تماما ، سيكون فكرة الجمع بينهما من أجل بناء تطبيق ويب ديناميكي قوي , يستعمل مفاهيم تطوير الويب وواجهاته الحديثة . و بغض النظر عن اللغة التي تختارها ، PHP أو جافاسكربت ، لمشروعك . فإنه لا يوجد الأسهل أو الأسرع تعلما . يتحكم في هذا العديد من العوامل و النقاط , قد يكون من بينها حتى إنطباعك و تعودك على سياق الإثنين مثلا ! يمكنك التعرف أكثر على اللغتين في ويكي حسوب : عن PHP عن جافاسكربت كما يمكنك تصفح المقالات الخاصة بكل منهما في الأكاديمية : مقالات في جافاسكربت مقالات في PHP
-
لا يمكن قراءة خاصية من التابع where مباشرة على هذا النحو : return sections::where("section_name","==","البنوك")->bank; و في حالة المحاولة سيتم إظهار خطأ يتم فيه إخبارك أن الخاصية bank غير موجودة . قبل ذلك نحتاج تنفيذ الإستعلام الذي قمنا للتو ببناءه عن طريق أحد التوابع get أو first : $first = sections::where("section_name","==","البنوك")->get(); $second = sections::where("section_name","==","البنوك")->first(); الأولى ستقوم بإعادة مجموعة كائنات كل منها يمتلك الخاصية bank و بالتالي و للقراءة منها نحتاج عمل دور على عناصر المجموعة على هذا النحو : foreach($first as $item){ echo $item->bank; } أما بالنسبة للثانية فهي سوف تقوم بإعادة أول كائن يحقق الإستعلام و يمكن قراءة الخاصية مباشرة منه : $second->bank أو : return sections::where("section_name","==","البنوك")->first()->bank;
- 4 اجابة
-
- 1
-
-
سنحتاج تحضير مصفوفة تحمل عدد مرات ظهور كل عنصر , ثم سيكون علينا تحديد الفهرس بالقيمة الأكبر أو الأصغر . مثال عملي : يمكننا الإستعانة بالدالة array_count_values لحساب عدد المرات التي ظهر فيها كل عنصر سلسلة نصية أو رقم من المصفوفة . $arr = array('محمد','رضا','أبراهيم','محمد','محمد','رضا','محمد'); $ordered = array_count_values($arr); ثم سيكون علينا قراءة العنصر بالقيمة الأكبر , يمكن ذلك عن طريق الدالة max , أو الأصغر عن طريق الدالة min , التان تقومان بجلب العنصر ذي القيمة الأكبر و الأصغر على التوالي في مصفوفة أعداد . $ordered = array_count_values($arr); $max = max($ordered); $min = min($ordered); و كخطوة أخيرة سنحتاج البحث عن المفتاح المرافق للقيمة الأكبر أو الأصغر المعادة في مصفوفة التي يتم إعادتها من الخطوة 1 . $maxVal = array_search($max, $ordered); $minVal = array_search($min, $ordered); فيكون الكود كاملا : $arr = array('محمد','رضا','أبراهيم','محمد','محمد','رضا','محمد'); $ordered = array_count_values($arr); $maxVal = array_search(max($ordered), $ordered); $minVal = array_search(min($ordered), $ordered); يمكنك الإطلاع على توثيق كل دالة مما تم إستعماله : الدالة array_count_values في PHP . الدالة array_search في PHP. الدالة max في PHP. الدالة min في PHP .
-
ليس تماما . سيتم إظهار الخطأ في حالة صحة العبارة المنطقية . على عكس abort_unless التي سيتم فيها إظهار الخطأ في حالة خطأ العبارة المنطقية . أي أنه في abort_if سيتم إظهار الخطأ إذا كان : Gate::denies('user_access') يساوي صحيح .
-
يقوم المساعد abort_if بطرح خطأ HTTP معين إذا تم تحقيق عبارة منطقية معين . مثال : abort_if(!$user->is_admin ,403); هنا سيتم عرض أو إعادة خطأ 403 ممنوع في حالة ما كانت العبارة المنطقية : !$user->is_admin صحيحة . و بنفس المنطق : abort_if(Gate::denies('user_access'), Response::HTTP_FORBIDDEN, '403 Forbidden'); أو يكفي : abort_if(Gate::denies('user_access'), 403); سيتم طرح أو إعادة خطأ 403 في حالة ما كانت العبارة المنطقية التالية : Gate::denies('user_access') صحيحة . و في حالة الخطأ سيتم تجاوز الخطأ و إكمال قراءة الشيفرة البرمجية التالية بشكل عادي . يمكنك التعرف أيضا على المساعدين abort_unless و abort .
-
ستكون العملية معقدة بعض الشيء , بحيث أنه سيكون عليك تطبيق العملية بالإستعانة بالواجهة البرمجية المقدمة من يوتيوب . سيكون عليك إرسال طلبات HTTP إلى نقاط وصول معينة تقوم فيها بالتحقق من أن مستخدم X مشترك بالقناة X أو لا . و بناء عليه ستقوم إما بعرض المحتوى أو حجب المحتوى و إظهار زر الإشتراك . و طبعا ستعتمد في كتابة الشيفرة البرمجية الخاصة بهاته العملية على اللغة التي تم كتابة الواجهة الخلفية ,يفضل, للموقع بها . يمكنك إرسال الطلب إلى نقطة الوصول هاته : HTTP GET https://www.googleapis.com/youtube/v3/subscriptions?part=id&mine=true&key={YOUR_API_KEY} و ستحتاج إستبدال YOUR_API_KEY بالمفتاح الخاص بك عند فتح حساب مطور . سيتم جلب قائمة بأسماء كل المشتركين , ستحتاج فيها التحقق من إسم مشترك معين . و من ثم عرض الصفحة أو حجبها . كما يمكنك جلب إسم المشترك من ملفات تعريف الإرتباط في متصفح العميل .
-
يمكنك إستعمال هاته الأداة المقدمة من مطوري غوغل لإنشاء و تضمين شيفرة زر الإشتراك في القناة , يمكنك ذلك عن طريق : التصفح إلى صفحة الأداة هنا . قم بطباعة المعرف الخاص بالقناة أو يكفي مجرد إسمها . يمكنك تخصيص نسق العرض و والوضع , كما يمكنك التحكم في عرض عدد المشتركين أو لا. و أخيرا قم بنسخ الكود الذي يتم توليده في مربع النص أسفل الشاشة و لسقه في الكود المصدري لموقعك .
- 3 اجابة
-
- 1
-
-
أظن أن من الخطأ إستعمال نفس قواعد البيانات للتحقق Testing و حل المشاكل Debugging , بهاته الطريقة ستكون البيانات الحقيقية عرضة للفقدان كليا . الطريق الأقصر : و هي الإستعانة بالكثير من الحلول البديلة من مثل : تحضير نسخ إحتياطية Backups لقواعد البيانات قبل تجربة التطبيق أو قبل تحديث التهجير لقواعد البيانات , ثم إعادة التهجير بعد التحقق . تحضير نسخة إحتياطية تجريبية للتطبيق , يمكن إستعمال حزم من مثل laravel-backup من مجموعة spatie لعمل ذلك و التجربة عليه . لكن الأفضل ,و الطريق الأطول, يكون بفصل عملية التحقق Testing بشكل كامل عن نفس إتصال قواعد البيانات الذي يستخدمه تطبيقك . و لارافيل تجعل ذلك سهلا مع phpunit . فعلى سبيل المثال : إن كان تطبيقك يستخدم Mysql فعمليات التحقق يجب أن تستخدم ذاكرة مؤقتة و إتصال sqlite . و إن لم يتم كتابة الاختبارات Tests بشكل إما مواز أو مسبق للأكواد فسيجب لتحقيق هذا كتابة الإختبارات اللازمة لكل أجزاء التطبيق حتى يتم تحقيق هذا الأخير و إختبار التطبيق نفسه , لا نسخة إحتياطية منه ,في بيئة التطوير أو الإنتاج و بدون أي فقد لأية بيانات .
- 1 جواب
-
- 1
-
-
ليس تماما . ففي الشيفرة الأولى : abort_if(Gate::denies('user_create'), Response::HTTP_FORBIDDEN, '403 Forbidden'); أو يكفي : abort_if(Gate::denies('user_create'), 403); أو أيضا : abort_unless( !Gate::denies('user_create'), 403); نحن نقوم بمنع المستخدم من الوصول إلى حدث معين في حالة رفض البوابة Gate ذلك . لكن ما نقوم به هنا : Gate::define('create-post', function (User $user, Category $category, $pinned) { if (! $user->canPublishToGroup($category->group)) { return false; } elseif ($pinned && ! $user->canPinPosts()) { return false; } return true; }); ضمن التابع boot في ملف App\Providers\AuthServiceProvider هو تعريف للترخيص create-post . و لتوضيح العلاقة أكثر بين الشيفرتين ينبغي ذكر أنه : يتم التحكم في صلاحيات و تراخصي المستخدمين عن طريق البوابات Gates , بحيث يتم تعريف البوابة بإستعمال الواجهة Gate ضمن ملف AuthServiceProvider.php في التابع boot على هذا النحو : <?php use App\Models\User; use Illuminate\Support\Facades\Gate; /** * تسجيل أية خدمات ترخيص * * @return void */ public function boot() { $this->registerPolicies(); # تعريف البوابة Gate::define('access-db', function () { # إعادة القيمة البوليانية المسجلة في العمود المستهدف return auth()->user()->is_admin; }); } بعد ذلك يأتي إستعمالها في ترخيص الأحداث بإستعمال التابعين allows و denies على هذا النحو : public function index() { if (! Gate::allows('access-db')) { abort(403); } return view('admin_db); } أو إختصارا : public function index() { abort_if (! Gate::allows('access-db') ,403); return view('admin_db); }
-
جميل أنك اخترت واحدة من حزم مجموعة spatie . تقوم هاته الحزمة بتنفيذ الأمر بوساطة الأدوار عوضا عن مجموعات الصلاحيات , وتختصر بذلك الكثير . قبل الإستعمال تأكدي أنك قد قمت بالخطوات التالية على نحو صحيح : تثبيت الحزمة : composer require spatie/laravel-permission نشر ملفات التهجير و الإعداد : php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider" تنفيذ أمر التهجير : php artisan migrate بعد ذلك نحتاج إستعمالها و ضمها للتطبيق على نحو صحيح . سنحتاج إضافة السمة HasRoles لنموذج المستخدم User.php للإستفادة من التوابع التي توفرها هاته السمة : <?php use Illuminate\Foundation\Auth\User as Authenticatable; use Spatie\Permission\Traits\HasRoles; // تضمين السمة class User extends Authenticatable { use HasRoles; // إستعمال السمة // ... } 2. الان ما علينا إلا إنشاء أدوار Roles و أذونات Permissions , بحيث تتم العملية كالتالي : يتم نسب صلاحية معينة إلى دور معين . يتم نسب دور معين إلى مستخدم معين . مثال : نقوم بإنشاء الأذونات التالية : الحذف . الإضافة . التعديل . => نقوم بنسب الأذونات إلى الدور "Manager" . => نقوم بإعطاء المستخدم A الدور Manager . و بالطبع فإنه يمكن كتابة الشيفرة الخاصة بنسب الأذونات للدور و الدور للمستخدم في أي مكان من التطبيق , وليكن متحكما خاصا نسميه AssignRolesController : <?php namespace App\Http\Controllers; use App\Http\Controllers\Controller; use Spatie\Permission\Models\Role; use Spatie\Permission\Models\Permission; class AssignRolesController extends Controller { public function init() { # إنشاء دور $role = Role::create(['name' => 'writer']); # إنشاء صلاحية $permission = Permission::create(['name' => 'edit articles']); # عن طريق التابعين التاليين يمكن إعطاء الدور صلاحية معينة أو إعطاء الصلاحية إلى دور # ملاحظة : يكفي إستعمال أحدهما $role->givePermissionTo($permission); $permission->assignRole($role); # إعطاء مستخدم معين الدور $user = App\Models\User::first(); $user->assignRole('writer'); } } الان ما علينا إلا الوصول للتابع init لتنفيذ الأمر و إنشاء الأدوار و الأذونات . بعد ذلك و كخطوة أخيرة سنحتاج لحماية المسارات و المتحكمات . مثال : يجب علينا عدم السماح لمن ليس له صلاحية تعديل المقالات للوصول إلى المتحكم الخاص بتعديل المقالات . سنتسعمل في ذلك مفهوم الطبقات الوسيطة Middlewares , بحيث تتولى عملية التحقق من إمتلاك المستخدم لدور أو صلاحية معينة . و أبسط طريقة لعمل ذلك هي عن طريق المثال التالي : Route::group(['middleware' => ['can:edit articles']], function () { # كل المسارات التي يتم تمريرها هنا ستكون غير قابلة للوصول إلا لمن يمتلك صلاحية تعديل المقالات }); و متى ما حاول المستخدم اللوصول إلى المتحكمات الخاصة بعملية تعديل المقالات سيتم التحقق إن كان يمتلك صلاحية التعديل , و بشكل اخر : سيتم التحقق من أن المستخدم يمتلك دورا من ضمن صلاحياته تعديل المقالات .
- 5 اجابة
-
- 1
-
-
يمكنك التعامل مع الصلاحيات في لارافل عن طريق واحدة من الحزم : laravel-permission من مجموعة spatie . bouncer من JosephSilber . entrust من Zizaco . roles من romanbican . laravel-acl من kodeine . laratrust من santigarcor . تتشابه طريقة عمل هاته الحزم في الفكرة الأساسية و تختلف من حيث بعض المميزات الإضافية . و رغم كل ذلك يمكنك التعامل مع الصلاحيات دون الإستعانة بأي حزمة عن طريق مميزات مبنية ضمن اللارافل نفسه من مثل البوابات Gates و السياسات Policies . كما أحب التعامل مع هذا المفهوم بشكل منفصل و مرن أكثر عن طريق معاملة مجموعات الصلاحيات كنموذج Model و كجدول بقواعد البيانات , ثم إنشاء طبقات وسيطة Middlewares , بناءا على هذا الأخير , تقوم بالتحقق من إمتلاك المستخدم الموثق الحالي لمجموعة صلاحيات أو لا , ثم التحقق إن كانت هاته المجموعة تمتلك صلاحية ما و هكذا .. أي أن الأمر ممكن بكل الطرق , فكلها تشترك في المفهوم الأساسي و تتفاوت فيما بينها من ناحية بعض المميزات الإضافية .
-
يمكن ذلك عن طريق الإستعانة بمفهوم الويب سوكيت . بحيث نقوم : في الواجهة الخلفية للتطبيق بإنشاء قناة أو قنوات بحدث أو أحداث معينة . في الواجهة الأمامية للتطبيق بالإشتراك في هاته القنوات و الإستماع لهاته الأحداث . مثال عن الإستعمال : لما يضغط عمر زر الإعجاب بمنشور أحمد سيتم إرسال طلب HTTP إلى الخادم . سيقوم الخادم بمعالجة طلب عمر و تسجيل الإعجاب , و في نفس الوقت سيقوم بإثارة الحدث المرتبط بالعملية و ليكن NewLikeEvent ضمن القناة AhmedChannel . أحمد مشترك في القناة AhmedChannel و يستمع لأية إثارة لأحداثها , ففي حالة إثارة أية حدث سيقوم بالتقاط البيانات التي تم تمريرها عبره دون تحديث الصفحة . لما يتم إثارة الحدث NewLikeEvent ضمن القناة AhmedChannel ستقوم الواجهة الأمامية لأحمد بالتصرف بناء على البيانات التي تلتقطها عبر هاته القناة في هذا الحدث . كأن تقوم بإظهار إشعار أو تنبيه نصي . قد نحتاج إلى دريفر لمساعدتنا في تمرير البيانات و إنشاء القنوات من مثل Pusher. و للتوضيح أكثر سنقوم بإستعمال Pusher كونه معدا مسبقا للعمل مع لارافل بشكل متسق , و هو ما سنقوم به في هذا المثال .. يمكن عمل الفكرة عن طريق المنطق التالي : تثبيت حزمة خادم Pusher في تطبيق الللارافل : composer require pusher/pusher-php-server إنشاء تطبيق Pusher : نحتاج في هاته الخطوة إلى التسجيل في pusher.com و تسجيل تطبيق جديد . بعد ذلك سنحتاج نسخ معلومات التوثيق و نقوم بوضعها في ملف الإعداد env. كما يلي : PUSHER_APP_ID=PUT_YOUR_PUSHER_ID_HERE PUSHER_APP_KEY=PUT_YOUR_PUSHER_KEY_HERE PUSHER_APP_SECRET=PUT_YOUR_PUSHER_SECRET_HERE PUSHER_APP_CLUSTER=PUT_YOUR_PUSHER_CLUSTER_HERE كما أنه يجب تغيير قيمة BROADCAST_DRIVER: BROADCAST_DRIVER=pusher نقوم بإنشاء حدث معين : php artisan make:event Notify سنلاحظ إضافة ملف Notify.php داخل مجلد events , لنتأكد من أنه سيتم تعديله ليكون على هذا النحو : <?php namespace App\Events; use Illuminate\Broadcasting\Channel; use Illuminate\Queue\SerializesModels; use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Broadcasting\PresenceChannel; use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Contracts\Broadcasting\ShouldBroadcast; class Notify implements ShouldBroadcast { use Dispatchable, InteractsWithSockets, SerializesModels; public $data; /** * هنا يتم التقاط أية بيانات و تسجيلها ضمن هذا الكائن * * @return void */ public function __construct($passed_data) { $this->data = $passed_data; } /** * هنا يتم تعريف القنوات التي من المفترض أن ينتمي إليها هذا الحدث * * @return \Illuminate\Broadcasting\Channel|array */ public function broadcastOn() { return new Channel('notify-channel'); } } إنشاء متحكم يختص بالعملية : php artisan make:controller NotificationController ثم لنتأكد من وضع المحتوى التالي به : <?php namespace App\Http\Controllers; use Illuminate\Http\Request; class NotificationController extends Controller { public function notify() { // تحضير مصفوفة بيانات لتمريرها $data['message'] = 'قم بتمرير هاته الرسالة'; // Notify إثارة الحدث event(new App\Events\Norify($data)); } } سيكون علينا تعريف مسارين , مسار لإستهداف تابع المتحكم NotificationController و اخر لعرض الصفحة notification.blade.php : //web.php Route::get('notify','NotificationController@notify'); Route::view('/notification', 'notification'); ثم بملف ما بالواجهة الأمامية سيكون علينا فقط : الإشتراك في هاته القناة + الإستماع لأحداث هاته القناة : <!-- notification.blade.php --> <!DOCTYPE html> <head> <title>مثال عن إشعار</title> <script src="https://js.pusher.com/4.1/pusher.min.js"></script> <script> // الإشتراك ضمن نفس تطبيق البوشر var pusher = new Pusher('{{env("MIX_PUSHER_APP_KEY")}}', { cluster: '{{env("PUSHER_APP_CLUSTER")}}', encrypted: true }); // الاشتراك في نفس القناة var channel = pusher.subscribe('notify-channel'); // Notify الاستماع للحدث channel.bind('App\\Events\\Notify', function(data) { // إن تم اثارة الحدث قم بعرض البيانات alert(data.message); }); </script> </head> بدون تحديث الصفحة سيكون علينا التصفح إلى المسار notify/ و سنلاحظ ظهور التنبيه في مسار notifications/ . المسار الأول يمثل الطلب الذي أرسله عمر , و المسار الثاني يمثل صفحة عرض الاشعارات بالنسبة لأحمد . و بالطبع فإن هذا هو الشكل الأبسط للعملية , يمكن تطوير العملية كأن نقوم بتخصيص طريقة العرض في قائمة إشعارات منظمة و منسدلة مع ظهور إشعار جانبي في كل إثارة للحدث , كما يمكن تخصيص قنوات خاصة بكل مستخدم منفرد أو نقوم بتمرير بيانات أخرى و تطبيق العديد من الأفكار عليها .
- 1 جواب
-
- 3
-
-
تذكر جيدا ما أشرت إليه سابقا : لكن هذا لا يمنع من تقديم شيفرة كمثال متكامل . لاحظ المثال التالي : 1. نقوم بإنشاء بنية الملفات التالية : 2. بملف index.html سيكون علينا تعريف بنية مشابهة للتالي , كما أنه يجب علينا تحضير طلب الأجاكس و إرساله : <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>مثال عن رفع ملف</title> </head> <body> <section> <div> <label>قم بتحديد ملف</label> <input type="file" id="my_file"> </div> <div> <button type="button" id="submit_file_form">رفع الملف</button> </div> </section> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script> <script> $(document).ready(function(){ // 1 - إضافة حدث عن تقديم الاستمارة و رفع الملف $("#submit_file_form").click(function(){ // 2 - FormData تحضير نموذج عن الكائن var fd = new FormData(); // 3 - تحديد الملف var files = $('#my_file')[0].files; // 4 - التحقق من تحميل ملف if(files.length > 0 ){ // 5 - إضافة الملف إلى نموذج الكائن fd.append('file',files[0]); // 6 - تحضير طلب الاجاكس و إرسال نموذج الكائن $.ajax({ url: 'file_uploader.php', type: 'POST', data: fd, contentType: false, processData: false, success: function(response){ if(response){ alert('تم رفع الملف'); }else{ alert('لم يتم رفع الملف'); } }, }); }else{ alert("قم بتحديد ملف لرفعه."); } }); }); </script> </body> </html> 3. بملف file_uploader.php سيكون علينا إستقبال الطلب الحامل للملف و معالجته بشكل طبيعي تماما على نحو مشابه : <?php if(isset($_FILES['file']['name'])){ /* جلب اسم الملف */ $filename = $_FILES['file']['name']; /* تحضير وجهة تخزين للملف */ $location = "uploaded/".$filename; /* تحضير متغير يحمل حالة الرد */ $response = false; /* تخزين الملف */ $file_uploaded = move_uploaded_file($_FILES['file']['tmp_name'],$location); /* تغيير الحالة ان تم تخزين الملف بنجاح */ if($file_uploaded){ $response = true; } /* إعادة قيمة بوليانية في الرد */ return $response; } return false;
-
بداية يجب عليك معاملة أية مشاكل و صعوبات تظهر معك كأشياء طبيعية في المجال و من جانب اخر هي إمتحانات لك و لا يجب عليك التفكير في التوقف عن ما أقدمت عليه بسبب أشياء تواجهها لأنه , و بهاته الطريقة , سيكون لديك الكثير لتتوقف من أجله . كما أنه يجب عليك الإستفادة من الإجابات التي طرحها عليك الزملاء في أسئلتك السابقة : و بشكل عام فإنه يجب عليك فهم منطق هاته العمليات لا التوغل في مثال عن كود واحد و إعتباره كمعيار أو دستور للعملية . فمثلا : التشفير و فك التشفير : للتشفير : سيكون عليك تشفير سلسلة نصية معينة قبل إستعمالها , ولقراءته سنحتاج فك هذا التشفير . مثال عن الإستعمال : تشفير كلمة المرور قبل إدخالها في قاعدة البيانات في عملية تسجيل المستخدم - أ - , ثم فك تشفيرها للتحقق من مطابقتها لكلمة مرور قام المستخدم - أ - بإدخالها في عملية تسجيل دخوله لاحقا. أي : أننا نحتاج دالة تشفير و دالة فك تشفير . أو دالة و دالة معاكسة لها لعمل الفكرة , و قد تكتب خوارزميتك الخاصة للتشفير وفك التشفير أنت ذاتك فالعملية ليست حكرا على أحد لكن يقترح إستعمال المكتبات و الدوال المسبق تعريفها لأمان و سرعة أكثر . و دوال التشفير كثيرة من PHP نذكر من بينها : openssl_encrypt و الدالة المعاكسة لها : openssl_decrypt : توثيق . أمثلة : <?php $example = '@123456'; # السلسلة النصية المراد تشفيرها $cipher_algo = 'AES-128-CTR'; # خوارزمية التشفير $encryption_key = 'HsoubAcademy'; # مفتاح التشفير $options = 0; # خيارات التشفير $encryption_iv = 1234567891011121; # يعبر عن بادئة التشفير $encrypted_example = openssl_encrypt($example , $cipher_algo , $encryption_key , $options , $encryption_iv); echo $encrypted_example; // النتيجة : nLTaaXcr1A== $decrypted_example = openssl_decrypt($encrypted_example , $cipher_algo , $encryption_key , $options , $encryption_iv); echo $decrypted_example; // النتيجة : @123456 base64_encode و الدالة المعاكسة لها : base64_decode . توثيق . أمثلة : <?php $example = '@123456'; # السلسلة النصية المراد تشفيرها $encrypted_example = base64_encode($example); echo $encrypted_example; // النتيجة : QDEyMzQ1Ng== $decrypted_example = base64_decode($encrypted_example); echo $decrypted_example; // النتيجة : @123456 bin2hex و الدالة المعاكسة لها : hex2bin . توثيق و أمثلة . و غيرها الكثير .. كما يمكنك القراءة عن الموضوع أكثر هنا : رفع الصور و الملفات عن طريق الأجاكس : جافاسكربت تجعل ذلك بسيطا عن طريق إستعمال كائن يختص بنماذج البيانات و هو الكائن FormData , و لإرسال صورة أو ملف ما علينا إلا : إرسال نموذج من الكائن FormData يحمل الملف. لاحظ جيدا منطق الخطوات بالمثال التالي : $(document).ready(function(){ // 1 - إضافة حدث عن تقديم الاستمارة و رفع الملف $("#submit_file_form").click(function(){ // 2 - FormData تحضير نموذج عن الكائن var fd = new FormData(); // 3 - تحديد الملف var files = $('#my_file')[0].files; // 4 - التحقق من تحميل ملف if(files.length > 0 ){ // 5 - إضافة الملف إلى نموذج الكائن fd.append('file',files[0]); // 6 - تحضير طلب الاجاكس و إرسال نموذج الكائن $.ajax({ url: 'path/to/endpoint', type: 'POST', data: fd, contentType: false, processData: false, success: function(response){ if(response){ alert('تم رفع الملف'); }else{ alert('لم يتم رفع الملف'); } }, }); }else{ alert("قم بتحديد ملف لرفعه."); } }); }); معيارية MVC : و هي إختصار لـ Model , View , Controller و ببساطة شديدة هو نموذج معيارية Design Pattern يقضي بعزل منطق العمل عن واجهة المستخدم بهدف تحقيق إستقلالية كل منهما و تسهيل التعامل معهما في التطوير , الفحص و الصيانة . و يحقق بذلك أحد مبادئ التصميم المشهورة في علوم الحاسب : مبدأ فصل الإهتمامات separation of concerns (SoC). تقوم معيارية MVC بتقسيم التطبيق إلى ثلاث أجزاء : النموذج Model : يعبر عن المكون المركزي و الرئيسي للنمط Pattern . و يمثل بنية البيانات الخاصة بالتطبيق ، فعن طريقه تتم مباشرة إدارة بيانات وقواعد التطبيق . العرض View : و هي أي تمثيل للمعلومات مثل صفحة ويب , جداول أو نصوص . و تمثل ها هنا واجهة المستخدم . المتحكم Controller : و يقوم بإستقبال المدخلات و التحكم فيها و التحقق من إمكانية تمريرها للنموذج Model حتى يتصرف بناء عليها . هذا الفصل بين المكونات الثلاث هاته يجعلها تتفاعل فيما بينها بهاته الطريقة : النموذج Model مسؤول عن إدارة بيانات التطبيق . بحيث يتلقى مدخلات المستخدم من وحدة التحكم Controller . تستجيب وحدة التحكم Controller لإدخال المستخدم , وتتحقق من سلامتها ثم تقوم بتمرير مدخل المستخدم إلى النموذج Model , أو تقوم إستجابة لمدخل المستخدم بعرض View بتنسيق معين . و بشكل عام فإن أي فصل لهاته الثلاث مكونات , اهتمامات و وظائف هي معيارية تصميم MVC مثلها مثل أي معيارية أخرى , يتطلب التعمق فيها فهما أوسع و أعمق لأنماط ومعياريات التصميم Design Patterns . إن لم يكن لك إطلاع مسبق عنها فيمكنك أخذ فكرة عن الموضوع هنا و أيضا هنا . كما يمكنك الإطلاع عن الكود المصدري لهاته النماذج المصغرة لمعيارية MVC هنا و هنا . و بالطبع فإنه يمكنك التدرب على فصل هاته المكونات الثلاث لتطبيق الفكرة عمليا .
- 9 اجابة
-
- 2
-
-
الكود مثال عن رفع ملف إلى الواجهة الخلفية , مكتوب بالجيكوري . يمكنك الإشارة إلى النقطة الغير مفهومة و سيتم شرحها بإسهاب أكثر .
-
يمكنك إرسال نموذج عن الكائن FormData يتضمن الملف , بإستعمال POST عبر طلب الأجاكس . سيتبع الكود منطقا مشابها : $(document).ready(function(){ // إضافة حدث عن تقديم الاستمارة و رفع الملف $("#submit_file_form").click(function(){ // FormData تحضير نموذج عن الكائن var fd = new FormData(); // تحديد الملف var files = $('#my_file')[0].files; // التحقق من تحميل ملف if(files.length > 0 ){ // إضافة الملف إلى نموذج الكائن fd.append('file',files[0]); // تحضير طلب الاجاكس و إرسال نموذج الكائن $.ajax({ url: 'path/to/endpoint', type: 'POST', data: fd, contentType: false, processData: false, success: function(response){ if(response != 0){ alert('تم رفع الملف'); }else{ alert('لم يتم رفع الملف'); } }, }); }else{ alert("قم بتحديد ملف لرفعه."); } }); });