73 PDO 3 query

In questa lezione ci concentreremo su come gestire i risultati di una select. L’obbiettivo è quello di interrogare il database per ottenere un elenco dove a fianco del nome di ogni argomento si possa visualizzare quante domande sono state inserite per quell’argomento.

Andiamo a creare la pagina select_con_PDO.php, tralasciando le parti iniziali di connessione e html, ma concentrandoci sulla parte SQL:

$comandoSQL = “select argomento, count(*) as ‘numero_domande’ “.
“from argomenti join domande on idargomento=fkargomento “.
“group by argomento “.
“order by argomento”;

In $comandoSQL sto richiedendo il nome dell’argomento con un conteggio che però non visualizzi count(*), ma numero_domande. Il risultato è ottenuto dalla congiunzione della tabella argomenti e la tabella domande. Il legame è quello stabilito dal codice assegnato agli argomenti che si trova nel campo idargometo che è uguale al campo fkargomento della tabella domande. Quindi quando avremo un record con lo stesso codice, vuol dire che i risultati andranno uniti. Ma non voglio visualizzare una moltitudine di righe, voglio vedere una riga sola con a fianco il numero delle righe della domande, quindi effettuo un raggruppamento in base all’argomento (group by argomento ). Infine effettuo un ordinamento alfabetico sulla colonna argomento (order by argomento).

Eseguo un tentativo di esecuzione col metodo try e se ha successo lo memorizzo nella variabile $risultato:

try
{
$risultato = $conn_pdo->query($comandoSQL);
}
catch (PDOException $e)
{
echo $e->getMessage() . “<br/>”;
echo “Nessun risultato ottenuto  …”;
die();
}

Anche con i PDO possiamo iterare tra le righe del risultato, come facevamo in precedenza, ma con i PDO, in aggiunta, abbiamo la possibilità opzionale di specificare un parametro supplementare che specifica la modalità di recupero di queste righe. Il valore predefinito, mappato in variabili definite dentro la classe PDO, è il PDO::FETCH_BOTH che permette di iterare nel risultato sia con array in forma associativa (tramite il nome della colonna) o per posizione (numero della colonna)

while ( $riga = $risultato->fetch() )
{
//usando i nomi delle colonne restituite
echo $riga[‘argomento’].”: “.$riga[‘numero_domande’].$nl;
   //usando le posizioni delle colonne restituite
echo $riga[0].”: “.$riga[1].$nl;
 }

In questo esempio si è voluto stampare lo stesso risultato, il primo con la tecnica associativa, l’argomento e il count (rinominato in numero_domande). La seconda per posizione, 0 e 1. Otterremo una doppia stampa contenente i medesimi dati. PDO esalta l’uso degli oggetti e una variante della fetch, che si attiva specificando la giusta costante, permette la creazione automatica di oggetti che hanno specifiche classi. Ecco un esempio, data la seguente classe pubblica:

class user {
public
$iduser,
$email,
$psw;

con il seguente metodo utente che, dopo aver effettuato controlli ed elaborazioni, mi restituisce i dati estratti dalla tabella user e li compone insieme:

 public function utente()
{
//svolge alcuni controlli
//….//e altre elaborazioni
//…return $this->iduser.”:”.$this->email.”:”.$this->psw;
}

Potrei con la fetch estrarre i dati dalla tabella, creare oggetti che utilizzano la classe user e richiamare il metodo utente. Invece, utilizzando il parametro PDO::FETCH_CLASS, verranno estratti i dati e in automatico verrà creato un oggetto che farà uso della classe specificata:

 $risultato = $conn_pdo->query(“select * from users”,  PDO::FETCH_CLASS, “user”) ;

Estraggo i dati dalla tabella users e creo un oggetto con classe user. Avendo usato nella classe user i nomi delle sue proprietà uguali ai nomi dei campi della tabella, tali proprietà verranno compilate con i dati estratti dalla riga della tabella (iduser, email, psw). Il seguente while, che itera tra gli oggetti costruiti col risultato della query, mi permette di usare automaticamente il metodo utente:

while ( $oggetto = $risultato->fetch() )
echo $oggetto->utente().$nl;

Questo nuovo oggetto conterrà i dati estratti dalla tabella users e avrà la classe user.

Un altro metodo davvero utile dei PDO è PDO::ATTR_CURSOR che permette di utilizzare i cursori. Di norma quando eseguo una query, i dati estratti vengono letti tutti e, come in questo caso, viene costruita una matrice completa ($risultato), tale approccio potrebbe comportare difficolà se il nummero di righe fosse enorme, inoltre non abbiamo la possibilità di scorrere avanti e indietro queste righe del risultato senza scaricare prima TUTTE le righe da server, cosa i cursori permette.

Attiviamo il cursore:

$conn_pdo->setAttribute(PDO::ATTR_CURSOR, PDO::CURSOR_SCROLL);

all’attributo ATTR_CURSOR assegno il valore CURSOR_SCROLL, quindi è permesso andare avanti e indietro.

A questo punto alla fetch associativa aggiungo l’attributo FETCH_ORI_NEXT per ottenere il data successivo (in avanti)

while ( $riga = $risultato->fetch(PDO::FETCH_ASSOC, PDO::FETCH_ORI_NEXT) )
echo $riga[‘iduser’].”: “.$riga[‘email’].$nl;

Inserendolo in un ciclo while come nell’esempio mi vengono assegnate tutte le righe in avanti partendo dal primo risultato memorizzato in $risultato:

$risultato = $conn_pdo->query(“select * from users”);

Interessante è l’estrazione all’indietro partendo dall’ultimo risultato:

$riga=$stmt->fetch(PDO::FETCH_ASSOC, PDO::FETCH_ORI_LAST);

PDO::FETCH_ORI_LAST recuperal’ultima riga, quindi dovremmo consumarla (col comando echo) e perciò necessario usare un do while:

do
{
echo $riga[‘iduser’].”: “.$riga[‘email’].$nl;
} while ( $riga = $stmt->fetch(PDO::FETCH_ASSOC, PDO::FETCH_ORI_PRIOR) );

Il ciclo continua fintanto che la richiesta PDO::FETCH_ORI_PRIOR (precedente) non darà esito negativo (quindi il dopo il primo risultato).

Valido potrebbe essere prelevare la riga in una certa posizione, nel comando fetch specifico PDO::FETCH_ORI_ABS che sta per origine assoluta e aggiungere come terzo arametro l’offset, cioè il numero della riga desiderata:

$riga = $risultato->fetch(PDO::FETCH_ASSOC, PDO::FETCH_ORI_ABS, 10);

Il parametro REL calcola lo spostamento dal punto in cui siamo, vogliamo andare avanti di 5

$riga = $risultato->fetch(PDO::FETCH_ASSOC, PDO::FETCH_ORI_REL, 5);

andare indietro di 5

$riga = $risultato->fetch(PDO::FETCH_ASSOC, PDO::FETCH_ORI_REL, -5);

Possiamo usare i prepared statments creando prima il comando col metodo prepare e comunicando, tramite un array, la modalità di scroll

$stmt = $conn_pdo->prepare(“select * from users”, array(PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL));
$stmt->execute();

La chiave è l’attributo PDO::ATTR_CURSOR a cui assegniamo il valorePDO::CURSOR_SCROLL.

Una volta utilizzato un cursore è necessario chiuderlo, altrimenti le successive query rischierebbero di fallire.

$stmt->closeCursor();

Di seguito il codice completo della pagina select_con_PDO.php:

<html>
<head>
<title>QUIZ MAKER – n. domande per ogni argomento </title>
</head>
<body>

<?php
class user {
public
$iduser,
$email,
$psw;

public function utente()
{
//svolge alcuni controlli
//….

//e altre elaborazioni
//…

return $this->iduser.”:”.$this->email.”:”.$this->psw;
}
}

?>
<?php
include($_SERVER[‘DOCUMENT_ROOT’].”/fagtest/my_include/setup_con_DB.php”);

//recupero credenziali da file ESTERNO alla cartella pubblica del sito

try
{
//CONNESSIONE
$conn_pdo = new PDO(“mysql:host=localhost;dbname={$accessData[‘dbname’]}”,
$accessData[‘username’],$accessData[‘password’]);
}

catch (PDOException $e)
{
echo $e->getMessage() . “<br/>”;
echo “Connessione al server fallita. Impossibile procedere. Contattare …”;
die();
}

//ATTIVAZIONE ECCEZIONI PER METODO QUERY
$conn_pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$comandoSQL = “select argomento, count(*) as ‘numero_domande’ “.
“from argomenti join domande on idargomento=fkargomento “.
“group by argomento “.
“order by argomento”;
try
{
$risultato = $conn_pdo->query($comandoSQL);
}

catch (PDOException $e)
{
echo $e->getMessage() . “<br/>”;
echo “Nessun risultato ottenuto  …”;

die();
}

//fetch mode di default: PDO::FETCH_BOTH (associativo e per n. colonna)
while ( $riga = $risultato->fetch() )
{
//usando i nomi delle colonne restituite
echo $riga[‘argomento’].”: “.$riga[‘numero_domande’].$nl;
   //usando le posizioni delle colonne restituite
echo $riga[0].”: “.$riga[1].$nl;
 }
echo “——————————-“.$nl;

//fetch mode PDO::FETCH_CLASS
$risultato = $conn_pdo->query(“select * from users”,
PDO::FETCH_CLASS,
“user”) ;

while ( $oggetto = $risultato->fetch() )
echo $oggetto->utente().$nl;

echo “——————————-“.$nl;

//fetch con cursore (non supportati da MySql)
//attiviamo modalita’ con cursore
$conn_pdo->setAttribute(PDO::ATTR_CURSOR, PDO::CURSOR_SCROLL);

$risultato = $conn_pdo->query(“select * from users”);

//in avanti
while ( $riga = $risultato->fetch(PDO::FETCH_ASSOC, PDO::FETCH_ORI_NEXT) )
echo $riga[‘iduser’].”: “.$riga[‘email’].$nl;

echo “——————————-“.$nl;

//ed ora all’indietro
$risultato = $conn_pdo->query(“select * from users”);
$stmt = $conn_pdo->prepare(“select * from users”, array(PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL));
$stmt->execute();
$riga=$stmt->fetch(PDO::FETCH_ASSOC, PDO::FETCH_ORI_LAST);
// a ritroso …
do
{
echo $riga[‘iduser’].”: “.$riga[‘email’].$nl;
} while ( $riga = $stmt->fetch(PDO::FETCH_ASSOC, PDO::FETCH_ORI_PRIOR) );
 $stmt->closeCursor();

//CHIUDIAMO LA CONNESSIONE E LIBERIAMO LE RISORSE OCCUPATE …
$conn=null;

?>
</body>
</html>