Per memorizzare i dati in Drupal ci sono diverse soluzioni:
- Configuration API: per impostazini del modulo, content type, views, ruoli o comunque cose che devono essere esportate in altri ambienti
- State API: per ultima esecuzione del Cron, o ultimo aggiornamento completato
- Entity API: per nodi, utenti, termini di tassonomia, media
- Cache API: per risposte di API esterne
- Sessione o TempStore: per selezione temporanea di uno specifico utente
- file settings.php o variabili d'ambiente: per credenziali, percorsi, override locali
Configuration API
La Configuration API memorizza le impostazioni che determinano il funzionamento del sito, ad esempio:
- nome del sito
- moduli installati
- content type
- configurazione dei campi
- Views
- ruoli e permission
- image style
- form mode e view mode
- block placement
- impostazioni di un modulo custom
Le configurazioni vengono normalmente esportate tramite file YAML in modo che possano essere importate su altri ambienti. La configurazione attiva viene normalmente conservata nel database nella tabella config, mentre i file YAML vengono utilizzati per installazione, esportazione e sincronizzazione.
La directory in cui memorizzare i files YAML per la configurazione viene impostata attraverso $settings['config_sync_directory'] e di solito si trova nella cartella /config/sync. Quando si esegue il comando drush config:export Drupal confronta la configurazione attiva con la directory di sincronizzazione e scrive i relativi files YAML in modo che corrispondano alla configurazione attiva, quando si esegue drush config:import Drupal modifica la configurazione attiva affinché corrisponda ai files presenti nella directory di sincronizzazione.
Simple configuration e configuration entity
La simple configuration è un singolo oggetto identificato da un nome, ad esempio nel file .settings.yml di un modulo.
Una configuration entity invece è un oggetto di configurazione che utilizza anche l'Entity API, ad esempio:
- content type
- field storage
- field configuration
- Views
- ruoli
- menu
- block placement
- image style
- form mode
- view mode
Di norma una configurazione è un oggetto immutabile, ma in caso di necessità è possibile anche modificare la configurazione:
// prendo l'oggetto di configurazione in modalità read-only
$config = $this->configFactory->get( 'nome_modulo.settings', );
// leggo i valori
$location = $config->get('location');
$latitude = $config->get('latitude');
// prendo l'oggetto di configurazione in modalità modifica
$config = $this->configFactory->getEditable( 'nome_modulo.settings', );
// modifico il valore
$config
->set('location', 'Rome')
->set('latitude', 41.9028)
->set('longitude', 12.4964)
->save();
// per rimuovere una configurazione
$config
->clear('obsolete_setting')
->save();
// per eliminare l'intero oggetto di configurazione
$config->delete();
Esportare e importare la configurazione
Per visualizzare la configurazione attiva è possibile usare il comando drush config:get nome_modulo.settings oppure per una singola proprietà drush config:get nome_modulo.settings seguito dal nome della proprietà.
Per vedere la differenza tra la configurazione attiva e quanto presente nella config directory si può usare drush config:status.
E' anche possibile modificare la configurazione attiva con:
drush config:set \
nome_modulo.settings \
forecast_days \
7
Per esportare la configurazione attiva si usa drush config:export e per importarla drush config:import.
Workflow fra ambienti:
- si fanno le modifiche nell'ambiente di sviluppo, tramite UI o codice
- le modifiche vengono salvate nell'Active configuration
- si esportano le modifiche con drush config:export
- vengono aggiornati i files YAML nella sync directory
- Git commit e push delle modifiche ai files YAML
- deploy in staging o produzione
- nell'ambiente di staging o produzione si lancia il comando drush config:import
- vengono apportate le modifiche nella Active configuration del nuovo ambiente
Configuration override
Alcuni valori è corretto che siano diversi a seconda dell'ambiente, ad esempio endpont di sviluppo e di produzione, funzionalità usate solo per lo sviluppo, credenziali. E' possibile impostare questi override nel file settings.php scrivendo ad esempio:
$config['nome_modulo.settings']['timezone'] = 'UTC';
In questo modo eseguendo:
$this->configFactory
->get('nome_modulo.settings')
->get('timezone');
il valore recuperato sarà UTC indipendentemente da quanto presente nella configurazione attiva. Se invece si vuole recuperare il valore senza l'override si può usare:
$value = $this->configFactory
->get('nome_modulo.settings')
->getOriginal('timezone', FALSE);
La configurazione recuperata con getEditable è sempre priva di override.
Credenziali e segreti
Credenziali e API key non devono finire in files sottoposti a controllo di versione, una possibile soluzione è quella di leggere il dato da una variabile d'ambiente nel file settings.php:
$weatherApiKey = getenv(
'NOME_MODULO_WEATHER_API_KEY',
);
if (is_string($weatherApiKey)) {
$config['nome_modulo.settings']['api_key'] =
$weatherApiKey;
}
// e poi nel modulo per leggere l'Api Key
$apiKey = $config->get('api_key');
Eventi della Configuration API
La Configuration API emette degli eventi durante operazioni come salvataggio, eliminazione, rename, importazione.
Cacheability della configurazione
State API
La State API memorizza informazioni relative allo stato corrente di una specifica installazione, ad esempio:
- ultima esecuzione di cron
- ultimo aggiornamento completato
- timestamp dell’ultima sincronizzazione
- necessità di ricostruire un indice
- stato interno di un processo
- ultima operazione automatica riuscita
Per utilizzare la State Api la classe riceve tramite Dependency Injection la StateInterface che espone i metodi get(), getMultiple(), set(), setMultiple(), delete() e deleteMultiple().
Leggere un valore
$lastRun = $this->state->get(
'nome_modulo.last_cron_run',
);
// con un valore predefinito
$lastRun = $this->state->get(
'nome_modulo.last_cron_run',
0,
);
// con più valori
$values = $this->state->getMultiple([
'nome_modulo.last_cron_run',
'nome_modulo.last_successful_forecast_refresh',
]);
Salvare un valore
$this->state->set(
'nome_modulo.last_cron_run',
$timestamp,
);
// più valori
$this->state->setMultiple([
'nome_modulo.last_cron_run' => $timestamp,
'nome_modulo.last_successful_forecast_refresh' => $timestamp,
]);
Eliminare un valore
$this->state->delete(
'nome_modulo.last_cron_run',
);
// più valori
$this->state->deleteMultiple([
'nome_modulo.last_cron_run',
'nome_modulo.last_successful_forecast_refresh',
]);
Esempio completo nel file src/ForecastStatusStorage.php:
<?php
declare(strict_types=1);
namespace Drupal\nome_modulo;
use Drupal\Core\State\StateInterface;
/**
* Stores environment-specific forecast status information.
*/
final class ForecastStatusStorage {
/**
* State key containing the last cron execution timestamp.
*/
private const LAST_CRON_RUN =
'nome_modulo.last_cron_run';
/**
* State key containing the last successful refresh timestamp.
*/
private const LAST_SUCCESSFUL_REFRESH =
'nome_modulo.last_successful_forecast_refresh';
/**
* Constructs a ForecastStatusStorage object.
*/ public function __construct(
private readonly StateInterface $state
) { }
/**
* Returns the timestamp of the last cron execution.
*/
public function getLastCronRun(): ?int {
$value = $this->state->get(
self::LAST_CRON_RUN
);
return is_int($value) ? $value : null;
}
/**
* Stores the timestamp of the last cron execution.
*/
public function setLastCronRun(
int $timestamp
): void {
$this->state->set(
self::LAST_CRON_RUN,
$timestamp
);
}
/**
* Returns the timestamp of the last successful refresh.
*/
public function getLastSuccessfulRefresh(): ?int {
$value = $this->state->get(
self::LAST_SUCCESSFUL_REFRESH
);
return is_int($value) ? $value : null;
}
/**
* Stores the timestamp of the last successful refresh.
*/
public function setLastSuccessfulRefresh(
int $timestamp
): void {
$this->state->set(
self::LAST_SUCCESSFUL_REFRESH,
$timestamp
);
}
/**
* Removes all state values owned by the service.
*/
public function deleteAll(): void {
$this->state->deleteMultiple([
self::LAST_CRON_RUN,
self::LAST_SUCCESSFUL_REFRESH,
]);
}
}