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

Adnane Kadri

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

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

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

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

    51

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

  1. يمكنك عمل ذلك عن طريق تمرير عدة نقاط وصول لملفات scss الخاصة بك عن طريق : module.exports = { entry: { 'about': ['./src/css/about.scss'], 'contact': ['./src/css/contact.scss'], 'index': ['./src/css/index.scss'] }, plugins: [ new MiniCssExtractPlugin( { filename: "./css/[name].css" } ), ] } الwebpack ستقوم بقراءة مفاتيح المدخلات و تقوم بإستبدالها بالوسم name داخل الfilename فتكون المخرجات على هذا النحو : dist css about.min.css contact.min.css index.min.css ثم بملفات الHTML يمكنك تضمين كل ملف css داخل dist/css حسب الحاجة أو حسب صفحة الHTML الحالية. ملاحظة : تأكد أن تقوم بتضمين الminifier على نحو صحيح : const MiniCssExtractPlugin = require('mini-css-extract-plugin'); و في حالة ظهور أي أخطاء على هاته الشاكلة : could not resolve module mini-css-extract-plugin تأكد أن تقوم بتثبيت الـ Plugin : npm install --save-dev mini-css-extract-plugin
  2. إضافة إلى ما تفضل الأخ @Wael Aljamalبشرحه . طبقا للتوثيق الرسمي فإن الخاصية query عبارة عن كائن يحتوي كل الخصائص لكل بارمتر ممرر في الروات . و في حالة تعطيل الquery-parser فإن الخاصية query ستقوم بإعادة كائن فارغ و بطبيعة الحال فإن أي قراءة لخاصية عن طريق query ستقوم بإعادة undefined , ويخص بالقول طلبيات الـ GET فقط أي أنه معطل في ما غير ذلك . فحل المشكلة هو إما بتغيير الطريقة إلى GET فيكون : <form id="login" action="userlogin" method="GET"> <div> <label for="email">Email: </label> <input type="text" id="email" name="email"></input> </div> <input type="submit" value="Submit"></input> </form> أو بالقراءة من الPOST . قد تحتاج تسطيب body parser في نسخ متقدمة من Express (+4.16) و من ثم استعماله على هذا النحو : قم بتسطيب الحزمة : npm install --save body-parser ثم استعمالها بشكل Globally : var bodyParser = require('body-parser') app.use( bodyParser.json() ); // لدعم تشفير الجيسون في البودي app.use(bodyParser.urlencoded({ // لدعم تشفير اليو ار ال في البودي extended: true })); أما ان كنت تستخدم نسخ أقل من 4.16 فتستطيع مباشرة : app.use(express.json()); // دعم تشفير الجيسون في البودي app.use(express.urlencoded()); // دعم تشفير اليو ار ال في البودي ومن ثم القراءة على هذا النحو : app.post('/your-end-point', function(req, res) { var email = req.body.email; });
  3. لأنك نفسك تقوم بتعطيل الـ Input بإضافة الخاصية disabled في حالة الإختبار منجز . عوضا عن ذلك يمكنك إستعمال الخاصية checked على هذا النحو : <form action="/projects/{{$project->id}}/tasks/{{$task->id}}" method="POST" class="w3-bar-item w3-left w3-large" style="display:inline;padding:0;margin:0;"> @method('PATCH') @csrf <input type="checkbox" name="done"class="w3-check cnsb-bor-green w3-border-left" {{$task->done ? 'checked':''}} onchange="this.form.submit()"> </form>
  4. بداية تأكد أن تقوم بتحديث الflutter plugin في الأندرويد ستوديو عن طريق : > File > Settings > Plugin > Updates و قد تتعطل الاقتراحات تلقائيا بمجرد تفعيلك لوضع حفظ الطاقة , فإن كان كذلك تأكد أن تقوم بإلغاء التفعيل عن طريق : > File > Power Save Mode إن لم تختف المشكلة قم بالتأكد أنك غير معطل لإقتراحات المحرر عن طريق : > File > Settings > Editor > General > Code Completion إن لم تختف المشكلة بعد هاته المراحل تأكد أن تقوم بمسح الملفات المؤقتة وإعادة التشغيل عن طريق : File Invalidate caches / Restart Invalidate and Restart
  5. يمكنك تسجيل serviceProvider الخاص بالحزمة لأشخاص معينين فقط , وجعل عملية تسجيله ديناميكية عن طريق التعديل بملف AppServiceProvider.php : <?php namespace App\Providers; use Barryvdh\Debugbar\ServiceProvider; class AppServiceProvider extends ServiceProvider { /** * Bootstrap any application services. * * @return void */ public function boot() { } public function register(){ if(auth()->user() && in_array(auth()->id(), [1,2,3])) { $this->app->register(new Barryvdh\Debugbar\ServiceProvider()); } }
  6. لتحليل هاته الخوارزمية قمت بالعمل التالي : 1 - إيجاد كل المصفوفات الفرعية التي يمكن تشكيلها من عنصر فأكثر من عناصر المصفوفة الأصلية . 2 - حساب مجموع أعداد كل مصفوفة فرعية . و أيضا لا يجب أن ننسى مجموع كل المجموعات الفرعية التي يمكن تشكيلها من هاته المجموعة الفرعية (قد تبدوا الفكرة معقدة لكن ينبغي تبسيطها و فهم أيضا أنه يمكن معاملة المجموعة الفرعية على أنها مجموعة أصلية غير محسوب مجموعها بعد و قد يحتوي مجموع إحداها على المجموع المطلوب فلا ينبغي أبدا إغفالها ). 3 - في كل دورة لحساب المجاميع يتم تسجيل هذا المجموع , و لا داعي لتسجيله إن كان محصلا بالفعل من عدد n عناصر من قبل . 3 - الان و قد تم حساب كل المجاميع الممكنة , يجب أخذ أكبرها قيمة و فقط . الان و قد تم فهم الخوارزمية يمكن تطبيقها عن طريق عملية متكررة للعثور على مجموع أعداد كل مصفوفة فرعية يمكن تشكيلها عن طريق مصفوفة . بالنسبة للكود سيكون مشابها لما يطبق نفس المنطق , و قد قمت بكتابته على هذا النحو : إنشاء دالة تتولى الخطوة 2 و 1 معا , بحيث تقوم بإيجاد المصفوفات الفرعية و المصفوفات الفرعية الممكن تشكيلها من هاته المصفوفة الفرعية و هكذا . و كل مرة يتم تشكيل مصفوفة فرعية يتم تسجيل مجموع عناصرها : var sums = []; // تحضير مصفوفة مجاميع فارغة function getAllPossibleSums(group, sub_group) { // if( group.length == arr.length || (group.length <= arr.length && sub_group.length == 0) ){ // لا حاجة لتسجيل مجموع في بداية الدور وفي حال عدم وجود أي مصفوفة if( group.length > 0 ){ // حساب المجموع var total = group.reduce(function(a,b){return a+b;},0); // لا حاجة لتسجيل المجموع إلا إن كان غير مسجل if(sums.indexOf(total) == -1){ sums.push(total); } } } // بداية التكرار تكون هنا else{ // ايجاد و تسجيل مجموع كل العناصر getAllPossibleSums(group.concat(sub_group[0]),sub_group.slice(1)); // ايجاد وتسجيل مجموع كل العناصر الممكن تشكيلها من المصفوفة الفرعية getAllPossibleSums(group,sub_group.slice(1)); } // إعادة المجاميع return sums; } 2. عمل دالة تقوم بالخطوة رقم 3 : function getMaxNumInArray(arr){ let i; // البدء بالعنصر الأول let max = arr[0]; // اجتياز العناصر بعد الاندكس المختار ومقارنة كل عنصر مع العنصر المختار for (i = 1; i < arr.length; i++) { // في حالة وجود قيمة أكبر سجلها if (arr[i] > max){ max = arr[i]; } } // إعادة أكبر عنصر return max; } 3. عمل دالة تلخص عمل الماضيتين : function getMaxSubSum(arr) { var all = getAllPossibleSums([],arr); return getMaxNumInArr(all); } ثم أخيرا دمج الأكواد على هذا النحو : function getMaxSubSum(arr) { var all = getAllPossibleSums([],arr); return getMaxNumInArr(all); } var sums = []; /** * هاته الدالة تقوم بتلخيص منطق العملية * @param arr : array */ function getMaxSubSum(arr) { var all = getAllPossibleSums([],arr); return getMaxNum(all); } /** * هاته الدالة تقوم بتسجيل أي مجموع يمكن تشيكله من المصفوفة * @param group : array , sub_group: array */ function getAllPossibleSums(group, sub_group) { if( group.length == arr.length || (group.length <= arr.length && sub_group.length == 0) ){ if( group.length > 0 ){ var total = group.reduce(function(a,b){return a+b;},0); if(sums.indexOf(total) == -1){ sums.push(total); } } } else{ getAllPossibleSums(group.concat(sub_group[0]),sub_group.slice(1)); getAllPossibleSums(group,sub_group.slice(1)); } return sums; } /** * إيجاد العدد اﻷكبر قيمة في قائمة أعداد * @param arr : array */ function getMaxNumInArr(arr){ let i; let max = arr[0]; for (i = 1; i < arr.length; i++) { if (arr[i] > max){ max = arr[i]; } } return max; } و من ثم إستدعاء الدالة على هذا النحو : var arr = [ 1, -2, 3, 4, -9, 6]; console.log(getMaxSubSum(arr)); // 14
  7. يمكنك تحقيق الغرض كالتالي : <?php $output = ''; while($row = mysqli_fetch_array($result)) { $output .= substr($row['message'], 0, 35); } echo $output; ?>
  8. بالفعل هي مثل ما قلت ، وقد تم تصليح الأخطاء الإملائية التي وردت بارك الله فيك و وفقك الله لما يحبه و يرضاه.
  9. يمكنك تخصيص نوع البيانات في البارمتر الممرر في الراوت عن طريق التابع where , الذي يقبل البارمترز على الترتيب : param : معبرة عن اسم البارمتر الذي تقوم بتمريره . regex : تعبر عن Regular Expression تخص المقبول في هذا البارمتر فقط . و يتم تعريف التابع where التابع للinstance الذي اسمه Route في مثالك عن طريق : <? Route::get('news/edit/{id}', 'YourController@your_method')->where('id', '$[0-9]+^'); غير أن هاته الطريقة لن تحقق الغرض و سيتم رمي Exception Not Found 404 في حالة ما كان البارمتر id لا يماثل العبارة [0-9]+ , أي أنه ليس integer . أما في حالة ما أردت عمل Redirect لصفحة تريدها من داخل الController فيمكنك عمل validation بسيط للـ ID الممرر بهذا الشكل : <?php use Illuminate\Support\Facades\Validator; // لا تنسى تضمين الواجهات الللازمة class MyController extends Controller{ public function myMethod(Request $request){ $validator = Validator::make($request->all() , ['id' => 'integer']); if($validator->fails()){ // قم باعادة التوجيه } } }
  10. ﻷن السطر الأخير يقوم بطباعة المتغير friends قبل حدوث الـxhr أصلا أي قبل أن تطرأ عليه أي تغيرات , على عكس ما داخل التابع then الذي هو مرتبط بما بعد حدوث الطلب . و لحل المشكلة يمكنك فقط إنشاء ميثود تقوم بإستعمال المتغير بمنطق معين من ضمن المكون يتم استدعاءها داخل التابع then , و هذا هو الأصح عمله على هذا النحو : var friends = {} axios.get('http://localhost:8080/api/people') .then(function (response) { friends = response.data[0] // استدعاء الدالة بعد عمل تغيرات على المتغير useFriends(); }) .catch(function (error) { console.log(error) }) function useFriends(){ console.log(friends); }
  11. بملف index.js قم بتضمين الجيكويري على نحو صحيح : import '@laylazi/bootstrap-rtl/dist/css/bootstrap-rtl.min.css'; import '@laylazi/bootstrap-rtl/dist/js/bootstrap.min.js'; // import 'jquery/dist/jquery.min.js' عوضا عن هذا import $ from 'jquery'; // هذا import 'popper.js/dist/popper.min.js'; import '../sass/style.scss'; import '@fortawesome/fontawesome-free/js/all'; $(document).ready(function(){ console.log('جي كويري الان تعمل!'); }); أما عن المشكلة الثانية فلم أواجهها و تم الدخول عن طريق الرابط التشعبي بشكل عادي . و مثل ما أشار الأخ @سامح أشرف قد تكون لديك مشكلة في الملفات المؤقتة الخاصة بالمتصفح ،قم بتجربة متصفح آخر أو حذف الملفات المؤقتة.
  12. طبعا هذا ستحدده حاجتك لتعلم جافاسكربت من الأساس , فيمكن تعلم JS كلغة برمجة فقط مثلها أي لغة أخرى , أي يمكن تعلمها بمعزل تماما عن الـ Document Object Models API الذي هو المسؤول عن عمليات التلاعب بعناصر صفحة الويب . فإن كان هدفك تصميم و برمجة المواقع فستحتاج لهما بشكل كبير لتحسين تعاملك و تحكمك بعناصر الموقع , أما من جانب الجافاسكربت فأنت ستحتاج التعامل مع ما الـ Browser Object Models و الDocument Object Models (اختصارهما DOM , BOM) و الأساليب التي ستواجهها هناك ستهتم بشكل أساسي بتحديد العناصر والتلاعب بمظهرها ومحتواها . و للقيام بذلك ، ستحتاج إما إلى معالجتها مباشرة عبر محددات تشبه CSS ، أو عن طريق شجرة عناصر . و كلاهما يتطلب أن يكون تعاملك مألوفًا مع الـ Html و الـ css . أما إن كنت تريد تعلمها لعمل تطبيقات الهاتف فلن تحتاج التوغل فيهما حد الإحتراف و سيكفيك فيهما مستوى متوسط و هذا طبعا ﻷنك ستحتاج التعامل مع DOM افتراضية تتبع نفس منطق الDOM الفعلية . خلاصة الأمر أنك ستحتاج تعلمهما للتعامل مع جافاسكربت بشكل أمتع و أكثر حيوية و أكثر اختصارا للوقت و المفاهيم و ستخير بين تعلمهما إن قمت بإتخاذ مسار اخر غير تصميم و تطوير المواقع .
  13. طبعا العملية ستصبح سهلة مع التعود على مواجهة الأخطاء و حلها , لكن هذا لا يمنع من جعلها منهجية و منظمة لغرض إختصار الوقت و تنظيم العمل أكثر فحل مشكلة ما ,برأيي, يتطلب : مشكلة ! فبالطبع يجب أن تكون لديك مشكلة و من الواجب قراءتها و التأني في ذلك . إعادة إنتاج المشكلة و عزل كود مصدر المشكلة : أحب عادة حذف الأسطر التي تكون مسؤولة عن المشكلة , فإختفاء الخطأ يعني ثبوتها . ثم أقوم بإعادة انتاج المشكلة من جديد . هذا يجعلك تقوم بعزل كود المصدر المسؤول و وضع نظرتك على نطاق أضيق و أدق , فالتركيز في ثلاث أسطر أو أربع أفضل من التركيز في مشروع كامل أو ملف كامل. قم بوصف المشكلة : كأن تحاول الحصول على أكبر عدد من المعلومات عن هاته المشكلة . (مثال عن ذلك : الزر يشتغل و يستدعي دالة بالفعل لكن الـ console يقول أن هاته الدالة غير معرفة , و أرى أني قد عرفتها بالفعل في ملف index.js) بناءا على ذلك حاول الإجتهاد في حل المشكلة مما جمعت من وصف للمشكلة و تحديد لكود المصدر المسؤول , و لا بأس ببحث على الانترنت عن مشاكل إما مشابهة في الخطأ الظاهر أو في الوصف و الطرح أو السياق أو كود مصدر المشكلة . و ستصبح العملية سهلة و أبسط مع مرور الوقت و التعود على الأخطاء و على حلها . و قد يقودك إنتقال المشاكل من بسيطة , إلى معقدة إلى معقدة جدا , إلى تعلم الخوارزميات و الproblem solving التي ستنقل الأمر إلى مستوى اخر من التنظيم و المنهجية.
  14. و عليكم السلام و رحمة الله . بالنسبة لسؤالك الأول : لا يزال عليك عمل التفاعلية المطلوبة بنفسك , بملف index.js قم بإضافة الأسطر التالية : $('.collapse') .on('show.bs.collapse', function () { $(this).prev().find('svg') .removeClass('fa-angle-down') .addClass('fa-angle-up') }) .on('hide.bs.collapse', function () { $(this).prev().find('svg') .removeClass('fa-angle-up') .addClass('fa-angle-down') }); . و بالطبع يجب عليك تعريف الحالة الإفتراضية للأسهم , إما فوق أو تحت : <i class="fa fa-angle-down" aria-hidden="true"></i> 2. السؤال الثاني : لم يعمل الفيديو بسبب أنك تقوم بتعريف الدالة بعد نسبها للـ Event Listener على هذا النحو : var myvideo=document.getElementById("video1"); myvideo.addEventListener("click", function playPause() { if(myvideo.paused) myvideo.play(); else myvideo.pause(); }, false); و : <section class="video"> <video id="video1"> <source src="./videos/video.mp4" type="video/mp4"> </video> <div class="overlay "> <button onclick="playPause()"> <i class="fa fa-video-camera" aria-hidden="true"></i> مشاهدة الفيديو </button> </div> </section> في حين أنك تستدعي دالة غير معرفة أصلا وإسم هاته الدالة داخل تعريف الـ Event Listener متجاهل أصلا . فحل المشكلة إما بتعريف الدالة خارجا على هذا النحو : myvideo.addEventListener("click", function(){ playPause(); } , false); function playPause() { if(myvideo.paused) myvideo.play(); else myvideo.pause(); } أو بربط الEvent Listener بالزر عوضا عن الفيديو نفسه فيكون : var myvideo=document.getElementById("video1"); var playVideoButton=document.getElementById("playVideoButton"); playVideoButton.addEventListener("click", function playPause() { if(myvideo.paused) myvideo.play(); else myvideo.pause(); }, false); و طبعا لا تنسى تعريف الزر بـ ID: <section class="video"> <video id="video1"> <source src="./videos/video.mp4" type="video/mp4"> </video> <div class="overlay "> <button id="playVideoButton"> <i class="fa fa-video-camera" aria-hidden="true"></i> مشاهدة الفيديو </button> </div> </section> و أفضل إستعمال الطريقة الثانية لأن العنصر بالايدي video غير ظاهر و لاحظ أن الoverlay فوقه فهو غير قابل للوصول أما عن الزر فهو غير ذلك . أما عن تنسيق زر تشغيل الفيديو فيمكنك إستبدال الأيقونة بغيرها : <div class="overlay "> <button id="playVideoButton"> <i class="fa fa-play" aria-hidden="true"></i> مشاهدة الفيديو </button> </div> و بالطبع لا تنسى أن تقوم بإعادة compile للملفات عن طريق : npm run build ملاحظات : لا أعلم إن كنت قد واجهت نفس الأخطاء بالنسبة للwebpack على هذا النحو : > product@1.0.0 build > webpack serve --mode development sh: 1: webpack: Permission denied بحيث تم حلها عن طريق تغيير الوضع : chmod 775 -R /product و لا ألزمك طبعا و لكن من اﻷفضل , و كشيء من الشائع بين الأوساط , تعريف السكربت watch عوضا عن build بملف package.json , كما يجب أيضا عمل سكربت للproduction : "scripts": { "watch": "webpack serve --mode development", "prod" : "webpack serve --mode production" }, فيتم الإستدعاء كالتالي : npm run watch npm run prod
  15. كشيء جميل في اللارافل فإنه تم تم بناءه مع وضع الاختبارات في الاعتبار على عكس أطر عمل أخرى (مثال : الcodegniter) , فقد تم تضمين دعم الاختبارات باستخدام الphp Unit و يأتي كل Fresh Laravel Application مع ملف اعداد phpunit.xml و مجلد tests . بحيث يحتوي المجلد tests افتراضيًّا على مجلّدين : Feature (تعبر عن مجموعة اختبارات كل منها هو اختبار جزء من الشيفرة) و Unit (تعبر عن مجموعة اختبارات الوحدة). يتم تشغيل الاختبارات التي قمت بإنشاءها عن طريق طباعة الأمر في التارمنل : phpunit أو : vendor/bin/phpunit أي أن الاختبارات التي يتم عملها ببيئة عمل الphpunit في اللارافل هي أحد الخيارين : اختبار وحدة unit test : و يقصد بها اختبارات مكتوبة من منظور المبرمجين. وهي مصممة للتأكد من أن طريقة معينة (أو وحدة) للclass تؤدي مجموعة من المهام المحددة. اختبار ميزة feature test: و يقصد بها اختبار جزء أكبر من التعليمات البرمجية الخاصة بك ، بما في ذلك كيفية تفاعل العديد من الكائنات مع بعضها البعض أو حتى طلب HTTP إلى نقطة نهاية من الاي بي اي الخاص بك . أما عن الأشياء التي تحتاج إختبارها فلارافل توفر أغلب ذلك , خصوصا و قد تم تحسينها في النسخ الأخيرة . فكما ذكر @Sam Ahwيمكنك إختبار كل جانب من التطبيق : جانب الHTTP : بحيث يوفر اللارافل مجموعة من الmethods لمحاكاة طلب الى الخادم و التحقق من تفاصيله . مثال : <?php class TasksTest extends TestCase { /**@test */ // هكذا يتم تعريف الاختبار public function a_task_can_be_retrieved() { $response = $this->json('GET', '/api/v1/task/12'); // محاكاة الطلب $response->assertStatus(200); // التحقق من ان كود الحالة هو كما متوقع } /** * يمكن تعريفه هكذا ايضا * */ public function test_a_task_can_be_updated() { $response = $this->json('PATCH', '/api/v1/task/12' ,$some_data); // محاكاة الطلب $response->assertStatus(200); // التأكد من كود الحالة } /**@test */ public function a_task_name_is_required() { $response = $this->json('POST', '/api/v1/task' ,$some_data_that_has_not_a_name); // محاكاة الطلب $response->assertStatus(422); // التأكد من كود الحالة } } اختبارات المتصفح . Browser Tests أو كما هو شائع : Laravel Dusk . بحيث يمكنك إختبار كل ما يخص الواجهات . (التفاعل مع الواجهة , ضغط أزرار , نقر على روابط , التعامل مع القوائم المنسدلة , إرفاق الملفات عن طريق مدخلات الملفات .. و غيرها ) غير أن هاته الإختبارات تحتاج تسطيب حزمة laravel/dusk . و ستتلقى عنك عناء اختبار التطبيق بنفسك . مثال من ويكي حسوب : <?php namespace Tests\Browser; use App\User; use Tests\DuskTestCase; use Laravel\Dusk\Chrome; use Illuminate\Foundation\Testing\DatabaseMigrations; class ExampleTest extends DuskTestCase { use DatabaseMigrations; /** * مثال عن اختبار متصفح بسيط. * * @return void */ public function testBasicExample() { $user = factory(User::class)->create([ 'email' => 'taylor@laravel.com', ]); $this->browse(function ($browser) use ($user) { $browser->visit('/login') ->type('email', $user->email) ->type('password', 'secret') ->press('Login') ->assertPathIs('/home'); }); } } 3 . اختبارات قواعد البيانات : عن طريق الmodel factories و الdatabase seeders يمكنك ملئ قواعد البيانات الخاصة بك ببيانات مزيفة عن طريق مكتبة faker و من ثم اختبارها عن طريق مجموعة من التوكيدات . 4 . اختبارات تزييف الأحداث أو الـ Mocking : ويقصد بالأحداث أشياء مثل أحداث إرسال الايميلات App\Mails أو الأحداث داخل App\Events أو الإشعارات داخل App\Notifications أو تزييف الJobs داخل App\Jobs بل و حتى تزييف التخزين و التوكيد على عمله بشكل صحيح . فلارافل يمكنك من محاكاة ذلك و التوكيد على عمله دون عمله فعليا . إنشاء الاختبارات : يكون عن طريق الterminal : php artisan make:test UserTest 2. و من ثم التعديل على ملف الtest المنشئ بإضافة اختبارات بشكل methods معرفة بشكل صحيح (كما تم ذكر ذلك) . <?php class UserTest extends TestCase { /**@test*/ public function a_user_can_be_banned() { } } 3 . و من ثم محاكاة العملية التي تنوي إجراء الإختبار عليها : <?php class UserTest extends TestCase { /**@test*/ public function a_user_can_be_banned() { $response = $this->json('PATCH', '/users/34', ['is_banned' => true]); // } } 4 . و أخيرا استعمال التوكيد الصحيح : <?php class UserTest extends TestCase { /**@test*/ public function a_user_can_be_banned() { $response = $this->json('PATCH', '/users/34', ['is_banned' => true]); $response->assertStatus(200)->assertExactJson(['is_banned' => true,]); } } .. التوغل في كتابة الإختبارات و إختبار كل صغيرة و كبيرة بجوانب موقعك سيأخذ بك و تطبيقاتك إلى مستوى اخر من التطوير و يجعلك تمارس أحد مبادئ التطوير المتقدمة و هو الTDD أو الـ Test Driven Development و لن تضطر للعمل بطرق الإختبار التقليدية و ستكتفي بطباعة أمر واحد في التارمينل لإختبار تطبيقك ككل , في مرة واحدة.
  16. طبعا لن تحتاج لتثبيت تطبيقك بحقوق ملكية أو حق ترخيص الاستعمال إلا في حالة ما كنت تقوم ببيع السكربتات و البرامج الخاصة بك و ترغب في إدارة مفاتيح التراخيص عن بُعد في السكربتات التي تباع . و لا أظن إنه يوجد حزم جاهزة في اللارافل لتطبيق العملية . ولكن يمكنك بسهولة إنشاء حزمة تحقق من حقوق الملكية خاصة بك كأن تنصب على السرفر و تتوسط العمليات بين الواجهة الأمامية و الخلفية للتحقق من صلاحية الترخيص . مثال عن منطق العملية : تقوم بعرض خطط اشتراك (مثلا : سنويا , شهري , 3 شهور ) . يختار العميل الذي يقوم بشراء السكربت خطة ويتلقى مفتاحًا عامًا public_key يتم إنشاؤه. هذا المفتاح العام سيكون غير صالح حتى يتم تفعيله. للتفعيل ، يرسل لك العميل المفتاح العام من الخادم الخاص به. تقوم بإرسال المفتاح الخاص prvate_key إلى المجال الذي تم إرسال طلب التنشيط منه. يتلقى العميل ردك ويحفظ المفتاح الخاص على الخادم الخاص به. الآن تطبيقه يعمل بكامل طاقته. في المستقبل ، سيرسل العميل بشكل دوري طلبات لتحديث مفتاحه الخاص. إذا لم يتم تلقي تحديثات خلال فترة معينة ، فسيتوقف السكربت الخاص به عن العمل. و بالطبع سيمكنك إدارة هاته المفاتيح التي يميز كل منها نسخة من التطبيق الذي قمت بالبيع منه. و أظن أنه هو نفس منطق ما تعمل به برامج الترخيص الإضافية مثل ايون كيوب . فهي تعمل بشكل جد جيد مع الـphp لكنني لم أتحقق من ذلك باستخدام اللارافل.
  17. سؤالك غير واضح أخي الكريم يرجى توضيحه أكثر , هل تقصد تثبيت تطبيق ما بحقوق ملكية أم عن كيفية تطبيق مبادئ تطوير الويب الأخلاقي ؟
  18. الان و بعد الإنتهاء من جانب الباك اند يتطلب عليك التحقق إن كان الرد من الـ API يحمل أخطاء بأكواد حالات معينة ومن ثم التصرف بناء عليه , كأن تقوم بتعطيل عملية التسجيل أو مواصلة العملية بعد تسجيل التوكن و اعادة التوجيه . و هذا طبعا يخضع لأكواد الحالات بالـ API الذي تقوم بجلب منه البيانات و يخضع أيضا لتعريفك للـ errors . فعلى سبيل المثال يكون الكود كالتالي : // قم باستدعاء الدالة من المكان الصحيح login() async{ var response = await http.post('/your-end-point',auth_data); if(response.statusCode == 422){ print(jsonDecode(response.body['errors'])) // طباعة الاخطاء في حالة وجودها } else{ // اكمال عملية تسجيل الدخول و عمل اعادة التوجيه بعد تسجيل التوكن وحفظه } } ومن المفضل في الباك اند في اللارافل استعمال الواجهة : <?php use Illuminate\Support\Facades\Validator; .. عوضا عن الميثود validate ضمن الـ instance الذي اسمه Request : <?php class myController extends Controller{ public function myMethod(Request $request){ $request->validate($rules); } } حتى تتحصل على حرية أكبر ولا تترك الـ Laravel Exceptions يتولى إعادة التوجيه و تسجيل الأخطاء بالجلسة التي هي من المفروض خارج العملية فيما يخص بناء الAPI و باقي العملية.
  19. مكونات الـ regular expression التي سيتم استعمالها : ^ : بداية العبارة . 0-9 : رقم بين 0 و 9 . + : محدد التكرار , اي قبول عنصر واحد في التجميعة أو أكثر . / : علامة الـ / الفاصلة بين العددين . [ .. ] : حاضنتين لتشكيل تجميعة . $ : نهاية العبارة . فتكون العبارة كالتالي : ^[0-9]+/[0-9]+$ أي نقبل فقط : الأرقام بين 0 و 9 , قابلة للتكرار / الأرقام بين 0 و 9 قابلة للتكرار . و يكون كل ما هو ليس pos_num/pos_num غير مقبول . أمثلة : 12/100 مقبول في حين أن e0/100 غير مقبول .
  20. ليس من الخطأ استعمال fetch عوضا عن axios فكلاهما مكتبتان لطلبات الhttp مثلها مثل : Node http node-fetch fetch-polyfill reqwest .. وغيرها الكثير .. و اختيار مكتبة الاجاكس قد يخضع لعدة معايير نذكر منها : دعم المتصفحات , فالمكتبة التي تدعمها أغلب المتصفحات تكون لها الأفضلية . على سبيل المثال فإن fetch لا تعمل في النسخة 11 من انترنت اكسبلورر و لا توجد طريقة لتحقيق ذلك كون fetch طريقة تخص المصتفح في حين أن مكتبة axios تعمل في كلها . مهلة الاستجابة , و أقصد بهذا التحكم في المهلة الاختيارية قبل إحباط الطلب فليست كل المكتبات توفر هاته الميزة , وإن كانت فليست كلها توفرها بطريقة بسيطة وسهلة مثل تلك التي تقدمها مكتبة axios . التحويل التلقائي للبيانات الى صيغة JSON . رغم أن العملية جد بسيطة و لا تحتاج الا عمل parse للبيانات المستدعية عن طريق الـAPI لكن الافضلية تبقى لمن يبسط العمليةإاكثر . فعلى سبيل المثال تحتاج لتحويل البيانات الى json باستعمال fetch عن طريق : // fetch() fetch('https://my.api.co/api/v1/items') .then(response => response.json()) // تحويل .then(data => { console.log(data) }) .catch(error => console.error(error)); في حين أنك باستعمال axios لن تضطر لتحويلها : // axios axios.get('https://my.api.co/api/v1/teachers') .then(response => { console.log(response.data); // لا حاجة لتحويل البيانات }, error => { console.log(error); }); و طبعا , كود أقصر يعني كود أفضل . وقس على ذلك العشرات من طلبات الـ HTTP . 4. اعتراضات HTTP أو ( HTTP interceptors ) : القدرة على اعتراض طلبات الHttp بشكل globally مفهوم ستحتاج للتعامل معه بشكل كبير في بناء تطبيقات الصفحة الواحدة SPA و تطبيقات الويب التقدمية , فقبل عرض أي نتائج تحتاج طبعا إلى فحص طلبات HTTP ( كأن تتفحص حالة الطلب أو كود الحالة ) ومن ثم التصرف بناءا عليها , كالتسجيل والمصادقة و ما إلى ذلك , ولن تضطر لكتابة رمز منفصل لكل طلب HTTP. و مكتبة axios تجعل الأمر جد سهل فعلى سبيل المثال : // تعريف بملف اعداد axios axios.interceptors.request.use(response => response, config => { // يشير الكود 403 في توثيق الاي بي اي لهذا التطبيق الى ان التوكن قد استهلك استعماله وهو غير صالح if(response.data.status == 403){ alert('يرجى اعادة تسجيل الدخول'); } return config; }); // هنا يمكنك ارسال طلب في حين أن مكتبة fetch لا توفر طريقة مباشرة لإعتراض الطلبات و هذا ما يجعل axios إفضل بهاته التقطة . و كنقطة فرعية لا ينبغي إغفال المجتمع المستعمل لمكتبة axios في الـ vue و الreact الangular و الnode و غيرها , و كونها شعبية يعني وفرة المعلومة و سهولة الوصول إليها , وهذا شيء نحتاجه كمطورين . اما عن رأيي الشخصي فأفضل استعمال مكتبة axios , فهي توفر كود أقصر و أفضل قراءة . و لها شعبية مستعملين كبيرة و واسعة , كما أنها سهلة الادارة و الاعداد . وفي النهاية يبقى الاختيار لك , فـ axios توفر حزمة مضغوطة لمعظم إحتياجات اتصال HTTP الخاصة بك. ومع ذلك , فان استخدام طريقة fetch التي توفرها متصفحات الويب لا تحتاج منك استدعاء و استعمال عميل HTTP API . فذلك شيء يحدده درجة تعقيد مشروعك و تعاملك مع الAPI و حاجتك لمختلف مفاهيم اتصال HTTP الخاصة بك .
  21. طبعا قد يعتمد هذا على نوع البيانات المدرجة بقاعدة البيانات فالظاهر أن user_Received_id و user_send_id عبارة عن strings او نوع اخر فتأكد أن تقوم بتمرير النوع الصحيح هل يمكن الاطلاع على بنية بيانات الجدول بالphpmyadmin ؟
  22. لتفحص في المشكل أكثر يمكنك تفحص الخطأ عن طريق : <?php $binding_params = $stmt->bind_param("sss",$topic_id,$user_Received_id,$user_send_id); if(! $binding_params){ die( "يوجد خطأ في تمرير المتغيرات : (" .$conn->errno . ") " . $conn->error); } لكن يبدو أن السبب في ظهور المشكلة هو أن الدالة bind_param تقبل أن يكون البارمتر اﻷول عن نوع البيانات الممررة بالترتيب , فان كنا نقوم بتمرير 3 strings : <?php $stm->bind_param ('sss', $first_str ,$second_str ,$third_str) أما في هاته الحالة فنحن نقوم بتمرير معرفات ID فالمفروض يتم تمرير i ترميزا لinteger عوضا عن تمرير s فيقوم بتجاهل كل ما هو ليس string فتكون : <?php $stm->bind_param ('iii', $first_int ,$second_int ,$third_int) و لربما يكون السبب في ارجاع عمود واحد user_send_id هو أن هذا العمود مسجل بنوع string أو varchar و ليس id , فحتى تتجنب مشكل تحديد الuser_Received_id و الtopic_id فقط قم بتمرير iis عوضا عن iii فتكون : <?php $stm->bind_param ('iis', $first_int ,$second_int ,$im_a_string)
×
×
  • أضف...