Drupal Vertiefung
Datum: Februar 2022
Lesedauer: 14 Minuten
Nachdem ich im letzten Blogpost einen Überblick über Drupal gezeigt habe, gehe ich hier auf einzelne Themen nochmals genauer ein.
Aus diesem Grund ist dieser Post auch etwas fortgeschrittener.
Custom Block
Ein Block ist ein Teil eines Layouts und hat einen Inhalt. Blöcke können in beliebigen Regions platziert und danach auch verschoben werden. Sie können
unter anderem Text, ein Formular oder auch Bilder beinhalten.
Sie sind wiederverwendbar und können so konfiguriert werden, dass sie nur auf bestimmten Seiten von ausgewählten Personen gesehen werden können.
Blöcke sind nicht zu verwechseln mit Content Types. Ein Content type ist eine Art von Inhalt. Dieser wird im Inhaltsbereich angezeigt.
Beispiel
Ein Block kann auf zwei verschiedene Varianten erstellt werden.
Browser
In Drupal kann man unter Structure
> Block layout
> Add custom block
einen Block hinzufügen.
Dieser kann in einer Region platziert werden.
So wird er dann dargestellt.
Code
Im Browser können schon sehr viele Dinge konfiguriert werden. Jedoch ist man im Code etwas flexibler.
<?php
namespace Drupal\hello_block\Plugin\Block;
use Drupal\Core\Block\BlockBase;
/**
*
* Provides a 'Hello' Block.
* @Block(
* id = "hello_block",
* admin_label = @Translation("Hello block"),
* category = @Translation("Hello World"),
* )
*/
class HelloBlock extends BlockBase {
/**
* {@inheritdoc}
*/
public function build() {
return [
'#markup' => $this->t('Hello, World!'),
];
}
}
Auf der Zeile zehn wird von der Block Annotation
gebrauch gemacht. Damit gibt man an, dass es sich beim Code um einen Block handelt.
Die nachfolgenden drei Zeilen bestimmen die id
, das admin_label
und die category
. Diese Dinge sind dann in Drupal sichtbar.
In der build
Methode kann man das Markup des Blocks erstellen.
Dieser Block wird im Frontend folgendermassen dargestellt.
Regions
Vorhin wurde der Block in einer Region platziert.
Regions sind Bereiche, welche gestyled und positioniert werden können. In diese kann man dann die Blöcke legen.
Bartik
Dieses Beispiel zeige ich anhand des Bark Themes (opens in a new tab).
Im Info File werden alle Regions definiert.
regions:
header: Header
primary_menu: 'Primary menu'
secondary_menu: 'Secondary menu'
page_top: 'Page top'
page_bottom: 'Page bottom'
highlighted: Highlighted
featured_top: 'Featured top'
breadcrumb: Breadcrumb
content: Content
sidebar_first: 'Sidebar first'
sidebar_second: 'Sidebar second'
featured_bottom_first: 'Featured bottom first'
featured_bottom_second: 'Featured bottom second'
featured_bottom_third: 'Featured bottom third'
footer_first: 'Footer first'
footer_second: 'Footer second'
footer_third: 'Footer third'
footer_fourth: 'Footer fourth'
footer_fifth: 'Footer fifth'
Ist das Theme aktiviert, sieht man in Drupal die verschiedenen Bereiche.
Nun kann man die Regions beliebig stylen, indem man mit CSS die korrekte Klasse anspricht.
/**
* @file
* Styles for Bartik's breadcrumbs.
*/
.breadcrumb {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-size: 0.929em;
}
Hooks
https://drupalize.me/tutorial/what-are-hooks?p=2766 (opens in a new tab)
Hooks sind dazu da, das Verhalten zu verändern oder erweitern, ohne dass dabei der bestehende Code zu verändert werden muss. Mithilfe von Hooks können also Komponenten miteinander kommunizieren.
Man kann sich das wie folgt vorstellen:
Der User löscht seinen Account.
Dabei fragt Drupal: "Der User mit der ID 19 löscht seinen Account. Kann hier irgendjemand etwas damit anfangen?"
Daraufhin können andere Module diese Information nutzen. So könnte ein Modul das Profilbild löschen, da diese nun nicht mehr benötigt wird. Ein anderes wäre dann für den Versand eine Umfrage Mail zuständig.
Eine Hook ist also ein Punkt an welchem andere Komponenten einhängen können.
Dabei unterscheidet man zwischen drei Typen von Hooks.
Answer questions
Die sogenannten info hooks
sind für das Sammeln von Informationen zuständig. Solche Hooks geben hauptsächlich Arrays zurück.
Beispiel: Die user_toolbar
Hook liefert den Link zum persönlichen Account des jeweiligen Benutzers, welcher dann in der Toolbar dargestellt wird.
Alter existing data
Diese Hook verändert bereits vorhandene Daten.
Eine info hook
sammelt eine Liste von Informationen. Danach wird die alter hook
aufgerufen. Diese kann dann die Liste noch verändern, bevor sie
verwendet wird.
Wahrscheinlich wird diese Form der Hook am häufigsten implementiert.
Beispiel: Die taxonomy_views_data_alter
Hook fügt den Artikel Taxonomy Terms hinzu, welche Informationen über das Node anzeigen.
React to events
Eine solche Hook wird aufgerufen, wenn im System eine Aktion ausgeführt wird.
Beispiel: Um das vorhin beschriebene Beispiel mit dem Löschen eines Users umzusetzen, würde man eine hook_user_cancel
Hook verwenden. Die würde dann alles Bereinigen, was vom User übrig blieb.
Naming
Eine Hook benennt man wie folgt: HOOK_entity_view()
Hierbei wird Hook mit dem maschine name
des Moduls ersetzt.
Beispiel: mymodule_entity_view
Diese Hook würde dann für jeden Entity Type
aufgerufen werden.
Man kann und sollte Hooks aber spezifischer machen. So spart man sich die If-Abfragen im Code.
mymodule_node_view
wird nur für Nodes gecalled.
mymodule_form_2_alter
verändert nur das Formular mit der angegebenen ID.
Auf der Drupal API Seite (opens in a new tab) findet man Hilfe zur Erstellung und Benennung von Hooks.
Beispiel
Ich will, dass bei jedem Artikel ein Text vorbeiläuft.
Dazu suche ich auf der Drupal Seite nach der passenden Hook:
https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Entity%21entity.api.php/function/hook_entity_view/9.3.x (opens in a new tab)
Im nächsten Schritt erstelle ich ein Modul. Im .module
File füge ich dann den gefunden Code ein und passe ihn meinen Wünschen entsprechend an.
In diesem Fall füge ich bei jedem Node ein ein marquee
Tag ein.
<?php
/**
* Implements hook_ENTITY_TYPE_view().
*/
function sneaker_node_view(array &$build, \Drupal\Core\Entity\EntityInterface $entity,
\Drupal\Core\Entity\Display\EntityViewDisplayInterface $display, $view_mode) {
$build['awesome'] = [
'#markup' => '<marquee>This is awesome.</marquee>',
'#allowed_tags' => ['marquee'],
];
}
So sehen danach die Artikel aus.
Preprocess Functions
Preprocess Funktionen ermöglichen die Veränderung und Erstellung von Variablen, bevor diese dann in den Template-Dateien verwendet werden.
Naming
THEME_preprocess_HOOK()
THEME
: Name des Themes oder Moduls
HOOK
: Template, in welcher die Variablen verwendet werden
Will man beispielsweise die Seite des Bartik Themes anpassen, wäre der Funktionsname so aufgebaut.
bartik_preprocess_page()
Für einen Artikel würde die Funktion so aussehen.
mymodule_preprocess_node()
Diese Namen findet man am leichtesten, wenn man die Seite im TWIG Debug Mode
anschaut.
Titel Variable definieren
In einer Preprocess Function wird ein Parameter mitgegeben. Durch diesen Array kann man dann die Variablen, welche man im Template File verwendet, anpassen.
<?php
function mytheme_preprocess_page(&$variables) {
$variables['title'] = 'My Custom Title';
}
{{ title }}
Überprüfung Autor
Auch das Überprüfen von anderen Variablen ist möglich. In diesem Fall schaue ich, ob der angemeldete User, den angezeigten Artikel erstellt hat. Trifft dies zu, so füge ich der Überschrift "- [you are the author]" hinzu.
<?php
function mytheme_preprocess_node(&$variables) {
$variables['current_user_is_owner'] = FALSE;
if ($variables['logged_in'] == TRUE && $variables['node']->getOwnerId() == $variables['user']->id()) {
$variables['label']['#suffix'] = '- [you are the author]';
$variables['current_user_is_owner'] = TRUE;
}
}
Im Node Template File zeige ich dieses Label auf Zeile sechs an.
<article{{ attributes.addClass(classes) }}>
<header>
{{ title_prefix }}
{% if label and not page %}
<h2{{ title_attributes.addClass('node__title') }}>
<a href="{{ url }}" rel="bookmark">{{ label }}</a>
</h2>
{% endif %}
{{ title_suffix }}
{% if display_submitted %}
<div class="node__meta">
{{ author_picture }}
<span{{ author_attributes }}>
{% trans %}Submitted by {{ author_name }} on {{ date }}{% endtrans %}
</span>
{{ metadata }}
</div>
{% endif %}
</header>
<div{{ content_attributes.addClass('node__content', 'clearfix') }}>
{{ content }}
</div>
</article>
Webform
Das Webform Modul findet man hier: https://www.drupal.org/project/webform (opens in a new tab)
Ich empfehle auch, das Webform UI Modul zu aktivieren. Dieses UI erleichtert einem das Bauen und Pflegen der Formulare enorm.
Unter Structure
> Webforms
kann man sich dann mit nur wenigen Klicks ein gutes Formular zusammenstellen. Die Konfigurationsmöglichkeiten sind fast
unbegrenzt.
Z. B. kann man in nur wenigen Sekunden Seiten anlegen um das Formular in mehrere Schritte zu teilen.
Services
Was ist ein Service?
Ein Service ist ein nützliches Objekt, welches etwas für einen macht. Dieses kann aus einer eigenen, oder fremden Klasse stammen. Eine solches Objekt
kann überall wiederverwendet werden.
Klassen, die etwas loggen oder Mails verschicken, sind beispielsweise Services.
Eine Klasse, welche Produkte (Sneaker, Cap,...) abbilden, wäre dann kein Service. Diese Objekte halten lediglich Daten.
Service erstellen
Ausgangslage
Man hat einen Controller. Dieser schreibt eine Hello World
auf den Bildschirm.
Leider können solche Controller schnell unübersichtlich werden. Zudem kann man den Code nur im Controller verwenden.
<?php
namespace Drupal\shout_hello\Controller;
use Symfony\Component\HttpFoundation\Response;
class HelloController {
public function hello($count){
$hello = 'H'.str_ repeat('e', $count).'llo!';
return new Response($hello);
}
}
Vorgehen
-
Als erstes erstellt man in
src
einen Ordner. (BeispielGreeting
) -
Darin muss man dann die Klasse anlegen. (Beispiel
HelloGenerator.php
) -
In der Klasse muss man zwingend den korrekten namespace definieren.
In diesem Fall:namespace Drupal\shout_hello\Greeting;
-
Nun kann man die Funktion in diese neue Klasse auslagern.
modules/shout_hello/src/Greeting/HelloGenerator.php<?php namespace Drupal\shout_hello\Greeting; class HelloGenerator { public function getHello($length) { return = 'H'.str_ repeat('e', $length).'llo!'; } }
-
Controller
modules/shout_hello/src/Controller/HelloController.php<?php namespace Drupal\shout_hello\Controller: use Drupal\shout_hello\Greeting\HelloGenerator; use Symfony\Component\HttpFoundation\Response; class HelloController { public function hello($count){ $helloGenerator = new HelloGenerator(); $hello = $helloGenerator->getRoar($count); return new Response($hello); } }
Nun ist die Logik in einer anderen Klasse. Diese nimmt einem Arbeit ab. Sie ist ein Service.
Mit dieser Variante muss man die Klasse mit new HelloGenerator()
erstellen. Dies ist noch nicht die schönste Variante. Um dies zu umgehen, kommt der
Service Container ins Spiel.
Service Container
In Drupal gibt es ein Objekt namens Container. Dieses wird auch "Dependency Injection Container" genannt.
Darin befinden sich alle Services. Dazu gehören folgende Klassen:
- Logger factory
- Translater
- DB connection
- File system
Um sich einen Überblick über alle Services zu verschaffen, kann man diese mit drupal container:debug
in der Konsole ausgeben.
Ausgangslage
Vorhin wurde das Objekt direkt erzeugt. Der Container kann uns dies aber abnehmen.
Vorgehen
Service Konfigurieren
Zuerst erstellt man im root des Moduls eine Datei. Diese trägt den Namen:
[modulname].services.yml
Darin konfiguriert man dann den Service.
services:
shout_hello.hello_generator:
class: Drupal\shout_hello\HelloGenerator
Auf der Zeile zwei gibt man den "Machine Name" an. Dieser darf frei erfunden, muss aber lowercase und unique sein.
Die nachfolgende Linie zeigt, wo sich der Service befindet.
Optional kann man auch noch Arguments mitgeben, die der Service benötigt.
Nachdem man den Cache gelehrt hat, findet man den Service im Container.
drupal container:debug | grep shout
Service aus dem Container holen
-
Nun kann man von ControllerBase erben und die
create
Method implementieren.modules/shout_hello/src/Controller/HelloController.php<?php namespace Drupal\shout_hello\Controller: use Drupal\Core\Controller\ControllerBase; use Drupal\shout_hello\Greeting\HelloGenerator; use Symfony\Component\HttpFoundation\Response; class HelloController extends ControllerBase { public function hello($count){ $helloGenerator = new HelloGenerator(); $hello = $helloGenerator->getRoar($count); return new Response($hello); } public static function create(ContainerInterface $container) { } }
-
Objekt instanziieren
modules/shout_hello/src/Controller/HelloController.php<?php namespace Drupal\shout_hello\Controller: use Drupal\Core\Controller\ControllerBase; use Drupal\shout_hello\Greeting\HelloGenerator; use Symfony\Component\HttpFoundation\Response; class HelloController extends ControllerBase { public function hello($count){ $helloGenerator = new HelloGenerator(); $hello = $helloGenerator->getRoar($count); return new Response($hello); } public static function create(ContainerInterface $container) { $helloGenerator = $container->get('shout_hello.hello_generator'); return new static($helloGenerator); } }
-
Konstruktor und Property erstellen
modules/shout_hello/src/Controller/HelloController.php<?php namespace Drupal\shout_hello\Controller: use Drupal\Core\Controller\ControllerBase; use Drupal\shout_hello\Greeting\HelloGenerator; use Symfony\Component\HttpFoundation\Response; class HelloController extends ControllerBase { private $helloGenerator; public function __construct(HelloGenerator $helloGenerator) { $this->helloGenerator = $helloGenerator; } public function hello($count){ $helloGenerator = new HelloGenerator(); $hello = $helloGenerator->getRoar($count); return new Response($hello); } public static function create(ContainerInterface $container) { $helloGenerator = $container->get('shout_hello.hello_generator'); return new static($helloGenerator); } }
-
In der
hello
Methode diegetHello
Funktion über den Service aufrufenmodules/shout_hello/src/Controller/HelloController.php<?php namespace Drupal\shout_hello\Controller: use Drupal\Core\Controller\ControllerBase; use Drupal\shout_hello\Greeting\HelloGenerator; use Symfony\Component\HttpFoundation\Response; class HelloController extends ControllerBase { private $helloGenerator; public function __construct(HelloGenerator $helloGenerator) { $this->helloGenerator = $helloGenerator; } public function hello($count){ $hello = $this->helloGenerator->getHello($count); return new Response($hello); } public static function create(ContainerInterface $container) { $helloGenerator = $container->get('shout_hello.hello_generator'); return new static($helloGenerator); } }
Grund
Es gibt drei wesentliche Gründe, warum man einen Service in den Container tun sollte.
- Durch die Verwendung eines Containers wird das Objekt erst erstellt, wenn es benötigt wird. So kann man Unmengen von Services haben, ohne dabei auf Performance Probleme zu stossen.
- Benötigt man einen Service mehrfach, so wird überall dasselbe Objekt verwendet. Ohne Container wäre, zu Lasten der Performance, jedes mal ein neues Objekt erstellt worden.
- Dank des Containers kann man nun alles im Konstruktor übergeben. Jetzt kann man andere Services im eigenen verwenden.
API
Um Abfragen durchzuführen, habe ich Postman (opens in a new tab) installiert. Dies muss man nicht machen, finde ich aber empfehlenswert.
JSON
Das JSON:API Modul ist normalerweise schon installiert. Es muss also bei Bedarf nur noch aktiviert werden.
Eine solche API kann sehr schnell alle Node Daten zur Verfügung stellen. Um dies zu bewerkstelligen braucht man auch nicht viele Kenntnisse.
Will man aber beispielsweise auf Bilder zugreifen, so sind mehrere Calls nötig, da man vorerst nur die ID erhält. Zudem wird man bei einem Request mit
vielen Daten überflutet.
Modul
Settings
In den Drupal Einstellungen gibt es zwei verschiedene Konfigurationsmöglichkeiten.
Zugriff
Wie die URL genau aufgebaut ist, erfährt man hier: https://www.drupal.org/docs/core-modules-and-themes/core-modules/jsonapi-module/api-overview (opens in a new tab)
So könnte dann ein Response aussehen.
REST
Die JSON:API fokussiert sich auf Drupals Stärken. Das REST Modul bietet jedoch viel mehr Möglichkeiten, da es extrem konfigurierbar ist. Zudem kann das Format, die Logik und die HTTP Method frei gewählt werden.
Feature | JSON:API | REST | Remark |
---|---|---|---|
Entities exposed as resources | ✔️ | ✔️ | REST: needs configuration per entity type. JSON:API exposes everything by default. Both respect entity access. |
Custom data exposed as resources | ✔️ | Write custom @RestResource plugins. JSON:API only supports entities. | |
Getting individual resources | ✔️ | ✔️ | |
Getting lists of resources | ✔️ | kinda | REST: needs a view with a "REST export" display. |
Paginating list of resources | ✔️ | REST: not supported! REST export views return all resources. Additional modules like Pager Serializer needed. | |
Filtering list of resources | ✔️ | kinda | REST: requires an exposed filter for each field and every possible operator. |
Sorting of resources | ✔️ | ||
Includes/embedding | ✔️ | Only in HAL+JSON | |
No unnecessary wrapping of field values | ✔️ | REST/HAL normalization exposes raw Drupal PHP structures, causing poor DX. JSON:API simplifies normalization. | |
Ability to omit fields consumer does not need | ✔️ | ||
Consistent URLs | ✔️ | ||
Consumer can discover available resource types | ✔️ | ||
Drupal-agnostic response structure | ✔️ | REST: HAL normalization is theoretically Drupal-free, but practically isn't. | |
Client libraries | ✔️ | ||
Extensible spec | WIP | ||
Zero configuration | ✔️ | REST: Each @RestResource plugin must be configured (formats, auth, HTTP methods). JSON:API handles this automatically. |
Modul
Um die RESTful API zu verwenden, können folgende zwei Module aktiviert werden.
Das erste Modul wird benötigt, um die Schnittstelle zu öffnen. REST UI bietet ein Interface, mit welchem man die verschiedensten Konfigurationen durchführen kann.
Settings
Mit dem RESTful Modul kann man für jede Resource die Berechtigungen festlegen.
Dank dem UI Modul findet man unter Configuration
> Web services
> REST
eine Seite, auf welcher man alle Ressourcen öffnen oder schliessen kann.
In meinem Fall habe ich die Content Seiten freigegeben.
Zugriff
Durch diese Freigabe der Nodes, kann ich dann darauf zugreifen.
Custom
Das RESTful Modul ist schon sehr gut. Jedoch ist man im Code jeweils flexibler.
Aus diesem Grund bietet Drupal, dass man Custom Module erstellen kann.
Ein solches Modul muss man selbst im Code erstellen. Dazu sind folgende Schritte notwendig.
-
Neues Modul im Ordner
/modules/custom/
erstellen -
Info File anlegen
demo_rest_api.info.ymlname: Demo REST API description: Define's a custom REST Resource package: Custom type: module core: 8.x core_version_requirement: ^8 || ^9
-
Eine Resource Klasse im Ordner
/src/Plugin/rest/resource/
erstellen/src/Plugin/rest/resource/DemoResource.php<?php namespace Drupal\demo_rest_api\Plugin\rest\resource; use Drupal\rest\Plugin\ResourceBase; use Drupal\rest\ResourceResponse; class DemoResource extends ResourceBase { }
-
Nun kann man mit der "Rest Resource Annotation" die ID, den Namen und die Pfade angeben.
/src/Plugin/rest/resource/DemoResource.php/** * * Provides a Demo Resource * @RestResource( * id = "demo_resource", * label = @Translation("Demo Resource"), * uri_paths = { * "canonical" = "/demo_rest_api/demo_resource" * } * ) */ class DemoResource extends ResourceBase { }
-
Nun existiert die Resource. Jedoch macht diese noch nichts.
Um einen Get Request abzufangen, muss man die entsprechende Funktion implementieren./src/Plugin/rest/resource/DemoResource.php/** * * Provides a Demo Resource * @RestResource( * id = "demo_resource", * label = @Translation("Demo Resource"), * uri_paths = { * "canonical" = "/demo_rest_api/demo_resource" * } * ) */ class DemoResource extends ResourceBase { /** * Responds to entity GET requests. * @return \Drupal\rest\ResourceResponse */ public function get() { $response = ['message' => 'Hello, this is a rest service']; return new ResourceResponse($response); } }
-
Wichtig ist noch, dass das Modul in Drupal aktiviert werden muss.
-
Nun kann man manuell in den Einstellungen die Links Zugänglich machen.
Alternativ kann man dem Modul auch ein YAML File hinzufügen. Dieses wird dann bei der Installation beachtet.demo_rest_api/config/install/rest.resource.demo_resource.ymlid: demo_resource plugin_id: demo_resource granularity: method configuration: GET: supported_formats: - json supported_auth: - cookie POST: supported_formats: - json supported_auth: - cookie
Hat man all diese Schritte durchgeführt, kann man eine Anfrage durchführen.
Sneaker Loader
namespace Drupal\demo_rest_api;
use Drupal\Core\Entity\EntityTypeManagerInterface;
class SneakerLoader {
/**
* The entity type manager service.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
*
* Constructs a new ContentUninstallValidator.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager service.
*/
public function __construct(EntityTypeManagerInterface $entity_type_manager) {
$this->entityTypeManager = $entity_type_manager;
}
public function getText() {
/** @var \Drupal\node\NodeInterface $sneaker */
$sneaker = $this->entityTypeManager->getStorage("node")->load(29);
return $sneaker->getTitle();
}
}
In diesem Service wird in der getText
Methode der Sneaker mit der ID 29 geholt. Davon wird er Titel zurückgegeben.
In der DemoResource.php
Datei, wo der GET definiert ist, wird die getText
Methode dann aufgerufen. Daher sieht dann der entsprechende Request so aus.
SneakerSaver
In meinem Webform, welches weiter oben beschrieben ist, habe ich einen sogenannten post handler
eingebaut. Wird das Formular abgeschickt, so
wird ein Post auf die angegebene URL gesendet.
Im Code habe ich dann eine SneakerSaver
Klasse erstellt. Die enthaltene Methode erstellt einen Sneaker anhand der mitgegebenen Daten.
<?php
namespace Drupal\demo_rest_api;
use Drupal\Core\Entity\EntityTypeManagerInterface;
class SneakerSaver {
/**
*
* The entity type manager service.
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
*
* Constructs a new ContentUninstallValidator.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager service.
*/
public function __construct(EntityTypeManagerInterface $entity_type_manager) {
$this->entityTypeManager = $entity_type_manager;
}
public function saveSneaker($data) {
/** @var \Drupal\node\NodeInterface $sneaker */
$sneaker = $this->entityTypeManager->getStorage('node')->create([
'type' => 'sneaker',
'title' => $data['title'],
'uid' => 1,
'status' => 1
]);
$sneaker->save();
return true;
}
}
In DemoResource
wird diese Methode im POST aufgerufen.
<?php
namespace Drupal\demo_rest_api\Plugin\rest\resource;
use Drupal\rest\Plugin\ResourceBase;
use Drupal\rest\ResourceResponse;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\demo_rest_api\SneakerLoader;
use Drupal\demo_rest_api\SneakerSaver;
/**
*
* Provides a Demo Resource
* @RestResource(
* id = "demo_resource",
* label = @Translation("Demo Resource"),
* uri_paths = {
* "canonical" = "/demo_rest_api/demo_resource",
* "create" = "/demo_rest_api/create_entity"
* }
* )
*/
class DemoResource extends ResourceBase {
private SneakerLoader $sneakerLoader;
private SneakerSaver $sneakerSaver;
public function __construct(array $configuration, $plugin_id, $plugin_definition, array
$serializer_formats, LoggerInterface $logger, SneakerLoader $sneakerLoader, SneakerSaver $sneakerSaver) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $serializer_formats, $logger);
$this->sneakerLoader = $sneakerLoader;
$this->sneakerSaver = $sneakerSaver;
}
public static function create(ContainerInterface $container, array $configuration, $plugin_id,
$plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->getParameter('serializer.formats'),
$container->get('logger.factory')->get('rest'),
$container->get("demo_rest_api.sneaker_loader"),
$container->get("demo_rest_api.sneaker_saver")
);
}
/**
* Responds to entity GET requests.
* @return \Drupal\rest\ResourceResponse
*/
public function get() {
$response = ['message' => $this->sneakerLoader->getText()];
return new ResourceResponse($response);
}
/**
*
* Responds to POST requests.
* @param mixed $data
*
* @return \Drupal\rest\ModifiedResourceResponse
* The HTTP response object.
*
* @throws \Symfony\Component\HttpKernel\Exception\HttpException
* Throws exception expected.
*/
public function post($data) {
$response = ['message' => $this->sneakerSaver->saveSneaker($data)];
return new ResourceResponse($response);
}
}
Wichtig ist hierbei noch, dass unter uri_paths
ein create
existiert und diese URL mit dem POST des Webforms übereinstimmt.
Der gesamte Ablauf ist aber hier (opens in a new tab) noch detaillierter beschreiben.