Zum Inhalt springen

Einen Blog in FireCMS Cloud erstellen

blog_example

Wir müssen die Vorteile der Verwendung eines Headless-CMS gegenüber einem traditionellen Blogging-Ansatz wie WordPress nicht erklären, aber hier sind einige:

  • Es ist einfacher und schneller zu entwickeln, da es keine Kopplung zwischen Frontend und Backend gibt.
  • Da Ihr Frontend unabhängig ist, ist es frei, es in jeder gewünschten Weise zu ändern. Das gilt genauso für das Backend.
  • Es eignet sich für Omnichannel-Anwendungen, Sie können das gleiche Backend und CMS mit mehreren Apps und Websites verwenden
  • Kleinere, spezialisierte Teams
  • Skalierbarkeit
  • Geringere Kosten
  • Flexibilität und Einfachheit

Wir werden eine Kollektion erstellen, die Blog-Beiträge enthält. Jeder der Blog- Beiträge enthält ein dynamisches Array von Elementen.

FireCMS hat eine eingebaute Funktion, die es Ihnen ermöglicht, dynamische Arrays zu erstellen. Die Array-Eigenschaft kann mit der Eigenschaft oneOf konfiguriert werden, um Objekte zu enthalten, die einen bestimmten Werttyp halten. Das ist perfekt für den Aufbau unserer Blog-Eintrag- Datenstruktur!

Dies sind die Typen, die wir verwenden werden:

  • Wir definieren den Typ der Blog-Kollektion:
type BlogEntry = {
name: string,
header_image: string,
created_on: Date,
status: string,
content: (BlogEntryImages | BlogEntryText | BlogEntryProducts)[];
}
  • Und jeden der Typen des content-Arrays:
type BlogEntryImages = {
type: "images";
value: string[];
}
type BlogEntryText = {
type: "text";
value: string;
}
type BlogEntryProducts = {
type: "products";
value: object[]; // Wir verwenden hier ein generisches Objekt, aber definieren Sie gerne einen Typ für Ihre Produkte
}

Beginnen wir mit der Initialisierung unserer Kollektion, ohne Eigenschaften:

export const blogCollection = buildCollection<BlogEntry>({
name: "Blog entry",
path: "blog",
properties: {}
});

Dann fügen wir einige einfache Eigenschaften für unsere Einträge hinzu.

  • Wir möchten einen Titel haben, der immer gesetzt sein muss, also setzen wir die required- Eigenschaft in der Validierung auf true:
buildProperty({
name: "Title",
validation: { required: true },
dataType: "string"
})
  • Ein Bild, das oben im Blog-Beitrag erscheint:
buildProperty({
name: "Header image",
dataType: "string",
storage: {
storagePath: "images",
acceptedFiles: ["image/*"],
metadata: {
cacheControl: "max-age=1000000"
}
}
})
  • Und ein “created on”-Datum, das automatisch generiert wird, wenn das Dokument erstellt wird.
buildProperty( {
name: "Created on",
dataType: "date",
autoValue: "on_create"
})

Jetzt möchten wir eine status-String-Eigenschaft hinzufügen, die zwei mögliche Werte hat: published und draft. Wir möchten den published-Zustand nur erlauben, wenn die restlichen Felder korrekt sind.

In diesem Fall halten wir es einfach und prüfen nur, ob das Header-Bild gesetzt ist:

buildProperty(({ values }) => ({
name: "Status",
validation: { required: true },
dataType: "string",
columnWidth: 140,
enumValues: {
published: {
id: "published",
label: "Published",
disabled: !values.header_image,
},
draft: "Draft"
}
}))

Der Inhalt unserer Blog-Einträge muss dynamisch sein, damit Content- Manager in der Lage sind, komplexe Einträge mit verschiedenen Komponenten zu erstellen.

Der Inhalt wird ein Array von Objekten sein, die ein type-Attribut haben (das als Diskriminator dient) und ein value-Attribut.

Wir werden 3 Typen definieren:

  • images: ein Array von Bildern
  • text: ein Markdown-Textfeld
  • products: ein Array von Referenzen zu einer anderen Kollektion, in diesem Fall products.

Wir verwenden die oneOf-Eigenschaft in Array-Eigenschaften, die genau für diesen Anwendungsfall entwickelt wurde. Sie müssen nur definieren

buildProperty({
name: "Content",
description: "Beispiel eines komplexen Arrays mit mehreren Eigenschaften als Kinder",
validation: { required: true },
dataType: "array",
columnWidth: 400,
oneOf: {
typeField: "type", // Sie können diese `typeField` und `valueField`-Props weglassen, um die Standardwerte zu verwenden
valueField: "value",
properties: {
images: buildProperty({
name: "Images",
dataType: "array",
of: buildProperty({
dataType: "string",
storage: {
storagePath: "images",
acceptedFiles: ["image/*"],
metadata: {
cacheControl: "max-age=1000000"
}
}
}),
description: "Dieses Feld ermöglicht das gleichzeitige Hochladen mehrerer Bilder und das Neuanordnen"
}),
text: buildProperty({
dataType: "string",
name: "Text",
markdown: true
}),
products: buildProperty({
name: "Products",
dataType: "array",
of: {
dataType: "reference",
path: "products" // Sie müssen eine gültige Kollektion in diesem Pfad definieren
}
})
}
}
})

