Από MySQL σε PDO

Σε αυτή την περιοχή μπορείτε να βρείτε ή να αναζητήσετε πληροφορίες σχετικές με την PHP

Συντονιστές: WebDev Moderators, Super-Moderators, PHP Moderators

Απάντηση
gtsoukn
Δημοσιεύσεις: 33
Εγγραφή: 23 Ιούλ 2015 13:36

Από MySQL σε PDO

Δημοσίευση από gtsoukn » 12 Σεπ 2016 01:00

Καλησπέρα (για την ακρίβεια ...καλημέρα).

Το site του το έχω σε MySQL και ξεκινάω να το μετατρέψω σε PDO.
(Πιστεύω να το λέω σωστά, αν όχι συγχωρέστε με)

Καθότι άσχετος, ξανάρχισα το ψάξιμο.

Από τα μέχρι τώρα:
- Για σύνδεση στη βάση

Κώδικας: Επιλογή όλων

try {
	$conn = new PDO("mysql:host=$servername;dbname=db_pdo;charset=utf8", $username, $password);
	// set the PDO error mode to exception
	$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
	//echo "Επιτυχής σύνδεση";
	}
catch(PDOException $e)
	{
	echo "Αποτυχία σύνδεσης: " . $e->getMessage();
	}
* Στην αρχή του ίδιου αρχείου δίνω τις τιμές των $servername, $username και $password.

- Στην αρχική σελίδα για εμφάνιση μέρους των δεδομένων

Κώδικας: Επιλογή όλων

$stmt = $conn->query&#40;'SELECT GroupID, GroupINT, GroupEL, UpdateDate FROM pois_groups WHERE GroupID<>1 && UpdateDate<>0 ORDER BY UpdateDate DESC LIMIT 10'&#41;;
while&#40;$row = $stmt->fetch&#40;PDO&#58;&#58;FETCH_ASSOC&#41;&#41; &#123;
	echo '<tr><td style="width&#58; 70%"><img src="folder/' . $row&#91;'GroupINT'&#93; . '.bmp" alt="' . $row&#91;'GroupINT'&#93; . '" width="20" height="20">&nbsp;<a href="file.php?GroupID=' . $row&#91;'GroupID'&#93; . '" style="color&#58; #000000"><u>' . $row&#91;'GroupEL'&#93; . '</u></a></td><td style="text-align&#58; center">' . date&#40;'d/m/Y', $row&#91;'UpdateDate'&#93;&#41; . '</td></tr>';
&#125;
* Η ημερομηνία αποθηκεύεται σε μορφή TIMESTAMP.
* Το άνοιγμα και το κλείσιμο του πίνακα εννοείται ότι υπάρχει.

Με τα παραπάνω λειτουργεί σωστά, είναι όμως;

Στο http://www.w3schools.com/php/php_mysql_select.asp (παράγρ. Select Data With PDO) δίνει έναν άλλο παρόμοιο κώδικα αλλά δεν μπορώ να τον καταλάβω ώστε να τον τροποποιήσω στις ανάγκες μου.
Αν αυτός είναι ο σωστός τρόπος, πώς μπορώ να το τροποποιήσω για να βγάλω το παραπάνω αποτέλεσμα;

Ευχαριστώ
Γιώργος

Άβαταρ μέλους
jpk
Δημοσιεύσεις: 441
Εγγραφή: 09 Μαρ 2011 21:17

Από MySQL σε PDO

Δημοσίευση από jpk » 12 Σεπ 2016 20:33

Γεια σου gtksoun,

Θα κόψω την απάντησή μου στα δύο μυνήματα , σε αυτό θα δούμε τα βασικά και στο επόμενο την χρήση μια DB class που κληρονομεί από το PDO για να κάνεις την χρήση του ακόμα πιο εύκολη.

Αυτό που προφανώς θέλεις να κάνεις είναι να από mysql_* functions σε PDO. Η MySQL είναι σύστημα διαχείρισης βάσης δεδομένων ( relational database management system (RDBMS) ) οπότε κρατάμε την MySQL και χρησιμοποιώντας PDO . Άλλα RDBMS είναι π.χ. η DB2 , η MariaDB , PostgreSQL , Access , NoSQL , και αρκετά ακόμα , το PDO μπορείς να το χρησιμοποιήσεις για σύνδεση σε MySQL ή σχεδόν σε οποιοδήποτε άλλο RDBMS.

Ξεκινώ αμφισβητώντας κάτι που έγραψες , ότι είσαι άσχετος. Και μόνο το γεγονός ότι αποφάσισες να κάνεις αυτή την αλλαγή δείχνει ότι έχεις αρχίσει και κίνησε σε πολύ καλό δρόμο. Ας δούμε κάποιους λόγους που είναι πολύ σωστή η απόφασή σου να μεταβείς από την χρήση των mysql_* functions σε χρήση του PDO.

- Οι mysql_* functions είναι deprecated (βγάζουν δηλαδή μήνυμα προειδοποίησης να μην χρησιμοποιούνται) ήδη από το 2013 .
- Οι mysql_* functions έχουν αφαιρεθεί τελείως από τις νέες εκδόσεις της PHP.
- Οι mysql_* functions δημιουργούν εκ' φύσεως κενά ασφαλείας (καθώς δεν υποστηρίζουν prepared statements)
- Οι mysql_* functions απαιτούν μπερδεμένο functional και procedural και χωρίς να έχεις απλοποιημένο τρόπο να πιάσεις λάθη και exceptions.

Υπάρχουν πολλά ακόμα που οδήγησαν την PHP να τις κάνει deprecated το 2013 και να τις αφερέσει απόλυτα στην συνέχεια.

Με την χρήση του PDO κάνουμε χρήση αντικειμενοστρεφή προγραμματισμού. Δεν θα αρχίσω με το κλασικό παράδειγμα "ποδήλατο" για το τι είναι αντικείμενο , αν χρειάζεται πες μου.

PDO σημαίνει PHP Data Objects , (θα χρησιμοποιώ ενικό καθώς αναφερόμαστε σε ένα) , ουσιαστικά θα δημιουργήσουμε ένα αντικείμενο χειρισμού της βάσης δεδομένων μας (σύνδεσης , ερωτημάτων κ.ο.κ.).

Ας υποθέσουμε ότι έχουμε μια βάση (schema) που τη λέμε test_db σε αυτή έχει δικαιώματα ο χρήστης testUser που έχει password testPassword. Στην βάση μας έχουμε έναν πίνακα τον users που έχει μόνο τρία πεδία id (ΙΝΤ 10 , primary key , AI (πρωτεύων κλειδί που το αποδίδει η βάση αυξάνοντας κατά ένα) ) , name (VARCHAR 100) , email (VARCHAR 100). Η βάση μας έχει collation UTF8 (θα χρησιμοποιούμε δηλαδή το σετ χαρακτήρων UTF8).

Πάμε να φτιάξουμε το πρώτο PDO αντικείμενό μας:

Κώδικας: Επιλογή όλων

error_reporting&#40;E_ALL&#41;; 
ini_set&#40;'display_errors', '1'&#41;; // Για να ξέρουμε τι μας γίνεται 

$dbSchema = "test_db";
$dbUser = "testDbUser";
$dbPassword = "testPassword";

$db = new PDO&#40;"mysql&#58;dbname=".$dbSchema.";host=localhost;charset=utf8"
	,$dbUser,$dbPassword&#41;;
Όπως βλέπεις τα πράγματα είναι πολύ απλά , στις μεταβλητές που δώσαμε στον constructor ( δεν θα μεταφράζω τέτοια) λέμε στην
Πρώτη μεταβλητή: ότι αναφερόμαστε σε MySQL και η default βάση / σχήμα που θα χίνει η σύνδεση είναι η test_db , ακόμα του λέμε ότι ο host είναι ο localhost (όποιος και αν είναι αυτός) και να χρησιμοποιεί για αυτή την σύνδεση UTF8
Δεύτερη μεταβλητή: Ότι ο χρήστης είναι ο testUser
Τρίτη μεταβλητή: Ότι το password είναι το testPassword

