Entity API
Le entity sono oggetti utilizzati per la memorizzazione persistente di contenuti e configurazione e l'Entity API è il sistema utilizzato da Drupal per rappresentarli e gestirli. Ogni entità appartiene a uno dei due entity type: content entity type e configuration entity type. Alcuni content entity type supportano inoltre dei sottotipi chiamati bundle (content type per i node, vocabolari per i taxonomy term, media type per i media).
Content entity type
- node
- user
- taxonomy term
- media
- comment
Configuration entity type
- content type
- Views
- user roles
- block configuration
- field configuration
Base field e configurable field
I base fields sono, ad esempio per i nodi: nid, langcode, title, status (published o unpublished), created, promoted, sticky. I configurable fields sono aggiunti tramite configurazione e vengono descritti attraverso configuration entity.
Esempio di creazione del content type Weather alert nel file config/install/node.type.weather_alert.yml (verrà creato all'installazione del modulo):
langcode: en
status: true
dependencies:
enforced:
module:
- nome_modulo
name: 'Weather alert'
type: weather_alert
description: 'Provides weather alerts for a geographical area.'
help: null
new_revision: true
preview_mode: 1
display_submitted: true
Field API
Field Type
Definisce il tipo di campo, ad esempio list_string indica un stringa che può avere un valore tra una lista predefinita.
Field widget
Definisce come il valore viene inserito dall'utente nella UI, ad esempio un campo list_string può essere inserito con un tag <select> o con dei radio buttons.
Field formatter
Definisce come il valore viene visualizzato a forntend.
Field storage configuration
Definisce caratteristiche che valgono per tutto l'entity type, ad esempio nome del campo, cardinalità (quanti valori può avere il campo), storage settings. Ad esempio nel file config/install/field.storage.node.field_alert_level.yml:
langcode: en
status: true
dependencies:
module:
- options
id: node.field_alert_level
field_name: field_alert_level
entity_type: node
type: list_string
settings:
allowed_values:
-
value: advisory
label: Advisory
-
value: warning
label: Warning
-
value: severe
label: Severe
allowed_values_function: ''
module: options
locked: false
cardinality: 1
translatable: false
indexes: { }
persist_with_no_fields: false
custom_storage: false
Field configuration
Definisce cose come label, descrizione, se è required, valore di default.
Validazione
Prima di utilizzare un field bisogna assicurarsi che questo esista e abbia un valore:
// verificare che esista
if ( $node->hasField('field_alert_level') ) {
// ...
}
// per verificare che abbia un valore
if (
!$node
->get('field_alert_level')
->isEmpty()
) {
// ...
}
Per verificare che non violi nessuna regola:
$violations = $alert->validate();
if ($violations->count() === 0) {
$alert->save();
}
// leggere le violazioni
foreach ($violations as $violation) {
$message = $violation->getMessage();
$path = $violation->getPropertyPath();
}
Per verificare che il salvataggio sia andato a buon fine:
// valori possibili SAVED_NEW o SAVED_UPDATED
$result = $alert->save();
// dopo il primo salvataggio questo restituisce FALSE
$alert->isNew();
// dopo il primo salvataggio è disponibile
$alert->id();
Per verificare che il nodo caricato sia del bundle corretto:
// se il nodo esiste ritorna un NodeInterface, se non esiste ritorna NULL
$alert = $this->entityTypeManager
->getStorage('node')
->load(42);
if (
$alert instanceof NodeInterface
&& $alert->bundle() === 'weather_alert'
) {
// Il nodo caricato è del bundle Weather alert
}
// caricare più nodi
$alerts = $this->entityTypeManager
->getStorage('node')
->loadMultiple([
12,
35,
48,
]);
// il risultato è un array dove la chiave è l'id
[
12 => $node12,
35 => $node35,
48 => $node48,
]
Modificare una entity:
$alert = $storage->load(42);
if (
$alert instanceof NodeInterface
&& $alert->bundle() === 'weather_alert'
) {
$alert->set(
'field_alert_level', // nome del campo
'severe', // valore
);
$alert->setTitle( // metodo semantico (esiste anche setPublished() setUnpublished e altri)
'Severe weather warning',
);
$violations = $alert->validate();
if ($violations->count() === 0) {
$alert->save();
}
}
Metodo procedurale:
<?php
use Drupal\node\Entity\Node;
$node = Node::load(42);
$node = Node::create([
'type' => 'weather_alert',
]);
?>
Definizione di costraint custom
Nel file src/Plugin/Validation/Constraint/ WeatherAlertDateRangeConstraint.php:
<?php
declare(strict_types=1);
namespace Drupal\nome_modulo\Plugin\Validation\Constraint;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Validation\Attribute\Constraint;
use Symfony\Component\Validator\Constraint as SymfonyConstraint;
/**
* Validates the date range of a weather alert.
*/
#[
Constraint(
id: "WeatherAlertDateRange",
label: new TranslatableMarkup(
"Weather alert date range",
[],
["context" => "Validation"]
),
type: false
)
]
final class WeatherAlertDateRangeConstraint extends SymfonyConstraint {
/**
* Validation error message.
*/
public string $message = "The end date must be later than the start date.";
}
?>
Nel file src/Plugin/Validation/Constraint/ WeatherAlertDateRangeConstraintValidator.php:
<?php
declare(strict_types=1);
namespace Drupal\nome_modulo\Plugin\Validation\Constraint;
use Drupal\Core\Field\FieldItemListInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
/**
* Validates the weather alert date range.
*/
final class WeatherAlertDateRangeConstraintValidator extends ConstraintValidator
{
/**
* {@inheritdoc}
*/
public function validate(
mixed $value,
Constraint $constraint
): void {
if (
!$constraint instanceof WeatherAlertDateRangeConstraint ||
!$value instanceof FieldItemListInterface ||
$value->isEmpty()
) {
return;
}
$entity = $value->getEntity();
if (
!$entity->hasField("field_alert_start") ||
$entity->get("field_alert_start")->isEmpty()
) {
return;
}
$startItem = $entity->get("field_alert_start")->first();
$endItem = $value->first();
if (
$startItem === null ||
$endItem === null ||
empty($startItem->date) ||
empty($endItem->date)
) {
return;
}
if ($endItem->date <= $startItem->date) {
$this->context
->buildViolation($constraint->message)
->addViolation(); // mostrerà il messaggio The end date must be later than the start date.
}
}
}