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

Mustafa Suleiman

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

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

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

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

    355

كل منشورات العضو Mustafa Suleiman

  1. ستجد أسفل فيديو الدرس في نهاية الصفحة صندوق تعليقات كما هنا، أرجو طرح الأسئلة أسفل الدرس وليس هنا في قسم الأسئلة البرمجة حيث نطرح الأسئلة العامة الغير متعلقة بمحتوى الدورة أو الدرس، وذلك لمساعدتك بشكل أفضل.
  2. خلال دورة تطوير واجهات المستخدم يتم التطرق إلى الأساسيات أولاً للتالي HTML, CSS,JS و مكتبةjQuery، ثم خلال المشاريع يتم التطرق إلى أمور متقدمة بالإضافة للأساسيات. لذا بعد الإنتهاء ستجد أنه تم شرح نسبة كبيرة من اللغات السابقة. لكن هل تكتفي بذلك؟ حقيقًة الفرق بين مبرمج متميز ومبرمج عادي هو الرغبة في تعلم المزيد وبذل مجهود وعدم الإكتفاء، لأكون صريحًا معك لا توجد دورة توفر لك كل شيء، ستحتاج إلى البحث والمشاهدة والقراءة عن المزيد والتعمق فيما يتم شرحه وليس الإكتفاء بالشرح فقط. أتفهم الرغبة في إنهاء الدورة بشكل سريع، لكن ما الفائدة؟ الفائدة تكمن في زيادة مستواك وخبراتك بما ذكرت وتوظيف ذلك في نماذج وتطبيقات عملية لفهم كيف آلية عملها وما الفرق بين الخواص المختلفة وهكذا. بالطبع لا أطلب منك فهم كل ما هو موجود في موسوعة حسوب، لكن تفقد الخواص المتاحة وماذا تفعل وتعلم البعض منها وهكذا
  3. غالب الظن أنك تقوم بتعديل ملف تهجير موجود مسبقًا بدلاً من إنشاء ملف جديد، بمعنى لإنشاء جدول جديد أو تعديل جدول موجود، يجب عليك دائمًا إنشاء ملف تهجير جديد، لا تقم بتعديل ملفات التهجير الموجودة مسبقًا. وإن قمت بتعديل ملف تهجير موجود مسبقًا، فلن يتم تطبيق التغييرات، لأن لارافل تحتفظ بسجل بالتغييرات التي تم إجراؤها على قاعدة البيانات. لذا، لو قمت بتعديل ملف تهجير موجود مسبقًا، فلن يتم تطبيقه لأن لارافل تعتقد أنه تم تطبيقه بالفعل.
  4. تخيل معي لو حصل المهاجم على ملف تعريف ارتباط الجلسة Cookie للمستخدم أو بيانات الاعتماد الخاصة به، سيتمكن من انتحال شخصية المستخدم وإجراء طلبات نيابة عنه وذلك هجوم Session Hijacking، بالتالي المصادقة القائمة على الرموز تجعل من الصعب على المهاجمين اختطاف الجلسة، حيث يحتاجون إلى الحصول على الرمز أيضًا. بالإضافة إلى هجمات CSRF، حيث تساعد المصادقة من خلال الـ Token منع هجمات تزوير الطلبات عبر المواقع (CSRF)، حيث يخدع المهاجم متصفح المستخدم لإجراء طلبات غير مصرح بها إلى الخادم. ويصبح من الصعب على الخادم تتبع المستخدمين النشطين وإدارة جلساتهم أيضًا من خلال الرمز، لا يحتاج الخادم إلى إعادة المصادقة على المستخدم لكل طلب، والتعامل مع المزيد من الطلبات دون الحاجة إلى إعادة المصادقة على المستخدمين، مما يقلل من الحمل على الخادم ويحسن الأداء. ولا يحتاج الخادم أيضًا إلى الاحتفاظ بمعلومات الجلسة لكل مستخدم، ويستطيع المستخدمين الوصول إلى التطبيق من أجهزة أو جلسات متعددة دون الحاجة إلى إعادة المصادقة. بجانب أنه بإمكانك إلغاء الرموز أو إنهاء صلاحيتها، مما يسمح بالتحكم الدقيق في الوصول إلى التطبيق للمساعدة يساعد في تقليل مخاطر سرقة الـ Tokens، حيث يمكن إلغاء الـ Access Token المسروق دون الحاجة إلى تسجيل خروج المستخدم. ناهيك عن استخدام الرموز لتتبع نشاط المستخدم وتوفير سجل تدقيق أكثر تفصيلاً.
  5. من خلال دالة onSubmit كالتالي: import { IonMenuButton, IonHeader, IonTitle, IonToolbar, IonButtons, IonGrid, IonRow, IonCol, IonCard, IonCardHeader, IonCardTitle, IonButton, IonLoading, IonContent, IonCardSubtitle, useIonAlert, IonAlert, } from "@ionic/react"; import { IonPage } from "@ionic/react"; import "../css/event.css"; import axios from "../config/axios"; //جلب المعلومات import { CREATE_EVENT_URL, EVENTS_URL } from "../config/urls"; import { useContext, useEffect, useState } from "react"; import { AuthContext } from "../context/AuthContext"; //import { Storage } from "@capacitor/storage"; //import { useHistory } from "react-router"; import React from "react"; const Events = () => { const [showLoading, setShowLoading] = useState(false); const [posts, setPosts] = useState([]); const [title, setTitle] = useState(""); const [description, setDescription] = useState(""); const [price, setPrice] = useState(""); const [presentAlert] = useIonAlert(); const { jwt } = useContext(AuthContext); useEffect(() => { getEvent(); }, []); const onSubmit = async () => { const postData = new FormData(); postData.append("title", title); postData.append("description", description); postData.append("price", price); try { await axios .post(CREATE_EVENT_URL, postData, { headers: { Authorization: jwt, }, }) .then((res) => { console.log("Event created successfully:", res.data); setTitle(""); setDescription(""); setPrice(""); getEvent(); }); } catch (e) { console.error("Error creating event:", e.response ? e.response.data : e); } }; const getEvent = async () => { setShowLoading(true); try { await axios .get(EVENTS_URL, { headers: { Authorization: jwt, }, }) .then((res) => { console.log("Events fetched successfully:", res.data.getEvent); setPosts(res.data.getEvent); setShowLoading(false); }); } catch (e) { console.error("Error fetching events:", e.response ? e.response.data : e); setShowLoading(false); } }; return ( <IonPage id="main-content"> {showLoading && <IonLoading isOpen={showLoading} duration={1000} />} {!showLoading && ( <> <IonHeader> <IonToolbar color="primary"> <IonTitle>صفحة المناسبات</IonTitle> <IonButton fill="clear" slot="end" color="tertiary" routerLink="/login" > مرحبا </IonButton> <IonButtons slot="start"> <IonMenuButton></IonMenuButton> </IonButtons> </IonToolbar> </IonHeader> <IonContent> <IonGrid> <IonRow> <div className="col-center"> <IonCol sizeLg="4"> <IonCard className="edit-card"> <IonCardHeader> <IonCardTitle color="primary"> شارك مناسبتك الخاصة! </IonCardTitle> <IonButton id="present-alert" color="primary"> انشاء مناسبة </IonButton> <IonAlert trigger="present-alert" header="اضافة مناسبة" buttons={[ { text: "إلغاء", role: "cancel", cssClass: "secondary", }, { text: "موافق", handler: () => { onSubmit(); }, }, ]} inputs={[ { name: "title", placeholder: "العنوان", value: title, onIonChange: (e) => setTitle(e.target.value), }, { name: "price", type: "number", placeholder: "السعر", min: 1, max: 100, value: price, onIonChange: (e) => setPrice(e.target.value), }, { name: "description", type: "textarea", placeholder: "التفاصيل", value: description, onIonChange: (e) => setDescription(e.target.value), }, ]} ></IonAlert> </IonCardHeader> </IonCard> </IonCol> </div> </IonRow> </IonGrid> <IonGrid> <IonRow className="col-edit"> <IonCol> {posts.length > 0 ? ( posts.map((post) => ( <IonCard key={post.id}> <IonCardHeader> <IonCardTitle color="secondary"> {post.title} </IonCardTitle> <IonCardSubtitle>{post.price}$</IonCardSubtitle> </IonCardHeader> <IonButton color="primary" onClick={() => presentAlert({ header: "تفاصيل المناسبة", subHeader: "قم بتسجيل الدخول وسارع بالحجز", message: `${post.description}`, buttons: ["اغلاق"], }) } className="btn-edit" id="present-alert" > احجز الان </IonButton> </IonCard> )) ) : ( <IonCard> <IonCardHeader> <IonCardTitle color="secondary"> لا توجد مناسبات </IonCardTitle> </IonCardHeader> </IonCard> )} </IonCol> </IonRow> </IonGrid> </IonContent> </> )} </IonPage> ); }; export default Events; قمت بتغيير const [post, setPosts] = useState("") إلى const [posts, setPosts] = useState([]) أ] وضع مصفوفة كقيمة إفتراضية. لأن ذلك يسمح بتخزين مجموعة من الأحداث التي تم جلبها من API في متغير posts، بدلاً من تخزين حدث واحد فقط، واستخدام المصفوفة posts ضروري لعرض قائمة من الأحداث بشكل صحيح. ثم استخدمت الدالة posts.map للتكرار على عناصر المصفوفة posts وعرض IonCard لكل حدث على حدة لضمان عرض جميع الأحداث التي تم جلبها من API. بعد ذلك ضبط معالجات الأحداث onIonChange لحقول النموذج (title و price و description) بشكل صحيح، لتحديث قيم تلك الحقول في حالة تغييرها من قبل المستخدم. وبعد إنشاء حدث جديد، يتم استدعاء الدالة getEvent() مرة أخرى من أجل تحديث قائمة الأحداث المعروضة وإظهار الحدث الجديد الذي تم إنشاؤه. مع إضافة معالجة أساسية للأخطاء في الدالتين onSubmit و getEvent لعرض رسائل خطأ واضحة للمستخدم في حالة حدوث أي مشكلة. واستخدام العرض الشرطي لعرض قائمة الأحداث (posts.length > 0) أو رسالة "لا توجد مناسبات" إن لم تكن هناك أحداث.
  6. قبل تدريب النموذج، تحتاج إلى تحويل الصور إلى بيانات رقمية ليتمكن للنموذج من فهمها، وذلك عبر تحويل كل صورة إلى مصفوفة أرقام، حيث يمثل كل بكسل في الصورة قيمة رقمية. وطالما أنك قمت بتحميل الـ Data set من Kaggle إذن تلك البيانات Labeled، وتتوفر عدة طرق ونماذج يمكن استخدامها لتصنيف الصور واستخراج الـ features، لكن أشهرها وأكثرها فعالية هي الشبكات العصبية العميقة (Deep Neural Networks)، وخاصة الشبكات العصبية التلافيفية (Convolutional Neural Networks - CNNs) التي تُستخدم بكثرة في تحليل الصور. وتحتاج إلى تجهيز البيانات قبل إدخالها في النموذج، وذلك يتضمن: تغيير حجم الصور بحيث تكون كلها بنفس الأبعاد. تطبيع الصور (Normalization) لتسريع عملية التدريب. تقسيم البيانات إلى مجموعات تدريب وتحقق validation واختبار. ولو تستخدم الشبكات العصبية التلافيفية (CNNs)، فتستطيع استخدام مكتبات مثل TensorFlow أو PyTorch لبناء النموذج. للتوضيح إليك مثال مبسط لبناء نموذج CNN باستخدام Keras (واجهة برمجية لـ TensorFlow): from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense model = Sequential([ Conv2D(32, (3, 3), activation='relu', input_shape=(height, width, channels)), MaxPooling2D((2, 2)), Conv2D(64, (3, 3), activation='relu'), MaxPooling2D((2, 2)), Flatten(), Dense(128, activation='relu'), Dense(1, activation='sigmoid') # لأن لديك فئتين فقط: رجل أو امرأة ]) model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy']) وتستطيع تدريب النموذج باستخدام بيانات التدريب: model.fit(train_images, train_labels, epochs=10, validation_data=(validation_images, validation_labels)) بعد التدريب، بإمكانك تقييم النموذج باستخدام بيانات الاختبار: test_loss, test_acc = model.evaluate(test_images, test_labels) print(f'Test accuracy: {test_acc}') وللعلم، بدلاً من البدء من الصفر تستطيع استخدام نموذج مُدرب بالفعل والبناء عليه مثل VGG16 و ResNet50 وهي شبكات عصبية ضخمة تم تدريبها على ملايين الصور لتمييز آلاف الأشياء (حيوانات، سيارات، أشكال، ألوان، ...إلخ)، وتلك الشبكات أصبحت خبيرة في استخراج الـ Features الهامة من الصور. أي نزيل الطبقات الأخيرة المسؤولة عن التصنيف النهائي، ونحتفظ بالطبقات الأولى التي تعلمت استخراج الـ Features الهامة من الصور.
  7. بالإضافة إلى الطرق السابق، هناك طريق مختلفة لتنفيذ المطلوب. مثلاً باستخدام خوارزمية "فرّق تسُد" Divide and Conquer، حيث تقوم الخوارزمية بتقسيم المصفوفة إلى نصفين بشكل متكرر، ثم إيجاد أكبر قيمة في كل نصف، ثم مقارنة القيمتين الأكبر من كل نصف لإرجاع القيمة الأكبر في المصفوفة بأكملها. وهي تعتبر أكثر كفاءة من الطرق المذكورة سالفًا، خاصةً مع المصفوفات الكبيرة، حيث أن تعقيدها الزمني هو O(n log n) بدلاً من O(n) للطرق السابقة. أولاً داخل دالة بأي اسم نضع لها ثلاث معاملات: arr: المصفوفة التي نبحث فيها عن أكبر قيمة. left: مؤشر بداية الجزء الحالي من المصفوفة. right: مؤشر نهاية الجزء الحالي من المصفوفة. و تقومين بتقسيم المصفوفة إلى نصفين بشكل متكرر حتى تصل إلى عناصر فردية ثم تقارني أكبر قيمة من النصف الأيسر والأيمن وتُرجع القيمة الأكبر. ولنضع شرط لو كان المؤشر left يساوي المؤشر right، فيعني أننا وصلنا إلى جزء من المصفوفة يحتوي على عنصر واحد فقط، وهنا نُرجع قيمة ذلك العنصر (arr[left])، لأنه أكبر قيمة في هذا الجزء. ثم حساب منتصف المصفوفة بجمع left و right ثم القسمة على 2. بعد ذلك استدعاء الدالة بشكل متكرر للنصف الأيمن، ثم مقارنة أكبر قيمة من النصف الأيسر والأيمن وإرجاع الأكبر. في الدالة main، نقوم بإنشاء مصفوفة arr تحتوي على الأرقام، وتستدعي الدالة findMaxRecursively لإيجاد أكبر قيمة في المصفوفة، بالطبع عليك تمرير arr.size() - 1 كمؤشر نهاية المصفوفة لأن arr.size() تُرجع عدد العناصر في المصفوفة، بينما تبدأ عملية فهرسة عناصر المصفوفة من الصفر.
  8. ستجد أسفل فيديو الدرس في نهاية الصفحة صندوق تعليقات كما هنا، أرجو طرح الأسئلة أسفل الدرس وليس هنا في قسم الأسئلة البرمجة حيث نطرح الأسئلة العامة الغير متعلقة بمحتوى الدورة أو الدرس، وذلك لمساعدتك بشكل أفضل.
  9. من الأفضل الإعتماد على async/await لتجنب مثل تلك المشاكل، أي بدلاً من استخدام setTimeout بشكل متكرر داخل دالة textTypingEffect، تقوم باستخدام Promise واحدة فقط لكل نص ونستخدم await لانتظار مرور الوقت المحدد بين كل حرف. const landingHeader = document.querySelector(".landing__header"); const landingParagraph = document.querySelector(".landing__paragraph"); const landingHeaderText = "Iam Mustapha"; const landingParagraphText = "Full Stack Developer"; async function textTypingEffect(element, text, time) { element.textContent = ""; / for (let i = 0; i < text.length; i++) { element.textContent += text[i]; await new Promise((resolve) => setTimeout(resolve, time)); } } async function startTyping() { await textTypingEffect(landingHeader, landingHeaderText, 35); await textTypingEffect(landingParagraph, landingParagraphText, 5); } startTyping(); قمت بتبسيط الأمر باستخدام حلقة for واحدة داخل textTypingEffect، وأصبح الكود أكثر وضوحًا من خلال فصل التأخير الزمني عن عملية إضافة الحروف. وتم تحسين الأداء بشكل طفيف من خلال استخدام Promise واحدة فقط لكل نص بدلاً من إنشاء Promise جديدة لكل حرف.
  10. لنقم بالأمر من البداية، من خلال تحديد الجنس الحقل في نموذج HTML باستخدام قيمًا محددة لكل خيار. <select name="gender"> <option value="male">ذكر</option> <option value="female">أنثى</option> </select> في ملف PHP الذي يعالج بيانات الفورم والذي تقوم بإرسال البيانات إليه من خلال سمة action، عليك استخدام عبارة if للتحقق من قيمة حقل الجنس وتعيين القيمة المناسبة لمتغير جديد، ثم إدخال تلك البيانات في قاعدة البيانات: <?php $gender = $_POST['gender']; if ($gender == 'male') { $genderValue = 10; } elseif ($gender == 'female') { $genderValue = 5; } else { $genderValue = 0; } $sql = "INSERT INTO applicants (name, email, gender_value) VALUES ('$name', '$email', '$genderValue')"; ?> في صفحة عرض البيانات، تقوم باسترداد قيمة gender_value من قاعدة البيانات وعرضها في الجدول.
  11. بالطبع يتم شرح ذلك فهي من ضمن أساسيات علوم الحاسوب، ستجد مسار كامل باسم الخوارزميات وبنى المعطيات ويتم به شرح ما يلي: بنى المعطيات: المصفوفات Arrays القوائم المترابطة Linked Lists المكادس Stacks الأرتال Queues الأشجار الثنائية Binary trees أشجار البحث الثنائية Binary search tree جدول التقطيع Hash table تحليل الخوارزميات: التعرف على الخوارزميات تحليل الخوارزميات Algorithms analysis القوة الغاشمة Brute force الخوارزميات الجشعة Greedy algorithms التعاودية Recursion والبرمجة الديناميكية خوارزميات البحث والترتيب: خوارزمية البحث الخطي Linear Search خوارزمية البحث الثنائي Binary Search خوارزمية الترتيب بالفقاعات Bubble Sort خوارزمية الترتيب بالتحديد Selection Sort خوارزمية الترتيب بالإدراج Insertion Sort خوارزمية الترتيب بالدمج Merge Sort خوارزمية الترتيب السريع Quick Sort الخوارزميات الرياضية مثل الجذر التربيعي والتكعيبي والأعداد الأولية. خوارزميات الرسوم البيانية: الرسم البياني Graph تمثيل الرسوم البيانية بطريقة المصفوفة المجاورة Adjacency Matrix تمثيل الرسوم البيانية بطريقة القائمة المجاورة Adjacency List خوارزمية كروسكال Kruskal خوارزمية ديكسترا Dijkstra خوارزمية بيلمان-فورد Bellman-Ford
  12. لا يعني ذلك، سيتم الرد عليك لا تقلق بخصوص ذلك، أحيانًا يوجد ضغط على مركز المساعدة، لذا أرجو الإنتظار لبعض الوقت، شكرًا لتفهمك.
  13. عليك أولاً إنشاء Path: Path path = Path(); path.moveTo(0, 0); // نقطة البداية path.lineTo(100, 0); // الخط الأفقي path.quadraticBezierTo(120, 20, 100, 40); // المنحنى الرباعي path.lineTo(0, 40); // الخط الأفقي path.quadraticBezierTo(20, 20, 0, 0); // المنحنى الرباعي path.close(); // أغلاق المسار quadraticBezierTo هو لرسم الزوايا المنحنية. ثم إنشاء مقصّ Clipper: class CustomClipper extends CustomClipper<Rect> { @override Path getClip(Size size) { return path; } @override bool shouldReclip(CustomClipper<Rect> oldClipper) { return true; } } بعد ذلك استخدام Clipper مع عنصر Widget: ClipPath( clipper: CustomClipper(), child: Container( width: 100, height: 40, color: Colors.pink, ), ), من خلال تعديل قيم moveTo, lineTo, و quadraticBezierTo تستطيع تغيير أبعاد وموقع الشكل، وتغيير لون Container لتغيير لون الشكل. أو مباشرًة من خلال Custom Painting لإنشاء الشكل المطلوب كالتالي: class RoundedRectangleClipper extends CustomClipper<Path> { @override Path getClip(Size size) { final path = Path(); // تحديد نصف قطر الزوايا const double radius = 30.0; path.moveTo(radius, 0); path.lineTo(size.width - radius, 0); path.quadraticBezierTo(size.width, 0, size.width, radius); path.lineTo(size.width, size.height - radius); path.quadraticBezierTo(size.width, size.height, size.width - radius, size.height); path.lineTo(radius, size.height); path.quadraticBezierTo(0, size.height, 0, size.height - radius); path.lineTo(0, radius); path.quadraticBezierTo(0, 0, radius, 0); path.close(); return path; } @override bool shouldReclip(CustomClipper<Path> oldClipper) => false; } ثم استخدام CustomPainter في Widget: ClipPath( clipper: RoundedRectangleClipper(), child: Container( width: 162.52, height: 240.85, decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Colors.green, Colors.pink], ), ), ), ) تستطيع تعديل قيمة radius في RoundedRectangleClipper للتحكم في نصف قطر الزوايا.
  14. في حالة التعلم تحت الإشراف أو المُراقب Supervised Learning، يتم توفير بيانات الصور مع تسميات أو علامات labels التي تصف المحتوى أو الفئة أو النوع الذي ينتمي إليه كل صورة. للتوضيح، في مشروع التعرف على الأشخاص في الصور، تكون التسميات هي الأسماء أو الفئات مثل "رجل" أو "مرأة" أو "طفل"، وتلك التسميات تسمح للنموذج بالتعلم من البيانات وتصنيف الصور الجديدة بشكل صحيح. في حالة التعلم الغير مُراقب Unsupervised Learning أو التعلم بال_dispatcher (Semi-supervised Learning)، لا يتم توفير بيانات الصور مع تسميات. هنا النموذج يتعلم من البيانات دون معرفة ما هو المحتوى أو الفئة أو النوع الذي ينتمي إليه كل صورة. مثلاً في مشروع تحليل الصور الفوتوغرافية، يتعلم النموذج من البيانات لاكتشاف أنماط أو فئات معينة دون معرفة ما هو المحتوى الفعلي لكل صورة.
  15. ستجد أسفل فيديو الدرس في نهاية الصفحة صندوق تعليقات كما هنا، أرجو طرح الأسئلة أسفل الدرس وليس هنا في قسم الأسئلة البرمجة حيث نطرح الأسئلة العامة الغير متعلقة بمحتوى الدورة أو الدرس، وذلك لمساعدتك بشكل أفضل.
  16. طوال فترة دراسة للدورة تستطيع السؤال عن أي شيء تريده وسيتم توفير المساعدة لك، وبعد التخرج تستطيع طلب المساعدة أيضًا. وتستطيع الوصول للدورة مدى الحياة بدون مشكلة وستحصل على التحديثات الخاصة بها أيضًا. وبخصوص المجموعات، فلا يوجد مجموعات دراسية، يتم الدراسة بشكل منفرد.
  17. السؤال غير واضح أرجو توضيحه، هل المقصود هو هيكلية ملفات openCart أم طريقة تنظيم الأقسام categories به؟ عامًة تنقسم هيكلية ملفات أوبن كارت إلى قسمين رئيسيين، أولاً . المجلد الرئيسي root directory ويحتوي على ملفات النظام الأساسية والمجلدات الفرعية التي تشكل هيكل المتجر. من أهم الملفات به: index.php: ملف الدخول الرئيسي للمتجر. config.php: ملف الإعدادات الرئيسية للمتجر. .htaccess: ملف إعدادات خادم الويب. ثم مجلد الإدارة admin directory وبه جميع الملفات المتعلقة بلوحة تحكم المتجر، والوصول إليه عن طريق إضافة /admin إلى عنوان URL الرئيسي للمتجر، وأهم الملفات به: index.php: ملف الدخول الرئيسي للوحة التحكم. config.php: ملف الإعدادات الرئيسية للوحة التحكم. داخل كل من تلك المجلدات الرئيسية، ستجد العديد من المجلدات الفرعية التي تحتوي على ملفات محددة لوظائف معينة، مثل: catalog: يحتوي على جميع ملفات واجهة المتجر، مثل القوالب، والوحدات، واللغات. system: يحتوي على ملفات نواة نظام أوبن كارت، مثل مكتبات PHP وإعدادات قواعد البيانات. image: يحتوي على جميع الصور المستخدمة في المتجر، مثل صور المنتجات والشعارات. download: يحتوي على جميع الملفات القابلة للتحميل من قبل الزوار، مثل الكتيبات الإلكترونية. وباستطاعتك تعديل مظهر متجرك عن طريق تعديل ملفات القوالب الموجودة في مجلد catalog/view/theme.
  18. بخصوص الأمور المالية أو تغيير الدورة عليك التحدث لمركز المساعدة وسيتم مساعدتك بخصوص ذلك الشأن، بعد إرسال الرسالة أرجو الإنتظار لبعض الوقت.
  19. ستجد أسفل فيديو الدرس في نهاية الصفحة صندوق تعليقات كما هنا، أرجو طرح الأسئلة أسفل الدرس وليس هنا في قسم الأسئلة البرمجة حيث نطرح الأسئلة العامة الغير متعلقة بمحتوى الدورة أو الدرس، وذلك لمساعدتك بشكل أفضل.
  20. توجد كلمات مفتاحية عامة لمثل ذلك منها تصميم موقع إلكتروني، تطوير موقع الكتروني، شركات تصميم مواقع، وكالات تصميم مواقع وإنشاء مواقع إلكترونية، أو أفضل شركات/ِشركة تصميم مواقع. أو كلمات مفتاحية خاصة بنوع الموقع مثل تصميم متجر إلكتروني، تصميم موقع طبي أو تصميم موقع تعليمي. أو كلمات مفتاحية خاصة بالموقع الجغرافي، كتصميم مواقع إلكترونية في المدينة/الدولة أو أفضل شركات تصميم المواقع في المدينة/الدولة. مثلاً تصميم متجر إلكتروني في الرياض بأسعار معقولة. بالنسبة لأفضلية العمل مع شركة عن أخرى الأمر يختلف من عميل لآخر والهدف الخاص به، فمثلاً البعض يبحث عن الجودة مقابل السعر، والبعض يبحث عن أرخص سعر لتنفيذ الأمر، والبعض يبحث عن إحترافية وجودة عالية. بالنسبة لي الجودة مقابل السعر والإحترافية هي العوامل الأهم، لذا عليك تحديد الفئة التي تستهدفها من العملاء، فلا يمكنك استهداف الجميع، عليك تحديد الفئة والدولة ثم تحديد الكلمات التي تبحث عنها تلك الفئة ثم استهدافها وتقديم خدمة تُلبي احتياجاتهم مع توفير ميزة يحتاجونها وغير متوفرة لدى الآخرين أو متوفرة ولكن بحاجة للتحسين أو التنفيذ بشكل أفضل وهكذا.
  21. إنشاء ملف التهجير للجدول يتم من خلال: php artisan make:migration create_posts_table سيقوم الأمر بإنشاء ملف باسم create_posts_table.php في مجلد database/migrations، تستطيع تعديل الاسم كما تريد أي عدل كلمة posts. افتح ملف create_posts_table.php وقم بتعديله ليشمل بنية الجدول المطلوبة، وكمثال: public function up() { Schema::create('posts', function (Blueprint $table) { $table->id(); $table->string('title'); $table->text('content'); $table->string('slug')->unique(); $table->integer('author_id')->unsigned(); $table->timestamps(); $table->foreign('author_id')->references('id')->on('users'); }); } لاحظ create كتبنا بها posts وهو باسم الجدول الذي تريد إنشاؤه. ثم حدد كيف سيتم حذف الجدول من خلال دالة down، بينما تُستخدم طريقة up لإنشاء جداول جديدة أو تعديلها في قاعدة البيانات، نستخدم طريقة down لحذف تلك الجداول أو عكس التعديلات التي تم إجراؤها. public function down() { Schema::dropIfExists('posts'); } بعد حفظ ملف الهجرة، قم بتنفيذ الأمر التالي لبدء التهجير وإنشاء الجدول: php artisan migrate وفي حال أردت التراجع عن الأمر قم بتشغيل الأمر php artisan migrate:rollback، سيتم حذف جدول "posts" من قاعدة البيانات.
  22. ستجد أسفل فيديو الدرس في نهاية الصفحة صندوق تعليقات كما هنا، أرجو طرح الأسئلة أسفل الدرس وليس هنا في قسم الأسئلة البرمجة حيث نطرح الأسئلة العامة الغير متعلقة بمحتوى الدورة أو الدرس، وذلك لمساعدتك بشكل أفضل.
  23. نعم في حال لا تريد استخدام إضافة لفعل ذلك وأيضًا ستتمكن من تقليل أبعاد الصور في حال كانت كبيرة إن كنت بحاجة إلى ذلك. لكن لا أعتقد ذلك، فأنت تريد عرض الصور بشكل صغير وترك مساحة للزائر بعرض كامل الأبعاد للصورة عند الضغط عليها. لذا كل ما تحتاجه هو ضغط الصور بصيغة AVIF
  24. لا تقوم إضافة wp rocket بضغط الصور، طالما أنّ الصور عددها كبير تستطيع تحميل مجلد الصور من الخادم ثم إجراء عملية تقليص لأبعاد الصور وضغطها من خلال تغيير صيغتها إلى Webp أو AVIF وهي الصيغة الأفضل حاليًا لكن يجب أن توفر لها دعم من خلال إضافة، بينما لو كانت نسخة ووردبريس لديك 6.5 فهي تدعم تلك الصيغة حاليًا. وما سبق يتم بشكل تلقائي عن طريق فوتوشوب من خلال تنفيذ سكريبت. أو من خلال إضافة على ووردبريس مثل imagify التابعة لـ wp rocket، أو Optimole وهي الأفضل من حيث جودة الضغط. بخصوص إختبارات السرعة هل الفروقات كبيرة أم بسيطة، إن كانت بسيطة فلا مشكلة ذلك طبيعي.
×
×
  • أضف...