וובמאסטר - תיכנות ובניית אתרים

בניית פורום רקורסיבי - חלק ב'

ניר טייב/‏ 21 אפריל, 2008
F+
F-
במאמר זה נסביר כיצד להוסיף הודעה נפתחת, איך עושים חלוקה לעמודים וגם על טופס הוספת ההודעה. כמו כן נסביר על הקפצות ונעיצות.

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



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38  
<%
dim query, rs, strRecArray, cntFields, oConn
Set oConn=Server.CreateObject("ADODB.connection")
oConn.Open "Provider=microsoft.jet.oledb.4.0; data source="&Server.mapPath("db/forum.mdb")&";"
query = "SELECT forum.id, forum.parentId, forum.subject FROM forum ORDER BY forum.id"
SET rs = Server.CreateObject("ADODB.Recordset")
rs.Open query, oConn
cntFields = rs.fields.count ' count of fields that we selected
strRecArray = rs.getString(2,-1, ",", ",")
rs.Close
Set rs = Nothing
%>
<script type="text/javascript">
var recArray = "<% =strRecArray %>"
recArray = recArray.substr(0, (recArray.length-1))
recArray = recArray.split(",") /// Now we have array with one dimension of records
var cntFields = new Number(<% =cntFields %>);
for (var i=0;i<recArray.length;i+=cntFields){
    if (recArray[i+1]==0){
        showMessage(i);
        document.write("<hr />")
     }
}

function showMessage(index){
   var subject = new String(recArray[index+2]);
   document.write("<div style=\"padding-right: 20px\">");
   document.write(subject);
   document.write("<hr />");
   
   for (var a=0;a<recArray.length;a+=cntFields){
        if (recArray[a+1]==recArray[index])
             showMessage(a);
   }
   document.write("</div>");
}

</script>


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

כדי להבין את השיטה שבה אנחנו נעבוד עם הרשומות ששלפנו רצוי מאוד לקרוא את המאמר " עבודה עם getRows ב-JS צד שרת " , למרות שהמאמר מסביר על צד שרת אנחנו עובדים באותה דרך: מערך חד מימדי שכל איבר מכיל שדה ואין קבוצה של שדות כרשומה (כמו ב-VBS/ASP צד שרת). - קריאה נעימה.

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

ID מספור אוטומטי מפתח ראשי
parentId מספר ID של הודעת האב-הודעה ראשית תהיה 0
Subject טקסט נושא ההודעה - סימון כחובה required
message תזכיר - Memo ההודעה

לאחר שהוספנו את העמודה לטבלה עלינו לשלוף אותה. אז עכשיו השאילתא שלנו נראית ככה:

1
2  
query = "SELECT forum.id, forum.parentId, forum.subject, forum.message FROM forum ORDER BY forum.id"



פתיחת הודעות באותו עמוד

לאחר ששלפנו את ההודעות עלינו להדפיס אותם בדף ב-div נסתר (כדי ליצור אפקט הודעה נפתחת) עם ID ייחודי לכל הודעה (ה-ID יהיה מילה עם ה-ID של הרשומה) לכן הפוקנציה שלנו צריכה להיות כך:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17  
function showMessage(index){
   var subject = new String(recArray[index+2]);
   document.write("<div style=\"padding-right: 20px\">");
   document.write("<span >")
   document.write(subject);
   document.write("</span>")
   document.write("<div style=\"display:none;\" id=\"mess_" + recArray[index] + "\" >");
   document.write(recArray[index+3] + "<br />");
   document.write("<a href=\"addMess.asp?id=" + recArray[index] + "&rootId=" + recArray[index+4] + "\" >הוסף תגובה</a>")
   document.write("</div><hr />");
   
   for (var a=0;a<recArray.length;a+=cntFields){
        if (recArray[a+1]==recArray[index])
             showMessage(a);
   }
   document.write("</div>");
}


עכשיו כדי ליצור את אפקט ההודעה הנפתחת נכתוב פונקציה נפרדת שמקבלת את ה-ID של האלמנט ופותחת סוגרת בהתאם