Άν χρησιμοποιούμε PHP με version μικρότερη της 5.3.6 τότε το να χρησιμοποιεί UTF8 πρέπει να το δώσουμε σαν εντολή SET NAMES μετά την σύνδεση , π.χ.:

Κώδικας: Επιλογή όλων

error_reporting&#40;E_ALL&#41;;
ini_set&#40;'display_errors', '1'&#41;;

$dbSchema = "test_db";
$dbUser = "testDbUser";
$dbPassword = "testPassword";

$db = new PDO&#40;"mysql&#58;dbname=".$dbSchema.";host=localhost"
	,$dbUser,$dbPassword&#41;;
$db->exec&#40;"set names utf8"&#41;;
(Θα έχω περισσότερες επιλογές για την δημιουργία ενός PDO object στο επόμενο μήνυμα)

Δημιουργώντας αυτό το PDO object που το λέμε $db αλλά θα μπορούσαμε να το λέμε και $pdo ή και $eleni η PHP θα προσπαθήσει να συνδεθεί σε αυτή την βάση με αυτά τα στοιχεία που δώσαμε, δεν σε μπλέκω προσωρινά με το τι θα γίνει αν δεν τα καταφέρει.

Ας υποθέσουμε τώρα ότι θέλουμε να επιλέξουμε από τον πίνακα users όλους τους χρήστες που έχουν name "Θανάσης Παπακωνσταντίνου" :

Κώδικας: Επιλογή όλων

$statement = $db->prepare&#40;"SELECT * FROM users WHERE name = ?"&#41;;
$statement->execute&#40;array&#40;"Θανάσης Παπακωνσταντίνου"&#41;&#41;;
$r = $statement->fetchAll&#40;PDO&#58;&#58;FETCH_ASSOC&#41;;
var_dump&#40;$r&#41;;
Αν δεν υπάρχουν θα δείς
array(0) { }
ενώ αν υπάρχουν δύο θα δείς π.χ.:
array(2)
{
[0]=> array(3) { ["id"]=> string(1) "1" ["name"]=> string(47) "Θανάσης Παπακωνσταντίνου" ["email"]=> string(17) "test1@example.com" }
[1]=> array(3) { ["id"]=> string(1) "2" ["name"]=> string(47) "Θανάσης Παπακωνσταντίνου" ["email"]=> string(17) "test2@example.com" }
}
Θα παρατήρησες ότι στο κείμενο του sql query αντί για όνομα υπάρχει ένα ερωτηματικό. Ήρθε η ώρα να μιλήσουμε για αυτά τα ερωτηματικά και για τα prepared statements.

Όταν κάνουμε ένα ερώτημα (βάλε μέσα update , insert και οτιδήποτε) στην βάση ο ποιο ασφαλής (γρήγορος και εύκολος) τρόπος είναι να πούμε από νωρίτερα στην βάση το ποιο ερώτημα πρόκειται να κάνουμε. Με αυτόν τον τρόπο η βάση μας μπορεί να ξέρει από νωρίτερα το τι είδους μεταβλητές δέχεται και σε ποια πεδία και να φτιάξει ένα σενάριο που θα ακολουθεί για το πως θα εκτελέσει με τον πιο αποδοτικό τρόπο αυτά τα ερωτήματά μας (δεν θα μακρηγορήσω με την θεωρία). Επίσης και μόνο με την χρήση των prepared statements αποφεύγουμε ένα πολύ μεγάλο ποσοστό επιθέσεων SQL injections και άλλα.

Ας δούμε όμως αναλυτικά το τι κάναμε:
Φτιάξαμε ένα statement το $statement αυτό το κάναμε χρησιμοποιώντας το PDO object μας $db την μέθοδο prepare (που γυρίζει ένα αντικείμενο τύπου PDOStatement)
Στην συνέχεια είπαμε σε αυτό το statement να εκτελεστεί με μεταβλητή ένα array που έχει μέσα του μόνο ένα string το "Θανάσης Παπακωνσταντίνου". Επί της ουσίας αυτό το array θα χρησιμοποιήσει η βάση όπου βλέπει ερωτηματικά "?" στο sql query μας. Αν είχαμε δύο ερωτηματικά στο sql string μας τότε θα έπρεπε να έχουμε δύο στοιχεία μέσα στο array που θα χρησιμοποιούνταν αντιστοίχως.
Στην συνέχεια φτιάξαμε μια μεταβλητή $r για να κρατήσουμε μέσα της τα αποτελέσματα , αυτά τα πήραμε χρησιμοποιώντας την fetchAll μέθοδο του $statement μας η οποία κάνει ακριβώς αυτό fetch all (τα φέρνει όλα τα αποτελέσματα). Το PDO::FETCH_ASSOC
που έχουμε εκεί μέσα λέει στο $statement να τα φέρει σαν ένα πολυδιάστατο array. Αυτό έχει κλειδί για κάθε στοιχείο τον αριθμό του row / σειράς του αποτελέσματος και σαν τιμή σε κάθε ένα από αυτά ένα άλλο array με κλειδί το όνομα του πεδίου και τιμή την τιμή του.
Τελικά κάναμε var_dump το $r για να δούμε τι κάναμε

Ας δούμε όμως τι θα κάναμε αν θέλαμε να φτιάξουμε ένα βρόγχο για κάθε σειρά αποτελέσματος:

Κώδικας: Επιλογή όλων

if&#40;count&#40;$r&#41; > 0&#41;
&#123;
	foreach&#40;$r as $row&#41;
	&#123;
		var_dump&#40;$row&#41;;
	&#125;
&#125;
Πολύ σημαντικός ο πρώτος έλεγχος ότι δηλαδή υπάρχουν αποτελέσματα

Κώδικας: Επιλογή όλων

if&#40;count&#40;$r&#41; > 0
. Αν δεν υπάρχουν αποτελέσματα τότε ο βρόγχος μας δεν έχει κανένα νόημα (και θα μας πετάξει error το foreach)

Ας κάνουμε και ένα ερώτημα χωρίς καμία μεταβλητή (για να μην σε μπερδέψω χρησιμοποιώ τον ίδιο τρόπο)

Κώδικας: Επιλογή όλων

$statement = $db->prepare&#40;"SELECT * FROM users ORDER BY ID ASC LIMIT 0,1"&#41;;
$statement->execute&#40;array&#40;&#41;&#41;;
$r = $statement->fetchAll&#40;PDO&#58;&#58;FETCH_ASSOC&#41;;
var_dump&#40;$r&#41;;
Ας κάνουμε και ένα INSERT:

Κώδικας: Επιλογή όλων

$statement = $db->prepare&#40;"INSERT INTO users &#40;name,email&#41; VALUES &#40;?,?&#41;"&#41;;
$statement->execute&#40;array&#40;"Νίκος Καββαδίας","test44@example.com"&#41;&#41;;
Εδώ παρατηρείς ότι δεν παίρνω τίποτα σαν αποτέλεσμα , καθώς κάνω εισαγωγή. Ας υποθέσουμε ότι ήθελα να πάρω το id του row που έγινε εισαγωγή στον πίνακα:

Κώδικας: Επιλογή όλων

$statement = $db->prepare&#40;"INSERT INTO users &#40;name,email&#41; VALUES &#40;?,?&#41;"&#41;;
$statement->execute&#40;array&#40;"Νίκος Καββαδίας","test44@example.com"&#41;&#41;;
$id = $db->lastInsertId&#40;&#41;;
Ας κάνουμε και ένα update:

Κώδικας: Επιλογή όλων

$statement = $db->prepare&#40;"UPDATE users SET email = ? WHERE id = ?"&#41;;
$statement->execute&#40; array&#40; "testSomthingNew@example.com" , 1 &#41; &#41;;
Και ένα delete:

Κώδικας: Επιλογή όλων

$statement = $db->prepare&#40;"DELETE FROM users WHERE id = ?"&#41;;
$statement->execute&#40; array&#40;2&#41; &#41;;
Στο επόμενο θα δούμε ένα αντικείμενο που κληρονομεί από το PDO που λύνει κάποια ζητήματα και κάνει ακόμα ευκολότερη την χρήση του , όπως και σκέφτομαι και για ένα τρίτο μήνυμα με χειρισμό λαθών / exceptions.

Πες μου αν είμαστε εντάξει έως εδώ ή έχεις κάποια απορία για να συνεχίσω.

gtsoukn
Δημοσιεύσεις: 33
Εγγραφή: 23 Ιούλ 2015 13:36

Από MySQL σε PDO

Δημοσίευση από gtsoukn » 13 Σεπ 2016 00:44

Έχεις σκεφτεί να γράψεις κανένα βοήθημα;
(Εκτός αν το έχεις ήδη κάνει σε θέμα που δεν έχω ασχοληθεί)

Μπορώ να πω ότι τους κώδικες-παραδείγματα που παραθέτεις τους είχα.
Το θέμα όμως είναι ποιο;
Ότι τους χρησιμοποιούσα (ή θα τους χρησιμοποιούσα) "ως έχουν" χωρίς να κατανοώ το πώς και το γιατί.

Από τα παραπάνω έφτιαξα τη SELECT (μιας και είναι στην αρχική σελίδα) ως εξής:

Κώδικας: Επιλογή όλων

<?php
// Εμφάνιση 10 πρώτων ομάδων με σειρά τελευταίας ενημέρωσης
$stmt = $db->prepare&#40;'SELECT GroupID, GroupINT, GroupEL, UpdateDate FROM groups WHERE GroupID <> ? AND UpdateDate <> ? ORDER BY UpdateDate DESC LIMIT 10'&#41;;
$stmt->execute&#40;array&#40;1,0&#41;&#41;;      // GroupID <> 1 AND UpdateDate <> 0
$r = $stmt->fetchAll&#40;PDO&#58;&#58;FETCH_ASSOC&#41;;
foreach&#40;$r as $row&#41; &#123;
	echo '<tr><td style="width&#58; 70%"><img src="folder/' . $row&#91;'GroupINT'&#93; . '.bmp" alt="' . $row&#91;'GroupINT'&#93; . '" width="20" height="20">&nbsp;<a href="file.php?GroupID=' . $row&#91;'GroupID'&#93; . '" style="color&#58; #000000"><u>' . $row&#91;'GroupEL'&#93; . '</u></a></td><td style="text-align&#58; center">' . date&#40;'d/m/Y', $row&#91;'UpdateDate'&#93;&#41; . '</td></tr>';
&#125;
?>
Γιώργος

Άβαταρ μέλους
jpk
Δημοσιεύσεις: 441
Εγγραφή: 09 Μαρ 2011 21:17

Από MySQL σε PDO

Δημοσίευση από jpk » 14 Σεπ 2016 22:48

Γεια σου gtsoukn,

Μια χαρά αυτό που έγραψες στο επίπεδο χρήσης της βάσης μέσω PDO (για τα tables και τα υπόλοιπα δεν είναι σχετικά με το θέμα οπότε δεν τα σχολιάζω). Μόνο που έχει νόημα πριν μπεις στην foreach να ελέγξεις ότι έχεις κάτι για να λουπάρεις

Κώδικας: Επιλογή όλων

if&#40;count&#40;$r&#41; > 0&#41;
.

Σου έγραψα ότι στο επόμενο μήνυμα (στο παρόν) θα δούμε μια πιο απλή και εύκολη χρήση του PDO μέσω μιας κλάσης που κληρονομεί από αυτό. Όπως είδες προηγουμένως , σχεδόν για ότι έχει να κάνει με την χρήση του PDO γράφουμε τις ίδιες 3 / 4 γραμμές κώδικα ελάχιστα διαφοροποιημένες. Αυτό από μόνο του είναι καμπανάκι ότι κάτι θα έπρεπε να κάνουμε ώστε να χρησιμοποιούμε κυρίαρχα μία μέθοδο που θα έκανε ότι θέλαμε σε μια γραμμή , αλλά απλά και καθαρά.

Δεν θα ήταν πολύ χρήσιμο να είχαμε μια μέθοδο που σαν πρώτη όρισμα (μεταβλητή μεθόδου / argument) να έπαιρνε το sql string και σαν δεύτερό τις μεταβλητές αυτού για το prepared statement (αν υπήρχαν); Ακόμα θα ήταν χρήσιμο αν στο δεύτερο όρισμα είχαμε μια μεταβλητή (για το prepared statement) να μπορούσαμε να δώσουμε μόνο μία , ενώ αν είχαμε παραπάνω να δίναμε array. Ας την πούμε request αυτή την μέθοδο γιατί επί της ουσίας αυτό κάνει. Π.χ. (χρησιμοποιώντας τα παραπάνω παραδείγματα) να είχαμε κάτι τέτοιο:

Κώδικας: Επιλογή όλων

// SELECT με μια μεταβλητή 
$r = $db->request&#40;"SELECT * FROM users WHERE name = ?"
	,"Θανάσης Παπακωνσταντίνου"&#41;;

// SELECT με δύο μεταβλητές 
$r = $db->request&#40;"SELECT * FROM users WHERE name = ? AND email = ?"
	,array&#40;"Θανάσης Παπακωνσταντίνου" , "test44@example.com"&#41; &#41;;

// SELECT χωρίς μεταβλητές 
$r = $db->request&#40;"SELECT * FROM users ORDER BY ID ASC LIMIT 0,1"&#41;;

// INSERT
$db->request&#40;"INSERT INTO users &#40;name,email&#41; VALUES &#40;?,?&#41;"
	,array&#40;"Νίκος Καββαδίας","test44@example.com"&#41; &#41;;

// UPDATE 
$db->request&#40;"UPDATE users SET email = ? WHERE id = ?"
	,array&#40; "testSomthingNew@example.com" , 1 &#41; &#41;;

// DELETE 
$db->request&#40;"DELETE FROM users WHERE id = ?" , 2&#41;;
(Να σημειώσω ότι πια αντί για array() μπορούμε να γράφουμε απλά [] από την PHP 5.4 αλλά επειδή δεν ξέρω τι version έχεις χρησιμοποιώ το array() ) . Φυσικά πάντα πρέπει να ξέρουμε (ή να έχουμε μια καλή αντίληψη) του πως λειτουργούν τα βασικά στοιχεία μιας γλώσσας (όπως π.χ. το PDO) πριν πάμε να χρησιμοποιήσουμε κάτι που κάνει την χρήση τους πιο εύκολη. Για αυτό στο προηγούμενο μήνυμα είχα τα βασικά του PDO που από ότι φαίνεται τα κατάλαβες.

Όμως υπάρχουν και άλλα πράγματα που αξίζει να δούμε στην χρήση του PDO. Ένα από τα ενδιαφέροντα στοιχεία είναι ότι το PDO (αν δεν του πούμε αλλιώς) δεν θα χρησιμοποιήσει τα prepared statements σε επίπεδο βάσης αλλά θα μιμηθεί (emulate) την λειτουργία τους στο προγραμματιστικό επίπεδο. Έτσι το PDO από μόνο του θα κάνει ΣΑΝ ΝΑ ήταν prepared statement και όχι πραγματικό σε επίπεδο βάσης. Αυτό συμβαίνει για πολλούς λόγους , αρχικά το PDO δεν γράφτηκε μόνο για την MySQL , γράφτηκε και για RDBMS που δεν έχουν σε επίπεδο βάσης prepared statements , η MySQL κάποτε (νομίζω πριν δεκατρία χρόνια όμως) δεν υποστήριζε prepared statements, υπάρχει γενικά στην PHP η λογική ότι θα κάνει τις ελάχιστες κοινές λειτουργίες εκτός αν της πεις αλλιώς. Όπως και να έχει από την στιγμή που η βάση μας υποστηρίζει prepared statements καλό είναι ο καθείς να κάνει την δική του δουλειά. Αυτό θα το πετύχουμε βάζοντας στα attributes του PDO constructor PDO::ATTR_EMULATE_PREPARES false (τα υπόλοιπα που θα βάλω εκεί δεν τα εξηγώ περισσότερο , αν θες πες μου).

