YForm - Formular-Builder & ORM

Keywords: Form Builder ORM Database Table Manager Dataset Query Frontend Backend Validation CRUD YOrm

Übersicht

Universelles Formular- und Datenbank-Management-System mit ORM (YOrm), Table Manager für Backend-CRUD, Frontend-Formularen und E-Mail-Versand.

Kern-Klassen

rex_yform_manager_table

Methode Parameter Rückgabe Beschreibung
::get($tableName) string rex_yform_manager_table|null Lädt Table-Definition
::getAll() - array Alle registrierten Tabellen
query() - rex_yform_manager_query Query-Builder für Tabelle
getCSRFKey() - rex_csrf_token CSRF-Token für Tabelle
getRawValues() - array Alle Felder der Tabelle
getFields() - array Feld-Objekte
getName() - string Tabellenname (z.B. ‘rex_my_table’)
::deleteCache() - void Table-Cache löschen

rex_yform_manager_dataset

Methode Parameter Rückgabe Beschreibung
::get($id, $table) int, string rex_yform_manager_dataset|null Lädt Datensatz per ID
::create($table) string rex_yform_manager_dataset Neuer leerer Datensatz
::query() - rex_yform_manager_query Query-Builder (statisch)
getValue($key, $default) string, mixed mixed Feld-Wert abrufen
setValue($key, $value) string, mixed $this Feld-Wert setzen
save() - bool Speichert Datensatz (Insert/Update)
delete() - bool Löscht Datensatz
getMessages() - array Validierungs-Fehler
getId() - int Primary Key
getRelatedDataset($key) string rex_yform_manager_dataset|null Relation folgen
getRelatedCollection($key) string rex_yform_manager_collection 1:n Relation
getForm() - rex_yform YForm-Formular für Datensatz
executeForm($yform) rex_yform string Formular ausführen & ausgeben
::setModelClass($table, $class) string, string void Custom Model registrieren

rex_yform_manager_query

Methode Parameter Rückgabe Beschreibung
find() - rex_yform_manager_collection Alle Ergebnisse
findOne() - rex_yform_manager_dataset|null Erstes Ergebnis
findId($id) int rex_yform_manager_dataset|null Per ID suchen
where($column, $value, $operator) string, mixed, string $this WHERE-Bedingung
whereRaw($sql, $params) string, array $this Raw SQL WHERE
orderBy($column, $direction) string, string $this ORDER BY
limit($limit, $offset) int, int $this LIMIT
alias($alias) string $this Tabellen-Alias
joinRelation($field, $alias) string, string $this JOIN über Relation
select($field, $alias) string, string $this Spalte auswählen
selectRaw($sql) string $this Raw SQL SELECT
count() - int Anzahl Ergebnisse
exists() - bool Mindestens 1 Ergebnis?
chunk($size, $callback) int, callable void Batch-Processing
paginate($page, $perPage) int, int array Pagination-Daten

rex_yform

Methode Parameter Rückgabe Beschreibung
::factory() - rex_yform Neues Formular
setObjectparams($key, $value) string, mixed void Parameter setzen (form_action, form_method)
setValueField($type, $params) string, array void Value-Feld hinzufügen (text, textarea, select)
setValidateField($type, $params) string, array void Validation hinzufügen
setActionField($type, $params) string, array void Action (db, email, showtext)
getForm() - string Rendert Formular-HTML
::addTemplatePath($path) string void Template-Verzeichnis registrieren

Praxisbeispiele

Table Manager - Tabelle abrufen

// Tabelle laden
$table = rex_yform_manager_table::get('rex_my_products');

echo $table->getName(); // 'rex_my_products'

// Alle Felder
foreach ($table->getFields() as $field) {
    echo $field->getName() . ' - ' . $field->getTypeName();
}

// CSRF-Token für Frontend-Formulare
$token = $table->getCSRFKey();

Alle Tabellen auflisten

// Alle YForm-Tabellen
foreach (rex_yform_manager_table::getAll() as $table) {
    echo $table->getName() . '<br>';
}

// Mit Feldanzahl
foreach (rex_yform_manager_table::getAll() as $table) {
    echo $table->getName() . ': ' . count($table->getFields()) . ' Felder<br>';
}

Dataset abrufen

// Datensatz per ID
$product = rex_yform_manager_dataset::get(42, 'rex_products');