Diese Array-Konfiguration erstellt Objekte in der Datenquelle mit dem Format:

{
// ...
content: [
{
"type": "text",
"value": "Sonnenbrille oder Sonnenbrillen (umgangssprachlich auch Shades oder Sunnies genannt) sind eine Form von Schutzbrilllen, die hauptsächlich dazu dient, das Auge vor hellem Sonnenlicht und hochenergetischem sichtbaren Licht zu schützen. ..."
},
{
"type": "images",
"value": [
"images/photo-1511499767150-a48a237f0083.jpeg",
"images/photo-1577803645773-f96470509666.jpeg"
]
},
{
"type": "text",
"value": "Sonnenbrillen werden seit langem mit Prominenten und Filmschauspielern assoziiert, hauptsächlich aus dem Wunsch heraus, ihre Identität zu verbergen. Seit den 1940er Jahren sind Sonnenbrillen als Modeaccessoire beliebt, besonders am Strand."
},
{
"type": "products",
"value": [
{
"id": "B001UQ71F0",
"path": "products",
},
{
"id": "B001UQ71F0",
"path": "products",
}
]
}
],
// ...
}

Lassen Sie uns eine weitere Funktion von FireCMS nutzen: Benutzerdefinierte Ansichten für Entities!

FireCMS ermöglicht es Ihnen, Ihrer Entity-Ansicht zusätzliche Ansichten hinzuzufügen, die als React-Komponenten definiert sind. Die Props, die Sie für den Aufbau dieser Komponente erhalten, sind die Entity-Kollektion, die originale Entity und die modifizierten Werte.

In diesem Fall erstellen wir einige React-Komponenten, um unseren Blog-Eintrag so darzustellen, wie es die Frontend-App tun würde. Dies ist derselbe Code, den Sie in jedem SSR-Framework mit React, wie next.js, verwenden könnten.

Sie könnten auch ein komplexeres Setup haben, das Ihre Daten über eine API an Ihre SSR-App sendet und das Ergebnis rendert.

Den Code für BlogEntryPreview finden Sie in BlogEntryPreview.

Wenn Sie Ihre Komponente erstellt haben, gibt es 2 Möglichkeiten, sie zu Ihrer Komponente hinzuzufügen:

Der bevorzugte Weg ist, sie im CMS zu registrieren, damit sie im Ansichtsauswahl der Kollektion verfügbar ist.

Um dies zu tun, müssen Sie sie zur entityViews-Eigenschaft des FireCMS-Haupt-App-Konfigurations-Exports hinzufügen:

import { FireCMSAppConfig } from "@firecms/cloud";
const appConfig: FireCMSAppConfig = {
version: "1",
collections: [],
entityViews: [
{
key: "blog_preview",
name: "Preview",
Builder: BlogEntryPreview
}
],
}

Wenn Sie Ihre Kollektion im Code definieren, können Sie sie auch in der Kollektion selbst registrieren:

import {buildCollection} from "@firecms/core";
export const blogCollection = buildCollection<BlogEntry>({
name: "Blog entry",
path: "blog",
entityViews: [
{
key: "blog_preview",
name: "Preview",
Builder: BlogEntryPreview
}
],
properties: {
// ...
}
});

Wenn wir alle in diesem Tutorial erstellten Teile zusammenfügen, erhalten wir den folgenden Code für die Blog-Kollektion:

import { buildCollection, buildProperty } from "@firecms/core";
import { BlogEntryPreview } from "./BlogEntryPreview";
import { BlogEntry } from "./types";
export const blogCollection = buildCollection<BlogEntry>({
name: "Blog entry",
id: "blog",
path: "blog",
entityViews: [{
key: "preview",
name: "Preview",
Builder: BlogEntryPreview
}],
properties: {
name: buildProperty({
name: "Name",
validation: { required: true },
dataType: "string"
}),
header_image: buildProperty({
name: "Header image",
dataType: "string",
storage: {
mediaType: "image",
storagePath: "images",
acceptedFiles: ["image/*"],
metadata: {
cacheControl: "max-age=1000000"
}
}
}),
content: buildProperty({
name: "Content",
description: "Example of a complex array with multiple properties as children",
validation: { required: true },
dataType: "array",
columnWidth: 400,
oneOf: {
typeField: "type", // you can ommit these `typeField` and `valueField` props to use the defaults
valueField: "value",
properties: {
images: buildProperty({
name: "Images",
dataType: "array",
of: buildProperty({
dataType: "string",
storage: {
mediaType: "image",
storagePath: "images",
acceptedFiles: ["image/*"],
metadata: {
cacheControl: "max-age=1000000"
}
}
}),
description: "This fields allows uploading multiple images at once and reordering"
}),
text: buildProperty({
dataType: "string",
name: "Text",
markdown: true
}),
products: buildProperty({
name: "Products",
dataType: "array",
of: {
dataType: "reference",
path: "products" // you need to define a valid collection in this path
}
})
}
}
}),
status: buildProperty(({ values }) => ({
name: "Status",
validation: { required: true },
dataType: "string",
columnWidth: 140,
enumValues: {
published: {
id: "published",
label: "Published",
disabled: !values.header_image
},
draft: "Draft"
},
defaultValue: "draft"
})),
created_on: buildProperty({
name: "Created on",
dataType: "date",
autoValue: "on_create"
})
}
})