Ακόμα όμως και έτσι κάθε φορά που θέλουμε να εκτελέσουμε ένα query στην βάση και φτιάχνουμε ένα statement η PHP θα ζητήσει από την βάση να το κάνει prepare , ακόμα και αν το είχε κάνει prepare ακριβώς προηγουμένως. Σκέψου πόσες αχρείαστες κλήσεις γίνονται και πόσα νέα αντικείμενα PDOStatement γίνονται instantiate χωρίς λόγο . Αυτό θα μπορούσε να λυθεί αν κρατούσαμε τα PDOStatement που γυρίζει η prepare του PDO object μας σε διαφορετικές μεταβλητές. Θα γεμίζαμε βέβαια μεταβλητές και σε μια καθαρή OOP διάταξη θα έπρεπε ή να τις κάνουμε global με κάποιο τρόπο (π.χ. statics κάπου) ή / και θα έπρεπε να τις περνάμε συνέχεια σαν ορίσματα όπου θα χρησιμοποιούνταν. Το πόσο κακογραμμένο και μπερδεμένο κώδικα θα παρήγαγε κάτι τέτοιο νομίζω είναι αυτονόητο.

Ένα ακόμα θέμα είναι το debugging του τελευταίου ερωτήματος που εκτελέστηκε. Ενώ το PDOStatement αντικείμενο έχει την debugDumpParams μέθοδο το τι επιστρέφει αυτή δεν είναι και το χρησιμότερο για να το πάρεις και να κάνεις το ερώτημα μόνος στην βάση για να δεις τι δεν πήγε καλά.

Αυτά τα θέματα θα αντιμετωπίσει η τάξη DB που θα φτιάξουμε και είναι PDO object , (καθώς κληρονομεί από αυτό). Να σημειωθεί ότι δεν την χρησιμοποιώ έτσι άλλα σε ένα ευρύτερο data layer model που έχει μέσα αρκετά άλλα , οπότε την ξαναέγραψα για να την απομονώσω (για αυτό έβαλα Ελληνικά comments ενώ πιστεύω ότι γενικά είναι χρήσιμο να έχουμε Αγγλικά comments) :

Κώδικας: Επιλογή όλων

/**
 * Μια πολύ απλή κλάση που κληρονομεί από το PDO για να κάνουμε την 
 * χρήση του ακόμα πιο απλή και τα prepared statements να γίνονται 
 * prepared μόνο μια φορά.  
 */
class DB extends PDO
&#123;
	/**
	 * Τα PDOStatement object που έχουμε ήδη κάνει prepared 
	 * @var array of PDOStatement
	 */
	private $statements = array&#40;&#41;;
	
	/**
	 * Το sql query string του τελευταίου PDOStatement που εκτελέστηκε
	 * &#40;για λόγους debbuging&#41; 
	 * @var string
	 */
	private $lastUsedStatement;	
	
	/**
	 * Οι τιμές που χρησιμοποιήθηκαν στο τελευταίο PDOStatement που 
	 * εκτελέστηκε &#40;για λόγους debugging&#41;
	 * @var array
	 */
	private $lastUsedValues = array&#40;&#41;;	

	
	/**
	 * Παίρνει σαν ορίσματα το sql query string και την / τις μεταβλητές του 
	 * prepared statements αν αυτές υπάρχουν και επιστρέφει ένα πολυδιάστατο 
	 * array αν υπάρχει κάτι να επιστρέψει. 
	 * Σαν πρώτο κλειδί του array που επιστρέφεται είναι ο αύξων αριθμός 
	 * της γραμμής αποτελέσματος και σαν τιμή ένα άλλο array που έχει κλειδιά 
	 * τα ονόματα των πεδίων αποτελεσμάτων και την τιμές τις τιμές τους. 
	 * @param string $statement Το sql string
	 * @param none,string,array $values Η τιμή / τιμές που θα χρησιμοποιηθούν 
	 * στο prepare statement αν αυτές υπάρχουν 
	 * @throws Exception
	 * @return null,array Το αποτέλεσμα &#40;αν υπάρχει αυτό&#41; ως πολυδιάστατο array
	 */
	public function request&#40;$statement,$values = null&#41;
	&#123;
		$result=array&#40;&#41;;
		$statement = trim&#40;$statement&#41;;
		if&#40;is_null&#40;$values&#41;&#41;
		&#123;
			$values = array&#40;&#41;;
		&#125;
		else if&#40;!is_array&#40;$values&#41;&#41;
		&#123;
			$values = array&#40;$values&#41;;
		&#125;
		
		$this->lastUsedStatement = $statement;
		$this->lastUsedValues = $values;
		
		if&#40;substr_count&#40;$statement,"?"&#41; !== count&#40;$values&#41;&#41;
		&#123;
			throw new Exception&#40;
				"PREPARED STATEMENT QUESTION MARKS MISSMATCH &#58; ".$statement
				." VALUES&#40;".implode&#40;",", $values&#41;."&#41;"
				, 4561&#41;;
		&#125;
		if&#40;!isset&#40;$this->statements&#91;$statement&#93;&#41;&#41;
		&#123;
			$this->statements&#91;$statement&#93; = $this->prepare&#40;$statement&#41;;
		&#125;
		$this->statements&#91;$statement&#93;->execute&#40;$values&#41;;
		if&#40;$this->statements&#91;$statement&#93;->errorCode&#40;&#41;!="00000"&#41;
		&#123;
			$error = $this->statements&#91;$statement&#93;->errorInfo&#40;&#41;;
			throw new Exception&#40;$error&#91;2&#93;,&#40;int&#41;$error&#91;0&#93;&#41;;
		&#125;
		$result = $this->statements&#91;$statement&#93;->fetchAll&#40;&#41;;
		return $result;
	&#125;
	
	/**
	 * Επιστρέφει το sql query string του τελευταίου statement 
	 * χρησιμοποιώντας τις τιμές που δόθηκαν για την εκτέλεσή του 
	 * @return array
	 */
	public function debugLastStatement&#40;&#41;
	&#123;
		if&#40;count&#40;$this->lastUsedValues&#41; == 0&#41;
		&#123;
			// Αν δεν υπάρχουν τιμές να αντικαταστήσει
			return $this->lastUsedStatement;
		&#125;
		else
		&#123;
			$re = "";
			$sqlString = $this->lastUsedStatement; 
			// Όσο το $i είναι μικρότερο απο το πόσες είναι οι τιμές που 
			// χρησιμοποιήθηκαν στο τελευταίο statement και συνεχίζει να 
			// υπάρχει ερωτηματικό στο $sqlstring 
			for&#40;$i = 0; $i < count&#40;$this->lastUsedValues&#41; 
				&& mb_substr_count&#40;$sqlString,"?"&#41;; $i++&#41;
			&#123;
				$questionMarkPos = mb_strpos&#40;$sqlString, "?"&#41;; 
				$value = $this->lastUsedValues&#91;$i&#93;;
				if&#40; $value != null && !is_numeric&#40;$value&#41; &#41;
				&#123;
					$value = "'".$value."'";
				&#125;
				$re .= mb_substr&#40;$sqlString, 0 , $questionMarkPos&#41; . $value;
				$sqlString = mb_substr&#40;$sqlString, $questionMarkPos + 1&#41;;
			&#125;
			$re .= $sqlString;
			return $re;
		&#125;

	&#125;
