Project

General

Profile

Database » History » Version 12

Panayotis Katsaloulis, 10/02/2014 14:35

1 1 Panayotis Katsaloulis
h1. Σύνδεση με τη βάση δεδομένων μέσω του αντικειμένου Database
2
3 5 Αλέξανδρος Διαμαντίδης
Η σύνδεση με τη βάση δεδομένων από την έκδοση του Open eClass 3 και μετά γίνεται με τη χρήση του αντικειμένου Database που ορίζεται στο  @modules/db/database.php@. Στη κανονική χρήση των βιβλιοθηκών το αντικείμενο αυτό παρέχεται στο χρήστη μέσω της @init.php@, οπότε δε χρειάζεται κάποιο @require@ ή @include@ για να χρησιμοποιηθεί, απλά να χρησιμοποιηθούν οι μέθοδοί του.
4 1 Panayotis Katsaloulis
5 10 Panayotis Katsaloulis
Η πρόσβαση στο αντικείμενο αυτό γίνεται κυρίως με τη static μέθοδο @get()@. Αν και είναι δυνατή η κατασκευή ενός τέτοιου αντικειμένου, δεν είναι προτιμόμενη μέθοδος, γιατί μέσω της @get()@ υπάρχουν ορισμένες βελτιστοποιήσεις κατά τη σύνδεση με τη βάση. Σε περίπτωση που θέλουμε να συνδεθούμε στη προκαθορισμένη βάση δεδομένων του eClass τότε η πρόσβαση γίνεται ως εξής:
6 2 Panayotis Katsaloulis
<pre><code class="php">
7 1 Panayotis Katsaloulis
  Database::get();
8 2 Panayotis Katsaloulis
</code></pre>
9 1 Panayotis Katsaloulis
10 10 Panayotis Katsaloulis
Αν υπάρχει ανάγκη σύνδεσης σε κάποια άλλη βάση,  τότε δίνουμε στη μέθοδο get() μία παράμετρο. Παράδειγμα, για να συνδεθούμε στη βάση TM001 τότε η σωστή σύνταξη είναι η εξής:
11 2 Panayotis Katsaloulis
<pre><code class="php">
12 1 Panayotis Katsaloulis
  Database::get("TM001");
