# TerraFlowCMS CustomField-System

Das TerraFlowCMS CustomField-System ermöglicht es, benutzerdefinierte Felder zu verschiedenen Entities hinzuzufügen, ähnlich wie in Shopware 6. Es bietet eine flexible und erweiterbare Lösung für die Verwaltung zusätzlicher Daten.

## Übersicht

Das CustomField-System besteht aus folgenden Komponenten:

- **CustomFieldSet**: Gruppiert verwandte CustomFields
- **CustomField**: Definiert einzelne Felder mit Typ und Validierung
- **CustomFieldValue**: Speichert die tatsächlichen Werte für Entities
- **CustomFieldService**: Verwaltet das System
- **CustomFieldController**: Admin-Interface
- **CustomFieldExtension**: Twig-Integration

## Architektur

### Entities

#### CustomFieldSet
- Gruppiert verwandte CustomFields
- Zugewiesen zu einer spezifischen Entity-Klasse
- Hat Position und Aktivierungsstatus
- Kann Konfiguration enthalten

#### CustomField
- Definiert ein einzelnes Feld
- Hat einen Typ (text, number, select, etc.)
- Kann Validierung und Optionen haben
- Gehört zu einem CustomFieldSet

#### CustomFieldValue
- Speichert die tatsächlichen Werte
- Verknüpft mit Entity über entityName und entityId
- Unterstützt typisierte Werte

### Feldtypen

Das System unterstützt folgende Feldtypen:

- **text**: Einzeiliger Text
- **textarea**: Mehrzeiliger Text
- **number**: Ganzzahl
- **float**: Dezimalzahl
- **boolean**: Ja/Nein
- **date**: Datum
- **datetime**: Datum und Zeit
- **time**: Zeit
- **select**: Einfache Auswahl
- **multi-select**: Mehrfachauswahl
- **media**: Datei-Upload
- **color**: Farbauswahl
- **url**: URL
- **email**: E-Mail-Adresse
- **password**: Passwort
- **json**: JSON-Daten

## Verwendung

### Admin-Interface

1. **CustomFieldSet erstellen**:
   - Gehen Sie zu Admin → Custom Fields
   - Klicken Sie auf "Neues Set"
   - Definieren Sie Name, Technical Name und Entity

2. **CustomFields hinzufügen**:
   - Bearbeiten Sie ein CustomFieldSet
   - Klicken Sie auf "Neues Field"
   - Wählen Sie Typ und konfigurieren Sie das Feld

3. **Werte verwalten**:
   - CustomField-Werte werden automatisch in Entity-Formularen angezeigt
   - Werte werden automatisch gespeichert und geladen

### Programmatische Verwendung

#### Service verwenden

```php
use TerraFlow\Service\CustomFieldService;

// CustomFieldSet erstellen
$customFieldSet = new CustomFieldSet();
$customFieldSet->setName('SEO Settings');
$customFieldSet->setTechnicalName('seo_settings');
$customFieldSet->setEntityName('TerraFlow\\Entity\\Page');
$customFieldService->createCustomFieldSet($customFieldSet);

// CustomField erstellen
$customField = new CustomField();
$customField->setName('SEO Keywords');
$customField->setTechnicalName('seo_keywords');
$customField->setType(CustomField::TYPE_TEXT);
$customField->setCustomFieldSet($customFieldSet);
$customFieldService->createCustomField($customField);

// Werte speichern
$values = [
    'seo_keywords' => 'keyword1, keyword2',
    'meta_description' => 'Page description'
];
$customFieldService->saveCustomFieldValues('TerraFlow\\Entity\\Page', '123', $values);

// Werte laden
$values = $customFieldService->loadCustomFieldValues('TerraFlow\\Entity\\Page', '123');
```

#### Entity-Integration

