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

Adnane Kadri

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

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

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

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

    51

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

  1. لتفادي المشكل يمكننا تكبير المهلة الزمنية التي يتم فيها التحقق ما إن كان المستخدم قد قام بتحريك الشريط الجانبي إلى مهلة تراها مناسبة أكثر ، يمكننا بهذا إعطاء المستخدم وقتا أكثر لقراءة الرسائل بدون عمل أي إنزلاق للشريط الجانبي .
  2. تشير في الغالب الخطوط و الألوان الحمراء في vs code إلى أخطاء بالسياق في ملف web.php و ملف ForgetPasswordController.php , هلا تأكدت من عدم وجود أية أخطاء بهما ؟ كما أنه سيكون من المساعد لنا في تعقب المشكل و حله لو قمت بإرفاق الملفين أو نسخهما بشكل أكواد بإستعمال محرر الأكواد .
  3. يحدث هذا لأنك تقوم بتغيير قيمة 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 الإفتراضية . يمكنك الإطلاع على هذا المثال التجريبي .
  4. بالإضافة إلى ما قام المدرب سامح بشرحه يمكننا تلخيص الوظيفة في دالة مستقلة تماما يتم إستدعاءها عند الحاجة لها . لنقم أولا بتعريف الدالة المسؤولة عن العملية : 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(); } } }); }); }); و بالطبع فإنه يمكنك التعديل من سلوك التحريك كإضافة بعض الأنيميشن أو ربما تغيير المهلة التي يتم فيها إعتبار أن المستخدم توقف عن السكرول . تأكد فقط أن الكثير من الأفكار قد تحتاج تجزئة و تبسيط لها ,و لما لا نمذجة في سبورة أو ورقة , فهو صحيح أن البرمجة ومفاهيمها أشياء تجريدية , و لكنها تنطلق من الواقع و تحاول نمذجته و تبسيطه و لذلك حاول دوما تقسيم الفكرة إلى أفكار جزئية و لا يجب عليك أبدا النظر إلى أية فكرة بمجملها كفكرة واحدة موحدة غير قابلة للتقسيم .
  5. ستحتاج أولا التسجيل بحساب تجاري في بيبال .ثم سنحتاج الدخول إلى لوحة تحكم المطور , أين يمكنك التعرف على نمطين و حسابين : حساب تجريبي , أو حساب 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 .
  6. يمكنك للتخلص من المشكلة تثبيت webpack بشكل global , تأكد أن تقوم بطباعة الأمر التالي : npm install -g webpack أما إذا كان لديك Webpack مثبت محليًا ، ففي حالات خاصة جدا سنحتاج تحديد المكان الذي يجب أن يبحث فيه موجه الأوامر بشكل صريح للعثور عليه ، مثل : node_modules\.bin\webpack يكون هذا في ملف package.json أين يتم تعريف الأوامر ضمن الكائن script (هذا بافتراض أنك داخل دليل المشروع بالفعل وأنك قمت بالفعل بتسطيب الحزمة عن طريق الأمر webpack ) .
  7. عذرا , قد ورد خطأ في الكود هنا بالضبط : $('#messages_form_groups').insertBefore(newEmptyImg); فالمفروض أن يقوم هذا بـ تضمين العنصر الجديد قبل العنصر بالمعرف messages_form_groups , و هو ما نحتاجه و صوابه : $(newEmptyImg).insertBefore('#messages_form_groups'); قم باستبدال الخطأ بصوابه و أخبرني بالنتيجة
  8. لا يؤدي تغيير خاصية 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>
  9. لعل السؤال "من أين أبدأ" واحد من أكثر الأسئلة شيوعا و اﻷكثر طرحا في المجال و رغم أن السؤال يطرح وحده بالنسبة لكل جديد على المجال إلا أني أظن أنه لا يجب سؤالها ابدا لأن كل لغة لها تخصص معين ولها طريقة كتابة وسياق خاص بها و لربما لها طريقة خاصة بالتعلم ! و لذلك قبل طرح هذا السؤال يجب أولا : اختيار مجال معين من مثل : تطوير مواقع الويب , تطوير تطبيقات الأندرويد , تطوير الألعاب و غيرها من المجالات . البحث عن لغات هذا المجال الأكثر شيوعا و استعمالا وافهم وظيفة كل لغة تخص هذا المجال . يفضل تعلم مبادئ البرمجة و التفكير المنطقي قبل التوغل في أي مجال . أشياء من مثل هاته يمكن إكتسابها و تعلمها عن طريق تعلم الخوارزميات و بنية البيانات . يمكنك الان تعلم لغات هذا المجال عن طريق دورات أو معاهد تكوينية . يمكنك إلقاء نظرة على دورات الأكاديمية هنا . التطبيق العملي و الاختبار و التجربة و الكثير منه . يساعدك كل من المراحل السابقة على تحديد هدفك و توضيحه أكثر و أكثر , و هو ما ستحتاج إليه على طول مسيرتك في المجال . كما أن هذا سيولد لديك الكثير من الأسئلة التي ستوسع نظرتك للمجال و تطور من ثروتك النظرية مما سيدفعك أسرع و أسرع للتطبيق العملي و الإنتاج .
  10. لإستعمال خدمات zoom في تطبيق لارافيل يجدر بك فتح حساب مطور في zoom لإستعمال الواجهة البرمجية الخاصة بهم بشكل طبيعي . يمكنك قراءة توثيق الواجهة البرمجية كاملا هنا . كما يمكنك إستعمال الـ sdk الخاص بـ zoom للاستفادة من كامل المميزات و الخدمات التي توفرها . يمكنك قراءة التوثيق كاملا هنا . كما يمكنك إستعمال هاته الحزمة للإستفادة من بعض الخدمات بشكل أبسط . كما يمكنك الاستزادة بقراءة هاته المقالات حول نفس الموضوع : كيفية إضافة zoom API إلى تطبيق لارافيل كيفية تضمين الـ embeded code الخاص بأي ملتقى zoom (مقالة). كيفية تضمين الـ embeded code الخاص بأي ملتقى zoom (نقاش في منتديات zoom). إدارة ملتقيات زوم عن طريق تطبيق لارافيل .
  11. لتوسيط الصورة يمكنك تعديل محاذاة العنصر الحاوي لعنصر الصورة ليحمل قيمة المحاذاة إلى الوسط : .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 و تنسيقها و تفاعليتها بكل حرية .
  12. يمكنك إستعمال التابع where لتحديد شرط التساوي بين الرقم الوزاري للمستخدم الحالي و الموظفين من جدول الموظفين : $no_ministry = Auth::user()->no_ministry; $selectemployees = Employee::select('id' , 'id_no' , 'name' , 'no_ministry' , 'staff' , 'phone') ->where('no_ministry' ,$no_ministry) ->get();
  13. سنحتاج في هذا توليد 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. بحيث يمكنك تخصيصها كما تشاء .
  14. هل يمكنك وصف المشكلة بشكل أكثر دقة ؟ كما أنه قد يكون من الجيد تضمين بعض الشيفرات البرمجية و الأكواد التي تظن أنها مسببة للمشكلة . يمكنك القراءة أكثر عن إرشادات الاستخدام .
  15. صحيح .خدمات Pusher مدفوعة لكنها تقدم في عرضها المجاني sandbox إمكانية تمرير 200 ألف رسالة بشكل يومي . و هو رقم جد مناسب في مثل حالتك .
  16. يمكن استعمال دريفر Pusher لتحقيق الغرض . لنقم بإتباع الخطوات التالية : 1. تثبيت حزمة خادم Pusher في تطبيق الللارافل : composer require pusher/pusher-php-server 2, إنشاء تطبيق 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 3. نقوم بإنشاء حدث معين : php artisan make:event NewTemperatureRecord سنلاحظ إضافة ملف NewTemperatureRecord.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 NewTemperatureRecord 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('temperature-channel'); } } إنشاء متحكم يختص بالعملية : php artisan make:controller TemperatureController ثم لنتأكد من وضع المحتوى التالي به : <?php namespace App\Http\Controllers; use Illuminate\Http\Request; class TemperatureController extends Controller { public function update(Request $request) { // تحضير مصفوفة بيانات لتمريرها $data['temp'] = $request->temp; // Notify إثارة الحدث event(new App\Events\NewTemperatureRecord($data)); } } و لتأكد من تعريف المسارات اللازمة , واحد لعرض الصفحة وواحد لإرسال طلبات التحديث : // web.php Route::post('temperature','TemperatureController@update'); Route::view('temperature', 'temperature'); ثم بملف ما بالواجهة الأمامية سيكون علينا فقط : الإشتراك في هاته القناة + الإستماع لأحداث هاته القناة : <!-- temperature.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('temperature-channel'); // Notify الاستماع للحدث channel.bind('App\\Events\\NewTemperatureRecord', function(data) { // إن تم اثارة الحدث قم بعرض البيانات document.querySelector('#temp').textContent = data.temp; }); </script> </head> <body> <div id="temp"> 35 </div> </body> بدون تحديث الصفحة سيكون علينا إرسال طلبات أجاكس إلى المسار temp/ بالفعل POST و سنلاحظ تحدث القيمة في صفحة العرض temp/ .
  17. يفضل فهم مبدأ عمل الالية و منطقها و إنشاء التطبيق على طريقتك الخاصة . ولنقل أنه يمكننا ذلك عن طريق الإستعانة بمفهوم الويب سوكيت web sockets . (يمكنك التعرف أكثر على هذا المفهوم هنا ) بحيث نقوم : - في الخادم (في الواجهة الخلفية للتطبيق) بإنشاء قناة أو قنوات بحدث أو أحداث معينة , ثم تمرير البيانات عبر هاته القنوات مستهدفين أحداثا معينة . - في المتصفح (في الواجهة الأمامية للتطبيق) بالإشتراك في هاته القنوات و الإستماع لهاته الأحداث و التصرف بناء عليها (كأن يتم عرض الرسائل بشكل ) . مثال عن الإستعمال : لما تكتب سارة رسالة بمربع نص و تضغط زر الإرسال إلى شيماء سيتم إرسال طلب HTTP إلى الخادم . سيقوم الخادم بمعالجة طلب سارة و تسجيل الرسالة بقواعد البيانات , و في نفس الوقت سيقوم بإثارة الحدث المرتبط بالعملية و ليكن NewMessageEvent ضمن القناة ShaimaaChannel . شيماء مشتركة في القناة ShaimaaChannel و تستمع لأية إثارة لأحداثها , ففي حالة إثارة أية حدث ستقوم بالتقاط البيانات التي تم تمريرها عبره دون تحديث الصفحة . لما يتم إثارة الحدث NewMessageEvent ضمن القناة ShaimaaChannel ستقوم الواجهة الأمامية لشيماء بالتصرف بناء على البيانات التي تلتقطها عبر هاته القناة في هذا الحدث . كأن تقوم بإظهار تنبيه نصي . و قد نحتاج إلى دريفر لمساعدتنا في تمرير البيانات و إنشاء القنوات من مثل 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 NewMessageEvent سنلاحظ إضافة ملف NewMessageEvent.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 NewMessageEvent 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('chaimaa-sara-channel'); } } إنشاء متحكم يختص بالعملية : php artisan make:controller MessagesController ثم لنتأكد من وضع المحتوى التالي به : <?php namespace App\Http\Controllers; use Illuminate\Http\Request; class MessagesController extends Controller { public function send(Request $request) { // تحضير مصفوفة بيانات لتمريرها $data['message'] = $request->message; // Notify إثارة الحدث event(new App\Events\NewMessageEvent($data)); } } سيكون علينا تعريف مسارين , مسار لإستهداف تابع المتحكم MessagesController و اخر لعرض الصفحة chat.blade.php : //web.php Route::post('chat','MessagesController@send'); Route::view('chat', 'chat'); ثم بملف ما بالواجهة الأمامية سيكون علينا فقط : الإشتراك في هاته القناة + الإستماع لأحداث هاته القناة : <!-- chat.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('sara-chaima-channel'); // Notify الاستماع للحدث channel.bind('App\\Events\\NewMessageEvent', function(data) { // إن تم اثارة الحدث قم بعرض البيانات alert(data.message); }); </script> </head> بدون تحديث الصفحة سيكون علينا إرسال طلبات أجاكس من نموذج سارة إلى المسار chat/ بالفعل POST و سنلاحظ ظهور الرسالة في مسار chat/ . المسار الأول يمثل الطلب الذي أرسلته سارة , و المسار الثاني يمثل صفحة عرض الرسائل بالنسبة لشيماء . و بالطبع فإن هذا هو الشكل الأبسط للعملية , يمكن تطوير العملية كأن نقوم بتخصيص طريقة العرض في قائمة رسائل منظمة بإنزلاق أفقي مع كل تحديث . قد نضيف أيضا إشعارا جانبيا في كل إثارة للحدث , كما يمكن تخصيص قنوات خاصة بكل مستخدم منفرد أو نقوم بتمرير بيانات أخرى و تطبيق العديد و العديد من الأفكار عليها .
  18. يحدث هذا لأنك تعطي عنصري الفقرتين عرضا أكبر من عرض الشاشة المتبقي : 600 بكسل . و هذا في ملف style.css , لاحظ : section.phara .container .row p.aa { font-size: 18px; font-weight: 300; line-height: 35px; padding-bottom: 50px; border-bottom: 1px solid #fff; width: auto; /*هنا*/ } section.phara .container .row p.bb { font-size: 18px; font-weight: 300; line-height: 35px; padding-top: 50px; width: auto; /*هنا*/ } للعنصرين : <section class="phara"> <div class="container"> <div class="row "> <div class="col-md-12 ml-auto"> <h5> Diagoona HTML Template </h5> <p class="aa"> ... </p> <p class="bb"> ... </p> <button type="button" name="button"> Continue... </button> </div> </div> </div> </section> و لإصلاح ذلك لن نحتاج إلا لإسناد قيمتين مناسبتين أكثر أو يفضل عرضا افتراضيا للخاصية width : width: auto; أما عن إضافة الطبقة السوداء فيمكنك إنشاء عنصر بالصف overlay يوضع أسفل عنصر الـ footer : <footer> <div class="copyright"> <p>Copyright 2020 Diagoona Co. | Design: TemplateMo</p> </div> </footer> <div class="overlay"></div> و ليحمل التنسيقات التالية : /* إضافة طبقة سوداء شفافة ثابتة و مربعة ذات طول كامل و نصف عرض*/ .overlay{ position: fixed; height: 100vh; width: 57vw; background-color: rgba(0,0,0,0.16); top: 0px; right:0px; z-index: -99; /* لضمان بقاء باقي العناصر قابلة للوصول */ } /* إضافة مثلث جانبي بنفس طول مربع الطبقة السابقة وبنفس درجة الشفافية */ .overlay::before{ content: ''; position: absolute; top: 0px; left: -150px; height: 100%; border-bottom: 100vh solid rgba(0,0,0,0.16); border-left: 150px solid transparent; } و لتوضيح منطق العملية لاحظ كيفية تداخل التركيبتين : ------------- - | | |\ | المربع | + | \ | | | م\ | | | ثلث\ ------------- ------
  19. لن يمكنك فعل ذلك بإستعمال التابع load , لأن هذا التابع يسمح بتحميل HTML أو محتوى نصي من الخادم ثم إضافته إلى عنصر DOM . أما لتحقيق غرضك فلن نحتاج إلا لتحديد العنصر الحاوي للمحتوى ثم إضافته إلى العنصر div . سيمكن إستعمال التابع append لهذا . رغم أن فكرة تحميل محتوى موجود داخل الـ div و إعادة تضمينه في نفس العنصر غير منطقية إلا أن الشيفرة ستكون مشابه للتالي : <div id="load_posts"></div> <script> setInterval(function(){ var content = $('#load_posts').html(); // $('#load_posts').html(''); لحذف المحتوى و إعادة تحميله $('#load_posts').append(content); }, 1000); </script>
  20. أظن أنه ينبغي عليك أولا معرفة المميزات التي قد تستفيد منها في تعلمك لسكراتش , ثم سيمكنك الحكم إن كان تعلم ذلك واجبا أم لا بالنسبة لك , رغم أن تعلمه يقترح و يفضل . لنقل أن من بعض مميزاته الاتي : هدفه : حيث أن تعلم سكراتش سيقوم بتبسيط تعاملك مع منطق لغات البرمجة و يجعل ذلك مألوفا بشكل كبير . تعلمه : تعلم سكراتش سهل و ممتع نوعا ما , أي أن طريق تعلمه لن يكون مملا بعض الشيء. يطور مهاراتك : واحدة من أهم أهداف سكراتش هي في تطوير مهاراتك في إدارة المشاريع و تسييرها . يطور منطقك : كما أنه سيساعدك بشكل كبير في تطوير المهارات التحليلية و المنطقية لديك . ممتع و تفاعلي : إستعماله مجاني و ذو واجهة تفاعلية .
  21. يمكنك تحريك الشريط الجانبي للأسفل تماما مباشرة بعد نجاح عملية تحميل أية رسائل جديدة و إضافتها إلى الوثيقة . أي إلى الدالة appendMessage . قد لا نحتاج الدالة getMessages في هاته العملية . أي أننا لا يجب أن نرغم المستخدم أن يكون الشريط الجانبي في أسفله تماما و دوما , و إنما أن يتم عمل التحريك التلقائي للشريط الجانبي بعد كل تضمين لعنصر رسالة جديد , و ذلك لعرضها . أي أن الشيفرة الخاصة بالعملية ستتبع المنطق التالي : const messages = document.getElementById('div_show_message'); function appendMessage() { const message = document.getElementsByClassName('message')[0]; const newMessage = message.cloneNode(true); messages.appendChild(newMessage); scrollToBottom(); } function scrollToBottom() { messages.scrollTop = messages.scrollHeight; } scrollToBottom();
  22. كون الدالتين تستعملان نفس الحدث فلن نحتاج إلا لوضعهما بالمكان الصحيح بدل تكرار الكود : أي بعد نجاح طلب الأجاكس , و المقصود هنا هو التابع success . نقوم في الكود الأول بإرسال طلب أجاكس لتخزين بيانات الرسالة بعد تقديم النموذج , لاحظ : $j('#messages_form') .on('submit', function(event) { event.preventDefault(); if ($j('#username') .val() != '' && $j('#message') .val() != '') { var form_data = $j(this) .serialize(); $j.ajax({ url: "send_messages_form.php", method: "POST", data: form_data, success: function(data) { $j('#messages_form')[0].reset(); load_unseen_notification(); } }); } else { alert(" رسالة فارغة رجاءا اكتب شىء "); } }); في حين أننا نقوم بعرض رسالة في قسم الرسائل في النجاح في الكود الثاني , لاحظ : success: function(response) { $j("#norec") .hide(); $j("#div_show_message") .append(response); $j('#messages_form') .trigger('reset'); }, error: function(xhr, ajaxOptions, thrownError) { alert(thrownError); } أي أن دمج الكودين لن يكون إلا في ما بعد نجاح طلب الأجاكس , و بالتالي : $j('#messages_form') .on('submit', function(event) { event.preventDefault(); if ($j('#username') .val() != '' && $j('#message') .val() != '') { var form_data = $j(this) .serialize(); $j.ajax({ url: "send_messages_form.php", method: "POST", data: form_data, success: function(data) { $j('#messages_form')[0].reset(); load_unseen_notification(); // عرض الرسالة $j("#norec") .hide(); $j("#div_show_message") .append(data); $j('#messages_form') .trigger('reset'); }, // إضافة حالة فشل الطلب error: function(xhr, ajaxOptions, thrownError) { alert(thrownError); } }); } else { alert(" رسالة فارغة رجاءا اكتب شىء "); } }); ثم كخطوة أخيرة لن تحتاج إلا تحريك الشريط الجانبي للأسفل , و بالطبع سيكون ذلك بعد نجاح طلب الأجاكس , أي أن العملية ستتبع منطقا واحد . وهو نفس المنطق المتبع في الكود المرفق . يمكنك المحاولة بقيام ذلك ثم سيمكن مساعدتك في حالة الاستعصاء .
  23. من الطبيعي جدا أن تجد بعض الصعوبة في عمل أي تطبيق عملي , ولكنه سيجب عليك تخطي هاته الصعوبة بالمحاولة و التكرار . و لذلك فإنه لن يكون لتقديم الكود أي منفعة . عوضا عن ذلك يمكنك الإنطلاق بالفعل في صياغة الكود الخاص بك , و سنساعدك في ذلك .
  24. لما لا تقوم فقط بإستعمال واحدة من الحزم التي تستعمل طريقة omnipay لتوحيد عمل بوابات الدفع لإضافة بوابة دفع بايسيرا كونك تطبيقك لارافيل ؟ يقترح : semyonchetvertnyh/omnipay-paysera . povils/omnipay-paysera . و لن يكون ذلك مشكلة إن قمت بالتعامل مع أي من حزمها من قبل . كما يمكنك أيضا إستعمال حزمة adumskis/laravel-paysera (لا تنتمي لمجموعة حزم omnipay) و عموما ستكون ملزما بإتباع الخطوات : فتح حساب بايسيرا , خاص أو تجاري (سيكون هذا ضروريا في عمليات توثيق التعاملات). ستحتاج طلب خدمة بوابة الدفع : يمكنك القيام بذلك مجانًا عن طريق تسجيل الدخول إلى حسابك والانتقال إلى الإعدادات > إعدادات الملف الشخصي > إدارة الخدمة . قم بإنشاء مشروع , يمكنك اتباع الخطوات الموضحة في الفيديو التالي . قم بربط بوابة الدفع , إما عن طريق الإضافة التي توفرها بايسيرا , أو عن طريق الواجهة البرمجية . يمكن الإستعانة بواحدة من الحزم السابقة أو إستعمال الواجهة بشكل مباشر .
×
×
  • أضف...