1
2
3
4  
function displayMessage(id){
   var obj = documentgetElementById(id);
   obj.style.display=(obj.style.display=="block"?"none":"block");
}


ועכשיו נגרום לכך שברגע שילחצו על הנושא אז ההודעה תיפתח

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18  
function showMessage(index){
   var subject = new String(recArray[index+2]);
   document.write("<div style=\"padding-right: 20px\">");
   /* ---> */document.write("<span onclick=\" displayMessage(mess_" + recArray[index] + ")\" >")
   document.write(subject);
   document.write("</span>")
   document.write("<div style=\"display:none;\" id=\"mess_" + recArray[index] + "\" >");
   document.write(recArray[index+3]);
   document.write("<a href=\"addMess.asp?id=\"" + recArray[index] + "&rootId=" + recArray[index+4] + "\" >הוסף תגובה</a>")
   // [index+4] is the field rootId that we create continue

   document.write("</div>");
   
   for (var a=o;a<recArray.length;a+=cntFields){
        if (recArray[a+1]==recArray[index+1])
             showMessage(a);
   }
}


וכאן סיימנו לעשות את ההודעה הנפתחת.

חלוקה לעמודים

לרוב אנו משתמשים ב-ADO כדי לעשות חלוקה לעמודים אבל אצלנו גם ההודעות וגם התגובות נמצאות באותה טבלה אז שיטה זו לא תעזור לנו כי השיטה הזו תפצל לנו שרשורים ולכן יהיו תגובות שלעולם לא נראה. לפני שנעשה את החלוקה לעמודים עצמה נוסיף לנו הודעה בטבלה בשם rootId תפקידה לשייך כל הודעה/תגובה לשרשור המקורי שלה עמודה זו תהיה מסוג מספר. הטבלה שלנו עכשיו נראית כך :
ID מספור אוטומטי מפתח ראשי
parentId מספר ID של הודעת האב-הודעה ראשית תהיה 0
Subject טקסט נושא ההודעה - סימון כחובה required
message תזכיר - Memo ההודעה
rootId מספר שלם ה-ID של ההודעה הראשית
msgDate תאריך/שעה תאריך כתיבת ההודעה (ערך ברירת מחדל now())

לאחר שהוספנו את העמודה נוסיף view שירכז לנו רק את ה-rootIdים שאנו צריכים View - שאילתא שנעשית במסד (לא ברמת ה-ASP) ומאפשרת לשמור נתונים מסוימים לעצמנו.

את ה-view באקסס אנו יוצרים בכך שאנו נכנסים לקטגורייה queries ושם אנו בוחרים ב-"צור שאילתא בתצוגת עיצוב". לאחר שאנו יודעים מה זה view בואו ניצור אותו. אנו ניכנס לאפשרות של כתיבת SQL ונכתוב את השאילתא הבאה (נקרא ל-view שלנו fixedForum ) :

1
2
3  
SELECT forum.rootID, Max(forum.rootID) AS MAXrootID, Max(forum.msgDate) AS MAXmsgDate
FROM forum
GROUP BY forum.rootID


השאילתא הזו תחזיר לנו את כל ה-rootId הקיימים בטבלה שלנו.
עכשיו החלוקה לעמודים מתבצעת כך:
עמוד ראשון - 15 ההודעות הראשונות
עמוד שני - 15 ההודעות הראשונות שאחרי 15 ההודעות הראשונות
עמוד שלישי - 15 ההודעות הראשונות אחרי 30 ההודעות הראשונות
וכך הלאה...
עכשיו איך אנו מבצעים זאת? בעזרת ה-view ואופרטור in אנו נשלוף את כל ההודעות שה-rootId שלהם נמצא בין ה-15 הראשונים.

1
2
3
4
5
6  
SELECT forum.id, forum.parentId, forum.subject, forum.message
FROM forum
WHERE forum.rootId in (SELECT TOP 15 fixedForum.rootId
                       FROM fixedForum
                     ORDER BY fixedForum.MAXmsgDate DESC)
ORDER BY forum.id


