Database API
La Database API, da utilizzare solo nei casi in cui non è possibile usare altre API, utilizza una classe Connection che rappresenta una connessione al database e fornisce metodi come:
- select()
- query()
- insert()
- update()
- delete()
- merge()
- upsert()
- truncate()
- startTransaction()
- schema()
Supponendo di avere una tabella con le colonne id, location, temperature e created, per prima cosa dobbiamo creare la connessione:
use Drupal\Core\Database\Connection;
final class ExampleStorage {
public function __construct(
private readonly Connection $database,
) {}
}
Esempio procedurale:
function nome_modulo_example(): void {
$database = \Drupal::database();
// ...
}
Schema API
La Schema API permette di descrivere le tabelle attraverso array associativi, quindi nel file nome_modulo.install verrà chiamato hook_schema() in modo che all'installazione del modulo vengano create automaticamente le tabelle descritte e alla disinstallazione vengano eliminate. L'hook_schema() è sempre procedurale, ad esempio:
<?php
<?php
/**
* Implements hook_schema().
*/
function nome_modulo_schema(): array {
$schema = [];
$schema['nome_modulo_example_measurement'] = [ // nome della tabella
'description' =>
'Stores example weather measurements.',
'fields' => [ // dentro fields ci sono le colonne
'id' => [
'description' =>
'Primary identifier.',
'type' => 'serial', // campo numerico auto incrementale
'not null' => TRUE,
],
'location' => [
'description' =>
'Human-readable location.',
'type' => 'varchar', //il tipo varchar richiede una lunghezza
'length' => 128,
'not null' => TRUE,
],
'temperature' => [
'description' =>
'Measured temperature.',
'type' => 'numeric', //il tipo numeric richiede precision e scale
'precision' => 6,
'scale' => 2,
'not null' => TRUE,
],
'created' => [
'description' =>
'Unix timestamp when the measurement was recorded.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
],
],
'primary key' => [ // una o più colonne da usare come identificativo univoco
'id', //il field chiamato id definito sopra
],
'indexes' => [ //importante per velocizzare la query
'location' => [
'location',
],
'created' => [
'created',
],
],
];
return $schema;
}
?>
Oggetto Schema
L'oggetto recuperato con:
$schema = $database->schema();
permette di eseguire queste operazioni:
- tableExists()
- fieldExists()
- indexExists()
- createTable()
- dropTable()
- addField()
- dropField()
- changeField()
- addIndex()
- dropIndex()
- addUniqueKey()
- dropUniqueKey()
Query di SELECT
Le query di select possono ssere statiche o dinamiche. Una query statica contiene direttamente SQL:
$result = $this->database->query(
'SELECT id, location, temperature
FROM {nome_modulo_example_measurement} //il nome della tabella deve essere dentro {} senza eventuale prefisso
WHERE location = :location', //placeholde rper proteggere da SQL injection
[
':location' => $location,
],
);
//sbagliato
WHERE location = '$location'";
//corretto
WHERE location = :location';
Una query dinamica invece viene costruita attraverso metodi PHP:
$query = $this->database->select(
'nome_modulo_example_measurement', //qui no parentesi {} nel nome della tabella
'm', // alias della tabella
);
$query->fields(
'm',
[
'id',
'location',
'temperature',
'created',
],
);
$query->condition( //eventualmente si può passare terzo parametro '=' che indica l'operatore ma è già di default
'm.location',
$location,
);
$query->orderBy(
'm.created',
'DESC',
);
$query->range( //per usare clausola LIMIT
0, //indica l'offset
10, //indica il limit
);
$result = $query->execute();
Esempi di condition:
$query->condition(
'm.temperature',
30,
'>=',
);
$query->condition(
'm.location',
[
'Turin',
'Milan',
'Rome',
],
'IN',
);
// condizione con due parametri
// equivalente di
// WHERE ( location = 'Turin' OR location = 'Milan' ) AND temperature >= 25
<?php
$query = $this->database->select(
'nome_modulo_example_measurement',
'm',
);
$query->fields(
'm',
[
'id',
'location',
'temperature',
],
);
$group = $query->orConditionGroup()
->condition(
'm.location',
'Turin',
)->condition(
'm.location',
'Milan',
);
$query
->condition($group)
->condition(
'm.temperature',
25,
'>=',
);
Esempio con JOIN:
<?php
$query = $this->database->select(
'nome_modulo_example_measurement',
'm',
);
$alias = $query->leftJoin( //join restituisce l'alias della tabella
'users_field_data',
'u',
'u.uid = m.uid',
);
$query->fields(
'm',
[
'id',
'temperature',
],
);
$query->addField(
$alias,
'name',
'username',
);
Esempio di COUNT:
$count = $this->database
->select(
'nome_modulo_example_measurement',
'm',
)
->condition(
'm.location',
$location,
)
->countQuery()
->execute()
->fetchField();
Result set
L'esecuzione della query ritorna un oggetto che implementa StatementInterface e che può essere iterato:
$result = $query->execute();
foreach ($result as $record) {
$id = $record->id;
$temperature = $record->temperature;
}
// per recuperare l'oggetto successivo
$record = $result->fetchObject();
// per ottenere un array associativo
$record = $result->fetchAssoc();
// per recuperare un singolo valore
$temperature = $this->database
->query(
'SELECT temperature
FROM {nome_modulo_example_measurement}
WHERE id = :id',
[
':id' => $id,
],
)->fetchField();
// per recuperare tutti i record di una singola colonna
//restituisce
// [ 1, 2, 3, ]
$ids = $result->fetchCol();
// per recuperare tutti i record (meglio non usarlo perchè il result set potrebbe essere grande)
$records = $result->fetchAll();
// ottenere un array indicizzato attraverso un campo
//restituisce
// [ 1 => $record1, 2 => $record2, ]
$records = $result->fetchAllAssoc(
'id',
);
//supponendo di avere una query del tipo SELECT id, location
//rstituisce
//[ 1 => 'Turin', 2 => 'Milan', ]
$locations = $result->fetchAllKeyed();
Query di INSERT
Per inserire un record:
$id = $this->database
->insert(
'nome_modulo_example_measurement',
)
->fields([
'location' => 'Turin',
'temperature' => 28.4,
'created' => time(),
])
->execute(); //restituisce l'id generato
//per inserire più record
$query = $this->database
->insert(
'nome_modulo_example_measurement',
)
->fields([
'location',
'temperature',
'created',
]);
$query->values([
'Turin',
28.4,
1786000000,
]);
$query->values([
'Milan',
30.1,
1786000000,
]);
$query->execute();
Query di UPDATE
Per modificare un record:
$updated = $this->database
->update(
'nome_modulo_example_measurement',
)
->fields([
'temperature' => 29.2,
])
->condition(
'id',
$id,
)
->execute();
Query di DELETE
Per eliminare una riga:
$deleted = $this->database
->delete(
'nome_modulo_example_measurement',
)
->condition(
'id',
$id,
)
->execute();
Se invece si vuole svuotare una tabella esiste anche il metodo truncate che però è concettualmente diverso.
Transazioni
Una transazione permette di trattare più operazioni di scrittura come un'unica unità logica e si usa nei casi in cui un'operazione deve essere considerata valida soltanto se tutte le scritture collegate hanno successo.
<?php
use Throwable;
$transaction =
$this->database
->startTransaction();
try {
$measurementId =
$this->database
->insert(
'nome_modulo_example_measurement',
)
->fields([
'location' => 'Turin',
'temperature' => 28.4,
'created' => time(),
])->execute();
$this->database
->update(
'nome_modulo_example_statistics',
)
->expression(
'measurement_count',
'measurement_count + 1',
)
->condition(
'location',
'Turin',
)
->execute();
$transaction->commitOrRelease();
} catch (Throwable $exception) {
$transaction->rollBack();
throw $exception;
}