PDO: estrazione dati dal database

Come con mysqli, anche con PDO basta impostare una SELECT query per recuperare i dati e utilizzarli nella nostra applicazione.

$sql = "SELECT name, lastname FROM users";
$stmt = $pdo->prepare($sql);
$stmt->execute();

Nonostante PDO sia relativamente semplice da usare, ci sono alcuni dettagli che è bene tenere a mente, specie quando nella prepared statement si fa uso di LIKE. Normalmente in SQL scriveremmo questo:

$stmt = $pdo->prepare("SELECT * FROM users WHERE name LIKE '%?%'");
$stmt->execute();

Ma non avevamo detto niente apici?

Questa forma produce subito un errore, e per capire la natura dell’errore bisogna comprendere la funzione del segnaposto posizionale: esso rappresenta (e sostituisce) interamente una variabile che deve quindi essere predisposta prima, e in seguito agganciata alla query durante l’execute():

$search = "%$search%";
$stmt  = $pdo->prepare("SELECT * FROM users WHERE name LIKE ?");
$stmt->execute([$search]);

Quindi, per sapere quanti risultati abbiamo attenuto? Possiamo usare il metodo rowCount();

$stmt->rowCount();

Quando invece si devono contare le righe che soddisfano determinati criteri nel vostro database, in nessun caso si devono selezionare le righe effettive e poi usarle con rowCount().

Piuttosto, bisognerebbe sempre chiedere al database di contare le righe e quindi restituire il numero di righe, con una query come questa:

"SELECT count(1) FROM users"
// oppure
"SELECT count(*) FROM books WHERE category_id = 1"

(Da notare che non importa quale costante venga usata come parametro della funzione count(), sia esso *, 0 o 1 o qualsiasi cosa – funziona tutto allo stesso modo, basta che sia una costante rispetto al nome del campo. Di quest’ultimo, verranno conteggiati solo i valori non nulli).

Fortunatamente PDO ha una funzione dedicata per ottenere quel singolo numero direttamente dalla query, con il metodo fetchColumn().

Se nella query non verrà utilizzata alcuna variabile, possiamo usare questa funzione direttamente all’interno della query():

$count = $pdo->query("SELECT count(*) FROM users")->fetchColumn();

Se invece all’interno della query deve essere utilizzata una variabile, quest’ultima deve sempre essere sostituita con un parametro ed eseguita utilizzando un prepared statement:

$stmt = $pdo->prepare("SELECT count(*) FROM cutlery WHERE category_id = ?");
$stmt->execute([$category_id]);
$count = $stmt->fetchColumn();

Ottenere dati da una dichiarazione: foreach()

Il modo più semplice e più diretto per ottenere risultati multipli da una sola dichiarazione è con l’utilizzo del ciclo foreach() di PHP, che grazie all’interfaccia Traversable permette all’oggetto PDOStatement di essere iterato nel ciclo foreach():

$stmt = $pdo->query('SELECT username FROM users');
foreach ($stmt as $row)
{
    echo $row['username']" <br/>\n";
}

Questa richiesta produce un elenco popolato dai valori presenti nella colonna username della tabella users, del tipo:

Carlo

Marco

Riccardo

Antonio

Francesco

Ottenere dati da una dichiarazione: fetch()

PDOStatement::fetch raccoglie una singola riga dal database e sposta il puntatore interno nel set di risultati, pertanto le chiamate successive a questa funzione restituiranno tutte le righe risultanti una per volta.

Si tratta di un analogo a mysqli_fetch_array(), ma funziona in maniera leggermente differente: al posto di tante funzioni diverse (mysqli_fetch_assoc(), mysqli_fetch_row(), ...) ce n’è solo una e il suo comportamento varia in base al parametro che le viene associato, noto anche come fetch_style:

  • PDO::FETCH_NUM – restituisce un array indicizzato per numero di colonna, a partire dalla colonna 0
  • PDO::FETCH_ASSOC – restituisce un array associativo
  • PDO::FETCH_BOTH – restituisce entrambi quelli precedenti
  • PDO::FETCH_OBJ – restituisce un oggetto
  • PDO::FETCH_LAZY – permette tutti e tre i metodi(numeric,  associative e oggetto) senza sovraccaricare la memoria.

