Data Engine: Unterschied zwischen den Versionen

Aus Dokument
Zur Navigation springen Zur Suche springen
Keine Bearbeitungszusammenfassung
Der Seiteninhalt wurde durch einen anderen Text ersetzt: „ * Rust-Datenmodell für Drupal-Entities
Markierung: Ersetzt
Zeile 1: Zeile 1:


* [[Rust-Datenmodell für Drupal-Entities]]
* [[Rust-Datenmodell für Drupal-Entities]]
= Rust-Datenmodell für Drupal-Entities =
== Übersicht ==
Dieser Artikel dokumentiert ein Rust-Datenmodell, das die zentralen
Entity-Konzepte von Drupal (Node, User, Taxonomy Term, Media, Paragraph)
als <pre>struct</pre>/<pre>enum</pre>-Typen abbildet. Das Modell enthält bewusst '''keine
Logik''' (keine <pre>impl</pre>-Methoden) – es dient als reine Datenrepräsentation.
Eine (De-)Serialisierung (z. B. mit [https://serde.rs/ serde] für
Drupal-JSON:API-Antworten) ist bewusst noch nicht Teil dieses Modells
und folgt in einem späteren Schritt.
== Motivation ==
Drupal modelliert Inhalte über ein generisches Entity-System:
* '''Entity-Typen''' (<pre>node</pre>, <pre>user</pre>, <pre>taxonomy_term</pre>, <pre>media</pre>, …) definieren die grundlegende Art eines Objekts.
* '''Bundles''' (bei Nodes „Content Types" genannt, z. B. <pre>article</pre>, <pre>page</pre>) spezialisieren einen Entity-Typ.
* '''Felder''' (<pre>field_*</pre>) sind pro Bundle konfigurierbar, typisiert und können einfach- oder mehrwertig (Kardinalität) sein.
Um mit einem Drupal-Backend aus Rust heraus zu arbeiten, braucht man
typisierte Gegenstücke zu diesen Konzepten. Das hier beschriebene
Modell bildet diese Struktur 1:1 in Rust ab.
== Grundtypen ==
<pre>
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum EntityType {
    Node,
    User,
    TaxonomyTerm,
    Media,
    Paragraph,
    File,
    Menu,
    Block,
    Custom(String),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PublishStatus {
    Published,
    Unpublished,
}
pub type LangCode = String;
</pre>
<pre>EntityType</pre> bildet die von Drupal vordefinierten Entity-Typen ab; der
<pre>Custom</pre>-Zweig deckt eigene, per Modul definierte Entity-Typen ab, ohne
das Enum ändern zu müssen.
== Feldwerte: <pre>FieldValue</pre> ==
Drupal-Felder sind stark typisiert (Text, Zahl, Datum, Referenz, Bild, …).
Dies wird über ein Enum mit Payload pro Variante abgebildet:
<pre>
#[derive(Debug, Clone)]
pub enum FieldValue {
    Text(String),
    TextLong { value: String, format: Option<String> },
    Integer(i64),
    Decimal(f64),
    Boolean(bool),
    DateTime(DateTime<Utc>),
    Email(String),
    Link { uri: String, title: Option<String> },
    EntityReference { target_id: u64, target_type: EntityType },
    Image {
        target_id: u64,
        alt: Option<String>,
        title: Option<String>,
        width: Option<u32>,
        height: Option<u32>,
    },
    List(Vec<FieldValue>),
}
pub type FieldMap = HashMap<String, FieldValue>;
</pre>
* <pre>List(Vec<FieldValue>)</pre> bildet mehrwertige Felder ab (Kardinalität > 1).
* <pre>FieldMap</pre> ist die generische Ablage für alle <pre>field_*</pre>-Werte eines Bundles, adressiert über den Feldmaschinennamen (z. B. <pre>field_tags</pre>).
== Entity-Typen im Detail ==
=== Gemeinsame Basis ===
<pre>
#[derive(Debug, Clone)]
pub struct EntityBase {
    pub id: u64,
    pub uuid: Uuid,
    pub entity_type: EntityType,
    pub bundle: String,
    pub langcode: LangCode,
    pub created: DateTime<Utc>,
    pub changed: DateTime<Utc>,
}
</pre>
<pre>EntityBase</pre> fasst Eigenschaften zusammen, die praktisch jede
Content-Entity in Drupal besitzt, und wird per Komposition in konkrete
Entity-Structs eingebettet (siehe <pre>Node</pre>, <pre>Media</pre>). Beim späteren
Ergänzen von serde bietet sich dafür <pre>#[serde(flatten)]</pre> an, damit die
Felder von <pre>EntityBase</pre> beim (De-)Serialisieren auf derselben Ebene
wie die restlichen Felder erscheinen.
=== Node ===
<pre>
#[derive(Debug, Clone)]
pub struct Node {
    pub base: EntityBase,
    pub title: String,
    pub status: PublishStatus,
    pub author_id: u64,
    pub fields: FieldMap,
}
</pre>
=== User ===
<pre>
#[derive(Debug, Clone)]
pub struct User {
    pub id: u64,
    pub uuid: Uuid,
    pub name: String,
    pub mail: String,
    pub status: bool,
    pub roles: Vec<String>,
    pub created: DateTime<Utc>,
    pub fields: FieldMap,
}
</pre>
<pre>User</pre> nutzt bewusst kein <pre>EntityBase</pre>, da User-Entities in Drupal kein
<pre>bundle</pre>/<pre>langcode</pre> im klassischen Sinn besitzen und stattdessen
<pre>roles</pre> und einen einfachen <pre>status: bool</pre> (aktiv/blockiert) haben.
=== TaxonomyTerm ===
<pre>
#[derive(Debug, Clone)]
pub struct TaxonomyTerm {
    pub id: u64,
    pub uuid: Uuid,
    pub vocabulary: String,
    pub name: String,
    pub description: Option<String>,
    pub parent_id: Option<u64>,
    pub weight: i32,
    pub fields: FieldMap,
}
</pre>
Bildet Taxonomiebegriffe inkl. hierarchischer Eltern-Referenz
(<pre>parent_id</pre>) und Sortiergewicht (<pre>weight</pre>) ab.
=== Media & Paragraph ===
<pre>
#[derive(Debug, Clone)]
pub struct Media {
    pub base: EntityBase,
    pub name: String,
    pub fields: FieldMap,
}
#[derive(Debug, Clone)]
pub struct Paragraph {
    pub id: u64,
    pub uuid: Uuid,
    pub bundle: String,
    pub fields: FieldMap,
}
</pre>
<pre>Paragraph</pre> ist bewusst schlank gehalten, da Paragraphs in Drupal keine
eigenständigen, direkt aufrufbaren Entities mit URL/Alias sind, sondern
stets über eine Host-Entity referenziert werden.
== Schema-Introspektion (optional) ==
Für Anwendungsfälle, in denen Feld- und Bundle-Definitionen selbst
(nicht nur Werte) abgebildet werden müssen – etwa um ein Drupal-Schema
zu spiegeln oder Formulare dynamisch zu generieren:
<pre>
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Cardinality {
    Single,
    Multiple,
    Unlimited,
}
#[derive(Debug, Clone)]
pub struct FieldDefinition {
    pub field_name: String,
    pub field_type: String,
    pub cardinality: Cardinality,
    pub required: bool,
}
#[derive(Debug, Clone)]
pub struct BundleDefinition {
    pub entity_type: EntityType,
    pub bundle: String,
    pub label: String,
    pub fields: Vec<FieldDefinition>,
}
</pre>
== Design-Entscheidungen ==
{| class="wikitable"
|-
! Entscheidung !! Begründung
|-
| <pre>FieldValue</pre> als Enum mit Payload pro Variante || Bildet Drupals starke Feldtypisierung typsicher ab, statt alles als <pre>String</pre> zu behandeln.
|-
| <pre>FieldMap = HashMap<String, FieldValue></pre> || Bundles sind zur Compile-Zeit nicht bekannt; generische Map bleibt flexibel für beliebige <pre>field_*</pre>-Namen.
|-
| <pre>EntityBase</pre> als Kompositionsfeld || Vermeidet Duplikation gemeinsamer Properties zwischen Node/Media, ohne Vererbung (die es in Rust nicht gibt).
|-
| Kein <pre>impl</pre>-Block || Modell bleibt reine Datenschicht; Geschäftslogik (Validierung, API-Calls) gehört in separate Module.
|-
| <pre>EntityType::Custom(String)</pre> || Deckt durch Contrib-/Custom-Module definierte Entity-Typen ab, ohne das Enum erweitern zu müssen.
|}
== Offene Erweiterungspunkte ==
* Weitere <pre>FieldValue</pre>-Varianten je nach genutzten Feldtypen (z. B. <pre>Geolocation</pre>, <pre>Address</pre>, <pre>File</pre> losgelöst von <pre>Image</pre>).
* Übersetzungen (<pre>translations: HashMap<LangCode, FieldMap></pre>), falls Drupal-Mehrsprachigkeit (Content Translation) abgebildet werden soll.
* Konkretes Mapping auf die JSON:API-Hüllstruktur (<pre>data</pre>, <pre>attributes</pre>, <pre>relationships</pre>, <pre>included</pre>), falls direkt gegen die Drupal-JSON:API deserialisiert werden soll, statt ein eigenes flaches Modell zu pflegen.
== Verwendete Crates ==
* [https://crates.io/crates/chrono chrono] – Zeitstempel (<pre>created</pre>, <pre>changed</pre>)
* [https://crates.io/crates/uuid uuid] – Entity-UUIDs
Hinweis: [https://serde.rs/ serde] (für die (De-)Serialisierung) ist
bewusst noch nicht Teil dieses Modells und wird in einem späteren
Schritt ergänzt.
== Siehe auch ==
* Drupal-Dokumentation: [https://www.drupal.org/docs/drupal-apis/entity-api Entity API]
* Drupal-Dokumentation: [https://www.drupal.org/docs/core-modules-and-themes/core-modules/jsonapi-module JSON:API-Modul]
[[Kategorie:Rust]]
[[Kategorie:Drupal]]

Version vom 12. August 2026, 01:24 Uhr