if ($product) {
    echo $product->name;
    echo $product->price;
    echo $product->description;
}

// Mit Fallback
$product = rex_yform_manager_dataset::get($id, 'rex_products');
if (!$product) {
    echo 'Produkt nicht gefunden';
    exit;
}

Dataset erstellen & speichern

// Neuen Datensatz erstellen
$product = rex_yform_manager_dataset::create('rex_products');
$product->name = 'Neues Produkt';
$product->price = 99.90;
$product->description = 'Beschreibung...';
$product->status = 1;

if ($product->save()) {
    echo 'Gespeichert mit ID: ' . $product->getId();
} else {
    echo 'Fehler: ' . implode('<br>', $product->getMessages());
}

Dataset aktualisieren

// Bestehenden Datensatz laden und ändern
$product = rex_yform_manager_dataset::get(42, 'rex_products');

if ($product) {
    $product->name = 'Geänderter Name';
    $product->price = 149.90;
    
    if ($product->save()) {
        echo 'Aktualisiert';
    } else {
        echo 'Fehler: ' . implode('<br>', $product->getMessages());
    }
}

Dataset löschen

// Datensatz löschen
$product = rex_yform_manager_dataset::get(42, 'rex_products');

if ($product && $product->delete()) {
    echo 'Gelöscht';
} else {
    echo 'Fehler beim Löschen';
}

// Oder direkt via Query
rex_yform_manager_dataset::query('rex_products')
    ->findId(42)
    ?->delete();

Custom Model Class

// In lib/Models/Product.php
class Product extends rex_yform_manager_dataset
{
    // Eigene Methoden
    public function getFormattedPrice()
    {
        return number_format($this->price, 2, ',', '.') . ' €';
    }
    
    public function isAvailable()
    {
        return $this->status == 1 && $this->stock > 0;
    }
    
    // Accessor (wird automatisch bei $product->full_name aufgerufen)
    public function getFullName()
    {
        return $this->name . ' (' . $this->sku . ')';
    }
}

// In boot.php registrieren
rex_yform_manager_dataset::setModelClass('rex_products', Product::class);

// Verwendung
$product = Product::query()->findId(42);
echo $product->getFormattedPrice(); // "99,90 €"
echo $product->full_name; // "Mein Produkt (SKU-123)" (via getFullName)

Query - Einfache Abfragen

// Alle Datensätze
$products = rex_yform_manager_dataset::query('rex_products')
    ->find();

foreach ($products as $product) {
    echo $product->name;
}

// Mit WHERE
$products = rex_yform_manager_dataset::query('rex_products')
    ->where('status', 1)
    ->where('price', 100, '>')
    ->find();

// Mit ORDER BY und LIMIT
$products = rex_yform_manager_dataset::query('rex_products')
    ->where('status', 1)
    ->orderBy('name', 'ASC')
    ->limit(10)
    ->find();

Query - Komplexe Abfragen

// Mit Alias und JOIN
$products = rex_yform_manager_dataset::query('rex_products')
    ->alias('p')
    ->joinRelation('category_id', 'c') // Folgt Relation-Feld
    ->select('c.name', 'category_name')
    ->where('p.status', 1)
    ->orderBy('p.name')
    ->find();

foreach ($products as $product) {
    echo $product->name . ' - ' . $product->category_name;
}

// Raw SQL
$products = rex_yform_manager_dataset::query('rex_products')
    ->whereRaw('LOWER(name) LIKE ?', ['%redaxo%'])
    ->selectRaw('COUNT(*) as total')
    ->find();

Query - Count & Exists

// Anzahl
$count = rex_yform_manager_dataset::query('rex_products')
    ->where('status', 1)
    ->count();

echo 'Aktive Produkte: ' . $count;

// Existiert?
$exists = rex_yform_manager_dataset::query('rex_products')
    ->where('sku', 'PROD-123')
    ->exists();

if ($exists) {
    echo 'SKU bereits vergeben';
}

Query - Pagination

// Seite 1, 20 Einträge pro Seite
$page = rex_request::get('page', 'int', 1);

$result = rex_yform_manager_dataset::query('rex_products')
    ->where('status', 1)
    ->orderBy('name')
    ->paginate($page, 20);

// $result = ['items' => Collection, 'total' => int, 'page' => int, 'perPage' => int, 'pages' => int]

foreach ($result['items'] as $product) {
    echo $product->name . '<br>';
}

