Lavorare con il database in Drupal 7 – Lezione 4 – Query dinamiche (SELECT)
Probabilmente siamo arrivati alla parte più interessante del Database API di Drupal: le query dinamiche. Si chiamano così perché Drupal costruisce dinamicamente la stringa della query. Tutte le query di inserimento, aggiornamento, eliminazione o unione possono essere dinamiche. Le query di selezione possono essere sia dinamiche che statiche, ma è consigliato usare sempre le query dinamiche anche per le SELECT.
Tutte le query dinamiche vengono create come oggetti query, ai quali possiamo aggiungere condizioni e parametri quando necessario. Come nelle query statiche, la maggior parte dei casi utilizza un procedural wrapper. Tutti i parametri successivi possono essere aggiunti direttamente all’oggetto query.
Query di selezione db_select()
Le query di selezione dinamiche iniziano con la funzione db_select(), ad esempio:
<?php
$query = db_select('node', 'n', $options);
?>
In questo esempio 'node' è la tabella a cui ci riferiamo (l’equivalente della clausola FROM in SQL). Non è necessario racchiudere il nome della tabella tra parentesi graffe, poiché il costruttore di query lo farà automaticamente. Il secondo parametro è l’alias della tabella. Se non viene fornito, Drupal userà il nome della tabella stessa. Il parametro $options è facoltativo e ha la stessa struttura di quello usato nelle query statiche.
Le query di selezione dinamiche possono essere molto semplici o estremamente complesse. Vedremo i principi fondamentali per comprenderne il funzionamento, ma le loro possibilità sono molto ampie.
Esempio di db_select()
Di seguito un esempio semplice di query di selezione sulla tabella degli utenti. Più avanti vedremo come utilizzare i join per costruire query più complesse.
<?php
$query = db_select('users', 'u');
$query
->condition('u.uid', 0, '<>')
->fields('u', array('uid', 'name', 'status', 'created', 'access'))
->range(0, 50);
$result = $query->execute();
?>
Questo esempio equivale alla seguente query SQL:
SELECT uid, name, status, created, access FROM {users} u WHERE uid <> 0 LIMIT 50 OFFSET 0;
Join (INNER, LEFT, RIGHT)
Per unire altre tabelle alla query si utilizzano i metodi join(), innerJoin(), leftJoin() o rightJoin(), come nell’esempio seguente:
<?php
$table_alias = $query->join('user', 'u', 'n.uid = u.uid AND u.uid = :uid', array(':uid' => 5));
?>
Questo aggiunge un INNER JOIN (tipo predefinito) con la tabella “user”, alias “u”. La condizione ON è “n.uid = u.uid AND u.uid = :uid”, dove il segnaposto :uid viene sostituito con 5. I segnaposti proteggono la query da SQL injection, quindi non bisogna mai concatenare valori direttamente nella stringa SQL.
Il valore restituito dal metodo join() è l’alias effettivo della tabella, che Drupal rinominerà automaticamente se necessario per evitare conflitti.
È anche possibile eseguire join con sottoquery:
<?php
$myselect = db_select('mytable')
->fields('mytable')
->condition('myfield', 'myvalue');
$alias = $query->join($myselect, 'myalias', 'n.nid = myalias.nid');
?>
Campi (fields, addField)
Per aggiungere un campo alla query si usa addField():
<?php
$title_field = $query->addField('n', 'title', 'my_title');
?>
Questo aggiunge il campo “title” dalla tabella con alias “n” e lo rinomina “my_title”. Se l’alias non è definito, Drupal ne genera uno automaticamente (es. “title” o “n_title”).
Per selezionare più campi si possono chiamare più volte addField() o usare fields():
<?php
$query->fields('n', array('nid', 'title', 'created', 'uid'));
?>
Questo equivale a chiamare addField() quattro volte. Se si chiama fields() senza elencare campi, il risultato sarà equivalente a SELECT n.*.
DISTINCT
Per rimuovere duplicati dai risultati, si usa distinct():
<?php $query->distinct(); ?>
Attenzione: DISTINCT può ridurre le prestazioni, quindi usarlo solo se necessario.
Espressioni (addExpression)
Il costruttore di query consente anche di aggiungere espressioni SQL, ad esempio funzioni di aggregazione o calcoli:
<?php
$count_alias = $query->addExpression('COUNT(uid)', 'uid_count');
$count_alias = $query->addExpression('created - :offset', 'uid_count', array(':offset' => 3600));
?>
ORDER BY
Per ordinare i risultati, usa orderBy():
<?php
$query->orderBy('title', 'DESC');
?>
Il secondo parametro può essere “ASC” o “DESC” (ASC è il valore predefinito). È possibile aggiungere più ordinamenti chiamando più volte orderBy().
Ordine casuale (orderRandom)
Per ottenere risultati in ordine casuale, usa:
<?php $query->orderRandom(); ?>
Può essere combinato con orderBy().
GROUP BY
Per raggruppare i risultati per campo:
<?php
$query->groupBy('uid');
?>
Limitazioni (range)
Per limitare il numero di record restituiti:
<?php $query->range(5, 10); ?>
Restituisce 10 record a partire dal sesto (l’indice parte da 0).
Estensioni per ordinamento tabellare (extend)
Drupal fornisce l’estensione TableSort per aggiungere funzionalità di ordinamento alle tabelle:
<?php
$query = $query
->extend('TableSort')
->orderByHeader($header);
?>
Condizioni (condition)
Le condizioni si aggiungono con condition() o where() per la clausola WHERE, e havingCondition() o having() per HAVING. Gli operatori funzionano in modo simile tra loro.
Esecuzione (execute)
Per eseguire la query si chiama execute():
<?php $result = $query->execute(); ?>
Il risultato può essere gestito con un ciclo:
<?php
foreach ($result as $record) {
// Elaborazione di ogni record
}
?>
Conteggio risultati
Per ottenere il numero totale di record, si può usare countQuery():
<?php $num_rows = $query->countQuery()->execute()->fetchField(); ?>
Debug
Per visualizzare la query SQL generata, usa:
<?php print_r($query->__toString()); ?>