13 2 Panayotis Katsaloulis
</code></pre>
14 1 Panayotis Katsaloulis
15
Οι μέθοδοι που υποστηρίζονται είναι οι εξής:
16
<pre><code class="php">
17 10 Panayotis Katsaloulis
// Transactional Methods
18 2 Panayotis Katsaloulis
Database::get()->query($statement);
19
Database::get()->querySingle($statement);
20 1 Panayotis Katsaloulis
Database::get()->queryArray($statement);
21
Database::get()->queryFunc($statement, $callback_function);
22 10 Panayotis Katsaloulis
23
// Non-Transactional Methods
24
Database::get()->queryNT($statement);
25
Database::get()->querySingleNT($statement);
26
Database::get()->queryArrayNT($statement);
27
Database::get()->queryFuncNT($statement, $callback_function);
28 1 Panayotis Katsaloulis
</code></pre>
29
30 11 Panayotis Katsaloulis
Υπάρχουν δύο οικογένειες μεθόδων, οι transactional και η non-transactional. Η πρώτη κατηγορία είναι η προτιμώμενη μέθοδος οποτεδήποτε γίνεται ένα επερώτημα. Στην περίπτωση που βρισκόμαστε όμως εν μέσω ενός επερωτήματος και επιθυμούμε να εκτελέσουμε ένα «εσωτερικό» χωρίς ακόμα να έχει τελειώσει το «εξωτερικό» επερώτημα, τότε υποχρεωτικά το εσωτερικό επερώτημα πρέπει να μην είναι transactional γιατί αυτό δεν υποστηρίζεται από τη βάση δεδομένων. Τέτοια περίπτωση είναι πρακτικά κάθε φορά που στην @$callback_function@ της μεθόδου @queryFunc@ θέλουμε να εκτελέσουμε ένα επερώτημα. Τότε όλα ανεξαιρέτως τα επερωτήματα μέσα στη  @$callback_function@ πρέπει να είναι non-transactional, εφόσον η ίδια η πρώτη κλήση στη @queryFunc@ πρέπει να είναι transactional.
31 1 Panayotis Katsaloulis
32 10 Panayotis Katsaloulis
Η αντιστοιχία των μεθόδων είναι ως εξής:
33
|_. transactional |_. non-transactional |
34
| query  | queryNT  |
35
| querySingle  | querySingleNT |
36
| queryArray  | queryArrayNT |
37
| queryFunc  | queryFuncNT  |
38
39
Σε κάθε μία μέθοδο, η πρώτη παράμετρος είναι το επερώτημα SQL που μας ενδιαφέρει να εκτελέσουμε, ακολουθούμενο από μία λίστα με παραμέτρους που θα γίνει εισαγωγή στο επερώτημα. Είναι λάθος πρακτική σε περίπτωση που θέλουμε να εισάγουμε κάποια παράμετρο στο επερώτημα να τη δώσουμε λεκτικά στο επερώτημα αυτό καθ'εαυτό, από το να το δώσουμε σαν επιπρόσθετη παράμετρο. Για να σημειώσουμε στο ίδιο το επερώτημα σε ποιο σημείο είναι η κάθε παράμετρος, χρησιμοποιούμε το σύμβολο «?» ακολουθούμενο από ένα γράμμα που δηλώνει τον τύπο της παραμέτρου. Σε περίπτωση που ως μία παράμετρο δωθεί πίνακας, τότε ο πίνακας θα αναλυθεί στα επιμέρους στοιχεία του με τη σειρά με την οποία εμφανίζονται, ώστε το τελικό αποτέλεσμα των παραμέτρων τελικά να είναι ένας μονοδιάστατος πίνακας, ανεξάρτητα από την επιμέρους δομή των αρχικών παραμέτρων. 
40
41
Στον επόμενο πίνακα φαίνονται οι αντιστοιχίες τύπων και χαρακτήρων. Η αντιστοιχία είναι όμοια με αυτή της @printf@ της @stdio@ της C, ή του @java.util.Formatter@ της Java, μόνο που αντί για @%@ χρησιμοποιείται ο @?@:
42
|_. Χαρακτήρας |_. Τύπος παραμέτρου |
43
| @?d@  | ακέραιος αριθμός |
44 11 Panayotis Katsaloulis
| @?b@  | δυαδικός (true/false)  |
45 10 Panayotis Katsaloulis
| @?f@  | δεκαδικός αριθμός  |
46
| @?s@  | συμβολοσειρά  |
47 12 Panayotis Katsaloulis
| @?t@  | ώρα/ημερομηνία  |
48 10 Panayotis Katsaloulis
49 2 Panayotis Katsaloulis
Για παράδειγμα, αν θέλουμε να εκτελέσουμε το επόμενο επερώτημα SQL
50 1 Panayotis Katsaloulis
<pre><code class="sql">
51 2 Panayotis Katsaloulis
DELETE FROM course_review WHERE course_id = 1;
52 1 Panayotis Katsaloulis
</code></pre> o σωστός τρόπος να συντάξουμε τις παραμέτρους είναι ο εξής:
53 2 Panayotis Katsaloulis
<pre><code class="php">
54 10 Panayotis Katsaloulis
Database::get()->query("DELETE FROM course_review WHERE course_id = ?d", 1);
55 2 Panayotis Katsaloulis
</code></pre>
56
57 10 Panayotis Katsaloulis
*Δεν* είναι απαραίτητο να εσωκλείεται η παράμετρος σε μία βοηθητική συνάρτηση που να ορίζει ρητά τον τύπο της, σαν τις @int_val@ κλπ. αφού η διαδικασία αυτή εκτελείται αυτόματα από τη βιβλιοθήκη.  Σε περίπτωση που δε δωθεί ρητά ο τύπος, αυτό θα εγείρει μήνυμα λάθους που θα σημειώνεται το επερώτημα, σε ποιο σημείο δεν δηλώθηκε ρητά ο τύπος της παραμέτρου, ποια είναι η τρέχουσα τιμή και τι υπέθεσε το σύστημα πως είναι η τιμή αυτή.  
58 1 Panayotis Katsaloulis
59
Πριν τη λίστα με τις παραμέτρους που θα εισαγχθούν στο επερώτημα, είναι δυνατόν να παρέχουμε ως παράμετρο μια συνάρτηση επανάκλησης (_callback function_) ώστε να ενημερωθούμε αν κάτι δε πάει καλά. Υποχρεωτικά αυτή η συνάρτηση θα πρέπει να είναι δεύτερη παράμετρος (ή ειδικά για την @queryFunc@ να είναι τρίτη παράμετρος), αλλιώς θα αγνοηθεί από το σύστημα. Αν η δεύτερη (ή αντίστοιχα τρίτη) παράμετρος δεν είναι συνάρτηση, τότε η δυνατότητα επανάκλησης δε θα ενεργοποιηθεί καθόλου. Ένα παράδειγμα της χρήσης αυτής της μεδόδου δίνεται στη μέθοδο  <code class="php">query($statement)</code> και <code class="php">queryFunc($statement, $callback_function)</code>
60
61 8 Panayotis Katsaloulis
h2. Μέθοδος @query($statement);@
62 2 Panayotis Katsaloulis
63
Με τη μέθοδο αυτή εκτελούμε ένα απλό επερώτημα. Η επιστρεφόμενη τιμή αυτής της συνάρτησης είναι ένα αντικείμενο τύπου <code class="php">DBResult</code>. Παράδειγμα χρήσης:
64 8 Panayotis Katsaloulis
<pre><code class="php">
65 1 Panayotis Katsaloulis
$cid = 1;
66 10 Panayotis Katsaloulis
$affectedRows = Database::get()->query("DELETE FROM course_review WHERE course_id = ?d", $cid)->affectedRows;
67 1 Panayotis Katsaloulis
</code></pre>
68 8 Panayotis Katsaloulis
69 2 Panayotis Katsaloulis
Το αντικείμενο <code class="php">DBResult</code> έχει δύο ιδιότητες, την <code class="php">$lastInsertID</code> και την <code class="php">$affectedRows</code>. Μπορούμε να χρησιμοποιήσουμε αυτές τις τιμές δεδομένου του συγκεκριμένου επερωτήματος. 
70
71
Σε περίπτωση που μας ενδιεφέρει να ενημερωθούμε αν κάτι δεν έχει πάει καλά, αυτό γίνεται με τη συνάρτηση επανάκλησης, για παράδειγμα ως εξής:
72
<pre><code class="php">
73
$cid = 1;
74 10 Panayotis Katsaloulis
Database::get()->query("DELETE FROM course_review WHERE course_id = ?d", function ($errormsg) {
75 2 Panayotis Katsaloulis
        echo "An error has occured: " . $errormsg;
76
    },  $cid);