&#125;
Αν βγάλεις τα comments θα δεις ότι είναι πραγματικά πολύ μικρή. Πάμε να δούμε μια χρήση της αν και τα παραπάνω παραδείγματα με την request λειτουργούν μια χαρά.

Κώδικας: Επιλογή όλων

error_reporting&#40;E_ALL&#41;;
ini_set&#40;'display_errors', '1'&#41;; // Για να ξέρουμε τι μας γίνεται

$dbSchema = "test_db";
$dbUser = "testDbUser";
$dbPassword = "testPassword";
$pdoOptions = array&#40;PDO&#58;&#58;ATTR_DEFAULT_FETCH_MODE => PDO&#58;&#58;FETCH_ASSOC,
		PDO&#58;&#58;ATTR_EMULATE_PREPARES => false
		,PDO&#58;&#58;ATTR_ERRMODE => PDO&#58;&#58;ERRMODE_EXCEPTION&#41;;

$db = new DB&#40;"mysql&#58;dbname=".$dbSchema.";host=localhost;charset=utf8"
	,$dbUser,$dbPassword,$pdoOptions&#41;;

// SELECT με μια μεταβλητή 
$r = $db->request&#40;"SELECT * FROM users WHERE name = ?"
	,"Νίκος Καββαδίας"&#41;;
var_dump&#40;$r&#41;;
echo "<br/><br/>";
var_dump&#40;$db->debugLastStatement&#40;&#41;&#41;;
exit;
Η χρήση αυτής της κλάσης είναι κάτι που μπορεί να κάνει τα πράγματα ευκολότερα αλλά είναι κάτι που εσύ επιλέγεις. Κανονικά θα έπρεπε να γράψω και άλλο ένα μήνυμα για τα exception στο PDO αλλά αφενός από τον αρχικό σου κώδικα νομίζω ότι τα έχεις καταλάβει και αφετέρου δεν έχω να σου γράψω πολλά παραπάνω από ότι λένε τα tutorials. Παρ' όλα αυτά πες μου αν θες και ότι απορία έχεις για αυτά που έγραψα σε αυτό το μήνυμα.

gtsoukn
Δημοσιεύσεις: 33
Εγγραφή: 23 Ιούλ 2015 13:36

Από MySQL σε PDO

Δημοσίευση από gtsoukn » 15 Σεπ 2016 21:49

Καλησπέρα.

Μου φαίνεται ότι όσο προχωράμε τα πράγματα αρχίζουν και ζορίζουν.
Ειδικά και με τη χρήση ορολογιών π.χ. OOP = Object Oriented Programming = Αντικειμενοστραφής Προγραμματισμός (ας είναι καλά το Google) είναι δύσκολο ακόμα και στην ανάγνωση.

Ας τα πάρουμε από την αρχή (του post)

Κώδικας: Επιλογή όλων

if&#40;count&#40;$r&#41; > 0&#41;
Ήδη έκανα τη σχετική αλλαγή στον κώδικα που έδωσα στο προηγούμενο post (12 Σεπ 2016 23:44).
Δεν έχει νόημα να το ξαναγράψω εδώ μιας και είναι σχεδόν ίδια περίπτωση με αυτή που αναφέρω παρακάτω.

Κώδικας: Επιλογή όλων

// SELECT με δύο μεταβλητές
$r = $db->request&#40;"SELECT * FROM users WHERE name = ? AND email = ?"
   ,array&#40;"Θανάσης Παπακωνσταντίνου" , "test44@example.com"&#41; &#41;;
Εδώ με ...μπερδεύεις.
Πήγα να το εφαρμόσω και μου βγάζει σφάλμα
Fatal error: Call to undefined method PDO::request()
εκτός αν πρέπει να είναι όπως δίνεις τον κώδικα στο τέλος ($pdoOptions).

class DB extends PDO
Να υποθέσω ότι αυτό μπαίνει σε κάθε αρχείο (ή σε ξεχωριστό και να γίνεται include).
Στην περίπτωσή μου μάλλον έχω αρκετές περιπτώσεις που τα prepare επαναλαμβάνονται.
...έβαλα Ελληνικά comments ενώ πιστεύω ότι γενικά είναι χρήσιμο να έχουμε Αγγλικά comments
Για ποιο λόγο; Ρητορικό το ερώτημα μιας και θεωρώ ότι είναι άνευ ιδιαίτερης σημασίας.
Τα σχόλια είναι για να θυμίζουν ή να λένε στον προγραμματιστή τι ακολουθεί. Σε ένα πρόγραμμα μόνο για μένα όπως το εν λόγω ή μόνο με εφαρμογή στην Ελλάδα (για Έλληνες) τα σχόλια μπορούν να είναι στα Ελληνικά.
Αν φτιάξω μια εφαρμογή και την ανεβάσω π.χ. στο GitHub, τότε ναι, τα σχόλια θα είναι στα Αγγλικά.

Πριν πάω στα της εφαρμογής μου, να πω λίγο για το css αν και off-topic.
Ομολογώ ότι μ' αυτό δεν ασχολήθηκα.
Μεγάλο μέρος του είναι (ή ήταν) η εμφάνιση της ιστοσελίδας το οποίο (το ...design) δεν το έχω καθόλου.
Αυτό που έχω το πήρα έτοιμο από αλλού και έκανα ελάχιστες αλλαγές.
Για την εγγραφή μελών, πήρα αυτούσιο ελεύθερο λογισμικό με ενσωματωμένο css.
Η μορφοποίηση φορμών, κουμπιών κλπ είναι τελείως διαφορετική από αυτής του υπόλοιπου site.
Αν απαιτηθεί θα επανέλθω στην αντίστοιχη ενότητα του forum.

Στα της εφαρμογής...
Το επόμενο είναι η εμφάνιση όλων των ομάδων με αλφαβητική σειρά και επιπλέον εμφάνιση του αριθμού των κατηγοριών της κάθε ομάδας.
* Τις ομάδες θα τις πάρει από τον πίνακα π.χ. groups.
Εδώ προφανώς δεν είναι επανάληψη του prepare από την αρχική σελίδα μιας κι εκεί υπήρχε ο περιορισμός των 10 ομάδων, εκτός αν δημιουργηθεί ένα array με όλες τις ομάδες (για να χρησιμοποιείται σε κάθε περίπτωση) και μετά βάσει αυτού να δημιουργηθεί ένα άλλο array με τα απαιτούμενα κριτήρια. Δεν δοκίμασα αν γίνεται και δεν ξέρω αν έχει νόημα να γίνει κάτι τέτοιο.
* Ο υπολογισμός του αριθμού των κατηγοριών της κάθε ομάδας θα γίνεται από τον πίνακα π.χ. categories.

Κώδικας: Επιλογή όλων

