Costruire un blog con FireCMS Cloud

Non è necessario spiegare i vantaggi dell’utilizzo di un CMS headless rispetto a un approccio di blogging tradizionale come WordPress, ma eccone alcuni:
- è più facile e veloce da sviluppare, poiché non c’è accoppiamento tra frontend e backend.
- poiché il tuo frontend è indipendente, sei libero di modificarlo in qualsiasi modo tu voglia, lo stesso vale per il backend.
- è adatto per applicazioni omnicanale, puoi usare lo stesso backend e CMS con più app e siti web
- team più piccoli e specializzati
- scalabilità
- costi ridotti
- flessibilità e semplicità
Costruiamo un blog con FireCMS
Sezione intitolata “Costruiamo un blog con FireCMS”Costruiremo una collezione che ospita post del blog. Ogni post del blog includerà un array dinamico di elementi.
FireCMS ha una funzionalità integrata che ti permette di costruire array dinamici. La
proprietà array può essere configurata con la prop oneOf, per contenere oggetti
che contengono un tipo specifico di valore. È perfetto per costruire la struttura dati
delle voci del nostro blog!
Questi sono i tipi che utilizzeremo:
- Definiamo il tipo della collezione blog:
type BlogEntry = { name: string, header_image: string, created_on: Date, status: string, content: (BlogEntryImages | BlogEntryText | BlogEntryProducts)[];}- E ciascuno dei tipi dell’array
content:
type BlogEntryImages = { type: "images"; value: string[];}
type BlogEntryText = { type: "text"; value: string;}
type BlogEntryProducts = { type: "products"; value: object[]; // Usiamo un oggetto generico qui, ma sentiti libero di definire un tipo per i tuoi prodotti}Crea la collezione
Sezione intitolata “Crea la collezione”Iniziamo con la nostra collezione, senza proprietà:
export const blogCollection = buildCollection<BlogEntry>({ name: "Post del blog", path: "blog", properties: {}});Proprietà di base
Sezione intitolata “Proprietà di base”Aggiungiamo alcune proprietà semplici per le nostre voci.
- Vogliamo avere un titolo, che deve essere sempre impostato, quindi impostiamo la prop required in validation su true:
buildProperty({ name: "Titolo", validation: { required: true }, dataType: "string"})- Un’immagine che sarà in cima al post del blog:
buildProperty({ name: "Immagine di intestazione", dataType: "string", storage: { storagePath: "images", acceptedFiles: ["image/*"], metadata: { cacheControl: "max-age=1000000" } }})- e una data “creato il” che viene autogenerata quando viene creato il documento.
buildProperty( { name: "Creato il", dataType: "date", autoValue: "on_create"})Campo stato condizionale
Sezione intitolata “Campo stato condizionale”Ora vogliamo aggiungere una proprietà stringa status che avrà due valori possibili:
published e draft. Vogliamo consentire lo stato published
solo quando gli altri campi sono corretti.
In questo caso lo teniamo semplice e controlliamo solo se l’immagine di intestazione è impostata:
buildProperty(({ values }) => ({ name: "Stato", validation: { required: true }, dataType: "string", columnWidth: 140, enumValues: { published: { id: "published", label: "Pubblicato", disabled: !values.header_image, }, draft: "Bozza" }}))Contenuto della voce del blog
Sezione intitolata “Contenuto della voce del blog”Il contenuto delle nostre voci del blog deve essere dinamico, in modo che i content manager possano creare voci complesse con componenti diversi.
Il contenuto sarà un array di oggetti con un attributo type (che fungerà
da discriminatore) e un attributo value.
Definiremo 3 tipi:
images: un array di immaginitext: un campo di testo Markdownproducts: un array di riferimenti a un’altra collezione,productsin questo caso.
Usiamo la prop oneOf nelle proprietà array, progettata esattamente per questo
caso d’uso.
buildProperty({ name: "Contenuto", description: "Esempio di array complesso con più proprietà come figli", validation: { required: true }, dataType: "array", columnWidth: 400, oneOf: { typeField: "type", valueField: "value", properties: { images: buildProperty({ name: "Immagini", dataType: "array", of: buildProperty({ dataType: "string", storage: { storagePath: "images", acceptedFiles: ["image/*"], metadata: { cacheControl: "max-age=1000000" } } }), description: "Questo campo permette di caricare più immagini contemporaneamente e riordinarle" }), text: buildProperty({ dataType: "string", name: "Testo", markdown: true }), products: buildProperty({ name: "Prodotti", dataType: "array", of: { dataType: "reference", path: "products" // devi definire una collezione valida in questo percorso } }) } }})Creazione di una vista anteprima
Sezione intitolata “Creazione di una vista anteprima”Utilizziamo un’altra funzionalità di FireCMS: le viste personalizzate per le entità!
FireCMS ti permette di aggiungere viste aggiuntive alle tue viste entità, definite come componenti React. Le props che ricevi per costruire questo componente sono la collezione entità, l’entità originale e i valori modificati.
In questo caso, creeremo alcuni componenti React per rappresentare la nostra voce del blog
come farebbe l’app frontend. Questo è lo stesso codice che potresti
usare in qualsiasi framework SSR con React, come next.js.
Puoi trovare il codice per BlogEntryPreview in
BlogEntryPreview.
Una volta creato il componente, ci sono 2 modi per aggiungerlo:
Registrarlo nel CMS
Sezione intitolata “Registrarlo nel CMS”Il modo preferito è registrarlo nel CMS, in modo che sia disponibile nel selettore di viste della collezione.
Per farlo, aggiungilo alla prop entityViews della configurazione principale dell’app FireCMS:
import { FireCMSAppConfig } from "@firecms/cloud";const appConfig: FireCMSAppConfig = { version: "1", collections: [], entityViews: [ { key: "blog_preview", name: "Anteprima", Builder: BlogEntryPreview } ],}Registrarlo nella collezione
Sezione intitolata “Registrarlo nella collezione”Se stai definendo la collezione in codice, puoi anche registrarlo nella collezione stessa:
import {buildCollection} from "@firecms/core";export const blogCollection = buildCollection<BlogEntry>({ name: "Post del blog", path: "blog", entityViews: [ { key: "blog_preview", name: "Anteprima", Builder: BlogEntryPreview } ], properties: { // ... }});Codice completo:
Sezione intitolata “Codice completo:”Se uniamo tutte le parti costruite in questo tutorial, otteniamo il seguente codice per la collezione blog:
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" }) }})