Salta ai contenuti

Costruire un blog con FireCMS Cloud

blog_example

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à

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
}

Iniziamo con la nostra collezione, senza proprietà:

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

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"
})

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"
}
}))

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 immagini
  • text: un campo di testo Markdown
  • products: un array di riferimenti a un’altra collezione, products in 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
}
})
}
}
})

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:

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
}
],
}

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: {
// ...
}
});

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"
})
}
})