Definitie en gebruik van Content Entity Field-definities
Content entities moeten expliciet al hun velden definiëren door definities te verstrekken voor de entities class. Velddefinities zijn gebaseerd op de Typed data API (zie Hoe entities dit implementeren).
Velddefinities
Entiteitstypen definiëren hun basisvelden in een statische methode van de entiteitsklasse. Basisvelden zijn niet-aanpasbare velden die altijd bestaan in een bepaald entiteitstype, bijvoorbeeld de titel van een node of de aanmaak- en wijzigingsdatum. De entity manager vult de velden aan met configureerbare en niet-configureerbare velden die door andere modules worden geleverd, door hook_entity_field_info() en hook_entity_field_info_alter() aan te roepen. Ook velden die via de Field UI geconfigureerd zijn, worden toegevoegd (deze hooks bestaan niet meer volgens de API).
Velddefinities zijn eenvoudige objecten die FieldDefinitionInterface implementeren, terwijl basisvelden meestal worden aangemaakt met de klasse BaseFieldDefinition. Configureerbare velden implementeren rechtstreeks de interface met bijbehorende configuratieobjecten (de zogenaamde Field en FieldInstance).
Velddefinities zijn ook de plaats waar validatiebeperkingen voor veldelementen of velditemeigenschappen worden gedefinieerd. Alle implementaties van het veldtype-plug-in kunnen gebruikt worden. (Deze interface en klasse bestaan niet meer).
Velden zijn momenteel altijd een lijst van veldelementen, wat betekent dat de FieldItem-klasse, gedefinieerd als type, verpakt wordt in de FieldItemList-klasse die de lijst van deze veldelementen vertegenwoordigt.
Alle velden (inclusief basisvelden) kunnen ook widgets en formatters hebben voor hun weergave en bewerking.
Basisvelden
Hieronder volgt een verkort voorbeeld van de lijst met velddefinities van het entiteitstype node.
use Drupal\Core\Field\BaseFieldDefinition;
class Node implements NodeInterface {
/**
* {@inheritdoc}
*/
public static function baseFieldDefinitions($entity_type) {
// Het node-id is een integer, met de IntegerItem velditemklasse.
$fields['nid'] = BaseFieldDefinition::create('integer')
->setLabel(t('Node ID'))
->setDescription(t('The node ID.'))
->setReadOnly(TRUE);
// Het UUID-veld gebruikt het uuid_field-type dat ervoor zorgt dat een nieuwe UUID automatisch wordt gegenereerd wanneer een entiteit wordt aangemaakt.
$fields['uuid'] = BaseFieldDefinition::create('uuid')
->setLabel(t('UUID'))
->setDescription(t('The node UUID.'))
->setReadOnly(TRUE);
// De taalcode is gedefinieerd als een language_field, dat er opnieuw voor zorgt dat een geldige standaardtaalcode wordt ingesteld voor nieuwe entiteiten.
$fields['langcode'] = BaseFieldDefinition::create('language')
->setLabel(t('Language code'))
->setDescription(t('The node language code.'));
// De titel is StringItem, de standaardwaarde is een lege string en definieert een property constraint voor de waarde van maximaal 255 tekens.
$fields['title'] = BaseFieldDefinition::create('string')
->setLabel(t('Title'))
->setDescription(t('The title of this node, always treated as non-markup plain text.'))
->setRequired(TRUE)
->setTranslatable(TRUE)
->setSettings(array(
'default_value' => '',
'max_length' => 255,
));
// De uid is een entity reference naar het entiteitstype user, waardoor de user id toegankelijk is via $node->uid->target_id
// en de user entiteit met $node->uid->entity. NodeInterface definieert ook getAuthor() en getAuthorId(). (@todo: check owner vs. revisionAuthor)
$fields['uid'] = BaseFieldDefinition::create('entity_reference')
->setLabel(t('User ID'))
->setDescription(t('The user ID of the node author.'))
->setSettings(array(
'target_type' => 'user',
'default_value' => 0,
));
// Het veld 'changed' werkt automatisch de timestamp bij telkens wanneer de entiteit wordt opgeslagen.
$fields['changed'] = BaseFieldDefinition::create('changed')
->setLabel(t('Changed'))
->setDescription(t('The time that the node was last edited.'))
return $fields;
}
}
Meerwaardevelden
Om het maximumaantal items dat is toegestaan voor een veld op te geven, roep je de methode setCardinality() aan.
Bijvoorbeeld, om een veld te definiëren dat 3 items kan hebben:
->setCardinality(3);
Om een veld met onbeperkte waarden te definiëren, roep je aan:
->setCardinality(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
Voorbeeld van de definitie van een meerwaardig veld voor een verwijzing naar de entiteit "user":
$fields['my_field'] = BaseFieldDefinition::create('entity_reference')
->setLabel(t('The label of the field'))
->setDescription(t('The description of the field.'))
->setRevisionable(TRUE)
->setSetting('target_type', 'user')
->setSetting('handler', 'default')
->setTranslatable(TRUE)
->setDisplayOptions('view', [
'label' => 'hidden',
'type' => 'author',
'weight' => 0,
])
->setDisplayOptions('form', [
'type' => 'entity_reference_autocomplete',
'weight' => 5,
'settings' => [
'match_operator' => 'CONTAINS',
'size' => '60',
'autocomplete_type' => 'tags',
'placeholder' => '',
],
])
->setDisplayConfigurable('form', TRUE)
->setDisplayConfigurable('view', TRUE);
->setRequired(TRUE)
->setCardinality(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
Een entiteit kan ook velden leveren die alleen bestaan voor een bepaald bundle, of ze via het bundle aanpassen. Bijvoorbeeld, de titel van een node kan een ander label hebben voor elk bundle. Om wijzigingen per bundle mogelijk te maken, moet de definitie van het basisveld worden gekloond vóór het aanbrengen van wijzigingen, omdat dit anders de basisveldefinitie zou veranderen en alle bundles zou beïnvloeden.
use Drupal\node\Entity\NodeType;
/**
* {@inheritdoc}
*/
public static function bundleFieldDefinitions(EntityTypeInterface $entity_type, $bundle, array $base_field_definitions) {
$node_type = NodeType::load($bundle);
$fields = array();
if (isset($node_type->title_label)) {
$fields['title'] = clone $base_field_definitions['title'];
$fields['title']->setLabel($node_type->title_label);
}
return $fields;
}
Veldtypes
Drupal core levert een lijst met veldtypes die gebruikt kunnen worden voor basisvelden. Daarnaast kunnen modules extra veldtypes leveren die ook gebruikt kunnen worden.
- string: eenvoudige string
- boolean: boolean waarde, opgeslagen als integer.
- integer: integer met instellingen voor validatie van minimum en maximum (ook beschikbaar voor decimal en float)
- decimal: decimaal getal met configureerbare precisie en schaal.
- float: getal met drijvende komma
- language: bevat taalcodes en taal als berekende eigenschap
- timestamp: Unix timestamp, opgeslagen als integer
- created: timestamp dat de huidige tijd gebruikt als standaardwaarde.
- changed: timestamp dat automatisch wordt bijgewerkt naar de huidige tijd wanneer het object wordt opgeslagen.
- datetime: datum opgeslagen als ISO 8601 string.
- URI: bevat een URI. De link-module levert ook het veldtype "link" dat een linktitel kan bevatten en kan verwijzen naar interne of externe URI/route.
- uuid: UUID-veld dat een nieuwe UUID genereert als standaardwaarde.
- email: e-mailadres met passende validatie, widgets en formatters.
- entity_reference: verwijzing naar entiteit via target_id en berekende entity-eigenschap. entity_reference.module levert widgets en formatters wanneer ingeschakeld.
- map: kan willekeurig aantal eigenschappen bevatten, opgeslagen als geserialiseerde string
Configureerbare velden
Extra velden kunnen geregistreerd worden in hook_entity_base_field_info() en hook_entity_bundle_field_info(). In de volgende voorbeelden worden base- en bundlevelden toegevoegd.
use Drupal\Core\Field\BaseFieldDefinition;
/**
* Implements hook_entity_base_field_info().
*/
function path_entity_base_field_info(EntityTypeInterface $entity_type) {
if ($entity_type->id() === 'taxonomy_term' || $entity_type->id() === 'node') {
$fields['path'] = BaseFieldDefinition::create('path')
->setLabel(t('The path alias'))
->setComputed(TRUE);
return $fields;
}
}
/**
* Implements hook_entity_bundle_field_info().
*/
function field_entity_bundle_field_info(EntityTypeInterface $entity_type, $bundle, array $base_field_definitions) {
if ($entity_type->isFieldable()) {
// Configureerbare velden, die altijd aan een specifieke bundle gekoppeld zijn,
// worden 'per bundle' toegevoegd.
return Field::fieldInfo()->getBundleInstances($entity_type->id(), $bundle);
}
}
Er bestaan overeenkomstige alter hooks voor elk van bovenstaande.
Opslag
Als je veld geen speciale vereisten heeft, kan de Entity Field API zorgen voor databaseopslag en de databasetabellen bijwerken. Dit is de standaard voor velden die niet gemarkeerd zijn als berekend (setComputed(TRUE)) of specifiek eigen opslag instellen (setCustomStorage(TRUE)).
Stel dat je een nieuw basisveld wilt toevoegen aan alle Node-entiteiten, dat een eenvoudige boolean bevat om aan te geven of de inhoud is gemarkeerd.
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
/**
* Implements hook_entity_base_field_info().
*/
function MYMODULE_entity_base_field_info(EntityTypeInterface $entity_type) {
$fields = array();
// Voeg een 'Highlight' basisveld toe aan alle node types.
if ($entity_type->id() === 'node') {
$fields['highlight'] = BaseFieldDefinition::create('boolean')
->setLabel(t('Highlight'))
->setDescription(t('Whether or not the node is highlighted.'))
->setRevisionable(TRUE)
->setTranslatable(TRUE)
->setDisplayOptions('form', array(
'type' => 'boolean_checkbox',
'settings' => array(
'display_label' => TRUE,
),
))
->setDisplayConfigurable('form', TRUE);
}
return $fields;
}
Ik heb dit vaak geprobeerd, en het bezoeken van update.php zal geen kolom toevoegen aan de database, maar uitvoeren:
\Drupal::entityTypeManager()->clearCachedDefinitions();
\Drupal::service('entity.definition_update_manager')->applyUpdates();
kan ervoor zorgen dat een kolom in de database wordt aangemaakt. Opmerking: hiermee worden ook eventuele andere updates uitgevoerd die wachten op andere velddefinities.
Update: bovenstaande code werkt niet met Drupal 8.7
Zie het voorbeeld in deze change record
Installeren van een nieuwe field storage definitie
function example_update_8701() {
$field_storage_definition = BaseFieldDefinition::create('boolean')
->setLabel(t('Revision translation affected'))
->setDescription(t('Indicates if the last edit of a translation belongs to current revision.'))
->setReadOnly(TRUE)
->setRevisionable(TRUE)
->setTranslatable(TRUE);
\Drupal::entityDefinitionUpdateManager()
->installFieldStorageDefinition('revision_translation_affected', 'block_content', 'block_content', $field_storage_definition);
}
Als je custom module een nieuw veld toevoegt, wordt dit automatisch toegevoegd bij het inschakelen van de module en verwijderd bij het verwijderen van de module.
Als je module al geïnstalleerd is en je moet hook_update_N schrijven om velddefinities bij te werken, kun je bijvoorbeeld dit doen:
/**
* Add in highlight field to all nodes.
*/
function MYMODULE_update_8001() {
$entity_type = \Drupal::service('entity_type.manager')->getDefinition('node');
\Drupal::service('entity.definition_update_manager')->updateEntityType($entity_type);
}
of
/**
* Add 'revision_translation_affected' field to 'node' entities.
*/
function node_update_8001() {
// Installeer de definitie die dit veld had in
// \Drupal\node\Entity\Node::baseFieldDefinitions()
// op het moment dat deze updatefunctie werd geschreven. Als/wanneer code wordt
// uitgerold die deze definitie verandert, moet de bijbehorende module
// een updatefunctie implementeren die
// \Drupal::entityDefinitionUpdateManager()->updateFieldStorageDefinition()
// aanroept met de nieuwe definitie.
$storage_definition = BaseFieldDefinition::create('boolean')
->setLabel(t('Revision translation affected'))
->setDescription(t('Indicates if the last edit of a translation belongs to current revision.'))
->setReadOnly(TRUE)
->setRevisionable(TRUE)
->setTranslatable(TRUE);
\Drupal::entityDefinitionUpdateManager()
->installFieldStorageDefinition('revision_translation_affected', 'node', 'node', $storage_definition);
}
Zie https://www.drupal.org/node/2554097 voor meer informatie en voorbeelden.
Werken met velddefinities
Opmerking. Omdat objecten complexe data zijn, moeten ze ComplexDataInterface volgen. Vanuit het perspectief van typed data zijn alle opgenomen typed data-elementen in een complex data-object eigenschappen. Deze beperking/verplichte naamgeving kan opgeheven worden.
// Controleert of een entiteit een bepaald veld heeft.
$entity->hasField('field_tags');
// Geeft een array terug met benoemde sleutels voor alle velden en hun
// definities. Bijvoorbeeld het 'image'-veld.
$field_definitions = $entity->getFieldDefinitions();
// Geeft een array terug met sleutels voor alle velditemeigenschappen en hun
// definities van het image-veld. Bijvoorbeeld de 'file_id' en 'alt'-eigenschappen.
$property_definitions = $entity->image->getFieldDefinition()->getPropertyDefinitions();
// Geeft alleen de definitie terug voor de 'alt'-eigenschap.
$alt_definition = $entity->image->getFieldDefinition()->getPropertyDefinition('alt');
// Entiteitsvelddefinities kunnen ook worden opgevraagd via de entity manager,
// het volgende geeft alle velden terug die beschikbaar zijn voor alle bundles.
\Drupal::service('entity_field.manager')->getFieldStorageDefinitions($entity_type);
// Het volgende geeft velden terug die beschikbaar zijn voor de opgegeven bundle.
\Drupal::service('entity_field.manager')->getFieldDefinitions($entity_type, $bundle);
Widgets en formatters voor basisvelden
Basisvelden kunnen widgets en formatters specificeren die ze moeten gebruiken, net als configureerbare velden. De widget, formatter en vereiste instellingen worden in de FieldDefinition-class als volgt ingesteld:
use Drupal\Core\Field\BaseFieldDefinition;
// ...
$fields['title'] = BaseFieldDefinition::create('string')
->setLabel(t('Title'))
->setDescription(t('The title of this node, always treated as non-markup plain text.'))
->setRequired(TRUE)
->setTranslatable(TRUE)
->setSettings(array(
'default_value' => '',
'max_length' => 255,
))
->setDisplayOptions('view', array(
'label' => 'hidden',
'type' => 'string',
'weight' => -5,
))
->setDisplayOptions('form', array(
'type' => 'string',
'weight' => -5,
))
->setDisplayConfigurable('form', TRUE);
Dit gebruikt de formatter "string" en de widget, en stelt het gewicht in voor de node-titel. setDisplayConfigurable() kan gebruikt worden om het veld zichtbaar te maken in de UI voor formulier- en weergavebeheer, zodat je de volgorde en labels kunt aanpassen. Momenteel staat core niet toe om de widget of instellingen in de UI te wijzigen.
Om een veld standaard verborgen te maken, kun je ook de region key definiëren in de array die je aan setDisplayOptions() doorgeeft, en deze instellen op hidden.