```php
use TerraFlow\Interface\CustomFieldAwareInterface;
use TerraFlow\Entity\Traits\CustomFieldAwareTrait;

class Page implements CustomFieldAwareInterface
{
    use CustomFieldAwareTrait;

    // getCustomFieldEntityType(): string — z. B. 'page'
}

// Nach dem Laden aus der DB (postLoad): alle Werte als Array
$values = $page->getCustomFields(); // ['technical_name' => mixed, ...]
```

Hinweis: Direkt nach `new Page()` ist das Array leer, bis Werte gesetzt oder die Entity aus der DB inkl. Hydrator geladen wurde.

#### Twig-Integration

Empfohlen für `Page`, `Category`, `User`, `Media`, `Settings` (alle `CustomFieldAwareInterface`): dieselben Daten wie `$entity->getCustomFields()` in PHP – nach dem Laden aus der DB per `CustomFieldHydratorSubscriber` befüllt.

```twig
{# Wie getCustomFields() in PHP (Alias-Funktionen) #}
{% set customFields = get_custom_fields(page) %}
{% set customFields = getCustomFields(page) %}

{# Oder Twig-Attribut (nutzt Getter getCustomFields) #}
{% for name, value in page.customFields %}
    <div><strong>{{ name }}:</strong> {{ value }}</div>
{% endfor %}

{# Alternative: Typ + Id (logischer entityType, z. B. „page“, „category“ – wie getCustomFieldEntityType()) #}
{% set customFields = get_custom_fields('page', page.id) %}

{# Spezifischen Wert laden #}
{% set keywords = get_custom_field_value('page', page.id, 'seo_keywords') %}

{# CustomField rendern #}
{{ render_custom_field('page', page.id, 'featured_image', {'class': 'img-fluid'}) }}
```

## Plugin-Integration

### Event-Listener

```php
use TerraFlow\Event\CustomField\CustomFieldValueUpdateEvent;

public function onCustomFieldValueUpdate(CustomFieldValueUpdateEvent $event): void
{
    $field = $event->getCustomField();
    
    switch ($field->getTechnicalName()) {
        case 'seo_keywords':
            $this->updateSeoKeywords($event);
            break;
    }
}
```

### CustomFieldSets erstellen

```php
protected function createPluginData(): void
{
    $this->createSeoCustomFieldSet();
}

private function createSeoCustomFieldSet(): void
{
    $customFieldSet = new CustomFieldSet();
    $customFieldSet->setName('SEO Settings');
    $customFieldSet->setTechnicalName('seo_settings');
    $customFieldSet->setEntityName('TerraFlow\\Entity\\Page');
    
    // CustomFields hinzufügen
    $keywordsField = new CustomField();
    $keywordsField->setName('SEO Keywords');
    $keywordsField->setTechnicalName('seo_keywords');
    $keywordsField->setType(CustomField::TYPE_TEXT);
    $customFieldSet->addCustomField($keywordsField);
    
    $this->customFieldService->createCustomFieldSet($customFieldSet);
}
```

## Validierung

### Feld-Validierung

```php
// Validierung definieren
$validation = [
    'minLength' => 3,
    'maxLength' => 255,
    'pattern' => '/^[a-zA-Z0-9\s]+$/'
];
$customField->setValidation($validation);

// Werte validieren
$errors = $customFieldService->validateCustomFieldValues('TerraFlow\\Entity\\Page', $values);
if (!empty($errors)) {
    // Validierungsfehler behandeln
}
```

### Form-Integration

```php
// CustomField-Formular erstellen
$form = $this->createForm(CustomFieldValueType::class, null, [
    'custom_field' => $customField
]);
```

## Best Practices

### Naming Conventions

- **Technical Names**: Verwenden Sie snake_case (z.B. `seo_keywords`)
- **Display Names**: Verwenden Sie lesbare Namen (z.B. "SEO Keywords")
- **Entity Names**: Verwenden Sie vollständige Klassen-Namen

### Performance