// Pagination ausgeben
echo 'Seite ' . $result['page'] . ' von ' . $result['pages'];
echo ' (' . $result['total'] . ' gesamt)';

Query - Chunk Processing

// Große Datenmengen in Batches verarbeiten
rex_yform_manager_dataset::query('rex_products')
    ->chunk(100, function($products) {
        foreach ($products as $product) {
            // Verarbeitung
            $product->processHeavyOperation();
        }
    });

Relationen - belongsTo (n:1)

// Produkt hat eine Kategorie (category_id Relation-Feld)
$product = rex_yform_manager_dataset::get(42, 'rex_products');

// Kategorie laden
$category = $product->getRelatedDataset('category_id');
if ($category) {
    echo $category->name;
}

// Oder direkt via JOIN
$product = rex_yform_manager_dataset::query('rex_products')
    ->joinRelation('category_id', 'c')
    ->select('c.name', 'category_name')
    ->findId(42);

echo $product->category_name;

Relationen - hasMany (1:n)

// Kategorie hat viele Produkte
$category = rex_yform_manager_dataset::get(5, 'rex_categories');

// Alle Produkte der Kategorie
$products = $category->getRelatedCollection('category_id', 'rex_products');

foreach ($products as $product) {
    echo $product->name;
}

// Mit Filter
$products = rex_yform_manager_dataset::query('rex_products')
    ->where('category_id', $category->id)
    ->where('status', 1)
    ->orderBy('name')
    ->find();

Frontend-Formular (Pipe-Notation)

// In Modul-Ausgabe
$yform = rex_yform::factory();

// Formular-Parameter
$yform->setObjectparams('form_action', rex_getUrl());
$yform->setObjectparams('form_method', 'post');
$yform->setObjectparams('form_showformafterupdate', 0);

// Felder (Pipe-Notation)
$yform->setValueField('text', ['name', 'Name*']);
$yform->setValueField('text', ['email', 'E-Mail*']);
$yform->setValueField('textarea', ['message', 'Nachricht*']);

// Validierung
$yform->setValidateField('empty', ['name', 'Bitte Namen angeben']);
$yform->setValidateField('empty', ['email', 'Bitte E-Mail angeben']);
$yform->setValidateField('email', ['email', 'Ungültige E-Mail']);

// Aktion: E-Mail senden
$yform->setActionField('email', [
    'empfänger@example.com',
    'Kontaktformular',
    'Name: ###name###\nE-Mail: ###email###\nNachricht:\n###message###'
]);

// Erfolgs-Meldung
$yform->setActionField('showtext', ['', 'Vielen Dank für Ihre Nachricht!']);

// Ausgabe
echo $yform->getForm();

Frontend-Formular (PHP-API)

$yform = rex_yform::factory();

$yform->setObjectparams('form_action', rex_getUrl());

// Text-Feld
$yform->setValueField('text', [
    'name' => 'name',
    'label' => 'Name',
    'notice' => 'Ihr vollständiger Name'
]);

// Select mit Optionen
$yform->setValueField('select', [
    'name' => 'category',
    'label' => 'Kategorie',
    'options' => 'Allgemein,Support,Vertrieb'
]);

// Checkbox
$yform->setValueField('checkbox', [
    'name' => 'newsletter',
    'label' => 'Newsletter abonnieren'
]);

echo $yform->getForm();

Frontend-Formular in DB speichern

$yform = rex_yform::factory();

$yform->setObjectparams('form_action', rex_getUrl());

// Felder
$yform->setValueField('text', ['name', 'Name*']);
$yform->setValueField('text', ['email', 'E-Mail*']);
$yform->setValueField('textarea', ['message', 'Nachricht*']);

// Validierung
$yform->setValidateField('empty', ['name', 'Pflichtfeld']);
$yform->setValidateField('email', ['email', 'Ungültige E-Mail']);

// In DB speichern (rex_contact_messages)
$yform->setActionField('db', [
    'rex_contact_messages', // Tabellenname
    'name,email,message,createdate', // Felder
    '', // Main-ID (leer = INSERT)
    '', // DB-Feld für Main-ID
    '', // Hidden-Felder
    '', // Auto-Felder (z.B. createdate=NOW())
]);

// Erfolgs-Meldung
$yform->setActionField('showtext', ['', 'Nachricht gespeichert!']);