אבל השאילתא הזו מתאימה אך ורק לעמוד הראשון בעמוד השני והלאה נצטרך לעשות עוד תת שאילתא שנשתמש בה עם שילוב האופרטורים not in

1
2
3
4
5
6
7
8
9
10
11  
SELECT forum.id, forum.parentId, forum.subject, forum.message
FROM forum
WHERE forum.rootId in (SELECT TOP 15 fixedForum.rootId
                       FROM fixedForum
                       WHERE fixedForum.rootId not in(
                                  SELECT TOP (15 *x) fixedForum.rootId
                                FROM fixedForum
                                ORDER BY fixedForum.MAXmsgDate)
                           ORDER BY fixedForum. MAXmsgDate DESC
                        )
ORDER BY forum.id DESC


X הוא משתנה חיצוני המסמל את מספר העמוד בו אנחנו נמצאים עכשיו כדי לעשות דפדוף בתיבת select אנו צריכים לעשות שליפה חדשה (שליפה נוספת).
בשליפה אנו נשלוף את המס' הכללי של השרשורים בעזרת הפונקציה count .לאחר מכן נכניס לתוך משתנה את מס' השרשורים שיש בעמוד (לפי בחירה) ולאחר מכן ניצור משתנה בשם pageCount שיכיל את התוצאה של פעילת החילוק הבאה : מס' השורשים הכללי חלקיי מס' השרשורים בעמוד בעצם המשתנה pageCount בודק עד לכמה עמודים נעשה החילוק.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21  
<%
paggingQuery = "SELECT COUNT(fixedForum.rootID) as total FROM fixedForum"
Set rs1 = Server.CreateObject("ADODB.Recordset")
rs1.Open paggingQuery, oConn
cntRec = cint(rs1.fields("total"))
rs1.Close
set rs1 = nothing
%>
<select name="NextPre" onChange="location.href='forum.asp?id='+NextPre.value">
<script type="text/javascript">
var total, recForPage, pageCount
total = Math.round(<%=cntRec %>)
recForPage = 15
pageCount = Math.round(total/recForPage)
if (pageCount<=0)
      pageCount=1;
if (pageCount<(total/recForPage)) pageCount++;
for (var i=1;i<=pageCount;i++)
  document.write("<option value=\""+i+"\">"+i+"</option>");
</script>
</select>


לבינתיים ככה נראה הקוד שלנו :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100  
<html dir="rtl"><%
dim query, rs, strRecArray, cntFields, x, subSubQuery, subQuery, id
x = cInt(Request.QueryString("pageid")-1)

if len(request.queryString("ID"))=0 then
   x=0
else
     x = cint(cint(request.queryString("id"))-1)
end if

subSubQuery = " SELECT TOP "&(15 *x)&" fixedForum.rootId FROM fixedForum ORDER BY fixedForum.MaxmsgDate)"

if x<=0 then
 subQuery = "SELECT TOP 15 fixedForum.rootID FROM fixedForum ORDER BY fixedForum.rootID DESC, fixedForum.MaxmsgDate DESC"
else
 subQuery = "SELECT TOP 15 fixedForum.rootID FROM fixedForum WHERE rootID not in (" & subSubQuery & ") ORDER BY fixedForum.rootID DESC, fixedForum.MaxmsgDate DESC"
END if

query ="SELECT forum.id, forum.parentId, forum.subjet,forum.message,forum.rootId" & _
             " FROM forum" &_
             " WHERE forum.rootId in ("&subQuery&")" &_
             " ORDER BY forum.id"

Set oConn=Server.CreateObject("ADODB.connection")
oConn.Open "Provider=microsoft.jet.oledb.4.0; data source="&Server.mapPath("db/forum.mdb")&";"
query ="SELECT forum.id, forum.parentId, forum.subject,forum.message,forum.rootId" & _
             " FROM forum" &_
             " WHERE forum.rootId in ("&subQuery&")" &_
             " ORDER BY forum.id"
