E-Commerce & Magnolia - eine technische Sicht
Digital Experience Plattformen sind in aller Munde. Die Verbindungen innerhalb von Systemlandschaften bilden hierfür die technische Grundlage und zahlen dabei auf verschiedene Faktoren ein. Ein Faktor hierbei ist eine möglichst intuitive und nahtlose Autoren-Experience. Sprich, ein Autor/Creator/Marketer sollte sich nur innerhalb weniger Systeme, möglicherweise nur eines Systems bewegen und dort alle Möglichkeiten der Content-Erstellung vorfinden. Er sollte Daten aus verschiedenen Umsystemen nutzen und miteinander verbinden können, um eine zielgruppengerechte Customer Journey erschaffen.
Wir legen heute den Fokus auf die Verbindung von CMS und Ecommerce System, konkreter gefragt - wie kommen Produktdaten in Magnolia CMS. Hier hat Magnolia neben einer vollumfassenden Lösung aber auch eine schmale und vom Connector unabhängige Lösung geschaffen, die es ermöglicht externe Systeme innerhalb von Komponenten, Content - Apps und vielen anderen Ansatzpunkten einzubinden. Hierzu wird das Magnolia JavaScript Framework und das JavaScript UI module genutzt.
Kommen wir aber zu etwas mehr Praxis.
Folgende Anforderungen sind gegeben. Ein Redakteur möchte Produkte (bspw. für eine Slider oder Liste) in eine Seite integrieren. Das E-Commerce System stellt eine API bereit. Ziel ist es, die Produktdaten bzw. Produktreferenzen in Magnolias JCR/DB zu persistieren, um diese im Frontend (unabhängig ob Headless oder Klassisch) darzustellen. Wie einfach das geht, soll das folgende einfache Beispiel zeigen.
Wir nutzen hierfür die Produkt-API von Fake Store APIFake Store API Magnolia DX Core und das erwähnte JavaScript UI Module. Innerhalb von Magnolia nutzen wir Light Development.
Folgend ein Codebeispiel für eine Dialog Definition. Diese kann innerhalb von Komponenten oder auch Page Properties genutzt werden.
#dialog definition
form:
implementationClass:
info.magnolia.ui.javascript.form.FormViewWithChangeListener
properties:
product:
label: Products from API
$type: javascriptField
fieldScript: /jsfields-lm/webresources/product-chooser.html
height: 40
Dies fügt ein JavaScript Field in den Dialog ein. Dieses Feld lädt im Hintergrund eine eigene kleine Applikation / Website, welche mit dem Magnolia kommuniziert und Daten austauschen kann. Innerhalb des Script-Fields nutzen wir eine React App, um einen Product-Chooser darzustellen, welcher Daten aus der Schnittstelle (FakeStore API) holt, darstellt und man einzelne Produkte selektieren kann, welche dann einer Liste zugefügt werden. Die Kommunikation mit Magnolia erfolgt über Event Listener. Folgend das gesamte Script (CSS wurde entfernt).
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<script
src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-
dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
// config for magnolia communication
let correlationId;
let value;
//init app and get data from magnolia
window.addEventListener('message', function (event) {if (event.data.action === 'init') {
correlationId = event.data.correlationId;
value = JSON.parse(event.data.state.value || '{}');
const container = document.getElementById('root');
const root = ReactDOM.createRoot(container);
root.render(<Picker />);
}
},
false
);
//manipulate field height if the product list gets longer
function changeHeight() {
parent.window.postMessage(
{
action: 'changeHeight',
correlationId: correlationId,
value: (document.documentElement.offsetHeight + 30),
},
'*'
);
};
//picker react component
function Picker(props) {
const [productList, setProductList] = React.useState([]);
const [selectedProducts, setSelectedProducts] =
React.useState(VALUE?.selectedProducts || []);
//add product to list
const addProduct = (e) => {
let value = e.target.value;
let p = productList&& productList.find(e => e.id ===
parseInt(value));
setSelectedProducts((selectedProducts) ? [... selectedProducts, p] :
[]);
}
//delete product from list
const deleteProduct = (prodId) => {
setSelectedProducts(oldValues => {
return oldValues.filter(prod => prod.id !== prodId)
})
}
//get products from api to create a select box
React.useEffect(() => {
fetch('https://fakestoreapi.com/products')
.then((res) => res.json())
.then((json) => {
setProductList(json);
});
}, []);
//save data in magnolia jcr
React.useEffect(() => {
parent.window.postMessage(
{
action: 'changeValue',
correlationId: correlationId,
value: JSON.stringify({ selectedProducts }),
},
'*'
);
changeHeight();
}, [selectedProducts]);
//list and selected product cardsreturn (
<div>
<div>
<select class='plb' id='product-list-box'
onChange={addProduct}>
{productList.map((item, i) =>
(
<option value={item.id}>{item.title}</option>
)
)
}
</select>
</div>
<div>
{selectedProducts && (
<div id="products" className="products">
{ selectedProducts.map((product) => {
return <div className="product">
<div onClick={() =>
deleteProduct(product.id)} className="product-remove">X</div>
<img className="product-image"
src={product.image} />
<div className="product-
id">{product.id}</div>
<div className="product-
name">{product.title}</div>
</div>;
}) }
</div>
)}
</div>
</div>
)
};
</script>
</body>
</html>
In der Autorenoberfläche sieht dies wie folgt aus:
Im JCR wird hierzu ein JSON-Objekt gespeichert, welches ich im Frontend meiner Wahl weiter nutzen kann. Abhängigkeiten zu Preis oder Artikelverfügbarkeiten sollten nicht im CMS gespeichert und durch das Frontend mit dem E-Commerce System abgeglichen werden. Diese Integration ist beispielhaft und sehr einfach, da keine Authentifizierung etc. benötigt wird. Aber auch diese komplexeren Fälle lassen sich mit dem JSField Feature umsetzen, bspw. eine Integration zu Salesforce Commerce bzw. eine weitreichende Integration in die Salesforce Welt rund um Marketing Automation und CRM.
Warum sind JS-Fields ein leistungsstarkes Feature?
Flexibilität
JS-Fields ermöglichen Entwicklern schnell und unkompliziert APIs anzubinden und innerhalb von Magnolia zu verwenden. Dabei ist man Unabhängig von der Datenstruktur oder Einsatzzweck, da sich JS-Fields für Komponenten und Apps wie auch Traits für Personalisierung nutzen lassen.
Anpassbarkeit
Über das Field Script ist man frei in der Gestaltung der Benutzeroberfläche für Autoren und kann sogar komplexe Prozesse innerhalb eines Feldes abbilden. Bspw. ist eine Produktauswahl in Salesforce ein mehrstufiger Prozess, welcher sich problemlos abbilden lässt.
Integration mit Apps
Man ist in der Lage Informationen und Daten aus Magnolia oder Umsystemen innerhalb von Magnolia darzustellen und genießt dabei die Vorteile aus Flexibilität und Anpassbarkeit. Man kann dem Redakteur notwendige Informationen im Backend verfügbar machen und somit die Author-Experience Erhöhen.
Developer-Friendly
JS-Fields können von Frontend Developern entwickelt werden, ohne tiefere Kenntnisse in JAVA zu benötigen. Komplexe Anbindungen an Umsysteme können hier vereinfacht werden.
Fazit
JS-Fields erweitern den bereits großen Funktionsumfang von Magnolia. Sie bringen mehr Möglichkeiten für Drittsystemintegrationen gepaart mit Developer-Friendliness, helfen die Author-Experience weiter zu verbessern und damit letztendlich auch die Customer Experience.
Einsatzgebiete sind endlos und helfen auch Integrationsaufwände zu reduzieren. Alles in allem ein weiterer Schritt zu mehr Composability und Content Orchestration.