<?php
	// Εμφάνιση όλων των ομάδων με αλφαβητική σειρά
	$stmt = $db->prepare&#40;'SELECT GroupID, GroupEL, GroupINT, UpdateDate FROM groups WHERE GroupID <> ? AND UpdateDate <> ? ORDER BY GroupEL'&#41;;
	$stmt->execute&#40;array&#40;1,0&#41;&#41;;
	$r = $stmt->fetchAll&#40;PDO&#58;&#58;FETCH_ASSOC&#41;;
	if&#40;count&#40;$r&#41; > 0&#41; &#123;
		foreach&#40;$r as $row&#41; &#123;
			echo '<tr><td style="width&#58; 50%"> &nbsp; &nbsp; <img src="folder/' . $row&#91;'GroupINT'&#93; . '.bmp" alt="' . $row&#91;'GroupINT'&#93; . '" width="20" height="20">&nbsp;<a href="file.php?GroupID=' . $row&#91;'GroupID'&#93; . '" style="color&#58; #000000"><u>' . $row&#91;'GroupEL'&#93; . '</u></a></td><td style="text-align&#58; center">' . date&#40;'d/m/Y', $row&#91;'UpdateDate'&#93;&#41; . '</td>';
			// Υπολογισμός κατηγοριών ομάδας από τον πίνακα categories
			$GroupID = $row&#91;'GroupID'&#93;;
			$stmt2 = $db->prepare&#40;'SELECT COUNT&#40;GroupID&#41; AS CategorySum FROM categories WHERE GroupID = ?'&#41;;
			$stmt2->execute&#40;array&#40;$GroupID&#41;&#41;;
			$s = $stmt2->fetchAll&#40;PDO&#58;&#58;FETCH_ASSOC&#41;;
			foreach&#40;$s as $sum&#41; &#123;
				echo '<td style="text-align&#58; center">' . $sum&#91;'CategorySum'&#93; . '</td></tr>';
			&#125;
		&#125;
	&#125; else &#123;
		echo '<br><table style="width&#58; 100%"><tr><td style="text-align&#58; center">Δεν βρέθηκαν εγγραφές</td></tr></table></br>';
	&#125;
?>
Για το $stmt2 έφτιαξα αρχικά ένα array με όλες τις εγγραφές και πεδία το $GroupID (κοινό με το $stmt) και το COUNT(GroupID) AS CategorySum με το σκεπτικό να συνδέσω τα δυο arrays σε ένα τρίτο που να περιέχει όλα τα στοιχεία με βάση το κοινό $GroupID αλλά δεν τα κατάφερα.
Γίνεται κάτι τέτοιο;

Να σημειώσω εδώ ότι σχετικά με το rowCount() βρήκα ένα πιθανό πρόβλημα. Δεν ξέρω αν έχει σχέση με την περίπτωσή μου. Το μεταφέρω ως έχει από το http://stackoverflow.com/questions/8833 ... t-with-pdo
From the PDO Doc:
For most databases, PDOStatement::rowCount() does not return the number of rows affected by a SELECT statement. Instead, use PDO::query() to issue a SELECT COUNT(*) statement with the same predicates as your intended SELECT statement, then use PDOStatement::fetchColumn() to retrieve the number of rows that will be returned. Your application can then perform the correct action.
Ο κώδικας που προτείνεται είναι:

Κώδικας: Επιλογή όλων

$nRows = $pdo->query&#40;'select count&#40;*&#41; from blah'&#41;->fetchColumn&#40;&#41;; 
echo $nRows;
Σε δοκιμή, και αυτός ο κώδικας μου δουλεύει.
Γιώργος

Άβαταρ μέλους
jpk
Δημοσιεύσεις: 441
Εγγραφή: 09 Μαρ 2011 21:17

Από MySQL σε PDO

Δημοσίευση από jpk » 16 Σεπ 2016 19:32

Γεια σου gtsoukn,
Η κλάση DB (που κληρονομεί από την PDO) που σου έδωσα (στο προηγούμενο μήνυμα) είναι κυρίαρχα για να μας κάνει την ζωή πιο εύκολη , έτσι χρησιμοποιώντας μόνο μια μέθοδο (την request) στο 99% των περιπτώσεων να κάνουμε ότι θα κάναμε σε 3 / 4 γραμμές αλλά και πιο αποδοτικά. Δες την και αν δες βολεύει απλά μην την χρησιμοποιείς. Αν είναι να την χρησιμοποιήσεις τότε ναι πρέπει να την βγάλεις σε ένα άλλο αρχείο (ας το πούμε DB.php) και να το κάνεις require_once πριν το κάνεις αυτό. Η require_once είναι σαν την include που είπες μόνο που αν κάτι ήδη έχει "φορτωθεί" δεν το ξαναπαίρνει , είναι πολύ χρήσιμη για τις κλάσεις. Το αντικείμενό της βάσης σου μετά θα πρέπει να είναι DB (που είναι και PDO) και όχι PDO απλά. Π.χ.:

Κώδικας: Επιλογή όλων

error_reporting&#40;E_ALL&#41;; 
ini_set&#40;'display_errors', '1'&#41;; // Για να ξέρουμε τι μας γίνεται 

$dbSchema = "test_db"; 
$dbUser = "testDbUser"; 
$dbPassword = "testPassword"; 
$pdoOptions = array&#40;PDO&#58;&#58;ATTR_DEFAULT_FETCH_MODE => PDO&#58;&#58;FETCH_ASSOC, 
      PDO&#58;&#58;ATTR_EMULATE_PREPARES => false 
      ,PDO&#58;&#58;ATTR_ERRMODE => PDO&#58;&#58;ERRMODE_EXCEPTION&#41;; 

require_once&#40;"DB.php"&#41;; // Ας υποθέσουμε ότι το DB.php είναι στον ίδιο φάκελο
$db = new DB&#40;"mysql&#58;dbname=".$dbSchema.";host=localhost;charset=utf8" 
   ,$dbUser,$dbPassword,$pdoOptions&#41;; 

// SELECT με μια μεταβλητή 
$r = $db->request&#40;"SELECT * FROM users WHERE name = ?" 
   ,"Νίκος Καββαδίας"&#41;; 
var_dump&#40;$r&#41;; 
echo "<br/><br/>"; 
var_dump&#40;$db->debugLastStatement&#40;&#41;&#41;; 
exit;
Τα σχόλια είναι πολύ χρήσιμα γιατί βοηθούν τον άλλο προγραμματιστή που θα δει τον κώδικά μας να κατανοεί τι ακριβώς κάνουμε και γιατί χωρίς να χρειάζεται πολύ χρόνο. Ο "άλλος προγραμματιστής" είναι πολύ πιθανόν να είμαστε εμείς οι ίδιοι σε μελλοντικό χρόνο , και δεν υπάρχει τίποτα χειρότερο από το να περνάς γενεές δεκατέσσερις τον ίδιο σου τον εαυτό γιατί δεν έβαλε σωστά σχόλια. Για τα Ελληνικά / Αγγλικά που λες έχεις δίκιο ότι δεν παίζει ρόλο , απλά προτιμώ τα Αγγλικά γιατί ποτέ δεν ξέρεις ποιος αυτός ο "άλλος προγραμματιστής" θα είναι , άσε που το να γράφεις σχόλια στα Ελληνικά παίρνει περισσότερο χρόνο (για εμένα) μεταφράζοντας προγραμματιστικούς όρους με τρόπο που να βγαίνει νόημα.

Πολύ σωστά γράφεις ότι για να γνωρίζεις τον αριθμό των εγγραφών χρησιμοποιείς την COUNT , μόνο που δεν έχει νόημα να κάνεις COUNT τα πάντα αλλά μόνο το πρωτεύων κλειδί, π.χ.:

Κώδικας: Επιλογή όλων

$r = $db->request&#40;"SELECT COUNT&#40;id&#41; AS counter FROM users"&#41;;
$counter = $r&#91;0&#93;&#91;"counter"&#93;;
Εδώ όπως βλέπεις ενώ υπάρχει μόνο ένα αποτέλεσμα και αυτό έρχεται σε δική του "γραμμή" αποτελεσμάτων και άρα στο array που έχει επιστραφεί αυτό το row θα είναι στο $r[0]

Αν και κάτι κατάλαβα για το θέμα των κατηγοριών και των ομάδων που λες για να έχω πραγματικά άποψη θα πρέπει να μοιραστείς την δόμηση των δεδομένων σου , μπορείς αν θέλεις είτε να γράψεις τους διαφορετικούς πίνακες με τα πεδία που έχουν και πως αυτοί σχετίζονται ή ακόμα καλύτερα το τι σου βγάζει για κάθε πίνακα το:

Κώδικας: Επιλογή όλων

SHOW CREATE TABLE x 
Όπου x ο κάθε πίνακας που μας ενδιαφέρει , πως αυτοί συνδέονται και γράψε μια ακόμα φορά το τι θες να κάνεις:

Για το css έχεις δίκιο , ας δούμε τώρα το θέμα της βάσης και μετά σε άλλο θέμα περιγράφεις αναλυτικά το τι θες να κάνεις.

gtsoukn
Δημοσιεύσεις: 33
Εγγραφή: 23 Ιούλ 2015 13:36

Από MySQL σε PDO

Δημοσίευση από gtsoukn » 18 Σεπ 2016 00:44

Καλησπέρα ...και πάλι.

Έβαλα σε εφαρμογή την κλάση DB, μετατρέποντας τα λίγα μέχρι τώρα αρχεία που είχα φτιάξει χωρίς τη χρήση της και όλα δείχνουν να δουλεύουν σωστά.

Και έφτασα στο σημείο να χρησιμοποιήσω ένα ίδιο request που είχα σε άλλο αρχείο.
Sorry αλλά ή δεν μπορώ να το καταλάβω/εφαρμόσω ή η περίπτωσή μου δεν είναι επανάληψη.

Κώδικας: Επιλογή όλων

$s = $db->request&#40;'SELECT COUNT&#40;id&#41; AS sum FROM data WHERE id = ?' ,array&#40;$id&#41;&#41;;
Αυτό το request που χρησιμοποιείται σε αρκετά σημεία του site είναι επανάληψη;
Μάλλον όχι καθώς το array θα είναι πάντα διαφορετικό αφού το αποτέλεσμα εξαρτάται από μεταβλητή ($id).

Προφανώς επανάληψη θεωρείται μόνο όταν πρόκειται για σταθερές τιμές, π.χ.

Κώδικας: Επιλογή όλων

$r = $db->request&#40;"SELECT * FROM users WHERE name = ? AND email = ?"
   ,array&#40;"Θανάσης Παπακωνσταντίνου" , "test44@example.com"&#41; &#41;;
Επίσης, αυτό πρέπει να μπει σε κάποιο αρχείο το οποίο καλείται τουλάχιστον από το αρχείο που θα χρησιμοποιήσει το αποτέλεσμα. (Στο παράδειγμα το έχεις σε αρχείο με τα στοιχεία σύνδεσης στη βάση)
Σωστά;
Γιώργος

Άβαταρ μέλους
jpk
Δημοσιεύσεις: 441
Εγγραφή: 09 Μαρ 2011 21:17

Από MySQL σε PDO

Δημοσίευση από jpk » 18 Σεπ 2016 06:40

Γεια σου gtsoukn,
Αφού σου δουλεύει σημαίνει ότι βρίσκεσαι σε καλό δρόμο , αν έχεις βγάλει την κλάση που σου έδωσα σε άλλο αρχείο , μία φορά το κάνεις require_once πριν το χρησιμοποιήσεις την πρώτη φορά.

Όπως σου έγραψα AN έχει μόνο MIA παράμετρο για αντικατάσταση για το statement σου (ένα ερωτηματικό) μπορείς ΑΚΟΜΑ ΚΑΙ ΝΑ χρησιμοποιείς την request με μια παράμετρο και όχι array. Έτσι π.χ. το:

Κώδικας: Επιλογή όλων

$r = $db->request&#40;"SELECT COUNT&#40;id&#41; AS counter FROM data WHERE id = "' , $id &#41;;
είναι ίδιο με το:

Κώδικας: Επιλογή όλων

$r = $db->request&#40;"SELECT COUNT&#40;id&#41; AS counter FROM data WHERE id = "' , array&#40;$id &#41; &#41;;
AN είχαμε βέβαια ΠΑΝΩ ΑΠΟ ΜΙΑ παράμετρο στο query μας (ερωτηματικά) τότε θα έπρεπε σίγουρα να είναι array.

Το θέμα της επαναχρησιμοποίησης των prepared statements το ρυθμίζει η τάξη που σου έδωσα , δεν έχεις κάτι να κάνεις εσύ (και ναι ισχύει για όλα τα prepared statements , δεν υπάρχει διαφορά αν οι μεταβλητές που δίνεις είναι "καρφωτές" ή άλλες μεταβλητές) , όπως και δεν έχεις λόγο να σε απασχολεί.

Η τάξη που σου έδωσα (που κληρονομώντας από την PDO , είναι PDO και η ίδια) είναι για να απλοποιήσει ακόμα περισσότερο την χρήση του PDO με μία μέθοδο την request στο 99% των περιπτώσεων. Αν σε μπερδεύει μην την χρησιμοποιείς

Άβαταρ μέλους
dva_dev
Script Master
Δημοσιεύσεις: 3790
Εγγραφή: 16 Σεπ 2005 01:32
Επικοινωνία:

Από MySQL σε PDO

Δημοσίευση από dva_dev » 18 Σεπ 2016 11:35

@jpk
Θα πρότεινα να χρησιμοποιείς named parameters αντί placeholders στα queries.
Π.χ. Δες στο http://php.net/manual/en/pdostatement.execute.php το
Example #2 Execute a prepared statement with an array of insert values (named parameters)
αντί του
Example #3 Execute a prepared statement with an array of insert values (placeholders)
που χρησιμοποιείς

Άβαταρ μέλους
jpk
Δημοσιεύσεις: 441
Εγγραφή: 09 Μαρ 2011 21:17

Από MySQL σε PDO

Δημοσίευση από jpk » 18 Σεπ 2016 13:31

Γεια σου dva_dev,

Κάθε πρόταση καλή είναι. Κατά την άποψή μου , ο κοινός (προγραμματιστικά) τρόπος χρήσης των prepared statements είναι με placeholders. Το PDO ( για λόγους που έχω μια ιδέα αλλά δεν έχει νόημα να αναλύσω ) δέχεται εκτός από placeholders και named parameters. Η άποψή μου είναι ότι ο κώδικας που παράγεται με την χρήση των named parameters και δεν είναι τόσο καθαρός και δεν προσφέρει τίποτα παραπάνω , αλλά αυτό είναι απλά μια άποψη χρήσης του PDO.

Εκτός από τα bugs που έχουν αναφερθεί στον PDO sql parser όταν χρησιμοποιούνται named parameters , που δεν τα θεωρώ σοβαρό θέμα και εκτός από την άποψή μου περί "καθαρού" κώδικα υπάρχει και κάτι πιο αντικειμενικό. Ας το δούμε:

Κώδικας: Επιλογή όλων

error_reporting&#40;E_ALL&#41;;
ini_set&#40;'display_errors', '1'&#41;;

$dbSchema = "test_db";
$dbUser = "testDbUser";
$dbPassword = "testPassword";
$pdoOptions = array&#40;PDO&#58;&#58;ATTR_DEFAULT_FETCH_MODE => PDO&#58;&#58;FETCH_ASSOC,
		PDO&#58;&#58;ATTR_EMULATE_PREPARES => false
		,PDO&#58;&#58;ATTR_ERRMODE => PDO&#58;&#58;ERRMODE_EXCEPTION&#41;;

$db = new PDO&#40;"mysql&#58;dbname=".$dbSchema.";host=localhost"
	,$dbUser,$dbPassword,$pdoOptions&#41;;
$db->exec&#40;"SET NAMES utf8"&#41;;

$statement = $db->prepare&#40;"SELECT * FROM users WHERE id = &#58;id"&#41;; 
$id = 1;
$statement->bindParam&#40;'&#58;id', $id, PDO&#58;&#58;PARAM_INT&#41;;
$statement->execute&#40;&#41;; 
$r = $statement->fetchAll&#40;PDO&#58;&#58;FETCH_ASSOC&#41;; 
var_dump&#40;$r&#41;;
Τι έγινε όμως πραγματικά στην βάση; Μπορούμε να το δούμε από τα log της MySQL αν τα ενεργοποιήσουμε, ας δούμε το τι έγινε:

Κώδικας: Επιλογή όλων

160918 13&#58;20&#58;20	 3151 Connect	testDbUser@localhost on test_db
		 3151 Query	SET NAMES utf8
		 3151 Prepare	SELECT * FROM users WHERE id = ?
		 3151 Close stmt	
		 3151 Quit	
Τι σημαίνει αυτό; Ότι ένα για να έχω το ίδιο επίπεδο ασφάλειας με την χρήση placeholders , θα πρέπει αν χρησιμοποιήσω named parameters να ορίζω το τι είδους είναι οι μεταβλητές μου ( π.χ. εδώ PDO::PARAM_INT). Όμως από το log μάθαμε και κάτι σημαντικότερο. Ότι αν χρησιμοποιούμε named parameters βάζουμε το PDO να κάνει parsing το sql query ώστε να βάλει placeholders τελικά όταν θα κάνει το ερώτημα στην βάση. Είχαμε κάποιο λόγο να τα κάνουμε όλα αυτά, με ότι επιπτώσεις έχουν;

(και για όποιους έχουν διάθεση να διαβάσουν τι παραπάνω κάνει το PDO όταν χρησιμοποιούμε named parameters αντι για placeholders https://github.com/php/php-src/blob/mas ... pdo_stmt.c (κάντε search bindParam και register_bound_param) . Όχι ότι έχει ιδιαίτερο νόημα αλλά να σημειώσω ότι ακόμα και ας διαφωνώ με αποφάσεις σαν τα named parameters στο PDO αυτές πρέπει να κριθούν από το πότε αυτές λήφθηκαν ( πως ήταν οι βάσεις τότε , ποιοι έγραφαν PHP και πως) που είναι πάνω από 13 χρόνια πίσω , γενικά οι άνθρωποι που εργάστηκαν για το PDO http://php.net/credits.php αξίζουν τα σέβη μας και σίγουρα τις δικές μου ευχαριστίες. Είναι τόση η δύναμη που έχει το PDO που σχεδόν σε όλες τις γλώσσες που ξέρω έχουν φτιάξει κάτι ανάλογο ή οι αναζητήσεις για αυτό είναι πολλές μετά την επιτυχία που είχε.)

Παρ' όλα αυτά επειδή ποτέ δεν ξέρεις από που μπορεί να πάρεις κάτι καινούργιο , έχεις κάποιο λόγο που κάνεις αυτή την πρόταση ή είναι απλά η προσωπική σου προτίμηση @dva_dev ;

Άβαταρ μέλους
jpk
Δημοσιεύσεις: 441
Εγγραφή: 09 Μαρ 2011 21:17

Από MySQL σε PDO

Δημοσίευση από jpk » 24 Σεπ 2016 17:26

Μου επισημάνθηκε (πολύ σωστά) και το αναφέρω εδώ για να μην μπερδευτεί με την θεωρία στην Αγγλική της ορολογία , όποιος διαβάσει αυτό το thread στο μέλλον (και αποφασίσει να κάνει τα δικά του tests ώστε να έχει άποψη) , ότι και τα named parameters σε placeholders αναφέρονται , αυτό που γράφει ο @dva_dva σαν σκέτα placeholders (με τα ερωτηματικά) τα λέμε positional parameters και αυτά σε placeholders αναφέρονται. Άρα η σύγκριση που έκανα με κώδικα , log από την βάση , και κώδικα του PDO ήταν ανάμεσα σε named parameters και positional parameters για placeholders.

gtsoukn
Δημοσιεύσεις: 33
Εγγραφή: 23 Ιούλ 2015 13:36

Από MySQL σε PDO

Δημοσίευση από gtsoukn » 01 Οκτ 2016 01:01

Καλησπέρα.

Έχω μια περίπτωση που έχει εισαγωγή δεδομένων και ανέβασμα αρχείων.
Τοπικά (XAMPP) λειτουργεί σωστά.
Όταν το ίδιο το τρέχω online βγάζει σφάλμα και δεν ανεβάζει τα αρχεία.

Αναλυτικά:
Τα δεδομένα εισάγονται μέσω φόρμας

Κώδικας: Επιλογή όλων

<form method="post" action="insert_db.php" enctype="multipart/form-data">
Η φόρμα για τη "δήλωση" των αρχείων (εικόνες .bmp) για ανέβασμα είναι

Κώδικας: Επιλογή όλων

<div>
	<label for="Icon">Εικονίδιο .bmp 100x100px&#58;</label>
	<input type="file" name="Icon" id="Icon"><br/>
</div>
Το insert_db.php όσο αφορά το ανέβασμα των αρχείων είναι από το http://www.w3schools.com/php/php_file_upload.asp τροποποιημένο για την περίπτωσή μου.

Όπως είπα, τοπικά δουλεύει σωστά.
Στο online, ανεβάζει τα δεδομένα εκτός από τα αρχεία και βγάζει το παρακάτω σφάλμα
Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[HY000]: General error: 2053 ' in /var/www/vhosts/***.gr/test.***.gr/db.php:76 Stack trace: #0 /var/www/vhosts/***.gr/test.***.gr/db.php(76): PDOStatement->fetchAll() #1 /var/www/vhosts/***.gr/test.***.gr/insert_db.php(54): DB->request('INSERT INTO ...', Array) #2 {main} thrown in /var/www/vhosts/***.gr/test.***.gr/db.php on line 76
Το db.php είναι αυτό που υπάρχει εδώ: http://www.freestuff.gr/forums/viewtopi ... 156#612156 (class DB extends PDO).
Αν διαβάζω σωστά το μήνυμα σφάλματος, κάτι παίζει στην INSERT.
Αλλά, εκ του αποτελέσματος, η INSERT δουλεύει αφού τα δεδομένα που περιέχει εισάγονται.

Τι μπορεί να συμβαίνει και δεν ανεβαίνουν τα αρχεία ή/και πού μπορεί να είναι το λάθος;

Ευχαριστώ.
Γιώργος

Άβαταρ μέλους
fafos
Script Master
Δημοσιεύσεις: 6231
Εγγραφή: 30 Νοέμ 2004 03:09

Από MySQL σε PDO

Δημοσίευση από fafos » 01 Οκτ 2016 01:10

pos na katalavoume ti ftaiei otan den mas dineis to query tou insert?

to fetchAll() pou kollaei me to insert?
Οι πάνες και οι πολιτικοί πρέπει να αλλάζονται συχνά για τον ίδιο λόγο...

gtsoukn
Δημοσιεύσεις: 33
Εγγραφή: 23 Ιούλ 2015 13:36

Από MySQL σε PDO

Δημοσίευση από gtsoukn » 01 Οκτ 2016 01:24

Κώδικας: Επιλογή όλων

$db->request&#40;'INSERT INTO table &#40;field1, field2, field3, field4, field5&#41; VALUES &#40;?,?,?,?,?&#41;' ,array&#40;$field1, $field2, $field3, $field4, $field5&#41;&#41;;
Δεν το έδωσα επειδή αυτό δουλεύει, sorry.

Το fetchAll() περιέχεται στο db.php.
Γιώργος

Άβαταρ μέλους
fafos
Script Master
Δημοσιεύσεις: 6231
Εγγραφή: 30 Νοέμ 2004 03:09

Από MySQL σε PDO

Δημοσίευση από fafos » 01 Οκτ 2016 01:36

auth h class nomizo oti anaferetai mono se select... me to fetch se update h insert h PDO petaei error giati apla ths zhtas na kanei array kati pou kaneis eisagogh..
Οι πάνες και οι πολιτικοί πρέπει να αλλάζονται συχνά για τον ίδιο λόγο...

Απάντηση

Επιστροφή στο “PHP Προγραμματισμός”

Μέλη σε σύνδεση

Μέλη σε αυτήν τη Δ. Συζήτηση: Δεν υπάρχουν εγγεγραμμένα μέλη και 2 επισκέπτες