In base a quanto riportato sopra, si può dire che fetch() si possa usare in due occasioni:

  1. Quando ci si aspetta una sola riga, per ottenere quella determinata riga. Per esempio:
    $row = $stmt->fetch(PDO::FETCH_ASSOC);
    restituirà una sola riga, nella forma di un array associativo.

  2. Quando abbiamo bisogno di una qualche forma di elaborazione dei dati restituiti, prima che vengano in qualche modo utilizzati. In questo caso deve essere eseguito attraverso il solito ciclo while{}:
$stmt = $pdo->query('SELECT username FROM users');
while ($row = $stmt->fetch())
{
    echo $row['username'] . "\n";
}

Anche questa query, come quella del ciclo foreach(), restituisce un elenco di valori presenti nella colonna username della tabella users.

Di default, cioè se non si specifica nessun altro metodo, quello utilizzato è PDO::FETCH_BOTH

È possibile inoltre specificarlo all’interno dell’array delle impostazioni PDO

$options = [
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_NUM
];

// oppure 
$stmt = $pdo->query('SELECT name, lastname FROM users');
$result = $stmt->setFetchMode(PDO::FETCH_NUM);
while ($row = $stmt->fetch())
{
    echo $row[0] . "\t" . $row[1] . "<br/>\n";
}

FETCH_NUM usa un indice numerico e rende quindi obbligatorio rispettare l’ordine dei campi espresso nell’istruzione SELECT

Ottenere dati da una dichiarazione: fetchColumn()

Una funzione di aiuto precisa che restituisce il valore del campo singolo di una riga. Utile quando selezioniamo un solo campo:

// recupera il valore della colonna nome di un determinato id
$stmt = $pdo->prepare("SELECT name FROM users WHERE id=?");
$stmt->execute([$id]);
$name = $stmt->fetchColumn();

// recupera il numero delle righe della tabella utilizzando method chaining
$count = $pdo->query("SELECT count(*) FROM users")->fetchColumn();

Ottenere dati dagli statement in decine di diversi formati: fetchAll()

Questa è la funzione più interessante, con le caratteristiche più sorprendenti. Principalmente grazie alla sua esistenza PDO si può chiamare wrapper, poiché questa funzione può automatizzare molte operazioni che altrimenti dovrebbero essere eseguite manualmente.

PDOStatement::fetchAll() restituisce un array che contiene tutte le righe restituite dalla query. Da questo possiamo trarre 2 conclusioni:

  1. Questa funzione non dovrebbe essere utilizzata nel caso in cui debbano essere selezionate centinaia o migliaia di righe: conviene piuttosto utilizzare il ciclo while{} che recupera le righe una ad una, piuttosto che tutte insieme in un array.
  2. Di certo non sarebbe utile nelle pagine web attuali, considerando che le info da database non sono mai così estreme.

Sareste stupiti nel sapere in quanti formati diversi questa funzione possa restituire dati (e quanto poco un utente medio di PHP ne conosca), tutti controllati dalle variabili PDO::FETCH_*. Eccone alcune:

Ottenere un semplice array

Per impostazione predefinita, questa funzione restituisce un semplice array enumerato costituito da tutte le righe restituite. Le costanti di formattazione delle righe, come PDO::FETCH_NUM, PDO::FETCH_ASSOC, PDO::FETCH_OBJ, etc. possono modificare il formato della riga.

$data = $pdo->query('SELECT name FROM users')->fetchAll(PDO::FETCH_ASSOC);
var_export($data);

/*
array (
  0 => array('Marco'),
  1 => array('Antonella'),
  2 => array('Mary'),
  3 => array('Federico'),
)*/

Questa funzione è utile ad esempio per inviare al client un output in formato JSON

$stmt = $pdo->query('SELECT username, user_firstname, user_lastname FROM users');
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo '<pre>'; print json_encode($result); echo '<pre>';

