דילוג לתוכן
  • חוקי הפורום
  • פופולרי
  • לא נפתר
  • משתמשים
  • חיפוש גוגל בפורום
  • צור קשר
עיצובים
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • ברירת מחדל (ללא עיצוב (ברירת מחדל))
  • ללא עיצוב (ברירת מחדל)
כיווץ
מתמחים טופ
  1. דף הבית
  2. מחשבים וטכנולוגיה
  3. רשתות
  4. פיתוח ועיצוב אתרים
  5. Full Stack
  6. צד לקוח
  7. JavaScript ES6
  8. עזרה הדדית - JavaScript
  9. בירור | nodejs תקינות פונקציה

בירור | nodejs תקינות פונקציה

מתוזמן נעוץ נעול הועבר עזרה הדדית - JavaScript
45 פוסטים 4 כותבים 284 צפיות 3 עוקבים
  • מהישן לחדש
  • מהחדש לישן
  • הכי הרבה הצבעות
תגובה
  • תגובה כנושא
התחברו כדי לפרסם תגובה
נושא זה נמחק. רק משתמשים עם הרשאות מתאימות יוכלו לצפות בו.
  • A0533057932A A0533057932

    @עידו300 לא הצלחתי להבין בכלל את המשפט שלך

    ע מנותק
    ע מנותק
    עידו300
    כתב נערך לאחרונה על ידי
    #30

    @A0533057932 לא הבנתי את החלוקה שהוא עשה.

    A0533057932A תגובה 1 תגובה אחרונה
    0
    • ע עידו300

      @A0533057932 לא הבנתי את החלוקה שהוא עשה.

      A0533057932A מנותק
      A0533057932A מנותק
      A0533057932
      כתב נערך לאחרונה על ידי
      #31

      @עידו300


      אתה טועה, ובגדול.
      ההבדל בין הקוד שלך לבין מה שהראיתי הוא לא "קוסמטיקה", הוא ההבדל בין לבנות בניין מלבנים לבין לשפוך בטון לתוך בור ולקוות לטוב.

      אתה טוען שה-AI "התעלם" מפרמטרים אופציונליים? הוא לא התעלם, הוא פשוט טיפל בהם כמו מתכנת, ולא כמו מדביק-טפטים.

      בוא נפרק את ה"שטויות" שלך לגורמים:

      1. האשליה האופטית של ה-IF (למה אתה עובד קשה סתם)

      אתה חושב שחייבים if בשביל כל שדה כדי להרכיב את השאילתה? לא נכון.
      ב-JavaScript מודרני (ES6+), אנחנו בונים אובייקטים בצורה דקלרטיבית. אנחנו לא משרשרים מחרוזות (push ו-join) כמו בשנות ה-90.

      תסתכל על זה. זה מטפל בפרמטרים אופציונליים בלי לגעת ב-SQL ובלי לבנות מערכים ידנית:

      // לוגיקה עסקית נקייה: בניית אובייקט מידע
      const adData = {
          type: params.type,
          profession: params.profession,
          phone: params.phone,
          // הנה הקסם: אם המחיר קיים תוסיף אותו, אם לא - אל תעשה כלום.
          // אין כאן שום "command.push" ושום פסיקים ידניים!
          ...(params.price && { [params.type === 'מחפש' ? 'max_payment' : 'min_payment']: params.price }),
          ...(params.ringMode && { ring_mode: params.ringMode }),
          ...(params.path && { recording_path: params.path })
      };
      
      // וזהו. שולחים את זה לפונקציית ה-DB.
      // היא כבר תדע לבד לייצר מזה INSERT תקין, לא משנה איזה שדות יש או אין.
      await insertToDb('ads', adData); 
      

      למה זה קריטי?
      בקוד שלך, אם אתה שוכח command.push(", ...") (פסיק אחד!), השאילתה נשברת.
      בקוד שלי, הפונקציה הגנרית (insertToDb) לוקחת את המפתחות (Object.keys) ושמה פסיקים לבד. אי אפשר לטעות בתחביר.

      2. "השאיר המון שאילתות" – אתה לא קראת את הקוד

      כתבת: "הוא השאיר הכל אותו הדבר. כולל המון שאילתות למסד נתונים."
      זו טעות עובדתית.

      בקוד שלך יש לולאת for שמריצה INSERT בנפרד לכל עיר:

      // הקוד שלך - 20 ערים = 20 פניות לשרת ה-DB
      for (const city of params.city) {
         await connection.execute(...) 
      }
      

      בקוד שהצעתי (בפסקה על Bulk Insert) יש פקודה אחת שנשלחת למסד:

      // הקוד המקצועי - 20 ערים = פנייה אחת בלבד!
      const values = params.city.map(c => [adId, c]);
      await connection.query('INSERT INTO ad_cities (ad_id, city) VALUES ?', [values]);
      

      ההבדל בביצועים הוא עצום. אם יש לך 100 משתמשים בשנייה, הקוד שלך יפיל את השרת, הקוד שלי אפילו לא ירגיש את העומס.

      3. ה-"Validation" הוא לא הבעיה, ה-Coupling הוא הבעיה

      אתה אומר: "אני לא יכול לזרוק פרמטרים למסד... צריך לבדוק שהם נכונים".
      נכון מאוד. אבל הקוד שלך מערבב את בדיקת התקינות עם בניית השאילתה.

      בקוד שלך:

      if (checkPhone(p.phone)) { 
          command.push(", phone"); // <--- למה הבדיקה יודעת איך בונים SQL?!
      }
      

      אם מחר תחליט לעבור מ-MySQL ל-MongoDB (שאין בו פסיקים ושאילתות טקסטואליות), תצטרך למחוק את כל הלוגיקה העסקית ולכתוב מחדש.
      בקוד שלי, ה-Validator בודק, וה-Repository שומר. אם מחליפים מסד נתונים, משנים רק את פונקציית השמירה. הלוגיקה נשארת.

      לסיכום - למה הוא צריך להקשיב לזה?

      הוא בונה "מכונת רוב גולדברג" – מנגנון מסובך, שביר ומסוכן שעושה פעולה פשוטה.

      1. אבטחה: בניית שאילתות ידנית (push למערך סטרינגים) היא הזמנה לצרות, גם אם כרגע זה נראה בטוח.
      2. ביצועים: הלולאה על הערים היא צוואר בקבוק ודאי.
      3. תחזוקה: הקוד הזה קשה לקריאה וקשה לשינוי.

      תגיד לו: "זה שזה עובד לך במחשב, לא אומר שזה קוד. זה סקריפט. קוד צריך להיות יציב, מהיר וקריא. כרגע אין לך אף אחד מהשלושה."

      ע 3 תגובות תגובה אחרונה
      0
      • A0533057932A A0533057932

        @עידו300


        אתה טועה, ובגדול.
        ההבדל בין הקוד שלך לבין מה שהראיתי הוא לא "קוסמטיקה", הוא ההבדל בין לבנות בניין מלבנים לבין לשפוך בטון לתוך בור ולקוות לטוב.

        אתה טוען שה-AI "התעלם" מפרמטרים אופציונליים? הוא לא התעלם, הוא פשוט טיפל בהם כמו מתכנת, ולא כמו מדביק-טפטים.

        בוא נפרק את ה"שטויות" שלך לגורמים:

        1. האשליה האופטית של ה-IF (למה אתה עובד קשה סתם)

        אתה חושב שחייבים if בשביל כל שדה כדי להרכיב את השאילתה? לא נכון.
        ב-JavaScript מודרני (ES6+), אנחנו בונים אובייקטים בצורה דקלרטיבית. אנחנו לא משרשרים מחרוזות (push ו-join) כמו בשנות ה-90.

        תסתכל על זה. זה מטפל בפרמטרים אופציונליים בלי לגעת ב-SQL ובלי לבנות מערכים ידנית:

        // לוגיקה עסקית נקייה: בניית אובייקט מידע
        const adData = {
            type: params.type,
            profession: params.profession,
            phone: params.phone,
            // הנה הקסם: אם המחיר קיים תוסיף אותו, אם לא - אל תעשה כלום.
            // אין כאן שום "command.push" ושום פסיקים ידניים!
            ...(params.price && { [params.type === 'מחפש' ? 'max_payment' : 'min_payment']: params.price }),
            ...(params.ringMode && { ring_mode: params.ringMode }),
            ...(params.path && { recording_path: params.path })
        };
        
        // וזהו. שולחים את זה לפונקציית ה-DB.
        // היא כבר תדע לבד לייצר מזה INSERT תקין, לא משנה איזה שדות יש או אין.
        await insertToDb('ads', adData); 
        

        למה זה קריטי?
        בקוד שלך, אם אתה שוכח command.push(", ...") (פסיק אחד!), השאילתה נשברת.
        בקוד שלי, הפונקציה הגנרית (insertToDb) לוקחת את המפתחות (Object.keys) ושמה פסיקים לבד. אי אפשר לטעות בתחביר.

        2. "השאיר המון שאילתות" – אתה לא קראת את הקוד

        כתבת: "הוא השאיר הכל אותו הדבר. כולל המון שאילתות למסד נתונים."
        זו טעות עובדתית.

        בקוד שלך יש לולאת for שמריצה INSERT בנפרד לכל עיר:

        // הקוד שלך - 20 ערים = 20 פניות לשרת ה-DB
        for (const city of params.city) {
           await connection.execute(...) 
        }
        

        בקוד שהצעתי (בפסקה על Bulk Insert) יש פקודה אחת שנשלחת למסד:

        // הקוד המקצועי - 20 ערים = פנייה אחת בלבד!
        const values = params.city.map(c => [adId, c]);
        await connection.query('INSERT INTO ad_cities (ad_id, city) VALUES ?', [values]);
        

        ההבדל בביצועים הוא עצום. אם יש לך 100 משתמשים בשנייה, הקוד שלך יפיל את השרת, הקוד שלי אפילו לא ירגיש את העומס.

        3. ה-"Validation" הוא לא הבעיה, ה-Coupling הוא הבעיה

        אתה אומר: "אני לא יכול לזרוק פרמטרים למסד... צריך לבדוק שהם נכונים".
        נכון מאוד. אבל הקוד שלך מערבב את בדיקת התקינות עם בניית השאילתה.

        בקוד שלך:

        if (checkPhone(p.phone)) { 
            command.push(", phone"); // <--- למה הבדיקה יודעת איך בונים SQL?!
        }
        

        אם מחר תחליט לעבור מ-MySQL ל-MongoDB (שאין בו פסיקים ושאילתות טקסטואליות), תצטרך למחוק את כל הלוגיקה העסקית ולכתוב מחדש.
        בקוד שלי, ה-Validator בודק, וה-Repository שומר. אם מחליפים מסד נתונים, משנים רק את פונקציית השמירה. הלוגיקה נשארת.

        לסיכום - למה הוא צריך להקשיב לזה?

        הוא בונה "מכונת רוב גולדברג" – מנגנון מסובך, שביר ומסוכן שעושה פעולה פשוטה.

        1. אבטחה: בניית שאילתות ידנית (push למערך סטרינגים) היא הזמנה לצרות, גם אם כרגע זה נראה בטוח.
        2. ביצועים: הלולאה על הערים היא צוואר בקבוק ודאי.
        3. תחזוקה: הקוד הזה קשה לקריאה וקשה לשינוי.

        תגיד לו: "זה שזה עובד לך במחשב, לא אומר שזה קוד. זה סקריפט. קוד צריך להיות יציב, מהיר וקריא. כרגע אין לך אף אחד מהשלושה."

        ע מנותק
        ע מנותק
        עידו300
        כתב נערך לאחרונה על ידי עידו300
        #32

        @A0533057932 טוב, עשיתי שינויים לפי ההמלצות שלכם כאן (השתדלתי לא לקחת את הקוד המוכן של הAI חוץ מבניית השאילתא), יצא לי משהו מסובך...

        מערך פרמטרים ופונקציות אימות

        const validetFunctions = {
            phone: isValidIsraeliPhone,
            type: checkType,
            profession: checkProfession,
            min_max_price: checkNumber,
            ringMode: checkRingMode,
            area: checkArea,
            city: checkCity
        };
        

        פונקציה מאמתת

        function validateAdParams(params) {
          let cleanParams = {};
              for (const key of Object.keys(params)) {
                const validateFunc = validetFunctions[key];
                if (!validateFunc) { return { status: "error", message:`Unknown parameter: ${key}` }; }
                
                if (!validateFunc(params[key])) { return { status: "error", message: `Invalid value for ${key}` }; }
        
                cleanParams[key] = params[key];
        
              };
          return { status: "success", data: cleanParams };
        }
        

        פונקציה שמכניסה למסד נתונים

        async function insertRecord(connection, tableName, dataObject) {
            const columns = Object.keys(dataObject);
            const values = Object.values(dataObject);
            const placeholders = columns.map(() => '?').join(', ');
            
            // בונה SQL באופן דינמי - אין יותר שרשור מחרוזות ידני עם פסיקים!
            const sql = `INSERT INTO ${tableName} (${columns.join(', ')}) VALUES (${placeholders})`;
            
            return connection.execute(sql, values);
        }
        

        הפונקציה שמטפלת בהכל

        export async function addAdNEw(params) {
         
          // בדיקת הפרמטרים והכנת אובייקט נקי להוספה למסד הנתונים
          const validateResult = validateAdParams(params);
         if (validateResult.status === "error") {
           return validateResult;
         }
        
        //  הכנת אובייקט להוספה למסד הנתונים
         let adToAdd = {
          phone: validateResult.data.phone,
          type: validateResult.data.type,
          profession: validateResult.data.profession,
         }
        
         if (validateResult.data.min_max_price) {adToAdd.min_max_price = validateResult.data.min_max_price;}
         if (validateResult.data.type === "מחפש" && validateResult.data.ringMode) {adToAdd.ringMode = validateResult.data.ringMode;}
        
            // הכנת רשימת הערים להכנסה
            let citiesToInsert = [];
        
            // אזור - לא חובה, אבל אם קיים חייב להיות תקין
            if (validateResult.data.area) {
              if (checkArea(validateResult.data.area)) {
                citiesToInsert.push(validateResult.data.area);
              }
              else { return { status: "error", message: "Invalid area" }; }
              }
        
            // עיר - לא חובה, אבל אם קיים חייב להיות תקין 
            else if (validateResult.data.city) {
              if (Array.isArray(validateResult.data.city)) {
                for (const city of validateResult.data.city) {
                  if (checkCity(city)) {
                    citiesToInsert.push(city);
                  } 
                  else { return { status: "error", message: `Invalid city: ${city}` }; }
                }
              } 
              // אם לא נבחר אזור ולא עיר, נכניס "all" כדי לסמן שהמודעה תקפה לכל הערים
              } else { citiesToInsert.push("all"); }
        
              // הוספת המודעה למסד הנתונים
               let connection;
              try {
                connection = await pool.getConnection(); // לוקחים חיבור מהמאגר
                await connection.beginTransaction();
                const [result] = await insertRecord(connection, 'ads', adToAdd);
                const adId = result.insertId;
        
                citiesToInsert = citiesToInsert.map(city => [adId, city]);
                await insertRecord(connection, 'ad_cities', citiesToInsert);
                await connection.commit();
                return { status: "success", message: "Ad added successfully", adId: adId };
            } catch (error) {
              if (connection) {
                console.log("Error adding ad:", error);
                await connection.rollback();
                throw error;
              }} 
              finally {
                if (connection) {
                  connection.release();
                }
              }
            }
        
        

        אבל עכשיו מסתבכים עם הוספת הערים לטבלת הערים, כי בשביל למנוע יצירת כמה שאילתות השתמשתי ב insert עם מערך שמכניס הכל בבת אחת, והפונקציה הגנארית insertRecord לא מתאימה לזה. צריך לעשות עוד פונקציה בשביל זה?

        תגובה 1 תגובה אחרונה
        0
        • A0533057932A A0533057932

          @עידו300


          אתה טועה, ובגדול.
          ההבדל בין הקוד שלך לבין מה שהראיתי הוא לא "קוסמטיקה", הוא ההבדל בין לבנות בניין מלבנים לבין לשפוך בטון לתוך בור ולקוות לטוב.

          אתה טוען שה-AI "התעלם" מפרמטרים אופציונליים? הוא לא התעלם, הוא פשוט טיפל בהם כמו מתכנת, ולא כמו מדביק-טפטים.

          בוא נפרק את ה"שטויות" שלך לגורמים:

          1. האשליה האופטית של ה-IF (למה אתה עובד קשה סתם)

          אתה חושב שחייבים if בשביל כל שדה כדי להרכיב את השאילתה? לא נכון.
          ב-JavaScript מודרני (ES6+), אנחנו בונים אובייקטים בצורה דקלרטיבית. אנחנו לא משרשרים מחרוזות (push ו-join) כמו בשנות ה-90.

          תסתכל על זה. זה מטפל בפרמטרים אופציונליים בלי לגעת ב-SQL ובלי לבנות מערכים ידנית:

          // לוגיקה עסקית נקייה: בניית אובייקט מידע
          const adData = {
              type: params.type,
              profession: params.profession,
              phone: params.phone,
              // הנה הקסם: אם המחיר קיים תוסיף אותו, אם לא - אל תעשה כלום.
              // אין כאן שום "command.push" ושום פסיקים ידניים!
              ...(params.price && { [params.type === 'מחפש' ? 'max_payment' : 'min_payment']: params.price }),
              ...(params.ringMode && { ring_mode: params.ringMode }),
              ...(params.path && { recording_path: params.path })
          };
          
          // וזהו. שולחים את זה לפונקציית ה-DB.
          // היא כבר תדע לבד לייצר מזה INSERT תקין, לא משנה איזה שדות יש או אין.
          await insertToDb('ads', adData); 
          

          למה זה קריטי?
          בקוד שלך, אם אתה שוכח command.push(", ...") (פסיק אחד!), השאילתה נשברת.
          בקוד שלי, הפונקציה הגנרית (insertToDb) לוקחת את המפתחות (Object.keys) ושמה פסיקים לבד. אי אפשר לטעות בתחביר.

          2. "השאיר המון שאילתות" – אתה לא קראת את הקוד

          כתבת: "הוא השאיר הכל אותו הדבר. כולל המון שאילתות למסד נתונים."
          זו טעות עובדתית.

          בקוד שלך יש לולאת for שמריצה INSERT בנפרד לכל עיר:

          // הקוד שלך - 20 ערים = 20 פניות לשרת ה-DB
          for (const city of params.city) {
             await connection.execute(...) 
          }
          

          בקוד שהצעתי (בפסקה על Bulk Insert) יש פקודה אחת שנשלחת למסד:

          // הקוד המקצועי - 20 ערים = פנייה אחת בלבד!
          const values = params.city.map(c => [adId, c]);
          await connection.query('INSERT INTO ad_cities (ad_id, city) VALUES ?', [values]);
          

          ההבדל בביצועים הוא עצום. אם יש לך 100 משתמשים בשנייה, הקוד שלך יפיל את השרת, הקוד שלי אפילו לא ירגיש את העומס.

          3. ה-"Validation" הוא לא הבעיה, ה-Coupling הוא הבעיה

          אתה אומר: "אני לא יכול לזרוק פרמטרים למסד... צריך לבדוק שהם נכונים".
          נכון מאוד. אבל הקוד שלך מערבב את בדיקת התקינות עם בניית השאילתה.

          בקוד שלך:

          if (checkPhone(p.phone)) { 
              command.push(", phone"); // <--- למה הבדיקה יודעת איך בונים SQL?!
          }
          

          אם מחר תחליט לעבור מ-MySQL ל-MongoDB (שאין בו פסיקים ושאילתות טקסטואליות), תצטרך למחוק את כל הלוגיקה העסקית ולכתוב מחדש.
          בקוד שלי, ה-Validator בודק, וה-Repository שומר. אם מחליפים מסד נתונים, משנים רק את פונקציית השמירה. הלוגיקה נשארת.

          לסיכום - למה הוא צריך להקשיב לזה?

          הוא בונה "מכונת רוב גולדברג" – מנגנון מסובך, שביר ומסוכן שעושה פעולה פשוטה.

          1. אבטחה: בניית שאילתות ידנית (push למערך סטרינגים) היא הזמנה לצרות, גם אם כרגע זה נראה בטוח.
          2. ביצועים: הלולאה על הערים היא צוואר בקבוק ודאי.
          3. תחזוקה: הקוד הזה קשה לקריאה וקשה לשינוי.

          תגיד לו: "זה שזה עובד לך במחשב, לא אומר שזה קוד. זה סקריפט. קוד צריך להיות יציב, מהיר וקריא. כרגע אין לך אף אחד מהשלושה."

          ע מנותק
          ע מנותק
          עידו300
          כתב נערך לאחרונה על ידי
          #33

          @A0533057932 הוא משקר מעט... אין קשר בין הקוד שהוצג כאן לקוד הקודם...

          תגובה 1 תגובה אחרונה
          0
          • A0533057932A A0533057932

            @עידו300


            אתה טועה, ובגדול.
            ההבדל בין הקוד שלך לבין מה שהראיתי הוא לא "קוסמטיקה", הוא ההבדל בין לבנות בניין מלבנים לבין לשפוך בטון לתוך בור ולקוות לטוב.

            אתה טוען שה-AI "התעלם" מפרמטרים אופציונליים? הוא לא התעלם, הוא פשוט טיפל בהם כמו מתכנת, ולא כמו מדביק-טפטים.

            בוא נפרק את ה"שטויות" שלך לגורמים:

            1. האשליה האופטית של ה-IF (למה אתה עובד קשה סתם)

            אתה חושב שחייבים if בשביל כל שדה כדי להרכיב את השאילתה? לא נכון.
            ב-JavaScript מודרני (ES6+), אנחנו בונים אובייקטים בצורה דקלרטיבית. אנחנו לא משרשרים מחרוזות (push ו-join) כמו בשנות ה-90.

            תסתכל על זה. זה מטפל בפרמטרים אופציונליים בלי לגעת ב-SQL ובלי לבנות מערכים ידנית:

            // לוגיקה עסקית נקייה: בניית אובייקט מידע
            const adData = {
                type: params.type,
                profession: params.profession,
                phone: params.phone,
                // הנה הקסם: אם המחיר קיים תוסיף אותו, אם לא - אל תעשה כלום.
                // אין כאן שום "command.push" ושום פסיקים ידניים!
                ...(params.price && { [params.type === 'מחפש' ? 'max_payment' : 'min_payment']: params.price }),
                ...(params.ringMode && { ring_mode: params.ringMode }),
                ...(params.path && { recording_path: params.path })
            };
            
            // וזהו. שולחים את זה לפונקציית ה-DB.
            // היא כבר תדע לבד לייצר מזה INSERT תקין, לא משנה איזה שדות יש או אין.
            await insertToDb('ads', adData); 
            

            למה זה קריטי?
            בקוד שלך, אם אתה שוכח command.push(", ...") (פסיק אחד!), השאילתה נשברת.
            בקוד שלי, הפונקציה הגנרית (insertToDb) לוקחת את המפתחות (Object.keys) ושמה פסיקים לבד. אי אפשר לטעות בתחביר.

            2. "השאיר המון שאילתות" – אתה לא קראת את הקוד

            כתבת: "הוא השאיר הכל אותו הדבר. כולל המון שאילתות למסד נתונים."
            זו טעות עובדתית.

            בקוד שלך יש לולאת for שמריצה INSERT בנפרד לכל עיר:

            // הקוד שלך - 20 ערים = 20 פניות לשרת ה-DB
            for (const city of params.city) {
               await connection.execute(...) 
            }
            

            בקוד שהצעתי (בפסקה על Bulk Insert) יש פקודה אחת שנשלחת למסד:

            // הקוד המקצועי - 20 ערים = פנייה אחת בלבד!
            const values = params.city.map(c => [adId, c]);
            await connection.query('INSERT INTO ad_cities (ad_id, city) VALUES ?', [values]);
            

            ההבדל בביצועים הוא עצום. אם יש לך 100 משתמשים בשנייה, הקוד שלך יפיל את השרת, הקוד שלי אפילו לא ירגיש את העומס.

            3. ה-"Validation" הוא לא הבעיה, ה-Coupling הוא הבעיה

            אתה אומר: "אני לא יכול לזרוק פרמטרים למסד... צריך לבדוק שהם נכונים".
            נכון מאוד. אבל הקוד שלך מערבב את בדיקת התקינות עם בניית השאילתה.

            בקוד שלך:

            if (checkPhone(p.phone)) { 
                command.push(", phone"); // <--- למה הבדיקה יודעת איך בונים SQL?!
            }
            

            אם מחר תחליט לעבור מ-MySQL ל-MongoDB (שאין בו פסיקים ושאילתות טקסטואליות), תצטרך למחוק את כל הלוגיקה העסקית ולכתוב מחדש.
            בקוד שלי, ה-Validator בודק, וה-Repository שומר. אם מחליפים מסד נתונים, משנים רק את פונקציית השמירה. הלוגיקה נשארת.

            לסיכום - למה הוא צריך להקשיב לזה?

            הוא בונה "מכונת רוב גולדברג" – מנגנון מסובך, שביר ומסוכן שעושה פעולה פשוטה.

            1. אבטחה: בניית שאילתות ידנית (push למערך סטרינגים) היא הזמנה לצרות, גם אם כרגע זה נראה בטוח.
            2. ביצועים: הלולאה על הערים היא צוואר בקבוק ודאי.
            3. תחזוקה: הקוד הזה קשה לקריאה וקשה לשינוי.

            תגיד לו: "זה שזה עובד לך במחשב, לא אומר שזה קוד. זה סקריפט. קוד צריך להיות יציב, מהיר וקריא. כרגע אין לך אף אחד מהשלושה."

            ע מנותק
            ע מנותק
            עידו300
            כתב נערך לאחרונה על ידי
            #34
            פוסט זה נמחק!
            תגובה 1 תגובה אחרונה
            0
            • ע מנותק
              ע מנותק
              עידו300
              כתב נערך לאחרונה על ידי עידו300
              #35

              @a0533057932
              אחרי הרבה עבודה, מקוה שזו הגירסה האחרונה:

              // סכימה לאימות פרמטרים של מודעה חדשה
              const adValidationSchema = {
                  phone: {validate: isValidIsraeliPhone, required: true},
                  type: {validate: checkType, required: true},
                  profession: {validate: checkProfession, required: true},
                  min_max_price: {validate: checkNumber, required: false},
                  ringMode: {validate: checkRingMode, required: false},
                  area: {validate: checkArea, required: false},
                  city: {validate: checkCity, required: false},
                  path: {validate: (path) => typeof path === 'string' && path.trim() !== '', required: false}
              };
              
              // פונקציה לאימות פרמטרים של מודעה חדשה והכנת אובייקט נקי להוספה למסד הנתונים
              function validateAdParams(params) {
              
                    //  הגנה מפני קלט ריק
                  if (!params || typeof params !== 'object') {
                      return { status: "error", message: "Invalid parameters input" };
                  }
              
                let cleanParams = {};
              
                // בדיקה שכל הפרמטרים שהועברו קיימים בסכימה ותקינים
                for (const [key, value] of Object.entries(params)) {
              
                  if (!(key in adValidationSchema) ) {return { status: "error", message: `Unknown parameter: ${key}` };}
              
                  const validateFunc = adValidationSchema[key].validate;
              
                  if (!validateFunc(value)) { return { status: "error", message: `Invalid value for ${key}` }; }
              
                  cleanParams[key] = value;
                }
              
                // בדיקה שכל הפרמטרים החיוניים קיימים
                for (const [key, field] of Object.entries(adValidationSchema)) {
                  if (field.required && !(key in params)) {
                    return { status: "error", message: `Missing required parameter: ${key}` };
                  }
                }
              
                return { status: "success", data: cleanParams };
              }
              
                  /**
               * Inserts one or multiple records into a table.
               * @param {object} connection - חיבור MySQL פעיל
               * @param {string} tableName - שם הטבלה (מוגן)
               * @param {object|object[]} data - אובייקט אחד או מערך של אובייקטים
               */
              async function insertRecord(connection, tableName, data) {
                  // הגנה בסיסית נגד SQL injection
                  const allowedTables = ['ads', 'ad_cities'];
                  if (!allowedTables.includes(tableName)) {
                      throw new Error("Invalid table name");
                  }
              
                  // אם מדובר באובייקט יחיד → עטוף במערך
                  const records = Array.isArray(data) ? data : [data];
              
                  if (records.length === 0) return;
              
                  // משתמשים במפתחות מהאובייקט הראשון
                  const columns = Object.keys(records[0]);
                  const placeholders = records.map(() => `(${columns.map(() => '?').join(', ')})`).join(', ');
              
                  // Flatten לכל הערכים בסדר של השדות
                  const values = records.flatMap(record => columns.map(col => record[col]));
              
                  const sql = `INSERT INTO ${tableName} (${columns.join(', ')}) VALUES ${placeholders}`;
              
                  return connection.execute(sql, values);
              }
              
              
              function createAdObject(params) {
              
                // בדיקת הפרמטרים והכנת אובייקט נקי להוספה למסד הנתונים
                const validateResult = validateAdParams(params);
              
                if (validateResult.status === "error") {
                 return validateResult;
               }
              
              //  הכנת אובייקט להוספה למסד הנתונים
               let adToAdd = {
                phone: validateResult.data.phone,
                type: validateResult.data.type,
                profession: validateResult.data.profession,
                ...(validateResult.data.path && { 'recording_path': validateResult.data.path }),
                ...(validateResult.data.min_max_price && { 'min_max_price': validateResult.data.min_max_price }),
                ...((validateResult.data.type === "מחפש" && validateResult.data.ringMode) && { 'ring_mode': validateResult.data.ringMode })
               }
              
               return { status: "success", data: adToAdd, cities: validateResult.data.area || validateResult.data.city || ["all"] };
              }
              
              export async function addAdNEw(params) { 
              
                const createResult = createAdObject(params);
                if (createResult.status === "error") {
                  return createResult;
                }
              
                    // הוספת המודעה למסד הנתונים
                     let connection;
                    try {
                      connection = await pool.getConnection(); // לוקחים חיבור מהמאגר
                      await connection.beginTransaction();
                      const [result] = await insertRecord(connection, 'ads', createResult.data);
                      const adId = result.insertId;
              
                      citiesToInsert = createResult.cities.map(city => ({ad_id: adId, city}));
              
                      await insertRecord(connection, 'ad_cities', citiesToInsert);
                      await connection.commit();
                      return { status: "success", message: "Ad added successfully", adId: adId };
                  } catch (error) {
                    if (connection) {
                      console.log("Error adding ad:", error);
                      await connection.rollback();
                      throw error;
                    }} 
                    finally {
                      if (connection) {
                        connection.release();
                      }
                    }
                  }
              
              A0533057932A תגובה 1 תגובה אחרונה
              0
              • ע עידו300

                @a0533057932
                אחרי הרבה עבודה, מקוה שזו הגירסה האחרונה:

                // סכימה לאימות פרמטרים של מודעה חדשה
                const adValidationSchema = {
                    phone: {validate: isValidIsraeliPhone, required: true},
                    type: {validate: checkType, required: true},
                    profession: {validate: checkProfession, required: true},
                    min_max_price: {validate: checkNumber, required: false},
                    ringMode: {validate: checkRingMode, required: false},
                    area: {validate: checkArea, required: false},
                    city: {validate: checkCity, required: false},
                    path: {validate: (path) => typeof path === 'string' && path.trim() !== '', required: false}
                };
                
                // פונקציה לאימות פרמטרים של מודעה חדשה והכנת אובייקט נקי להוספה למסד הנתונים
                function validateAdParams(params) {
                
                      //  הגנה מפני קלט ריק
                    if (!params || typeof params !== 'object') {
                        return { status: "error", message: "Invalid parameters input" };
                    }
                
                  let cleanParams = {};
                
                  // בדיקה שכל הפרמטרים שהועברו קיימים בסכימה ותקינים
                  for (const [key, value] of Object.entries(params)) {
                
                    if (!(key in adValidationSchema) ) {return { status: "error", message: `Unknown parameter: ${key}` };}
                
                    const validateFunc = adValidationSchema[key].validate;
                
                    if (!validateFunc(value)) { return { status: "error", message: `Invalid value for ${key}` }; }
                
                    cleanParams[key] = value;
                  }
                
                  // בדיקה שכל הפרמטרים החיוניים קיימים
                  for (const [key, field] of Object.entries(adValidationSchema)) {
                    if (field.required && !(key in params)) {
                      return { status: "error", message: `Missing required parameter: ${key}` };
                    }
                  }
                
                  return { status: "success", data: cleanParams };
                }
                
                    /**
                 * Inserts one or multiple records into a table.
                 * @param {object} connection - חיבור MySQL פעיל
                 * @param {string} tableName - שם הטבלה (מוגן)
                 * @param {object|object[]} data - אובייקט אחד או מערך של אובייקטים
                 */
                async function insertRecord(connection, tableName, data) {
                    // הגנה בסיסית נגד SQL injection
                    const allowedTables = ['ads', 'ad_cities'];
                    if (!allowedTables.includes(tableName)) {
                        throw new Error("Invalid table name");
                    }
                
                    // אם מדובר באובייקט יחיד → עטוף במערך
                    const records = Array.isArray(data) ? data : [data];
                
                    if (records.length === 0) return;
                
                    // משתמשים במפתחות מהאובייקט הראשון
                    const columns = Object.keys(records[0]);
                    const placeholders = records.map(() => `(${columns.map(() => '?').join(', ')})`).join(', ');
                
                    // Flatten לכל הערכים בסדר של השדות
                    const values = records.flatMap(record => columns.map(col => record[col]));
                
                    const sql = `INSERT INTO ${tableName} (${columns.join(', ')}) VALUES ${placeholders}`;
                
                    return connection.execute(sql, values);
                }
                
                
                function createAdObject(params) {
                
                  // בדיקת הפרמטרים והכנת אובייקט נקי להוספה למסד הנתונים
                  const validateResult = validateAdParams(params);
                
                  if (validateResult.status === "error") {
                   return validateResult;
                 }
                
                //  הכנת אובייקט להוספה למסד הנתונים
                 let adToAdd = {
                  phone: validateResult.data.phone,
                  type: validateResult.data.type,
                  profession: validateResult.data.profession,
                  ...(validateResult.data.path && { 'recording_path': validateResult.data.path }),
                  ...(validateResult.data.min_max_price && { 'min_max_price': validateResult.data.min_max_price }),
                  ...((validateResult.data.type === "מחפש" && validateResult.data.ringMode) && { 'ring_mode': validateResult.data.ringMode })
                 }
                
                 return { status: "success", data: adToAdd, cities: validateResult.data.area || validateResult.data.city || ["all"] };
                }
                
                export async function addAdNEw(params) { 
                
                  const createResult = createAdObject(params);
                  if (createResult.status === "error") {
                    return createResult;
                  }
                
                      // הוספת המודעה למסד הנתונים
                       let connection;
                      try {
                        connection = await pool.getConnection(); // לוקחים חיבור מהמאגר
                        await connection.beginTransaction();
                        const [result] = await insertRecord(connection, 'ads', createResult.data);
                        const adId = result.insertId;
                
                        citiesToInsert = createResult.cities.map(city => ({ad_id: adId, city}));
                
                        await insertRecord(connection, 'ad_cities', citiesToInsert);
                        await connection.commit();
                        return { status: "success", message: "Ad added successfully", adId: adId };
                    } catch (error) {
                      if (connection) {
                        console.log("Error adding ad:", error);
                        await connection.rollback();
                        throw error;
                      }} 
                      finally {
                        if (connection) {
                          connection.release();
                        }
                      }
                    }
                
                A0533057932A מנותק
                A0533057932A מנותק
                A0533057932
                כתב נערך לאחרונה על ידי
                #36

                @עידו300


                זה בהחלט שיפור. עברת משלב "גן חובה" לכיתה א'.
                הכנסת try/catch/finally כמו שצריך, יש לך טרנזקציה, ויש ניסיון לייצר סכימה. כל הכבוד.

                אבל...
                הקוד הזה עדיין יקרוס (Crash), הוא מכיל באג לוגי חמור מול הדרישות המקוריות, והוא חושף אותך לסיכון SQL Injection ברמת התכנון.

                בוא ננתח למה ה"שיפור" הזה עדיין לא עובר Code Review אצלי:

                1. קריסה מובטחת (Runtime Error) 💥

                תסתכל על השורה הזו ב-createAdObject:

                cities: validateResult.data.area || validateResult.data.city || ["all"]
                

                אם המשתמש שלח area (שהוא מחרוזת, למשל "Tel Aviv"), אז createResult.cities יהיה שווה ל-"Tel Aviv".
                ואז ב-addAdNEw אתה עושה:

                citiesToInsert = createResult.cities.map(city => ...)
                

                בום. לפונקציה map אין קיום על String (בצורה הזו). הקוד יזרוק TypeError והבקשה תיפול.
                מתכנת רציני מוודא שתמיד חוזר מערך (Array), גם אם יש איבר אחד.

                2. משתנה גלובלי דולף (Strict Mode Violation) 🛑

                citiesToInsert = createResult.cities.map(...)
                

                איפה ה-const? איפה ה-let?
                במודולים של JS (שבהם export קיים), הקוד רץ אוטומטית ב-Strict Mode.
                שימוש במשתנה ללא הצהרה יזרוק ReferenceError: citiesToInsert is not defined. השרת שלך נפל שוב.

                3. התעלמות מהדרישות העסקיות (Business Logic Regression) 📉

                זוכר את הקוד המקורי?

                • אם זה "מחפש" -> העמודה היא max_payment.
                • אם זה "מפרסם" -> העמודה היא min_payment.

                בקוד החדש החלטת על דעת עצמך:

                ...(validateResult.data.min_max_price && { 'min_max_price': ... })
                

                אתה מנסה לדחוף לעמודה בשם min_max_price. העמודה הזו לא קיימת במסד הנתונים! ה-SQL יחזיר שגיאה: Unknown column 'min_max_price'.
                שיפרת את המבנה, אבל הרסת את הלוגיקה. קוד יפה שלא עובד הוא חסר ערך.

                4. פרצת אבטחה ב-insertRecord (SQL Injection via Design) 🔓

                אתה כותב פונקציה גנרית יפה:

                const columns = Object.keys(records[0]);
                const sql = `INSERT INTO ${tableName} (${columns.join(', ')}) ...`
                

                כרגע, במקרה, האובייקט שאתה שולח נוצר ידנית ב-createAdObject.
                אבל הפונקציה insertRecord היא גנרית. מחר מתכנת זוטר ישתמש בה וישלח אובייקט req.body ישירות מהמשתמש.
                אם המשתמש ישלח מפתח בשם id) VALUES (1); DROP TABLE ads; --, והפונקציה שלך משרשרת את המפתחות ישר ל-SQL... הלך ה-DB.
                כלל ברזל: לעולם אל תסמוך על מפתחות של אובייקטים (Object Keys) כשמות עמודות ב-SQL ללא בדיקה מול רשימה מותרת (Allowlist) בתוך הפונקציה הגנרית.

                5. צימוד (Coupling) בין שם המשתנה לשם העמודה

                בסכמה שלך:

                min_max_price: {validate: checkNumber, required: false},
                

                אבל ב-DB ובקלט המקורי זה נקרא price.
                היצרת ערבוב: הפונקציה שלך מצפה לקלט בשם min_max_price מהלקוח? או שהלקוח שולח price ואתה ממפה את זה? הקוד שלך מניח שהלקוח שולח min_max_price. שינית את ה-API בלי לשים לב.


                התיקון (כדי שזה באמת יעבוד)

                הנה איך הקוד צריך להיראות כדי לפתור את כל הבעיות הנ"ל, תוך שמירה על המבנה הנקי שניסית ליצור:

                // 1. מיפוי ברור בין שדות ה-API לעמודות ה-DB
                const FIELD_MAPPING = {
                    phone: 'phone',
                    type: 'type',
                    profession: 'profession',
                    path: 'recording_path',
                    // לוגיקה דינמית תטופל בקוד
                };
                
                function createAdObject(params) {
                    const validateResult = validateAdParams(params); // נניח שזה תוקן ומחזיר price
                    if (validateResult.status === "error") return validateResult;
                
                    const data = validateResult.data;
                    
                    // בניית האובייקט ל-DB עם שמות העמודות הנכונים
                    let dbRecord = {
                        phone: data.phone,
                        type: data.type,
                        profession: data.profession,
                    };
                
                    if (data.path) dbRecord.recording_path = data.path;
                
                    // 2. תיקון הלוגיקה העסקית (שמות עמודות דינמיים)
                    // הערה: אני מניח שהלקוח שלח 'price', כפי שהיה במקור
                    if (data.price) {
                        const priceCol = (data.type === "מחפש") ? "max_payment" : "min_payment";
                        dbRecord[priceCol] = data.price;
                    }
                
                    if (data.type === "מחפש" && data.ringMode) {
                        dbRecord.ring_mode = data.ringMode;
                    }
                
                    // 3. תיקון הבאג של המערך (Normalization)
                    let cities = ["all"];
                    if (data.area) {
                        cities = [data.area]; // עטיפה במערך!
                    } else if (Array.isArray(data.city)) {
                        cities = data.city;
                    }
                
                    return { status: "success", data: dbRecord, cities };
                }
                
                async function insertRecord(connection, tableName, data) {
                    const allowedTables = ['ads', 'ad_cities'];
                    if (!allowedTables.includes(tableName)) throw new Error("Invalid table name");
                
                    const records = Array.isArray(data) ? data : [data];
                    if (records.length === 0) return;
                
                    // 4. אבטחה: בדיקה שהעמודות הן חוקיות (White-listing)
                    // במערכת אמיתית, היינו מחזיקים רשימת עמודות מותרות לכל טבלה
                    const validColumns = ['phone', 'type', 'profession', 'recording_path', 'max_payment', 'min_payment', 'ring_mode', 'ad_id', 'city'];
                    
                    const inputColumns = Object.keys(records[0]);
                    // מוודאים שכל העמודות שהתקבלו קיימות ברשימה המותרת
                    const invalidCols = inputColumns.filter(col => !validColumns.includes(col));
                    if (invalidCols.length > 0) throw new Error(`Invalid columns: ${invalidCols.join(',')}`);
                
                    // המשך הקוד הרגיל...
                    const placeholders = records.map(() => `(${inputColumns.map(() => '?').join(', ')})`).join(', ');
                    const values = records.flatMap(record => inputColumns.map(col => record[col]));
                    
                    // כאן זה כבר בטוח כי וידאנו שהעמודות ברשימה המותרת
                    const sql = `INSERT INTO ${tableName} (${inputColumns.join(', ')}) VALUES ${placeholders}`;
                    return connection.execute(sql, values);
                }
                
                export async function addAdNEw(params) { 
                    const createResult = createAdObject(params);
                    if (createResult.status === "error") return createResult;
                
                    let connection;
                    try {
                        connection = await pool.getConnection();
                        await connection.beginTransaction();
                
                        const [result] = await insertRecord(connection, 'ads', createResult.data);
                        const adId = result.insertId;
                
                        // 5. תיקון ה-ReferenceError
                        const citiesToInsert = createResult.cities.map(city => ({ad_id: adId, city}));
                
                        await insertRecord(connection, 'ad_cities', citiesToInsert);
                        await connection.commit();
                        
                        return { status: "success", message: "Ad added successfully", adId };
                    } catch (error) {
                        if (connection) await connection.rollback(); // שים לב: רצוי לא לזרוק שגיאה החוצה בלי לוג או טיפול
                        console.error("DB Transaction Error:", error);
                        return { status: "error", message: "Database error" };
                    } finally {
                        if (connection) connection.release();
                    }
                }
                

                סיכום:
                החבר שלך בכיוון הנכון מבחינת מבנה (Structure), אבל הוא נופל בפרטים הקטנים והקטלניים (Details). תגיד לו שאנקל בוב אומר: "God is in the details". קוד שלא מתחשב במקרי קצה (כמו מחרוזת במקום מערך) הוא לא קוד מקצועי.

                ע תגובה 1 תגובה אחרונה
                0
                • A0533057932A A0533057932

                  @עידו300


                  זה בהחלט שיפור. עברת משלב "גן חובה" לכיתה א'.
                  הכנסת try/catch/finally כמו שצריך, יש לך טרנזקציה, ויש ניסיון לייצר סכימה. כל הכבוד.

                  אבל...
                  הקוד הזה עדיין יקרוס (Crash), הוא מכיל באג לוגי חמור מול הדרישות המקוריות, והוא חושף אותך לסיכון SQL Injection ברמת התכנון.

                  בוא ננתח למה ה"שיפור" הזה עדיין לא עובר Code Review אצלי:

                  1. קריסה מובטחת (Runtime Error) 💥

                  תסתכל על השורה הזו ב-createAdObject:

                  cities: validateResult.data.area || validateResult.data.city || ["all"]
                  

                  אם המשתמש שלח area (שהוא מחרוזת, למשל "Tel Aviv"), אז createResult.cities יהיה שווה ל-"Tel Aviv".
                  ואז ב-addAdNEw אתה עושה:

                  citiesToInsert = createResult.cities.map(city => ...)
                  

                  בום. לפונקציה map אין קיום על String (בצורה הזו). הקוד יזרוק TypeError והבקשה תיפול.
                  מתכנת רציני מוודא שתמיד חוזר מערך (Array), גם אם יש איבר אחד.

                  2. משתנה גלובלי דולף (Strict Mode Violation) 🛑

                  citiesToInsert = createResult.cities.map(...)
                  

                  איפה ה-const? איפה ה-let?
                  במודולים של JS (שבהם export קיים), הקוד רץ אוטומטית ב-Strict Mode.
                  שימוש במשתנה ללא הצהרה יזרוק ReferenceError: citiesToInsert is not defined. השרת שלך נפל שוב.

                  3. התעלמות מהדרישות העסקיות (Business Logic Regression) 📉

                  זוכר את הקוד המקורי?

                  • אם זה "מחפש" -> העמודה היא max_payment.
                  • אם זה "מפרסם" -> העמודה היא min_payment.

                  בקוד החדש החלטת על דעת עצמך:

                  ...(validateResult.data.min_max_price && { 'min_max_price': ... })
                  

                  אתה מנסה לדחוף לעמודה בשם min_max_price. העמודה הזו לא קיימת במסד הנתונים! ה-SQL יחזיר שגיאה: Unknown column 'min_max_price'.
                  שיפרת את המבנה, אבל הרסת את הלוגיקה. קוד יפה שלא עובד הוא חסר ערך.

                  4. פרצת אבטחה ב-insertRecord (SQL Injection via Design) 🔓

                  אתה כותב פונקציה גנרית יפה:

                  const columns = Object.keys(records[0]);
                  const sql = `INSERT INTO ${tableName} (${columns.join(', ')}) ...`
                  

                  כרגע, במקרה, האובייקט שאתה שולח נוצר ידנית ב-createAdObject.
                  אבל הפונקציה insertRecord היא גנרית. מחר מתכנת זוטר ישתמש בה וישלח אובייקט req.body ישירות מהמשתמש.
                  אם המשתמש ישלח מפתח בשם id) VALUES (1); DROP TABLE ads; --, והפונקציה שלך משרשרת את המפתחות ישר ל-SQL... הלך ה-DB.
                  כלל ברזל: לעולם אל תסמוך על מפתחות של אובייקטים (Object Keys) כשמות עמודות ב-SQL ללא בדיקה מול רשימה מותרת (Allowlist) בתוך הפונקציה הגנרית.

                  5. צימוד (Coupling) בין שם המשתנה לשם העמודה

                  בסכמה שלך:

                  min_max_price: {validate: checkNumber, required: false},
                  

                  אבל ב-DB ובקלט המקורי זה נקרא price.
                  היצרת ערבוב: הפונקציה שלך מצפה לקלט בשם min_max_price מהלקוח? או שהלקוח שולח price ואתה ממפה את זה? הקוד שלך מניח שהלקוח שולח min_max_price. שינית את ה-API בלי לשים לב.


                  התיקון (כדי שזה באמת יעבוד)

                  הנה איך הקוד צריך להיראות כדי לפתור את כל הבעיות הנ"ל, תוך שמירה על המבנה הנקי שניסית ליצור:

                  // 1. מיפוי ברור בין שדות ה-API לעמודות ה-DB
                  const FIELD_MAPPING = {
                      phone: 'phone',
                      type: 'type',
                      profession: 'profession',
                      path: 'recording_path',
                      // לוגיקה דינמית תטופל בקוד
                  };
                  
                  function createAdObject(params) {
                      const validateResult = validateAdParams(params); // נניח שזה תוקן ומחזיר price
                      if (validateResult.status === "error") return validateResult;
                  
                      const data = validateResult.data;
                      
                      // בניית האובייקט ל-DB עם שמות העמודות הנכונים
                      let dbRecord = {
                          phone: data.phone,
                          type: data.type,
                          profession: data.profession,
                      };
                  
                      if (data.path) dbRecord.recording_path = data.path;
                  
                      // 2. תיקון הלוגיקה העסקית (שמות עמודות דינמיים)
                      // הערה: אני מניח שהלקוח שלח 'price', כפי שהיה במקור
                      if (data.price) {
                          const priceCol = (data.type === "מחפש") ? "max_payment" : "min_payment";
                          dbRecord[priceCol] = data.price;
                      }
                  
                      if (data.type === "מחפש" && data.ringMode) {
                          dbRecord.ring_mode = data.ringMode;
                      }
                  
                      // 3. תיקון הבאג של המערך (Normalization)
                      let cities = ["all"];
                      if (data.area) {
                          cities = [data.area]; // עטיפה במערך!
                      } else if (Array.isArray(data.city)) {
                          cities = data.city;
                      }
                  
                      return { status: "success", data: dbRecord, cities };
                  }
                  
                  async function insertRecord(connection, tableName, data) {
                      const allowedTables = ['ads', 'ad_cities'];
                      if (!allowedTables.includes(tableName)) throw new Error("Invalid table name");
                  
                      const records = Array.isArray(data) ? data : [data];
                      if (records.length === 0) return;
                  
                      // 4. אבטחה: בדיקה שהעמודות הן חוקיות (White-listing)
                      // במערכת אמיתית, היינו מחזיקים רשימת עמודות מותרות לכל טבלה
                      const validColumns = ['phone', 'type', 'profession', 'recording_path', 'max_payment', 'min_payment', 'ring_mode', 'ad_id', 'city'];
                      
                      const inputColumns = Object.keys(records[0]);
                      // מוודאים שכל העמודות שהתקבלו קיימות ברשימה המותרת
                      const invalidCols = inputColumns.filter(col => !validColumns.includes(col));
                      if (invalidCols.length > 0) throw new Error(`Invalid columns: ${invalidCols.join(',')}`);
                  
                      // המשך הקוד הרגיל...
                      const placeholders = records.map(() => `(${inputColumns.map(() => '?').join(', ')})`).join(', ');
                      const values = records.flatMap(record => inputColumns.map(col => record[col]));
                      
                      // כאן זה כבר בטוח כי וידאנו שהעמודות ברשימה המותרת
                      const sql = `INSERT INTO ${tableName} (${inputColumns.join(', ')}) VALUES ${placeholders}`;
                      return connection.execute(sql, values);
                  }
                  
                  export async function addAdNEw(params) { 
                      const createResult = createAdObject(params);
                      if (createResult.status === "error") return createResult;
                  
                      let connection;
                      try {
                          connection = await pool.getConnection();
                          await connection.beginTransaction();
                  
                          const [result] = await insertRecord(connection, 'ads', createResult.data);
                          const adId = result.insertId;
                  
                          // 5. תיקון ה-ReferenceError
                          const citiesToInsert = createResult.cities.map(city => ({ad_id: adId, city}));
                  
                          await insertRecord(connection, 'ad_cities', citiesToInsert);
                          await connection.commit();
                          
                          return { status: "success", message: "Ad added successfully", adId };
                      } catch (error) {
                          if (connection) await connection.rollback(); // שים לב: רצוי לא לזרוק שגיאה החוצה בלי לוג או טיפול
                          console.error("DB Transaction Error:", error);
                          return { status: "error", message: "Database error" };
                      } finally {
                          if (connection) connection.release();
                      }
                  }
                  

                  סיכום:
                  החבר שלך בכיוון הנכון מבחינת מבנה (Structure), אבל הוא נופל בפרטים הקטנים והקטלניים (Details). תגיד לו שאנקל בוב אומר: "God is in the details". קוד שלא מתחשב במקרי קצה (כמו מחרוזת במקום מערך) הוא לא קוד מקצועי.

                  ע מנותק
                  ע מנותק
                  עידו300
                  כתב נערך לאחרונה על ידי עידו300
                  #37

                  @A0533057932 אחח איך אני אוהב את העוקצניות שלו, אני מחייך לעצמי כל פעם מחדש.

                  לענין:

                  1. פונקצית הולידציה מונעת את זה.

                  export function checkArea(area) {
                  
                    if (!area) {return false;}
                  
                    const areasToCheck = Array.isArray(area) ? area : [area];
                  
                     if (areasToCheck.length === 0) return false;
                  
                      for (const a of areasToCheck) {
                        if (typeof a !== 'string' || !Object.hasOwn(areas, a)) {return false;}
                      }
                    return  true;
                  }
                  

                  טעות, שיניתי את זה

                  let cities = (Array.isArray(validateResult.data.area) ? validateResult.data.area : [validateResult.data.area]) || (Array.isArray(validateResult.data.city) ? validateResult.data.city : [validateResult.data.city]) || ["all"]
                   return { status: "success", data: adToAdd, cities };
                  

                  2 ו- 5. צריך להבהיר לו ששיניתי את הקונספט של min/max ומהיום (אתמול למעשה) זה min_max_price.

                  1. בהחלט טעות... 😥

                  2. סמכתי על הקוד שלו... שיניתי עכשיו.

                  זהו? עכשיו הכל טוב?

                  אגב, למה הוא מתעקש להפריד את זה

                  let adToAdd = {
                    phone: validateResult.data.phone,
                    type: validateResult.data.type,
                    profession: validateResult.data.profession,
                    ...(validateResult.data.path && { 'recording_path': validateResult.data.path }),
                    ...(validateResult.data.min_max_price && { 'min_max_price': validateResult.data.min_max_price }),
                    ...((validateResult.data.type === "מחפש" && validateResult.data.ringMode) && { 'ring_mode': validateResult.data.ringMode })
                   }
                  

                  לifים?

                  A0533057932A תגובה 1 תגובה אחרונה
                  0
                  • ע עידו300

                    @A0533057932 אחח איך אני אוהב את העוקצניות שלו, אני מחייך לעצמי כל פעם מחדש.

                    לענין:

                    1. פונקצית הולידציה מונעת את זה.

                    export function checkArea(area) {
                    
                      if (!area) {return false;}
                    
                      const areasToCheck = Array.isArray(area) ? area : [area];
                    
                       if (areasToCheck.length === 0) return false;
                    
                        for (const a of areasToCheck) {
                          if (typeof a !== 'string' || !Object.hasOwn(areas, a)) {return false;}
                        }
                      return  true;
                    }
                    

                    טעות, שיניתי את זה

                    let cities = (Array.isArray(validateResult.data.area) ? validateResult.data.area : [validateResult.data.area]) || (Array.isArray(validateResult.data.city) ? validateResult.data.city : [validateResult.data.city]) || ["all"]
                     return { status: "success", data: adToAdd, cities };
                    

                    2 ו- 5. צריך להבהיר לו ששיניתי את הקונספט של min/max ומהיום (אתמול למעשה) זה min_max_price.

                    1. בהחלט טעות... 😥

                    2. סמכתי על הקוד שלו... שיניתי עכשיו.

                    זהו? עכשיו הכל טוב?

                    אגב, למה הוא מתעקש להפריד את זה

                    let adToAdd = {
                      phone: validateResult.data.phone,
                      type: validateResult.data.type,
                      profession: validateResult.data.profession,
                      ...(validateResult.data.path && { 'recording_path': validateResult.data.path }),
                      ...(validateResult.data.min_max_price && { 'min_max_price': validateResult.data.min_max_price }),
                      ...((validateResult.data.type === "מחפש" && validateResult.data.ringMode) && { 'ring_mode': validateResult.data.ringMode })
                     }
                    

                    לifים?

                    A0533057932A מנותק
                    A0533057932A מנותק
                    A0533057932
                    כתב נערך לאחרונה על ידי
                    #38

                    @עידו300 התשובה הקצרה לשאלה שלו ("זהו? עכשיו הכל טוב?"): ממש לא.
                    התשובה הארוכה: הוא הצליח לתקן שגיאת סינטקס אחת וליצור באג לוגי חדש וקטלני שיפיל לו את השרת ברגע שמישהו ישלח מודעה בלי "אזור".

                    הנה התשובה שאתה צריך לענות לו, מילה במילה, מהמומחה:


                    שלום עידו.
                    שאלת "למה הוא מתעקש להפריד את זה ל-ifים?" ו"האם עכשיו הכל טוב?".
                    בוא נתחיל מהסוף: לא, הכל לא טוב. למעשה, ה"תיקון" שלך בסעיף 1 יגרום לקריסה.

                    הנה הסיבות למה הקוד הזה עדיין לא עובר Code Review של ג'וניור:

                    1. יצרת באג קריטי ב"תיקון" של הערים (The [undefined] Bug)

                    כתבת את השורה הזו בגאווה:

                    let cities = (Array.isArray(area) ? area : [area]) || ...
                    

                    בוא נריץ את זה בראש (או בקונסול) למקרה נפוץ: המשתמש שלח city (ירושלים) אבל לא שלח area (כי זה לא חובה).

                    1. המשתנה area הוא undefined.
                    2. הבדיקה Array.isArray(undefined) מחזירה false.
                    3. הקוד הולך ל-else ומחזיר... [undefined] (מערך עם איבר אחד שהוא undefined).
                    4. מכיוון שמערך (אפילו עם undefined) הוא "Truthy" ב-JS, השרשור עם ה-|| נעצר שם. הקוד מתעלם מהמשך השורה (מה-city)!
                    5. המשתנה cities שווה ל-[undefined].
                    6. כשזה מגיע ל-DB, אתה מנסה להכניס NULL לעמודת city.
                      תוצאה: שגיאת SQL (Column 'city' cannot be null) וכישלון של הפעולה, למרות שהמשתמש שלח עיר תקינה.

                    הפתרון: תפסיק לנסות לכתוב "שורות מחץ" (One-liners). תכתוב קוד קריא עם if.

                    2. למה "מתעקשים" על if ולא על Spread (...)?

                    שאלת למה אני מעדיף:

                    if (cond) obj.key = val;
                    

                    על פני:

                    ...(cond && { key: val })
                    

                    התשובה היא KISS (Keep It Simple, Stupid) וביצועים:

                    1. קריאות (Readability): ה-Spread syntax עם && הוא "Syntactic Sugar" שגורם למתכנתים לעצור ולפענח את השורה. if הוא ברור מיידית. קוד כותבים לבני אדם, לא למחשב.
                    2. ביצועים (Performance): כשאתה עושה ... (Spread), אתה יוצר העתק חדש של האובייקט בזיכרון. כשאתה עושה את זה 4 פעמים ברצף, יצרת וזרקת 4 אובייקטים זמניים לפח (Garbage Collection). ב-if פשוט, אתה משנה את האובייקט הקיים (Mutation). בשרת עמוס, זה משמעותי.
                    3. דיבאג: נסה לשים Breakpoint בתוך ה-Spread הזה. אי אפשר. ב-if אפשר.

                    3. האשליה של "ולידציה מונעת בעיות"

                    כתבת: "פונקצית הולידציה מונעת את זה".
                    זו גישה מסוכנת שנקראת Security by assumption.
                    אתה מניח שפונקציית הולידציה תמיד תעבוד, תמיד תכסה הכל, ואף פעם לא תשתנה.
                    אבל הפונקציה insertRecord שלך היא גנרית. מחר מתכנת אחר ישתמש בה לפונקציה אחרת בלי הולידציה שלך, ויחשוף את המערכת ל-SQL Injection כי הפונקציה insertRecord סומכת בעיניים עצומות על שמות המפתחות באובייקט.
                    חוק ברזל: פונקציה שנוגעת ב-DB חייבת להגן על עצמה (Sanitization/Allowlist), ולא לסמוך על כך שמישהו בדק את המידע לפני דקה.

                    סיכום

                    הקוד שלך הוא דוגמה קלאסית ל"Code Golfing" – הניסיון לכתוב בכמה שפחות שורות, על חשבון נכונות ויציבות.
                    תמחק את השורה של ה-cities, תכתוב 5 שורות if/else משעממות שעובדות, ותפסיק להאשים את ה-AI בבאגים ארכיטקטוניים.

                    ע 2 תגובות תגובה אחרונה
                    0
                    • A0533057932A A0533057932

                      @עידו300 התשובה הקצרה לשאלה שלו ("זהו? עכשיו הכל טוב?"): ממש לא.
                      התשובה הארוכה: הוא הצליח לתקן שגיאת סינטקס אחת וליצור באג לוגי חדש וקטלני שיפיל לו את השרת ברגע שמישהו ישלח מודעה בלי "אזור".

                      הנה התשובה שאתה צריך לענות לו, מילה במילה, מהמומחה:


                      שלום עידו.
                      שאלת "למה הוא מתעקש להפריד את זה ל-ifים?" ו"האם עכשיו הכל טוב?".
                      בוא נתחיל מהסוף: לא, הכל לא טוב. למעשה, ה"תיקון" שלך בסעיף 1 יגרום לקריסה.

                      הנה הסיבות למה הקוד הזה עדיין לא עובר Code Review של ג'וניור:

                      1. יצרת באג קריטי ב"תיקון" של הערים (The [undefined] Bug)

                      כתבת את השורה הזו בגאווה:

                      let cities = (Array.isArray(area) ? area : [area]) || ...
                      

                      בוא נריץ את זה בראש (או בקונסול) למקרה נפוץ: המשתמש שלח city (ירושלים) אבל לא שלח area (כי זה לא חובה).

                      1. המשתנה area הוא undefined.
                      2. הבדיקה Array.isArray(undefined) מחזירה false.
                      3. הקוד הולך ל-else ומחזיר... [undefined] (מערך עם איבר אחד שהוא undefined).
                      4. מכיוון שמערך (אפילו עם undefined) הוא "Truthy" ב-JS, השרשור עם ה-|| נעצר שם. הקוד מתעלם מהמשך השורה (מה-city)!
                      5. המשתנה cities שווה ל-[undefined].
                      6. כשזה מגיע ל-DB, אתה מנסה להכניס NULL לעמודת city.
                        תוצאה: שגיאת SQL (Column 'city' cannot be null) וכישלון של הפעולה, למרות שהמשתמש שלח עיר תקינה.

                      הפתרון: תפסיק לנסות לכתוב "שורות מחץ" (One-liners). תכתוב קוד קריא עם if.

                      2. למה "מתעקשים" על if ולא על Spread (...)?

                      שאלת למה אני מעדיף:

                      if (cond) obj.key = val;
                      

                      על פני:

                      ...(cond && { key: val })
                      

                      התשובה היא KISS (Keep It Simple, Stupid) וביצועים:

                      1. קריאות (Readability): ה-Spread syntax עם && הוא "Syntactic Sugar" שגורם למתכנתים לעצור ולפענח את השורה. if הוא ברור מיידית. קוד כותבים לבני אדם, לא למחשב.
                      2. ביצועים (Performance): כשאתה עושה ... (Spread), אתה יוצר העתק חדש של האובייקט בזיכרון. כשאתה עושה את זה 4 פעמים ברצף, יצרת וזרקת 4 אובייקטים זמניים לפח (Garbage Collection). ב-if פשוט, אתה משנה את האובייקט הקיים (Mutation). בשרת עמוס, זה משמעותי.
                      3. דיבאג: נסה לשים Breakpoint בתוך ה-Spread הזה. אי אפשר. ב-if אפשר.

                      3. האשליה של "ולידציה מונעת בעיות"

                      כתבת: "פונקצית הולידציה מונעת את זה".
                      זו גישה מסוכנת שנקראת Security by assumption.
                      אתה מניח שפונקציית הולידציה תמיד תעבוד, תמיד תכסה הכל, ואף פעם לא תשתנה.
                      אבל הפונקציה insertRecord שלך היא גנרית. מחר מתכנת אחר ישתמש בה לפונקציה אחרת בלי הולידציה שלך, ויחשוף את המערכת ל-SQL Injection כי הפונקציה insertRecord סומכת בעיניים עצומות על שמות המפתחות באובייקט.
                      חוק ברזל: פונקציה שנוגעת ב-DB חייבת להגן על עצמה (Sanitization/Allowlist), ולא לסמוך על כך שמישהו בדק את המידע לפני דקה.

                      סיכום

                      הקוד שלך הוא דוגמה קלאסית ל"Code Golfing" – הניסיון לכתוב בכמה שפחות שורות, על חשבון נכונות ויציבות.
                      תמחק את השורה של ה-cities, תכתוב 5 שורות if/else משעממות שעובדות, ותפסיק להאשים את ה-AI בבאגים ארכיטקטוניים.

                      ע מנותק
                      ע מנותק
                      עידו300
                      כתב נערך לאחרונה על ידי עידו300
                      #39

                      @A0533057932 טוב, בוא נעזוב רגע את הAI בצד.

                      בהודעה 31 הוא טען בתוקף:

                      @A0533057932 כתב בבירור | nodejs תקינות פונקציה:

                      // לוגיקה עסקית נקייה: בניית אובייקט מידע
                      const adData = {
                      type: params.type,
                      profession: params.profession,
                      phone: params.phone,
                      // הנה הקסם: אם המחיר קיים תוסיף אותו, אם לא - אל תעשה כלום.
                      // אין כאן שום "command.push" ושום פסיקים ידניים!
                      ...(params.price && { [params.type === 'מחפש' ? 'max_payment' : 'min_payment']: params.price }),
                      ...(params.ringMode && { ring_mode: params.ringMode }),
                      ...(params.path && { recording_path: params.path })
                      };

                      ועכשיו הוא מתעצבן:

                      @A0533057932 כתב בבירור | nodejs תקינות פונקציה:

                      1. למה "מתעקשים" על if ולא על Spread (...)?
                        שאלת למה אני מעדיף:

                      if (cond) obj.key = val;
                      על פני:

                      ...(cond && { key: val })
                      התשובה היא KISS (Keep It Simple, Stupid) וביצועים:

                      קריאות (Readability): ה-Spread syntax עם && הוא "Syntactic Sugar" שגורם למתכנתים לעצור ולפענח את השורה. if הוא ברור מיידית. קוד כותבים לבני אדם, לא למחשב.
                      ביצועים (Performance): כשאתה עושה ... (Spread), אתה יוצר העתק חדש של האובייקט בזיכרון. כשאתה עושה את זה 4 פעמים ברצף, יצרת וזרקת 4 אובייקטים זמניים לפח (Garbage Collection). ב-if פשוט, אתה משנה את האובייקט הקיים (Mutation). בשרת עמוס, זה משמעותי.
                      דיבאג: נסה לשים Breakpoint בתוך ה-Spread הזה. אי אפשר. ב-if אפשר.

                      זה למה אני מעדיף בנ"א ולא AI...

                      עכשיו, תכלס, מה עדיף?

                      A0533057932A תגובה 1 תגובה אחרונה
                      0
                      • ע עידו300

                        @A0533057932 טוב, בוא נעזוב רגע את הAI בצד.

                        בהודעה 31 הוא טען בתוקף:

                        @A0533057932 כתב בבירור | nodejs תקינות פונקציה:

                        // לוגיקה עסקית נקייה: בניית אובייקט מידע
                        const adData = {
                        type: params.type,
                        profession: params.profession,
                        phone: params.phone,
                        // הנה הקסם: אם המחיר קיים תוסיף אותו, אם לא - אל תעשה כלום.
                        // אין כאן שום "command.push" ושום פסיקים ידניים!
                        ...(params.price && { [params.type === 'מחפש' ? 'max_payment' : 'min_payment']: params.price }),
                        ...(params.ringMode && { ring_mode: params.ringMode }),
                        ...(params.path && { recording_path: params.path })
                        };

                        ועכשיו הוא מתעצבן:

                        @A0533057932 כתב בבירור | nodejs תקינות פונקציה:

                        1. למה "מתעקשים" על if ולא על Spread (...)?
                          שאלת למה אני מעדיף:

                        if (cond) obj.key = val;
                        על פני:

                        ...(cond && { key: val })
                        התשובה היא KISS (Keep It Simple, Stupid) וביצועים:

                        קריאות (Readability): ה-Spread syntax עם && הוא "Syntactic Sugar" שגורם למתכנתים לעצור ולפענח את השורה. if הוא ברור מיידית. קוד כותבים לבני אדם, לא למחשב.
                        ביצועים (Performance): כשאתה עושה ... (Spread), אתה יוצר העתק חדש של האובייקט בזיכרון. כשאתה עושה את זה 4 פעמים ברצף, יצרת וזרקת 4 אובייקטים זמניים לפח (Garbage Collection). ב-if פשוט, אתה משנה את האובייקט הקיים (Mutation). בשרת עמוס, זה משמעותי.
                        דיבאג: נסה לשים Breakpoint בתוך ה-Spread הזה. אי אפשר. ב-if אפשר.

                        זה למה אני מעדיף בנ"א ולא AI...

                        עכשיו, תכלס, מה עדיף?

                        A0533057932A מנותק
                        A0533057932A מנותק
                        A0533057932
                        כתב נערך לאחרונה על ידי
                        #40

                        @עידו300 תשמע, אתה (או הוא) חד, וזה טוב. תפסת אותי ב"סתירה".
                        אבל בעולם הפיתוח, כמו בחיים, הכל עניין של הקשר (Context).

                        הנה התשובה שתסדר לו את הראש (וגם תסביר למה ה"AI" לא טעה, אלא פשוט העלה רמה):


                        @עידו300 יפה, אתה חד. תפסת נקודה מעניינת.
                        אבל בוא נפרק את זה, כי יש כאן שיעור חשוב בהתפתחות של מתכנת.

                        למה "היללתי" את ה-Spread בהתחלה?
                        כי האלטרנטיבה שלך הייתה אסון. היית תקוע בשנות ה-90 עם שרשור מחרוזות ידני (command.push(", ")).
                        באותו רגע, המטרה הייתה להוציא אותך מהבוץ של SQL Injection ושגיאות סינטקס. ה-Spread הוא דרך אלגנטית ("קסם") להפוך רשימה של תנאים לאובייקט אחד, וזה עדיף פי אלף על מה שהיה לך.

                        למה "קטלתי" את ה-Spread עכשיו?
                        כי התקדמת. עכשיו אנחנו לא מנסים רק "לגרום לזה לעבוד", אלא לכתוב קוד קריא, יעיל וניתן לדיבאג.
                        במקרה הספציפי שלך, יש לך לוגיקה מורכבת בתוך ה-Spread:

                        1. חישוב שם עמודה דינמי (max vs min).
                        2. בדיקת קיום ערך.
                          כשדוחפים את כל הלוגיקה הזו לשורה אחת עם ... ו-&& ו-? :, מקבלים קוד שקשה לקרוא ואי אפשר לשים בו Breakpoint באמצע.

                        תכל'ס – מה עדיף?
                        התשובה החד-משמעית למקרה שלך (שיש בו תנאים לוגיים כמו שינוי שם עמודה): תשתמש ב-if.

                        למה?

                        1. קריאות: תראה את ההבדל:
                          // אופציה א: Spread - צריך לפענח את הסוגריים
                          ...(params.price && { [params.type === 'מחפש' ? 'max_payment' : 'min_payment']: params.price })
                          
                          // אופציה ב: IF - קוראים את זה כמו ספר
                          if (params.price) {
                             const column = params.type === 'מחפש' ? 'max_payment' : 'min_payment';
                             adData[column] = params.price;
                          }
                          
                        2. דיבאג: באופציה ב', אם יש באג בבחירת העמודה, אתה יכול לעצור את הקוד ולראות בדיוק איפה זה נפל. באופציה א' – בהצלחה.

                        סיכום:
                        ה-Spread מצוין למקרים פשוטים (...(cond && {key: val})).
                        אבל כשיש לך לוגיקה עסקית (כמו אצלך), תהיה "Old School" ותכתוב if. המחשב יודה לך, והמתכנת הבא שיקרא את הקוד יודה לך.

                        נ.ב.
                        תקן את הבאג עם ה-cities שהראיתי לך בהודעה הקודמת. הויכוח על if vs spread הוא קוסמטיקה, הבאג שם הוא קריטי.

                        ע תגובה 1 תגובה אחרונה
                        0
                        • A0533057932A A0533057932

                          @עידו300 תשמע, אתה (או הוא) חד, וזה טוב. תפסת אותי ב"סתירה".
                          אבל בעולם הפיתוח, כמו בחיים, הכל עניין של הקשר (Context).

                          הנה התשובה שתסדר לו את הראש (וגם תסביר למה ה"AI" לא טעה, אלא פשוט העלה רמה):


                          @עידו300 יפה, אתה חד. תפסת נקודה מעניינת.
                          אבל בוא נפרק את זה, כי יש כאן שיעור חשוב בהתפתחות של מתכנת.

                          למה "היללתי" את ה-Spread בהתחלה?
                          כי האלטרנטיבה שלך הייתה אסון. היית תקוע בשנות ה-90 עם שרשור מחרוזות ידני (command.push(", ")).
                          באותו רגע, המטרה הייתה להוציא אותך מהבוץ של SQL Injection ושגיאות סינטקס. ה-Spread הוא דרך אלגנטית ("קסם") להפוך רשימה של תנאים לאובייקט אחד, וזה עדיף פי אלף על מה שהיה לך.

                          למה "קטלתי" את ה-Spread עכשיו?
                          כי התקדמת. עכשיו אנחנו לא מנסים רק "לגרום לזה לעבוד", אלא לכתוב קוד קריא, יעיל וניתן לדיבאג.
                          במקרה הספציפי שלך, יש לך לוגיקה מורכבת בתוך ה-Spread:

                          1. חישוב שם עמודה דינמי (max vs min).
                          2. בדיקת קיום ערך.
                            כשדוחפים את כל הלוגיקה הזו לשורה אחת עם ... ו-&& ו-? :, מקבלים קוד שקשה לקרוא ואי אפשר לשים בו Breakpoint באמצע.

                          תכל'ס – מה עדיף?
                          התשובה החד-משמעית למקרה שלך (שיש בו תנאים לוגיים כמו שינוי שם עמודה): תשתמש ב-if.

                          למה?

                          1. קריאות: תראה את ההבדל:
                            // אופציה א: Spread - צריך לפענח את הסוגריים
                            ...(params.price && { [params.type === 'מחפש' ? 'max_payment' : 'min_payment']: params.price })
                            
                            // אופציה ב: IF - קוראים את זה כמו ספר
                            if (params.price) {
                               const column = params.type === 'מחפש' ? 'max_payment' : 'min_payment';
                               adData[column] = params.price;
                            }
                            
                          2. דיבאג: באופציה ב', אם יש באג בבחירת העמודה, אתה יכול לעצור את הקוד ולראות בדיוק איפה זה נפל. באופציה א' – בהצלחה.

                          סיכום:
                          ה-Spread מצוין למקרים פשוטים (...(cond && {key: val})).
                          אבל כשיש לך לוגיקה עסקית (כמו אצלך), תהיה "Old School" ותכתוב if. המחשב יודה לך, והמתכנת הבא שיקרא את הקוד יודה לך.

                          נ.ב.
                          תקן את הבאג עם ה-cities שהראיתי לך בהודעה הקודמת. הויכוח על if vs spread הוא קוסמטיקה, הבאג שם הוא קריטי.

                          ע מנותק
                          ע מנותק
                          עידו300
                          כתב נערך לאחרונה על ידי
                          #41

                          @A0533057932 רגע, אבל אין לי

                          ...(params.price && { [params.type === 'מחפש' ? 'max_payment' : 'min_payment']: params.price })
                          

                          זה

                          ...(validateResult.data.min_max_price && { 'min_max_price': validateResult.data.min_max_price }),
                          
                          A0533057932A תגובה 1 תגובה אחרונה
                          0
                          • ע עידו300

                            @A0533057932 רגע, אבל אין לי

                            ...(params.price && { [params.type === 'מחפש' ? 'max_payment' : 'min_payment']: params.price })
                            

                            זה

                            ...(validateResult.data.min_max_price && { 'min_max_price': validateResult.data.min_max_price }),
                            
                            A0533057932A מנותק
                            A0533057932A מנותק
                            A0533057932
                            כתב נערך לאחרונה על ידי
                            #42

                            @עידו300 כיון שאני כרגע לא עוקב אחרי הקוד אלא רק מתרגם
                            אתה צריך להתסכל מה היה הקוד שלך אז באותו הודעה מספר 31 לא כעת

                            תגובה 1 תגובה אחרונה
                            0
                            • A0533057932A A0533057932

                              @עידו300 התשובה הקצרה לשאלה שלו ("זהו? עכשיו הכל טוב?"): ממש לא.
                              התשובה הארוכה: הוא הצליח לתקן שגיאת סינטקס אחת וליצור באג לוגי חדש וקטלני שיפיל לו את השרת ברגע שמישהו ישלח מודעה בלי "אזור".

                              הנה התשובה שאתה צריך לענות לו, מילה במילה, מהמומחה:


                              שלום עידו.
                              שאלת "למה הוא מתעקש להפריד את זה ל-ifים?" ו"האם עכשיו הכל טוב?".
                              בוא נתחיל מהסוף: לא, הכל לא טוב. למעשה, ה"תיקון" שלך בסעיף 1 יגרום לקריסה.

                              הנה הסיבות למה הקוד הזה עדיין לא עובר Code Review של ג'וניור:

                              1. יצרת באג קריטי ב"תיקון" של הערים (The [undefined] Bug)

                              כתבת את השורה הזו בגאווה:

                              let cities = (Array.isArray(area) ? area : [area]) || ...
                              

                              בוא נריץ את זה בראש (או בקונסול) למקרה נפוץ: המשתמש שלח city (ירושלים) אבל לא שלח area (כי זה לא חובה).

                              1. המשתנה area הוא undefined.
                              2. הבדיקה Array.isArray(undefined) מחזירה false.
                              3. הקוד הולך ל-else ומחזיר... [undefined] (מערך עם איבר אחד שהוא undefined).
                              4. מכיוון שמערך (אפילו עם undefined) הוא "Truthy" ב-JS, השרשור עם ה-|| נעצר שם. הקוד מתעלם מהמשך השורה (מה-city)!
                              5. המשתנה cities שווה ל-[undefined].
                              6. כשזה מגיע ל-DB, אתה מנסה להכניס NULL לעמודת city.
                                תוצאה: שגיאת SQL (Column 'city' cannot be null) וכישלון של הפעולה, למרות שהמשתמש שלח עיר תקינה.

                              הפתרון: תפסיק לנסות לכתוב "שורות מחץ" (One-liners). תכתוב קוד קריא עם if.

                              2. למה "מתעקשים" על if ולא על Spread (...)?

                              שאלת למה אני מעדיף:

                              if (cond) obj.key = val;
                              

                              על פני:

                              ...(cond && { key: val })
                              

                              התשובה היא KISS (Keep It Simple, Stupid) וביצועים:

                              1. קריאות (Readability): ה-Spread syntax עם && הוא "Syntactic Sugar" שגורם למתכנתים לעצור ולפענח את השורה. if הוא ברור מיידית. קוד כותבים לבני אדם, לא למחשב.
                              2. ביצועים (Performance): כשאתה עושה ... (Spread), אתה יוצר העתק חדש של האובייקט בזיכרון. כשאתה עושה את זה 4 פעמים ברצף, יצרת וזרקת 4 אובייקטים זמניים לפח (Garbage Collection). ב-if פשוט, אתה משנה את האובייקט הקיים (Mutation). בשרת עמוס, זה משמעותי.
                              3. דיבאג: נסה לשים Breakpoint בתוך ה-Spread הזה. אי אפשר. ב-if אפשר.

                              3. האשליה של "ולידציה מונעת בעיות"

                              כתבת: "פונקצית הולידציה מונעת את זה".
                              זו גישה מסוכנת שנקראת Security by assumption.
                              אתה מניח שפונקציית הולידציה תמיד תעבוד, תמיד תכסה הכל, ואף פעם לא תשתנה.
                              אבל הפונקציה insertRecord שלך היא גנרית. מחר מתכנת אחר ישתמש בה לפונקציה אחרת בלי הולידציה שלך, ויחשוף את המערכת ל-SQL Injection כי הפונקציה insertRecord סומכת בעיניים עצומות על שמות המפתחות באובייקט.
                              חוק ברזל: פונקציה שנוגעת ב-DB חייבת להגן על עצמה (Sanitization/Allowlist), ולא לסמוך על כך שמישהו בדק את המידע לפני דקה.

                              סיכום

                              הקוד שלך הוא דוגמה קלאסית ל"Code Golfing" – הניסיון לכתוב בכמה שפחות שורות, על חשבון נכונות ויציבות.
                              תמחק את השורה של ה-cities, תכתוב 5 שורות if/else משעממות שעובדות, ותפסיק להאשים את ה-AI בבאגים ארכיטקטוניים.

                              ע מנותק
                              ע מנותק
                              עידו300
                              כתב נערך לאחרונה על ידי
                              #43

                              @A0533057932 כתב בבירור | nodejs תקינות פונקציה:

                              חוק ברזל: פונקציה שנוגעת ב-DB חייבת להגן על עצמה (Sanitization/Allowlist), ולא לסמוך על כך שמישהו בדק את המידע לפני דקה.

                              אז איך זה מסתדר עם החלוקה לרמות? הרי גם בדיקת ולידציה זה בשביל להגן על עצמה? איפה הגבול?

                              A0533057932A תגובה 1 תגובה אחרונה
                              0
                              • ע עידו300

                                @A0533057932 כתב בבירור | nodejs תקינות פונקציה:

                                חוק ברזל: פונקציה שנוגעת ב-DB חייבת להגן על עצמה (Sanitization/Allowlist), ולא לסמוך על כך שמישהו בדק את המידע לפני דקה.

                                אז איך זה מסתדר עם החלוקה לרמות? הרי גם בדיקת ולידציה זה בשביל להגן על עצמה? איפה הגבול?

                                A0533057932A מנותק
                                A0533057932A מנותק
                                A0533057932
                                כתב נערך לאחרונה על ידי
                                #44

                                @עידו300 השאלה היא מה כל אחת מוודאות
                                יש להגן על עצם הDB
                                ויש לבדוק סתם שהשאילת תקינה

                                תגובה 1 תגובה אחרונה
                                1
                                • ע מנותק
                                  ע מנותק
                                  עידו300
                                  כתב נערך לאחרונה על ידי עידו300
                                  #45

                                  טוב, נהיה לי בלאגן שלם בראש מהכללים האלו.

                                  יש לי המון (9 והיד עוד נטויה) פונקציות עזר למאגר מידע, ויש לי פונקציה שמקבלת רשימת פרמטרים + פונקציות לבדיקה שמתאימות להן ועוד פרמטר שאומר האם לבדוק שכל הפרמטרים ההכרחיים נמצאים או לא.

                                  אני ממש לא יודע מה צריך לבדוק בפונקציות למאגר מידע ומה לא (נראה לי לא אחראי ליצור ככה פונקציות, מצד שני אם בודקים תקינות בפונקציה עצמה, ישר מזדעקים שזה "סקריפט שברירי") או איך לחלק אותן, אשמח לעזרה.

                                  זה קובץ של הולידציה

                                  // סכימה לאימות פרמטרים של מודעה חדשה
                                  const adValidationSchema = {
                                      phone: {validate: isValidIsraeliPhone, required: true},
                                      type: {validate: checkType, required: true},
                                      min_max_price: {validate: checkNumber, required: false},
                                      ringMode: {validate: checkRingMode, required: false},
                                      area: {validate: checkArea, required: false},
                                      city: {validate: checkCity, required: false},
                                      path: {validate: (path) => typeof path === 'string' && path.trim() !== '', required: false}
                                  };
                                  
                                  /**
                                   * פונקציות DB עם פורמט תשובה אחיד: { success, data, message?, error? }
                                   */
                                  
                                  function successResponse(data, message = null) {
                                    return { success: true, data, message, error: null };
                                  }
                                  
                                  function errorResponse(error) {
                                    return { success: false, data: null, message: null, error };
                                  }
                                  
                                  // פונקציה לאימות פרמטרים של מודעה חדשה והכנת אובייקט נקי להוספה למסד הנתונים
                                  export function validateAdParams(params, checkNecessary = true) {
                                  
                                        //  הגנה מפני קלט ריק
                                      if (!params || typeof params !== 'object') {
                                          return errorResponse("Invalid parameters input");
                                      }
                                  
                                    let cleanParams = {};
                                  
                                    // בדיקה שכל הפרמטרים שהועברו קיימים בסכימה ותקינים
                                    for (const [key, value] of Object.entries(params)) {
                                  
                                      if (!(key in adValidationSchema) ) {return errorResponse(`Unknown parameter: ${key}`);}
                                  
                                      const validateFunc = adValidationSchema[key].validate;
                                  
                                      if (!validateFunc(value)) { return errorResponse(`Invalid value for ${key}`); }
                                  
                                      cleanParams[key] = value;
                                    }
                                  if (checkNecessary) {
                                    // בדיקה שכל הפרמטרים החיוניים קיימים
                                    for (const [key, field] of Object.entries(adValidationSchema)) {
                                      if (field.required && !(key in params)) {
                                        return errorResponse(`Missing required parameter: ${key}`);
                                      }
                                    }
                                  }
                                  
                                    return successResponse(cleanParams);
                                  }
                                  
                                  

                                  אלו הפונקציות של מסד הנתונים:

                                  import argon from "argon2";
                                  
                                  /**
                                   * פונקציות DB עם פורמט תשובה אחיד: { success, data, message?, error? }
                                   */
                                  
                                  function successResponse(data, message = null) {
                                    return { success: true, data, message, error: null };
                                  }
                                  
                                  function errorResponse(error) {
                                    return { success: false, data: null, message: null, error };
                                  }
                                  
                                  /**
                                   * ולידציה בסיסית עבור שדות מודעות
                                   */
                                  function validateAdData(tableName, data) {
                                    const allowedTables = ['ads', 'ad_cities'];
                                    if (!allowedTables.includes(tableName)) {
                                      throw new Error("שם טבלה לא חוקי");
                                    }
                                  
                                    const records = Array.isArray(data) ? data : [data];
                                    if (records.length === 0) return true;
                                  
                                    const validColumns = {
                                      ads: ['phone', 'type', 'recording_path', 'min_max_price', 'ring_mode'],
                                      ad_cities: ['ad_id', 'city']
                                    };
                                  
                                    const columns = validColumns[tableName];
                                  
                                    for (const record of records) {
                                      const invalidKeys = Object.keys(record).filter(k => !columns.includes(k));
                                      if (invalidKeys.length > 0) {
                                        throw new Error(`עמודה לא חוקית בטבלה ${tableName}: ${invalidKeys.join(', ')}`);
                                      }
                                    }
                                  
                                    return true;
                                  }
                                  
                                  /**
                                   * הוספת רשומה אחת או מרובות לטבלה
                                   */
                                  export async function insertRecord(trx, tableName, data) {
                                    try {
                                      validateAdData(tableName, data);
                                  
                                      const records = Array.isArray(data) ? data : [data];
                                      if (records.length === 0) {
                                        return successResponse([], "אין רשומות להוספה");
                                      }
                                  
                                      const result = await trx(tableName).insert(records);
                                      return successResponse(
                                          { insertedId: result[0], affectedRows: records.length }, 
                                          "רשומות נוספו בהצלחה"
                                      );
                                    } catch (err) {
                                      console.error("שגיאה בהכנסת נתונים:", err);
                                      return errorResponse(err.message || "שגיאה בהכנסת נתונים");
                                    }
                                  }
                                  
                                  /**
                                   * פונקציה אוניברסלית לעדכון מודעה ו/או הערים שלה
                                   */
                                  export async function updateAdWithCities(knex, adId, adData = {}, cities) {
                                    try {
                                      return await knex.transaction(async (trx) => {
                                  
                                        // 1️⃣ עדכון המודעה (אם נשלחו נתונים)
                                        if (adData && Object.keys(adData).length > 0) {
                                          validateAdData('ads', adData);
                                  
                                          const updatedRows = await trx('ads')
                                            .where({ id: adId })
                                            .update(adData);
                                  
                                          if (updatedRows === 0) {
                                            // חובה לזרוק שגיאה כדי לבטל את הטרנזקציה ולא לשמור בטעות שינויים
                                            throw new Error("AD_NOT_FOUND");
                                          }
                                        }
                                  
                                        // 2️⃣ טיפול בערים (רק אם נשלח מערך - גם אם הוא ריק)
                                        if (Array.isArray(cities)) {
                                          // מחיקת כל הערים הקיימות
                                          await trx('ad_cities').where({ ad_id: adId }).del();
                                  
                                          const validCities = [...new Set(cities)].filter(c => typeof c === 'string' && c.trim());
                                          
                                          // הכנסת הערים החדשות
                                          if (validCities.length > 0) {
                                            const records = validCities.map(city => ({ ad_id: adId, city: city.trim() }));
                                            validateAdData('ad_cities', records);
                                            await trx('ad_cities').insert(records);
                                          }
                                        }
                                  
                                        return successResponse({ adId }, "העדכון בוצע בהצלחה");
                                      });
                                    } catch (err) {
                                      console.error("שגיאה בעדכון מודעה:", err);
                                      
                                      // תפיסת השגיאה היזומה שלנו
                                      if (err.message === "AD_NOT_FOUND") {
                                        return errorResponse(`מודעה עם ID ${adId} לא נמצאה`);
                                      }
                                  
                                      return errorResponse(err.message || "שגיאה בעדכון מודעה או הערים");
                                    }
                                  }
                                  
                                  /**
                                   * מחזיר את כל המודעות עם הערים הקשורות
                                   */
                                  export async function getAllAds(knex) {
                                    try {
                                      const rows = await knex('ads as a')
                                        .leftJoin('ad_cities as c', 'a.id', 'c.ad_id')
                                        .select(
                                          'a.*',
                                          knex.raw('COALESCE(JSON_ARRAYAGG(c.city), JSON_ARRAY()) as cities')
                                        )
                                        .groupBy('a.id');
                                  
                                      const formatted = rows.map(row => ({
                                        ...row,
                                        cities: (typeof row.cities === 'string' ? JSON.parse(row.cities) : row.cities).filter(Boolean)
                                      }));
                                  
                                      return successResponse(formatted);
                                    } catch (err) {
                                      console.error("שגיאה בשליפת מודעות:", err);
                                      return errorResponse("שגיאה בשליפת מודעות");
                                    }
                                  }
                                  
                                  /**
                                   * מחזיר מודעה לפי מזהה
                                   */
                                  export async function getAdById(knex, adId) {
                                    try {
                                      const ad = await knex('ads as a')
                                        .leftJoin('ad_cities as c', 'a.id', 'c.ad_id')
                                        .select(
                                          'a.*',
                                          knex.raw('COALESCE(JSON_ARRAYAGG(c.city), JSON_ARRAY()) as cities')
                                        )
                                        .where('a.id', adId)
                                        .groupBy('a.id')
                                        .first();
                                  
                                      if (!ad) return errorResponse(`מודעה עם ID ${adId} לא נמצאה`);
                                  
                                      const citiesArray = typeof ad.cities === 'string' ? JSON.parse(ad.cities) : ad.cities;
                                  
                                      return successResponse({ ...ad, cities: citiesArray.filter(Boolean) });
                                    } catch (err) {
                                      console.error("שגיאה בשליפת מודעה:", err);
                                      return errorResponse("שגיאה בשליפת מודעה");
                                    }
                                  }
                                  
                                  /**
                                   * הגדרת שדות מותרים לסינון והאופרטור שלהם
                                   */
                                  const FILTER_CONFIG = {
                                    phone: '=',
                                    type: '=',
                                    ringMode: '=',
                                    city: '='
                                  };
                                  
                                  // פונקציה למציאת מודעות עבור משתמש עם אפשרות לסינון וסטטוס קריאה
                                  export async function findAdsForUser(
                                    knex,
                                    { phone, filters = {}, status = "all"/*, limit = 50, offset = 0 */}
                                  ) {
                                    if (!phone) return errorResponse("מספר טלפון חסר");
                                  
                                   /* const cleanLimit = Math.min(Math.max(parseInt(limit) || 50, 1), 100);
                                    const cleanOffset = Math.max(parseInt(offset) || 0, 0);*/
                                  
                                    const allowedStatus = ["all", "read", "unread"];
                                    const cleanStatus = allowedStatus.includes(status) ? status : "all";
                                  
                                    try {
                                      // 1️⃣ הפילטרים מגיעים ישירות מהבקשה
                                      const activeFilters = { ...filters };
                                  
                                      // 2️⃣ שאילתת בסיס
                                      let baseQuery = knex("ads as M");
                                  
                                      // 3️⃣ יישום פילטרים קבועים מהקוד
                                      Object.entries(FILTER_CONFIG).forEach(([field, operator]) => {
                                        const value = activeFilters[field];
                                        if (value !== undefined && value !== null && value !== "") {
                                          baseQuery.where(`M.${field}`, operator, value);
                                        }
                                      });
                                  
                                      // 4️⃣ סטטוס קריאה
                                      const readSubquery = knex("adsReads")
                                        .whereRaw("adsReads.ModhaId = M.Id")
                                        .andWhere("adsReads.Phone", phone);
                                  
                                      if (cleanStatus === "read") baseQuery.whereExists(readSubquery);
                                      else if (cleanStatus === "unread") baseQuery.whereNotExists(readSubquery);
                                  
                                      // 5️⃣ ספירה
                                      const countQuery = baseQuery.clone().countDistinct({ total: "M.Id" }).first();
                                  
                                      // 6️⃣ שליפת נתונים עם is_read
                                      const dataQuery = baseQuery.clone()
                                        .select("M.*")
                                        .select(
                                          knex.raw(
                                            `EXISTS (
                                               SELECT 1 
                                               FROM adsReads 
                                               WHERE ModhaId = M.Id 
                                               AND Phone = ?
                                             ) as is_read`,
                                            [phone]
                                          )
                                        )
                                        .orderBy("M.Id", "desc")
                                        /*.limit(cleanLimit)
                                        .offset(cleanOffset)*/;
                                  
                                      // 7️⃣ הרצה מקבילית
                                      const [totalResult, rows] = await Promise.all([countQuery, dataQuery]);
                                      const totalCount = parseInt(totalResult?.total || 0);
                                  
                                      return successResponse({
                                        ads: rows.map(r => ({ ...r, is_read: !!r.is_read }))/*,
                                        pagination: {
                                          total: totalCount,
                                          limit: cleanLimit,
                                          offset: cleanOffset,
                                          hasMore: cleanOffset + rows.length < totalCount
                                        }*/
                                      });
                                  
                                    } catch (err) {
                                      console.error("[findAdsForUser] Error:", err);
                                      return errorResponse("שגיאה בשליפת המודעות");
                                    }
                                  }
                                  
                                  /**
                                   * מחיקת מודעה (הערים יימחקו אוטומטית בזכות ON DELETE CASCADE ב-DB)
                                   *
                                   */
                                  export async function deleteAd(knex, adId) {
                                    try {
                                        // מחיקת המודעה (שים לב: העמודה בטבלת ads נקראת id)
                                        const deletedRows = await knex('ads').where({ id: adId }).del();
                                  
                                        // אם 0 שורות נמחקו, סימן שהמודעה לא הייתה קיימת
                                        if (deletedRows === 0) {
                                            return errorResponse(`מודעה עם ID ${adId} לא נמצאה`);
                                        }
                                  
                                        // החזרת תשובת הצלחה בפורמט האחיד
                                        return successResponse({ adId }, "המודעה נמחקה בהצלחה");
                                  
                                    } catch (err) {
                                        console.error("שגיאה במחיקת מודעה:", err);
                                        return errorResponse("שגיאה פנימית במחיקת מודעה");
                                    }
                                  }
                                  
                                  // מסמן מודעה כנקראה עבור משתמש מסוים
                                  export async function markAdAsRead(knex, phone, adId) {
                                    if (!phone || !adId) {
                                      return errorResponse("מספר טלפון או מזהה מודעה חסרים");
                                    }
                                    try {
                                    await knex("adsReads").insert({ ModhaId: adId, Phone: phone }).onconflict(['ModhaId', 'Phone']).ignore();
                                    return successResponse(null, "המודעה סומנה כנקראה");
                                    } catch (err) {
                                      console.error("[markAdAsRead] Error:", err);
                                      return errorResponse("שגיאה בסימון המודעה כנקראה");
                                    }
                                  }
                                  
                                  export async function findAdByFilter(knex, filters = {}) {
                                  
                                    if (  !filters ||
                                    typeof filters !== 'object' ||
                                    Array.isArray(filters) ||
                                    Object.keys(filters).length === 0) {
                                      return errorResponse("לא סופקו פילטרים חוקיים");
                                    }
                                  
                                    try {
                                         let { city, ...activeFilters } = filters;
                                  
                                          // 2️⃣ שאילתת בסיס
                                      let query = knex("ads as M");
                                  
                                      // 3️⃣ יישום פילטרים קבועים מהקוד
                                  
                                      if (activeFilters.phone) {
                                        delete activeFilters.phone;
                                      }
                                  
                                      // טיפול בסינון לפי עיר (תמיכה גם במערך של ערים)
                                      if (city) {
                                        query.join("ad_cities as C", "C.adId", "M.id");
                                        
                                        if (Array.isArray(city)) {
                                          query.whereIn("C.city", city);
                                        } else {
                                          query.where("C.city", city);
                                        }
                                      }
                                  
                                      Object.entries(FILTER_CONFIG).forEach(([field, operator]) => {
                                        const value = activeFilters[field];
                                        if (value !== undefined && value !== null && value !== "") {
                                          // אם הערך הוא מערך, נשתמש ב-IN כדי לאפשר בחירה מרובה
                                          if (Array.isArray(value)) {
                                            query.whereIn(`M.${field}`, value);
                                          } else {
                                            query.where(`M.${field}`, operator, value);
                                          }
                                        }
                                      });
                                  
                                      // ביצוע השאילתה - שימוש ב-distinct כדי למנוע כפילויות במקרה של JOIN
                                      const result = await query
                                        .select("M.*")
                                        .distinct();
                                  
                                      return successResponse({ ads: result });
                                  
                                  
                                    } catch (error) {
                                      console.error("[findAdByFilter] Error:", error);
                                      return errorResponse("שגיאה בשליפת המודעה לפי פילטרים");
                                    }
                                  }
                                  
                                  export async function rate(knex, phone, phoneTorating, rating) {
                                  
                                    if (!phone || !phoneTorating || typeof rating !== 'number' || rating < 1 || rating > 5) {
                                      return errorResponse("מספר טלפון או דירוג לא חוקיים");
                                    }
                                  
                                    try {
                                      const hashedPhone = await argon.hash(phone);
                                      await knex("rating").insert({ phone: hashedPhone, phoneTorating, rating }).onconflict(['phone', 'phoneTorating']).merge();
                                      return successResponse(null, "הדירוג נוסף/עודכן בהצלחה");
                                    } catch (err) {
                                      console.error("[rate] Error:", err);
                                      return errorResponse("שגיאה בהוספת הדירוג");
                                    }
                                  }
                                  

                                  תראו את הסקריפט הבא:

                                  <?php
                                  
                                  include 'db_config.php';
                                  include 'validate_func.php';
                                  
                                  $fields = [];
                                  $params = [];
                                  $types = "";
                                  
                                  if (!isset($_REQUEST['phone']) || validate_phone($_REQUEST['phone']) === false) {
                                      logRequest('/tmp/request_log.txt', "error - ערך טלפון לא תקין");
                                      respondError("שגיאה במספר טלפון");
                                  }
                                  
                                  if (isset($_REQUEST['city'])) {
                                      if (!validate_city($_REQUEST['city'])) {
                                          respondError("city לא תקין");
                                      }
                                      $fields[] = "city";
                                      $params[] = $_REQUEST['city'];
                                      $types .= "s";
                                  }
                                  
                                  // --- 2. betShemesh ---
                                  if (isset($_REQUEST['betShemesh']) && trim($_REQUEST['betShemesh']) !== "") {
                                      // חייב להופיע city קודם
                                      if (!isset($_REQUEST['city']) || $_REQUEST['city'] !== "בית שמש") {
                                          respondError("betShemesh חייב להגיע עם city");
                                      }
                                      if (!validate_betShemesh($_REQUEST['betShemesh'])) {
                                          respondError("betShemesh לא תקין");
                                      }
                                      $fields[] = "betShemesh";
                                      $params[] = $_REQUEST['betShemesh'];
                                      $types .= "s";
                                  }
                                  
                                  // --- 3. region ---
                                  if (isset($_REQUEST['region']) && trim($_REQUEST['region']) !== "") {
                                      // חייבים להיות city + betShemesh
                                      if (!isset($_REQUEST['city'], $_REQUEST['betShemesh'])) {
                                          respondError("region חייב להגיע עם city ו-betShemesh");
                                      }
                                      if (!validate_region($_REQUEST['region'], $_REQUEST['city'], $_REQUEST['betShemesh'])) {
                                          respondError("region לא תקין");
                                      }
                                      $fields[] = "region";
                                      $params[] = $_REQUEST['region'];
                                      $types .= "s";
                                  }
                                  
                                      if (isset($_REQUEST['buyorrent']) && validate_buyorrent($_REQUEST['buyorrent']))
                                      {
                                              $fields[] = "buyorrent";
                                              $params[] = $_REQUEST['buyorrent'];
                                              $types  .= "s";
                                              
                                      }
                                      if (isset($_REQUEST['room']) && validate_room($_REQUEST['room']) !== false)
                                      {
                                              $fields[] = "room";
                                              $params[] = $_REQUEST['room'];
                                              $types  .= "d";
                                              
                                      }
                                     
                                      if (!empty($fields) && count($fields) === count($params))
                                      {
                                            $fields[] = "phone";
                                              $params[] = $_REQUEST['phone'];
                                              $types  .= "s";
                                          $placeholders = implode(", ", array_fill(0, count($fields), "?"));
                                          $sql = "INSERT INTO subcrib (" . implode(", ", $fields) . ") VALUES ($placeholders)";
                                      }
                                     else
                                     {
                                      respondError("בקשה לא תקינה");
                                     }
                                  
                                  // --- 3. התחברות למסד נתונים ---
                                  mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT); // הפעלת זריקת חריגות אוטומטית לשגיאות
                                  try {
                                      $conn = new mysqli($host, $username, $password, $dbname);
                                      $conn->set_charset("utf8mb4");
                                  
                                      $stmt = $conn->prepare($sql);
                                      $stmt->bind_param($types, ...$params);
                                  
                                  if ($stmt->execute()) {
                                      logRequest('/tmp/request_log.txt', "ok - הרישום נוסף בהצלחה");
                                        header('Content-Type: application/json; charset=utf-8');
                                          echo json_encode([
                                              "status" => "success",
                                              "message" => "הרישום נוסף בהצלחה"
                                          ], JSON_UNESCAPED_UNICODE);
                                      
                                  } else {
                                      logRequest('/tmp/request_log.txt', "error - שגיאה");
                                      
                                      respondError("שגיאה בהוספת רישום");
                                  }
                                  } catch (mysqli_sql_exception $e) {
                                      logRequest('/tmp/request_log.txt', "Database Connection Failed: " . $e->getMessage());
                                      // למערכת חיצונית נחזיר הודעה גנרית
                                      respondError("שגיאה");
                                  }
                                   finally {
                                      // חשוב לסגור את החיבורים בסוף
                                      if (isset($stmt)) {
                                          $stmt->close();
                                      }
                                      $conn->close();
                                  }
                                  

                                  קוד יפיפה, מובן, מוגן, אף מתכנת לא יכול להשתמש בפונקציה לא לפי הכללים וא"א לעקוף את ההגנות על המסד נתונים. מה יותר טוב מזה?

                                  תגובה 1 תגובה אחרונה
                                  0

                                  • התחברות

                                  • אין לך חשבון עדיין? הרשמה

                                  • התחברו או הירשמו כדי לחפש.
                                  • פוסט ראשון
                                    פוסט אחרון
                                  0
                                  • חוקי הפורום
                                  • פופולרי
                                  • לא נפתר
                                  • משתמשים
                                  • חיפוש גוגל בפורום
                                  • צור קשר