77
</code></pre>
78 1 Panayotis Katsaloulis
79 2 Panayotis Katsaloulis
80
h2. Μέθοδος @querySingle($statement);@
81
82
Με τη μέθοδο αυτή εκτελούμε ένα επερώτημα από το οποίο περιμένουμε να λάβουμε ως αποτέλεσμα ένα μοναδικό στοιχείο, ή με άλλα λόγια μία και μόνο μία γραμμή. Ο αριθμός των στηλών του αποτελέσματος δεν έχει σημασία. Η σύνταξη είναι ίδια με αυτή της μεθόδου <code class="php">query($statement);</code>
83
84
Ως αποτέλεσμα λαμβάνουμε ένα αντικείμενο όπου κάθε ιδότητά του (property) έχει τιμή ίδια με το όνομα της στήλης σε περιβάλλον SQL. Για το λόγο αυτό είναι προτιμότερο κάθε στήλη να έχει ένα μοναδικό όνομα, ώστε να είναι μετά άμεση η προσπέλαση σε αυτό. Αν δεν υπάρχουν αποτελέσματα επιστρέφεται το @null@, οπότε είναι θεμιτό να γίνεται έλεγχος για @null@ σε περίπτωση που ξέρουμε πως μπορεί να μην υπάρχουν αποτελέσματα. Για παράδειγμα
85
<pre><code class="php">
86 10 Panayotis Katsaloulis
$stat_object = Database::get()->querySingle("SELECT status FROM course_user WHERE user_id = ?d AND course_id = ?d", $uid, $course_id);
87 2 Panayotis Katsaloulis
if ($stat_object) {
88
  $status = $stat_object->status;
89
}
90
</code></pre> 
91
Ένα άλλο παράδειγμα: <pre><code class="php">
92
$openCoursesNum = Database::get()->querySingle("SELECT COUNT(id) as count FROM course_review WHERE is_certified = 1")->count;
93
</code></pre>
94
95
h2. Μέθοδος @queryArray($statement);@
96
97 1 Panayotis Katsaloulis
Αυτή η μέθοδος εκτελείται όταν περιμένουμε από το επερώτημα να επιστρέψει περισσότερο από ένα στοιχείο (ή γραμμή) και θέλουμε να πάρουμε αμέσως όλα τα αντικείμενα σε έναν πίνακα. Η μέθοδος αυτή μπορεί να χρησιμοποιηθεί σε περίπτωση που περιμένουμε τα αποτελέσματα να είναι λίγα στον αριθμό. Σε περίπτωση που είναι αρκετά, *είναι προτιμότερη η χρήση της μεθόδου @queryFunc@*.
98 2 Panayotis Katsaloulis
99
Η σύνταξη της μεθόδου είναι αντίστοιχη ομοίως της <code class="php">query($statement);</code>
100
101
Η επιστρεφόμενη τιμή είναι ένας πίνακας με αντικείμενα, όπου τα αντικείμενα είναι αντίστοιχα με της μεθόδου <code class="php">querySingle($statement);</code>
102
103
Παράδειγμα χρήσης της μεθόδου
104
105
<pre><code class="php">
106 10 Panayotis Katsaloulis
$moduleIDs = Database::get()->queryArray("SELECT module_id FROM course_module WHERE visible = 1 AND course_id = ?d", $course_id);
107 2 Panayotis Katsaloulis
foreach ($moduleIDs as $module) {
108
  $publicModules[] = $module->module_id;
109
}
110
</code></pre>
111
112 1 Panayotis Katsaloulis
h2. Μέθοδος @queryFunc($statement, $callback_function);@
113 2 Panayotis Katsaloulis
114
Η μέθοδος αυτή αναφέρεται σε επερωτήματα που μπορεί να έχουν πολλά αποτελέσματα, αλλά σε αντίθεση με την @queryArray@ λαμβάνουμε ένα αποτέλεσμα τη φορά. Αυτή η συνάρτηση είναι ιδανική όταν τα αποτελέσματα που αναμένουμε είναι αρκετά, αλλά είναι εξίσου χρήσιμη σε περίπτωση που περιμένουμε λίγα, ένα ή και καθόλου αποτελέσματα.
115
116
Ως δεύτερη παράμετρο δίνουμε μία function με μία και μοναδική παράμετρο, όπου θα αποθηκευτεί το τρέχον αποτέλεσμα της εκτέλεσης του επερωτήματος για κάθε _γραμμή_ (σε σύνταξη SQL). Η σύνταξη της παραμέτρου αυτής είναι ίδια με τη σύνταξη της συνάρτησης επανάκλησης σε περίπτωση λάθους, όπως αναφέρθηκε προηγουμένως.
117
118 1 Panayotis Katsaloulis
Επειδή η ορατότητα των εξωτερικών μεταβλητών μέσα στη συνάρτηση είναι περιορισμένη, μπορούμε να χρησιμοποιήσουμε το λεκτικό <code class="php">use($param1, &$param2 ...)</code>, ώστε να ενημερώσουμε την PHP πως ενδιαφερόμαστε να χρησιμοποιήσουμε τις μεταβλητές @$param1@  και @$param2@ εσωτερικά στη συνάρτησή μας. Ειδικά για τη παράμετρο @$param2@, με τη χρήση του συμβόλου @&@ υποδηλώνουμε πως η γίνεται «καθ'αναφορά» η δήλωση της μεταβλητής, δηλαδή οποιαδήποτε αλλαγή που θα γίνει εσωτερικά, θα αλλάζει συνολικά την τιμή της μεταβλητής αυτής.
119 2 Panayotis Katsaloulis
120
Ένα απλό παράδειγμα της μεθόδου αυτής είναι το εξής:
121
<pre><code class="php">
122 10 Panayotis Katsaloulis
DataBase::get()->queryFunc("SELECT title  FROM course_units WHERE course_id = ?d", function($unit) {
123 2 Panayotis Katsaloulis
    echo "title: " . $unit->title;
124
  }, intval($courseId));</code></pre>
125
126 1 Panayotis Katsaloulis
Ένα πλήρες παράδειγμα και με συνάρτησης επανάκλησης σε περίπτωση λάθους:
127
<pre><code class="php">
128 2 Panayotis Katsaloulis
$counter = 0;
129 10 Panayotis Katsaloulis
Database::get()->queryFunc("SELECT course.id as cid, course.code as code, FROM course WHERE course.id = ?d" , function ($course_info) use ($outer_variable, &$counter, &$title) {
130 2 Panayotis Katsaloulis
    $counter++;
131
    echo "For item #" . $counter . " with outer variable " . $outer_variable . " the course id is . " $course_info->id . " while the course code is " . $course_info->code;
132
  } , function ($errormsg) use($urlServer) {
133
    echo "Error " . $errormsg . " while connected on " . $urlServer;
134
    exit();
135
  } , $dbname);
136 3 Panayotis Katsaloulis
</code></pre>
137 10 Panayotis Katsaloulis
138
Να υπενθυμίσουμε πως αν θέλουμε να εκτελέσουμε ένα άλλο επερώτημα μέσα στη @callback_function@, τότε τα επερωτήματα μέσα θα πρέπει να είναι *non-transactional*.