- CustomField-Werte werden lazy geladen
- Verwenden Sie Indizes für häufige Abfragen
- Cachen Sie CustomFieldSets für bessere Performance

### Sicherheit

- Validieren Sie alle CustomField-Werte
- Verwenden Sie Prepared Statements
- Sanitizen Sie HTML-Ausgaben

## Erweiterte Features

### Konfiguration

```php
// CustomFieldSet-Konfiguration
$config = [
    'display_mode' => 'tabs',
    'required_fields' => ['seo_keywords'],
    'conditional_fields' => [
        'featured_image' => ['show_if' => 'type == "article"']
    ]
];
$customFieldSet->setConfig($config);
```

### Optionen für Select-Felder

```php
$options = [
    'red' => 'Rot',
    'green' => 'Grün',
    'blue' => 'Blau'
];
$customField->setOptions($options);
```

### JSON-Felder

```php
// Komplexe Daten als JSON speichern
$data = [
    'social_media' => [
        'facebook' => 'https://facebook.com/page',
        'twitter' => 'https://twitter.com/page'
    ],
    'analytics' => [
        'google_analytics' => 'UA-123456-1',
        'facebook_pixel' => '123456789'
    ]
];
$customField->setType(CustomField::TYPE_JSON);
```

## Migration

### Bestehende Daten migrieren

```php
// Migration für bestehende Entities
public function migrateExistingData(): void
{
    $pages = $this->entityManager->getRepository(Page::class)->findAll();
    
    foreach ($pages as $page) {
        // Bestehende Daten in CustomFields konvertieren
        $values = [
            'legacy_meta_description' => $page->getMetaDescription(),
            'legacy_keywords' => $page->getKeywords()
        ];
        
        $this->customFieldService->saveCustomFieldValues(
            Page::class,
            (string) $page->getId(),
            $values
        );
    }
}
```

## Troubleshooting

### Häufige Probleme

1. **CustomFields werden nicht angezeigt**:
   - Prüfen Sie, ob das CustomFieldSet aktiv ist
   - Prüfen Sie, ob die Entity-Name korrekt ist
   - Prüfen Sie die Berechtigungen

2. **Werte werden nicht gespeichert**:
   - Prüfen Sie die Validierung
   - Prüfen Sie die Feldtypen
   - Prüfen Sie die Datenbank-Verbindung

3. **Performance-Probleme**:
   - Verwenden Sie Indizes
   - Cachen Sie CustomFieldSets
   - Optimieren Sie Abfragen

### Debugging

```php
// CustomField-System debuggen
$customFieldSets = $customFieldService->getCustomFieldSetsForEntity('TerraFlow\\Entity\\Page');
foreach ($customFieldSets as $set) {
    echo "Set: " . $set->getName() . "\n";
    foreach ($set->getCustomFields() as $field) {
        echo "  Field: " . $field->getName() . " (" . $field->getType() . ")\n";
    }
}
```

## API-Referenz

### CustomFieldService

- `createCustomFieldSet(CustomFieldSet $set): bool`
- `updateCustomFieldSet(CustomFieldSet $set, array $originalData = []): bool`
- `deleteCustomFieldSet(CustomFieldSet $set): bool`
- `createCustomField(CustomField $field): bool`
- `updateCustomField(CustomField $field, array $originalData = []): bool`
- `deleteCustomField(CustomField $field): bool`
- `saveCustomFieldValues(string $entityName, string $entityId, array $values): bool`
- `loadCustomFieldValues(string $entityName, string $entityId): array`
- `validateCustomFieldValues(string $entityName, array $values): array`

### Twig-Funktionen

- `get_custom_fields(entityName, entityId): array`
- `get_custom_field_value(entityName, entityId, technicalName): mixed`
- `render_custom_field(entityName, entityId, technicalName, options): string`

Das CustomField-System bietet eine mächtige und flexible Lösung für die Erweiterung von Entities mit benutzerdefinierten Feldern, ähnlich wie in Shopware 6.
