Inleiding tot de Entity API in Drupal 8
Drupal 8 Entity-systeem
Entiteiten zijn getypeerde klassen met methoden
| Generieke methoden |
$entity->id() |
| Entiteit type specifieke methoden | $node->getTitle() |
Achtergrond
Het Entity-systeem werd geĂŻntroduceerd aan het einde van de Drupal 7 ontwikkelingscyclus met basisstandaarden voor het laden van entiteiten. De toegevoegde entity.module breidde de API verder uit door ondersteuning toe te voegen voor het opslaan en verwijderen van entiteiten en vele andere verbeteringen.
De meeste van deze verbeteringen zijn nu opgenomen in Drupal 8. Validatie van entiteiten wordt nu uitgevoerd in een eigen API (die bijvoorbeeld een entiteit kan valideren die via REST is opgeslagen en niet via een formulier).
Twee varianten
Entiteitstypes in de core zijn er in twee varianten.
Configuratie-object
Maakt gebruik van het Configuratiesysteem. Ondersteunt vertalingen en kan standaard gebruikersinstellingen bieden voor installaties. Configuratieobjecten worden opgeslagen in een gedeelde configuratiedatabasetabel als rijen.
Content-entiteit
Bestaat uit configureerbare en basisvelden, kan revisies hebben en ondersteunt vertalingen. Contentobjecten worden opgeslagen in een aangepaste databasetabel als rijen. De naam van de tabel komt overeen met de “id” van het object en de kolommen worden gedefinieerd door de methode “baseFieldDefinitions” van het object.
Bundles
Bundles zijn verschillende varianten van een entiteitstype. Bijvoorbeeld: voor het entiteitstype node zijn de bundles de verschillende nodetypes, zoals “artikel” en “pagina”.
Meestal wordt een bundle vertegenwoordigd door een configuratieobject, hoewel contrib-modules ook andere modellen hebben. Dus in het voorbeeld van nodes is het nodetype “article” zelf een configuratieobject. De configuratie slaat de verschillen op tussen contentobjecttypen, zoals instellingen en velden. Bij het maken van een nieuw entiteitstype met bundle-Entities creëer je zowel een content-entiteit die de details en bewerkingen van de content beheert, als een configuratieobject dat de verschillen tussen content-entiteitstypes afhandelt.
Annotaties
Wanneer je een nieuw entiteitstype maakt, moet je het annotatiesysteem gebruiken dat in de core is ingebouwd. Annotaties zien eruit als docblock-commentaren boven de klasse, maar worden geparseerd en gecachet door de Drupal-core. In veel opzichten vervangen annotaties enkele van de oudere stijlen die in Drupal 7 werden gebruikt.
Annotatieparser
Annotaties worden tijdens runtime gelezen en geparseerd door het annotatiemechanisme. Drupal 8 gebruikt de Doctrine-annotatieparser, die ze omzet in een object dat door PHP kan worden gebruikt.
Syntaxis. De syntaxis van een annotatie wordt omgeven door @ClassName(), bestaat meestal uit key/value-paren en kan arrays bevatten die accolades gebruiken. Top-level keys hoeven niet tussen aanhalingstekens te staan, maar arraykeys wel. Elk key/value-paar moet op een aparte regel staan, en die regel moet eindigen met een komma. Bepaalde functies kunnen worden uitgevoerd op waarden, met name de functie @Translation().
Niet-werkend voorbeeld van annotatiesyntaxis:
/**
* @ContentEntityType(
* id = "my_entity_type_id",
* label = @Translation("My entity type label"),
* example_pair = "this_examples_value",
* example_array = {
* "array_key" = "array_value",
* "some_other_key" = "some_other_value",
* },
* )
*/
Algemene top-level annotaties
| Key = "Voorbeeldwaarde" | Beschrijving | Entiteitsvariant |
| id = "node", |
Machinenaam voor het objecttype. |
Content & Config |
| label = @Translation("Node"), |
Leesbare naam voor het objecttype. |
Content & Config |
| admin_permission = "administer nodes", |
Permissie die administratieve toegang geeft om het objecttype te configureren en te beheren. Dit is vereist als jouw entiteit geen “access”-handler definieert. |
Content & Config |
| bundle_label = @Translation("Content type"), |
Optionele leesbare naam voor het bundelobjecttype. |
Content |
| bundle_entity_type = "node_type", |
Bij het maken van een contentobject met bundles moet deze waarde de “id” zijn van het configuratieobject. In dit geval is “node_type” een configuratieobject. |
Content |
| base_table = "node", |
Naam van de databasetabel voor het objecttype. |
Content |
| fieldable = TRUE, |
(boolean) Of dit entiteitstype kan worden uitgebreid met behulp van de Field UI. |
Content |
| field_ui_base_route = "entity.node_type.edit_form", | Naam van de route waaraan de Field UI voor de entiteit is gekoppeld. | Content |
Handlers
Handlers worden in de annotatie van het object gedefinieerd als een array. Ze ondersteunen de entiteit door specifieke delen van de uitvoering ervan toe te wijzen aan andere PHP-klassen. Deze klassen zullen de toegewezen delen van de entiteitsuitvoering “afhandelen”.
Storage - behandelt het laden, opslaan en verwijderen van het object. Standaard gebruiken contentobjecten Drupal\Core\Entity\Sql\SqlContentEntityStorage, terwijl configuratieobjecten Drupal\Core\Config\Entity\ConfigEntityStorage gebruiken. Je kunt een opslaghandler definiëren om de standaard opslagmethoden van jouw entiteit uit te breiden. Dit kan nuttig zijn om extra methoden te bieden voor het verzamelen van revisie-ID’s van de entiteit of het bepalen van het aantal vertalingen dat de entiteit heeft.
Voorbeeld: "storage" = "Drupal\node\NodeStorage",
Form - in de handlers-annotatie van elke entiteit zijn er meerdere form-handlers, die de add-, edit- en delete-formulieren van de entiteit toewijzen aan andere PHP-klassen.
Voorbeeld:
"form" = {
"add" = "Drupal\block\BlockForm",
"edit" = "Drupal\block\BlockForm",
"delete" = "Drupal\block\Form\BlockDeleteForm",
}
Daarnaast kun je een “default”-formulier definiëren dat de add- en edit-formulieren afhandelt, in plaats van deze afzonderlijk te definiëren. Merk op dat het delete-formulier bijna altijd door een aparte klasse wordt afgehandeld. Dit komt omdat het delete-formulier meestal een “bevestigingsformulier” is dat simpelweg vraagt of de gebruiker zeker weet dat hij het object wil verwijderen.
View builder - deze handler levert een klasse die de output van de entiteit afhandelt wanneer een eindgebruiker deze bekijkt. Bijvoorbeeld: bij het bezoeken van een node op een Drupal 8-site wordt de output van de entiteit afgehandeld door de klasse NodeViewBuilder.
Voorbeeld: "view_builder" = "Drupal\node\NodeViewBuilder",
List builder - de list builder klasse behandelt de lijst van objecten voor administratieve doeleinden. Deze klasse definieert de inhoud van de koppen, rijen en acties bij het bezoeken van de entiteitsbeheerpagina voor de entiteit. Bijvoorbeeld: bij het bezoeken van URI/admin/content van jouw Drupal-site wordt de tabelinhoud geleverd door de Entity List Builder klasse van Node.
Voorbeeld: "list_builder" = "Drupal\node\NodeListBuilder",
Route provider - een optionele handler die, indien geïmplementeerd, routes genereert om jouw object te beheren. Het implementeren van deze handler kan de noodzaak vervangen om entity-paden te definiëren in het routing.yml-bestand van jouw module. Merk op dat de route_provider samenwerkt met de links die voor jouw entiteit zijn gedefinieerd (zie het voorbeeld in de sectie “Links” hieronder). De route_provider-annotatie is een array.
Voorbeeld:
"route_provider" = {
"html" = "Drupal\Core\Entity\Routing\AdminHtmlRouteProvider",
}
Access - de access-handler kan worden gebruikt om dynamisch de permissies van jouw entiteit te controleren. Dit is een mapping naar een klasse die EntityAccessControlHandlerInterface implementeert. Core biedt een implementatie van deze interface in de vorm van EntityAccessControlHandler, maar voor robuuste controle over jouw entiteit wil je deze klasse uitbreiden met je eigen implementatie.
Voorbeeld: "access" = "NodeAccessControlHandler",
Views data - de views_data-handler laat een object de Views-module uitbreiden met aangepaste data die door jouw entiteit wordt geleverd. Dit kan zijn voor het toevoegen van de baseFieldDefinitions van jouw entiteit als view-velden, het joinen van tabellen op entiteitsrelaties, of andere view-gerelateerde dataveranderingen.
Voorbeeld: "views_data" = "Drupal\node\NodeViewsData",
Storage schema - de storage_schema-handler kan worden geĂŻmplementeerd om de databaseopslaginstellingen van het object verder aan te passen. Bijvoorbeeld door extra indexen toe te voegen.
Voorbeeld: "storage_schema" = "Drupal\node\NodeStorageSchema",
Translation - de translation-handler kan worden gebruikt om aan te passen hoe de formulieren van jouw entiteit omgaan met vertalingen.
Voorbeeld: "translation" = "Drupal\node\NodeTranslationHandler",
Volledig voorbeeld van handlers:
Drupal core biedt handlers die je “out of the box” kunt gebruiken, maar in veel gevallen wil je deze klassen uitbreiden met je eigen implementaties voor meer controle en aanpassing van jouw entiteit. Dit voorbeeld toont een uitgebreidere annotatie van handlers met gebruik van core-klassen die je kunt uitbreiden.
handlers = {
"view_builder" = "Drupal\Core\Entity\EntityViewBuilder",
"list_builder" = "Drupal\Core\Entity\EntityListBuilder",
"access" = "Drupal\Core\Entity\EntityAccessControlHandler",
"views_data" = "Drupal\views\EntityViewsData",
"storage" = "Drupal\Core\Entity\Sql\SqlContentEntityStorage",
"storage_schema" = "Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema",
"translation" = "Drupal\content_translation\ContentTranslationHandler",
"form" = {
"default" = "Drupal\Core\Entity\ContentEntityForm",
"add" = "Drupal\Core\Entity\ContentEntityForm",
"edit" = "Drupal\Core\Entity\ContentEntityForm",
"delete" = "Drupal\Core\Entity\ContentEntityDeleteForm",
},
"route_provider" = {
"html" = "Drupal\Core\Entity\Routing\AdminHtmlRouteProvider",
},
},
Links
Links worden in de annotatie van het object gedefinieerd met arraysyntaxis. Links hebben een specifieke set keys, waarvan de waarden URI’s zijn waar het objecttype of afzonderlijke objecten van dat type kunnen worden beheerd. Deze objecten kunnen zowel voor content- als voor configuratieobjecten worden gedefinieerd.
Voorbeeld:
id = "node",
handlers = {
"route_provider" = {
"html" = "Drupal\Core\Entity\Routing\AdminHtmlRouteProvider"
}
},
links = {
"canonical" = "/node/{node}",
"add-page" = "/node/add",
"add-form" = "/node/add/{node_type}",
"edit-form" = "/node/{node}/edit",
"delete-form" = "/node/{node}/delete",
"collection" = "/admin/content",
},
Merk op dat dit niet letterlijk uit de Node-module is genomen, het is slechts een voorbeeld.
Het maken van deze links creëert niet automatisch routes voor deze URI’s. Om deze links beschikbaar te maken, moet jouw module een eigen routing.yml-bestand implementeren of de route_provider-handler gebruiken in de annotatie van het object.
Links en route provider
De bovenstaande links, samen met de “route_provider”, maken de volgende benoemde routes beschikbaar voor Drupal.
| Link key | Routernaam | Voorbeeld route-URI | Beschrijving |
| canonical | entity.node.canonical | /node/1 | Bekijk een specifieke node |
| add-page | entity.node.add_page | /node/add | Kies welk nodetype toegevoegd moet worden |
| add-form | entity.node.add_form | /node/add/article | Voeg een node toe (van een specifiek bundle) |
| edit-form | entity.node.edit_form | /node/1/edit | Bewerkformulier voor een specifieke node |
| delete-form | entity.node.delete_form | /node/1/delete | Verwijderformulier voor een specifieke node |
| collection | entity.node.collection | /admin/content | Bekijk alle nodes in een lijstweergave |
Gebruik van links
Deze links kunnen worden opgevraagd met de methode toUrl() van het object:
$view_url_object = $entity->toUrl(); // Standaard is 'canonical'
$edit_url_string = $entity->toUrl('edit-form')->toString();
Referenties:
- Entity API - gegenereerde documentatie.
- Een aangepaste content-entiteit maken - een zeer eenvoudige aangepaste entiteit.
- Een content-entiteitstype maken in Drupal 8 - geavanceerd voorbeeld met handlers, permissies, routing en links.
- Een configuratie-entiteitstype maken in Drupal 8 - geavanceerd voorbeeld met handlers, routing en schema.
- [Extern] Entity Type Walkthrough - documentatie voor een algemeen entiteitstype in de praktijk