SET rs = Server.CreateObject("ADODB.Recordset")
rs.Open query, oConn
cntFields = rs.fields.count ' count of fields that we selected
strRecArray = rs.getString(2,-1, ",", ",")
rs.Close
Set rs = Nothing
%>
<script type="text/javascript">
var recArray = "<% =strRecArray %>"
recArray = recArray.substr(0, (recArray.length-1))
recArray = recArray.split(",") /// Now we have array with one dimension of records
var cntFields = new Number(<% =cntFields %>);
for (var i=0;i<recArray.length;i+=cntFields){
    if (recArray[i+1]==0){
        showMessage(i);
     }
}


function displayMessage(id){
   var obj = document.getElementById(id);
   obj.style.display=(obj.style.display=="block"?"none":"block");
}


function showMessage(index){
   var subject = new String(recArray[index+2]);
   document.write("<div style=\"padding-right: 20px\">");
   document.write("<span onclick=\" displayMessage(\'mess_" + recArray[index] + "\')\" >")
   document.write(subject);
   document.write("</span>")
   document.write("<div style=\"display:none;\" id=\"mess_" + recArray[index] + "\" >");
   document.write(recArray[index+3] + "<br />");
   document.write("<a href=\"addMess.asp?id=" + recArray[index] + "&rootId=" + recArray[index+4] + "\" >הוסף תגובה</a>")
   document.write("</div><hr />");
   
   for (var a=0;a<recArray.length;a+=cntFields){
        if (recArray[a+1]==recArray[index])
             showMessage(a);
   }
   document.write("</div>");
}

</script>

<br /><br /><br />
<%
paggingQuery = "SELECT COUNT(fixedForum.rootID) as total FROM fixedForum"
Set rs1 = Server.CreateObject("ADODB.Recordset")
rs1.Open paggingQuery, oConn
cntRec = cint(rs1.fields("total"))
rs1.Close
set rs1 = nothing
%>
<select name="NextPre" onChange="location.href='forum.asp?id='+NextPre.value">
<script type="text/javascript">
var total, recForPage, pageCount
total = Math.round(<% =cntRec %>)
recForPage = 15
pageCount = Math.round(total/recForPage)
if (pageCount<=0)
      pageCount=1;
for (var i=1;i<=pageCount;i++)
  document.write("<option value=\""+i+"\">"+i+"</option>");
</script>
</select>
</html>

<% oConn.Close
set oConn=Nothing
%>


הקפצות ונעיצות

כל הרעיון הוא שלכל הודעות האב אתה מצמיד עוד שדה לפיו אתה מסדר את סדר ההודעות.

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

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

ולכן הטבלה שלנו תראה כך עכשיו :

ID מספור אוטומטי מפתח ראשי
parentId מספר ID של הודעת האב-הודעה ראשית תהיה 0
Subject טקסט נושא ההודעה - סימון כחובה required
message תזכיר - Memo ההודעה
rootId מספר שלם ה-ID של ההודעה הראשית
msgDate תאריך/שעה תאריך כתיבת ההודעה (ערך ברירת מחדל now())
groupDate תאריך/שעה תאריך ההקפצה האחרונה או תאריך סיום הנעיצה

ועכשיו אנו רק נוסיף בפסוקית ה-ORDER BY את השדה - groupDate וזהו יש לנו הקפצות ונעיצות.

1
2
3
4
5
6  
<%
query = "SELECT forum.id, forum.parentId, forum.subjet, forum.message, forum.rootId" & _
             " FROM forum" &_
             "WHERE forum.rootId in ("&subQuery&")" &_
             " ORDER BY forum.id, foum.groupDate"
%>


עמוד הוספת ההודעה/תגובה

עכשיו אנו ניצור את עמוד הטופס addMess.asp . זה יהיה טופס HTML פשוט הכולל: נושא, הודעה ושני inputים נסתרים המכילים את ה-ID של ההודעה אליה אנו מגיבים(ה-parentId) ואת ה-rootId של השרשור (את שני הערכים אנו שולחים ב-queryString) :

1
2
3
4
5
6
7
8
9
10
11
12
13  
<html>
<head><title>הוספת הודעה</title></head>
<body>
 <form action="insMessage.asp" method="post">
   נושא: <input type="text" name="subject" /><br>
   ההודעה:<br>
   <textarea name="message"></textarea><br>
   <input type="hidden" name="parentId" value="<% =Request.QueryString("id") %>" />
   <input type="