echo $yform->getForm();

Dataset-Formular im Frontend

// Bestehenden Datensatz bearbeiten
$product = rex_yform_manager_dataset::get($id, 'rex_products');

if ($product) {
    // Formular aus Dataset generieren
    $yform = $product->getForm();
    
    // Parameter anpassen
    $yform->setObjectparams('form_action', rex_getUrl(REX_ARTICLE_ID, '', ['id' => $id]));
    $yform->setObjectparams('form_method', 'post');
    $yform->setObjectparams('getdata', true); // Daten laden
    
    // Erfolgs-Meldung
    $yform->setActionField('showtext', ['', 'Produkt aktualisiert']);
    
    // Ausgabe
    echo $product->executeForm($yform);
}

Neuen Datensatz via Frontend-Formular

// Neuen Datensatz erstellen
$product = rex_yform_manager_dataset::create('rex_products');

// Formular generieren
$yform = $product->getForm();

$yform->setObjectparams('form_action', rex_getUrl());
$yform->setActionField('showtext', ['', 'Produkt erstellt!']);

// Ausgabe
echo $product->executeForm($yform);

Custom Templates

// Custom Template-Pfad registrieren (boot.php)
rex_yform::addTemplatePath(rex_path::addon('project', 'ytemplates'));

// Template-Struktur:
// project/ytemplates/
//   bootstrap/
//     value.text.tpl.php
//     value.textarea.tpl.php
//   foundation/
//     ...

// Im Formular Template setzen
$yform->setObjectparams('form_ytemplate', 'bootstrap');

E-Mail-Versand mit Anhang

$yform = rex_yform::factory();

$yform->setObjectparams('form_action', rex_getUrl());

// Felder
$yform->setValueField('text', ['name', 'Name']);
$yform->setValueField('text', ['email', 'E-Mail']);
$yform->setValueField('upload', ['attachment', 'Anhang', '', '', 1]); // 1 = erforderlich

// E-Mail mit Anhang
$yform->setActionField('email', [
    'empfänger@example.com', // An
    'Kontakt mit Anhang', // Betreff
    'Name: ###name###\nE-Mail: ###email###', // Body
    '', // Von (leer = aus Config)
    '', // Von-Name
    '', // Reply-To
    '', // BCC
    '', // CC
    '', // Anhänge-Feld: attachment
    'attachment'
]);

echo $yform->getForm();

Validierung - Komplexe Regeln

$yform = rex_yform::factory();

// Pflichtfeld
$yform->setValidateField('empty', ['name', 'Name ist Pflicht']);

// E-Mail
$yform->setValidateField('email', ['email', 'Ungültige E-Mail']);

// Länge (min, max)
$yform->setValidateField('type', ['password', 'string', 'Passwort muss Text sein']);
$yform->setValidateField('compare_value', ['password', '>=', 8, 'Mindestens 8 Zeichen']);

// RegEx
$yform->setValidateField('preg_match', ['phone', '/^[0-9\s\-\+]+$/', 'Nur Zahlen, Leerzeichen, +, -']);

// Custom Validation
$yform->setValidateField('customfunction', [
    'email',
    'myCustomValidator',
    '',
    'E-Mail bereits registriert'
]);

// In lib/functions.php
function myCustomValidator($email, $params) {
    $exists = rex_yform_manager_dataset::query('rex_users')
        ->where('email', $email)
        ->exists();
    
    return !$exists; // true = valid, false = invalid
}

Datensatz-Events

// In Custom Model Class

class Product extends rex_yform_manager_dataset
{
    // Before Save
    public function preSave()
    {
        // Slug generieren
        if (empty($this->slug)) {
            $this->slug = rex_string::normalize($this->name);
        }
        
        // Timestamps
        if (!$this->getId()) {
            $this->created_at = date('Y-m-d H:i:s');
        }
        $this->updated_at = date('Y-m-d H:i:s');
    }
    
    // After Save
    public function postSave()
    {
        // Cache löschen
        rex_delete_cache();
        
        // Benachrichtigung
        if ($this->wasModified('status') && $this->status == 1) {
            $this->sendPublishNotification();
        }
    }
    
    // Before Delete
    public function preDelete()
    {
        // Beziehungen löschen
        rex_sql::factory()
            ->setTable('rex_product_images')
            ->setWhere(['product_id' => $this->id])
            ->delete();
    }
}