/*
[
{"username":"edd","user_firstname":"Eddy","user_lastname":"Jones"},
{"username":"Dino21","user_firstname":"Eddie","user_lastname":"Vedder"},
{"username":"CrashAndBurn","user_firstname":"Albert","user_lastname":"Fenton"},
{"username":"eddd","user_firstname":"ed","user_lastname":"China"}
]*/

Ottenere una colonna

Spesso è molto utile ottenere un array monodimensionale al di fuori della query, se viene recuperata solo una colonna formata da molte righe:

$data = $pdo->query('SELECT name FROM users')->fetchAll(PDO::FETCH_COLUMN);

/*
array (
  0 => array('Marco'),
  1 => array('Antonella'),
  2 => array('Mary'),
  3 => array('Federico'),
)*/

Ottenere le coppie chiave-valore

Ecco un altro formato estremamente utile, che possiamo utilizzare quando abbiamo bisogno di ottenere la stessa colonna, ma indicizzati non dai numeri in ordine ma da un altro campo. Ecco la costante PDO::FETCH_KEY_PAIR:

$data = $pdo->query('SELECT id, name FROM users')->fetchAll(PDO::FETCH_KEY_PAIR);
/* array (
  104 => 'John',
  110 => 'Mike',
  127 => 'Mary',
  132 => 'Kathy',
)*/

Si noti il fatto che si devono selezionare solamente 2 colonne, una delle quali ha valori univoci (come per esempio l’id).

Ottenere le righe indicizzate da un campo univoco

Lo stesso che per FETCH_KEY_PAIR ma questa volta viene restituita non una colonna ma un’intera riga, indicizzata da un campo unico, con FETCH_UNIQUE:

$data = $pdo->query('SELECT * FROM users')->fetchAll(PDO::FETCH_UNIQUE);
/* array (
  104 => array (
    'name' => 'John',
    'car' => 'Toyota',
  ),
  110 => array (
    'name' => 'Mike',
    'car' => 'Ford',
  ),
  120 => array (
    'name' => 'Mary',
    'car' => 'Mazda',
  ),
  121 => array (
    'name' => 'Kathy',
    'car' => 'Mazda',
  ),
)*/

Anche qui c’è da fare una precisazione: la prima colonna selezionata deve essere univoca, come la colonna degli ID (in questa query si presume che la prima colonna sia id, ma per essere sicuri è meglio indicarla esplicitamente).

Ottenere le righe raggruppate per campo comune

PDO::FETCH_GROUP raggruppa le righe in un array nidificato (nested array), in cui gli indici saranno valori univoci delle prime colonne e i valori saranno array simili a quelli restituiti dal semplice fetchAll(). Il seguente codice, ad esempio, separerà i maschi dalle femmine e li metterà in due distinti array:

$data = $pdo->query('SELECT sex, name, car FROM users')->fetchAll(PDO::FETCH_GROUP);
array(
  'male' => array (
    0 => array (
      'name' => 'John',
      'car' => 'Toyota',
    ),
    1 => array (
      'name' => 'Mike',
      'car' => 'Ford',
    ),
  ),
  'female' => array (
    0 => array (
      'name' => 'Mary',
      'car' => 'Mazda',
    ),
    1 => array (
      'name' => 'Kathy',
      'car' => 'Mazda',
    ),
  ),
)

Rispondi

Inserisci i tuoi dati qui sotto o clicca su un'icona per effettuare l'accesso:

Logo di WordPress.com

Stai commentando usando il tuo account WordPress.com. Chiudi sessione /  Modifica )

Google photo

Stai commentando usando il tuo account Google. Chiudi sessione /  Modifica )

Foto Twitter

Stai commentando usando il tuo account Twitter. Chiudi sessione /  Modifica )

Foto di Facebook

Stai commentando usando il tuo account Facebook. Chiudi sessione /  Modifica )

Connessione a %s...

Questo sito utilizza Akismet per ridurre lo spam. Scopri come vengono elaborati i dati derivati dai commenti.