hidden" name="rootId" value="<% =Request.QueryString("rootId") %>" />
   <input type="submit" value=" שלח " />
 </form>
</body>
</html>


לאחר שיצרנו את הטופס עלינו ליצור גם את העמוד שיכניס לנו את הנתונים למסד. לעמוד קוראים InsMessage.asp והוא ישתמש בפקודת ה-INSERT של ה-SQL כדי להכניס את הנתונים למסד.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52  
<%
Dim subject, message, parentId, rootId, oConn
Set oConn = Server.CreateObject("ADODB.Connection")
oConn.Open "Provider=microsoft.jet.oledb.4.0;Data Source=" & Server.MapPath("../db/ourSongDb.mdb") & ";"
subject = Request.Form("subject")
message = Request.Form("message")
parentId = Request.Form("parentId")
rootId = Request.Form("rootId")

if (parentId="") Then ' אם להכניס הודעה או תגובה
  insertNewMess 0,subject,message
else
  insertNewRess parentId,subject,message, rootId
End If

Sub insertNewMess (pId, subject, mess) ' פונקציה להכנסת הודעה חדשה
    Dim rs, SQLinsert, SQLUpdate
    oCon.BeginTrans
    SQLinsert = "INSERT INTO forum (parentid, subject, message,groupDate)"
    SQLinsert = SQLinsert & " VALUES (" & pId & ",'" & subject & "','" & mess & "',now())"
    oConn.Execute SQLinsert
    Set rs = Server.CreateObject("ADODB.Recordset")
    rs.Open "SELECT MAX(id) FROM forum",oConn
    oCon.CommitTrans
    SQLUpdate = "UPDATE forum SET forum.rootId=" & rs(0) & " WHERE forum.id=" & rs(0)
    oConn.Execute SQLUpdate
    rs.Close
    Set rs = nothing
End Sub


Sub insertNewRess(pId, subject, mess, rId)' פונקציה להכנסת תגובה חדשה
    Dim SQLupdate, SQLinsert, rs, dt, rsDt
    SQLinsert = "INSERT INTO forum (parentid, subject, message, rootId)"
    SQLinsert = SQLinsert & " Values(" & pId & ",'" & subject & "','" & mess & "'," & rId & ")"
    oConn.Execute SQLinsert
    Set rs = Server.CreateObject("ADODB.Recordset")
    rs.Open "SELECT forum.groupDate FROM forum WHERE forum.id=" & rId, oConn
    dt = Now()
    rsDt = CDate(rs.Fields("groupDate"))
    if (rsDt<=dt) Then ' בדיקה האם ההודעה הראשית(הודעת השרשור) נעוצה או לא
        SQLupdate = "UPDATE forum SET forum.groupDate=now() WHERE forum.id=" & rId
        oConn.Execute SQLupdate
    End If
    rs.Close
    Set rs = nothing
End Sub

oConn.Close
Set oConn = nothing
Response.Redirect("index.asp") ' להחזיר לעמוד הצגת ההודעות
%>


חשוב לציין שלהוספת הודעה חדשה עלינו לשלוח parentID עם הערך 0
ולהוספת תגובה עלינו לשלוח parentId שיהיה שווה ל-ID של ההודעה אליה אנו מגיבים וגם את ה-rootId של ההודעה אחרת היא לא תוצג וגם לא תיעשה הקפצה.

וזהו סיימנו את המאמר. תכנות נעים :

ניר טייב

בונה אתרים ומתכנת בשפות:
HTML, CSS, JavaScript, PHP 5, JSP&Servlets ורובי.
תגיות: ASP‏  /  JS‏  /  פורום‏  /  רקורסיה‏  /  נעיצה‏  /  הקפצה‏  

תגובות בפייסבוק

תגובות למאמר



תגיות פופולאריות

X
הצטרף לעמוד שלנו בפייסבוק להישאר מעודכן!
וובמאסטר © כל הזכויות שמורות