Mass Operations

// Mehrere Datensätze auf einmal aktualisieren
$products = rex_yform_manager_dataset::query('rex_products')
    ->where('category_id', 5)
    ->find();

foreach ($products as $product) {
    $product->status = 0;
    $product->save();
}

// Oder via SQL (schneller für große Mengen)
rex_sql::factory()
    ->setTable('rex_products')
    ->setWhere(['category_id' => 5])
    ->setValue('status', 0)
    ->update();

Soft Deletes

// In Custom Model mit deleted_at Feld

class Product extends rex_yform_manager_dataset
{
    // Überschreibe delete()
    public function delete()
    {
        $this->deleted_at = date('Y-m-d H:i:s');
        return $this->save();
    }
    
    // Query-Scope für nur aktive
    public static function active()
    {
        return static::query()
            ->whereRaw('deleted_at IS NULL');
    }
    
    // Wiederherstellen
    public function restore()
    {
        $this->deleted_at = null;
        return $this->save();
    }
}

// Verwendung
$products = Product::active()->find(); // Nur nicht-gelöschte

Export nach CSV

// Datensätze exportieren
$products = rex_yform_manager_dataset::query('rex_products')
    ->where('status', 1)
    ->orderBy('name')
    ->find();

header('Content-Type: text/csv; charset=UTF-8');
header('Content-Disposition: attachment; filename="products.csv"');

$fp = fopen('php://output', 'w');

// Header
fputcsv($fp, ['ID', 'Name', 'Price', 'SKU'], ';');

// Daten
foreach ($products as $product) {
    fputcsv($fp, [
        $product->id,
        $product->name,
        $product->price,
        $product->sku
    ], ';');
}

fclose($fp);
exit;

Import aus CSV

// CSV importieren
$file = $_FILES['import']['tmp_name'];

if (($handle = fopen($file, 'r')) !== false) {
    // Header überspringen
    fgetcsv($handle, 1000, ';');
    
    while (($data = fgetcsv($handle, 1000, ';')) !== false) {
        $product = rex_yform_manager_dataset::create('rex_products');
        $product->name = $data[0];
        $product->price = $data[1];
        $product->sku = $data[2];
        $product->status = 1;
        
        if (!$product->save()) {
            echo 'Fehler: ' . implode(', ', $product->getMessages());
        }
    }
    
    fclose($handle);
    echo 'Import abgeschlossen';
}

Table Cache löschen

// Nach Schema-Änderungen Cache löschen
rex_yform_manager_table::deleteCache();

// Z.B. in update.php nach Tabellen-Änderungen
$addon = rex_addon::get('myAddon');

// Tabelle modifizieren
$sql = rex_sql::factory();
$sql->setQuery('ALTER TABLE rex_my_table ADD COLUMN new_field VARCHAR(255)');

// Cache löschen
rex_yform_manager_table::deleteCache();

JSON-Export

// Datensätze als JSON
$products = rex_yform_manager_dataset::query('rex_products')
    ->where('status', 1)
    ->find();

$data = [];
foreach ($products as $product) {
    $data[] = [
        'id' => $product->id,
        'name' => $product->name,
        'price' => $product->price,
        'category' => $product->getRelatedDataset('category_id')->name ?? null
    ];
}

header('Content-Type: application/json');
echo json_encode($data, JSON_PRETTY_PRINT);
exit;

REST API mit YForm

// API-Endpoint in lib/Api.php

class Product_Api extends rex_yform_rest
{
    public function get()
    {
        $id = rex_request::get('id', 'int');
        
        if ($id) {
            // Einzelner Datensatz
            $product = rex_yform_manager_dataset::get($id, 'rex_products');
            return $this->sendJson($product ? $product->getData() : null);
        }
        
        // Liste
        $products = rex_yform_manager_dataset::query('rex_products')
            ->where('status', 1)
            ->find();
        
        $data = [];
        foreach ($products as $product) {
            $data[] = $product->getData();
        }
        
        return $this->sendJson($data);
    }
    
    public function post()
    {
        $data = json_decode(file_get_contents('php://input'), true);
        
        $product = rex_yform_manager_dataset::create('rex_products');
        $product->setData($data);
        
        if ($product->save()) {
            return $this->sendJson($product->getData(), 201);
        }
        
        return $this->sendJson(['errors' => $product->getMessages()], 400);
    }
}