Skip to content

Dynamische Inhalte: SchemaHelper, strukturierte Mappings, FAQ- und Listen-Funktionen - #18

Open
skerbis wants to merge 1 commit into
mainfrom
feature/dynamic-content-schema
Open

Dynamische Inhalte: SchemaHelper, strukturierte Mappings, FAQ- und Listen-Funktionen#18
skerbis wants to merge 1 commit into
mainfrom
feature/dynamic-content-schema

Conversation

@skerbis

@skerbis skerbis commented Sep 6, 2026

Copy link
Copy Markdown
Member

Hallo Oli,

dieser PR erweitert das AddOn für dynamische Inhalte aus YForm-Tabellen (Produkte, FAQ, Events, Standorte, Listen). Ausgangspunkt war, dass Product und FAQPage in der Typauswahl der dynamischen URLs zwar angeboten werden, das Ergebnis aber kein valides Markup ergab: offers wurde als flacher Wert statt als Offer-Objekt ausgegeben, und FAQPage kann mit dem „1 Datensatz = 1 Schema“-Mechanismus grundsätzlich kein mainEntity-Array aus mehreren Zeilen bilden.

Was drin ist

SchemaHelper (lib/SchemaHelper.php)
Statische Helfer für valide Teilobjekte: offer(), openingHoursSpecification(), postalAddress(), contactPoint(), geoCoordinates(), aggregateRating(), brand(), organization(), person(), place(), question(), faqPage(), itemList(), withType(). Preise (12,50 €12.50), Verfügbarkeiten (InStock, ja/nein, 1/0 → Schema.org-URL), Wochentage (Mo, MontagMonday) und Uhrzeiten werden normalisiert. Die bisher mehrfach handgebauten PostalAddress/ContactPoint/GeoCoordinates-Arrays für Organization und LocalBusiness im Generator laufen jetzt darüber.

Strukturierte Feld-Zuordnung für dynamische URL-Profile (lib/Mapping/DynamicFieldMapper.php)
Neben field/static gibt es zwei neue Mapping-Formate:

{"type":"nested","fields":{"price":{"type":"field","value":"preis"},"priceCurrency":{"type":"static","value":"EUR"}}}
{"type":"opening_hours","rows":[{"days":["Monday","Friday"],"opens":{"type":"field","value":"mo_von"},"closes":{"type":"field","value":"mo_bis"}}]}

Unterstützte Properties: offers, brand, aggregateRating, address, contactPoint, location (Place), organizer/provider (Organization), author (Person), openingHoursSpecification. Beide Auflösungspfade (DynamicJsonLd.php und JsonLdGenerator::generateDynamicSchema()) nutzen dieselbe Klasse. Bestehende flache Mappings bleiben unverändert.

Backend-UI (pages/dynamic_urls_edit.php)
Für diese Properties gibt es die Option „Strukturiert“, die Unterfelder einblendet (jeweils Feld oder statischer Wert); Öffnungszeiten haben einen Zeilen-Editor mit Wochentagen. Die Vorschau zeigt das verschachtelte Ergebnis. Beim Speichern werden Mappings serverseitig auf bekannte Formate sowie gültige Property- und Spaltennamen reduziert. Nebenbei: die Vorschau zeigte bisher nie Beispielwerte, weil sampleData als Objekt mit .length abgefragt wurde.

FAQPage
Aus der Typauswahl entfernt; bestehende Zuordnungen bleiben mit Warnhinweis bearbeitbar. Stattdessen gibt es eine Template-Funktion, die alle passenden Zeilen zu einem Schema zusammenfasst.

Template-Funktionen für mehrzeilige Inhalte (lib/DynamicContent.php)

echo jsonld_render_faq('rex_faq', 'frage', 'antwort', ['status' => 1], ['order_by' => 'prio ASC']);
echo jsonld_render_item_list('rex_produkte', 'Product', ['status' => 1], [
    'name' => 'titel',
    'offers' => ['type' => 'nested', 'fields' => ['price' => ['type' => 'field', 'value' => 'preis']]],
], ['url_namespace' => 'produkt', 'list_type' => 'CollectionPage']);
echo jsonld_render_schema($beliebigesSchemaArray);

Filter sind Spalte-Wert-Paare (Prepared Statements, Arrays → IN, nullIS NULL), Tabellen-, Spalten- und order_by-Angaben werden validiert. Debug-Overlay wird bedient.

Weitere Properties in der Auswahl: Product.aggregateRating/url, LocalBusiness.openingHoursSpecification/url/priceRange, Event.offers/url, Service.offers. README und CHANGELOG (Abschnitt „Unreleased“, keine Versionsanhebung) sind ergänzt.

Bewusst nicht angefasst

Verhaltensänderungen

  • GeoCoordinates werden nur noch ausgegeben, wenn Breiten- und Längengrad numerisch und ungleich 0 sind (vorher wurde jeder nicht-leere String durchgereicht).
  • Feld-Mappings werden beim Speichern neu kodiert; unbekannte Formate werden verworfen.

Geprüft

  • rexstan Level 8 im Docker-Container: identisch zur Baseline vor den Änderungen, keine neuen Meldungen.
  • CLI-Skript mit gebootetem REDAXO: 52 Prüfungen der Helfer, der Mapping-Auflösung, der Bereinigung und von DynamicContent gegen eine temporäre DB-Tabelle, alle grün.
  • Eingebettetes JS mit node --check.
  • Nicht im Browser getestet, da in meiner Instanz kein URL-Profil mit Daten vorhanden ist. Die Prüfung gegen validator.schema.org bleibt manuell.

Viele Grüße
Thomas

🤖 Generated with Claude Code

…sten-Funktionen

- SchemaHelper mit Helfern für Offer, PostalAddress, ContactPoint,
  GeoCoordinates, OpeningHoursSpecification, AggregateRating, Brand,
  Organization, Person, Place, Question/FAQPage und ItemList inkl.
  Normalisierung von Preisen, Verfügbarkeiten, Wochentagen und Uhrzeiten
- Organization-/LocalBusiness-Teilobjekte im Generator auf SchemaHelper
  konsolidiert
- Strukturierte Feld-Zuordnung ("nested"/"opening_hours") für dynamische
  URL-Profile in beiden Auflösungspfaden, Backend-UI mit Unterfeldern,
  Öffnungszeiten-Zeilen und verschachtelter Vorschau, serverseitige
  Bereinigung der Mappings beim Speichern
- FAQPage aus der Typauswahl entfernt (Bestand bleibt mit Hinweis
  bearbeitbar); jsonld_render_faq(), jsonld_render_item_list() und
  jsonld_render_schema() als Template-Funktionen (DynamicContent)
- README und CHANGELOG ergänzt

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant