Gatsby con Drupal | Mostrando datos desde Drupal (1era Parte)

Video de Youtube
URL de Video remoto

Si no ves el video, puedes refrescar el navegador, presionando (Ctrl+Shift+R | Ctrl+F5 o Shift+F5), o abrirlo directamente desde el Canal de Youtube... HAZ CLIC AQUI

     A estas alturas ya podemos presumir de que conocemos, al menos lo básico, para montar y poner en funcionamiento, una aplicación desarrollada con Gatsby conectada a Drupal 9; pero con esto a penas, habremos avanzado el primer paso, ya que todavía nos faltará lo más importante que consistirá en mostrar dichos datos de forma dinámica, dentro de nuestro Gatsby utilizando como base de datos, nuestro Drupal.

     Ya hemos comprobado que si entramos en "http://gatsbymobile.lndo.site/_graphql", que es la url de nuestra instalación actual, explicada en el Capítulo Conectando Gatsby con Drupal 9,  accederemos al panel gráfico de GraphsQL y podremos comenzar a crear nuestras propias consultas, seleccionando los campos que nos hagan falta y a continuación, presionando el botón del Play, colocado en la parte superior, se mostrarán los contenidos relacionados en la pantalla de la derecha.

Gatsby con Drupal | www.drupaladicto.com - Consultor especializado en drupal y symfony

        Si todo funcionó, deberías poder ejecutar consultas aquí que muestren los datos que Gatsby extrajo de Drupal. Ten en cuenta que Gatsby crea colecciones GraphQL, a partir del contenido de Drupal, siguiendo una convención de nomenclatura específica: allNode {CONTENT-TYPE}. El patrón es el texto "allNode", seguido del nombre de la máquina del tipo de contenido de Drupal.

     Puedes probar copiando el siguiente código y pegándolo en el apartado de Queries, a continuación presiona el botón Play y verás que el resultado será el listado de nodos, con su Título, Id y el campo Body, la fecha de creación del contenido y su url, todo desde Drupal.

query MiQuery {
  allNodeArticle {
    edges {
      node {
        id
        title
        body {
          processed
        }
        created
        path {
          alias
        }
      }
    }
  }
}

     El módulo gatsby-source-drupal permite realizar, diversas configuraciones como filtros, cargas rápidas, autenticación entre Drupal y Gatsby, así como otras muchas que puedes conocer visitando su Documentación de Gatsby

     UUID vs drupal_id vs drupal_internal__nid

        Cada nodo de Drupal en la base de datos GraphQL de Gatsby tiene 3 ID únicos diferentes que debe tener en cuenta:

  • uuid: Esto lo genera Gatsby al obtener contenido y es único internamente, pero no tiene ninguna relación con Drupal.
  • drupal_id: Este es un UUID generado por Drupal, es único tanto en Gatsby como en Drupal, y debe preferirse cuando necesite una forma única de hacer referencia al contenido de Drupal. Esto asegura que la ID que se usa dentro de Gatsby sea también la misma que la que usa Drupal y hace que sea más fácil razonar
  • drupal_internal__nid: Este es el node.nid de Drupal, en la mayoría de los casos puede hacer referencia a la entidad en Drupal a través del campo drupal_id, pero en los casos en que lo necesite, puede usarlo.

     Drupal entity reference fields

        En el modelo de datos de Drupal, las piezas de contenido (entidades) pueden declarar relaciones con otras piezas de contenido utilizando lo que se conoce como referencias de entidad. Cuando Gatsby obtiene datos de Drupal, estas referencias de entidad se migran a las "relationships" de GraphQL, por lo que no ve esos campos donde podría esperarlos en el esquema GraphQL. No se preocupe, todavía están ahí, solo necesita mirar en el campo "relationships".

        Un ejemplo de relaciones, podemos encotrarlo al intentar utilizar imágenes, ya que el campo "media" permite hacer referencia a diversos tipos de contenido, como video, audio e imágenes.

Gatsby con Drupal | www.drupaladicto.com - Consultor especializado en drupal y symfony

     Añadir Material UI para modificar el aspecto de Gastby

        Para darle a nuestra aplicación algo de estilo, usaremos la popular biblioteca de Material UI component. Es un conjunto de componentes de React que implementan el lenguaje de diseño de la interfaz de usuario de Google Material.

     NOTA:
        Para evitar posibles errores, antes de instalar Material UI, asegúrate de actualizar tu versión de Gatsby Haz clic aqui

     Puedes comprobar la versión que de gatsby que tienes instalada, ejecutando el siguiente comando:

lando npm outdated

     En pantalla verás un resultado parecido a la siguiente imagen, con las versiones actuales y las que deberías actualizar:

Drupal 9 headless Gatsby  | www.drupaladicto.com - Consultor especializado en drupal y symfony

     Y para actualizar a la versión más reciente, ejecutar el siguiente comando:

lando npm i gatsby@latest

     Instalar la biblioteca de componentes de Material UI

        Si estamos ejecutando Gatsby, antes de poder instalar, tendremos que detenerlo utilizando la combinación de teclas:

Ctrl + C

        La forma más rápida de comenzar a usar Material UI con Gatsby es a través del gatsby-plugin-material-ui plugin. Para ello lo primero que tendremo que hacer es descargarlo utilizando el siguiente comando, al igual que hicimos con el gatsby-drupal-source, ya que estamos trabajando con Lando:

lando npm install --save gatsby-plugin-material-ui @mui/material @emotion/react @emotion/styled @mui/styles

     Y a continuación, abriremos el archivo de configuración de Gastsby y colocaremos el siguiente código, en el apartado de plugins, para que podamos acceder a sus opciones.

`gatsby-plugin-material-ui`,

 

Drupal 9 headless Gatsby  | www.drupaladicto.com - Consultor especializado en drupal y symfony

    Ahora podremos volver a ponerlo en funcionamiento con el comando:

lando gatsby develop --host 0.0.0.0

     Actualizando el Layout Component

        Actualice el componente Layout predeterminado, para hacer uso de los componentes Material UI Container y Box para proporcionar un diseño de página básico. Para ello, buscaremos el archivo y sustituiremos su contenido por el siguiente código, está ubicado en la ruta: src/components/layout.js

Drupal 9 headless Gatsby  | www.drupaladicto.com - Consultor especializado en drupal y symfony
import React from 'react'
import PropTypes from 'prop-types'
import Helmet from 'react-helmet'
import { StaticQuery, graphql } from 'gatsby'
import Box from '@mui/material/Box';
import Container from '@mui/material/Container';

import Navigation from './Navigation/Navigation';

const Layout = (props) => {
    const {children} = props;

    return (
        <StaticQuery
            query={graphql`
        query SiteTitleQuery {
          site {
            siteMetadata {
              title
            }
          }
        }
      `}
            render={data => (
                <>
                    <Helmet
                        title={data.site.siteMetadata.title}
                        meta={[
                            {name: 'description', content: 'Sample'},
                            {name: 'keywords', content: 'sample, something'},
                        ]}
                    >
                        <html lang="en"/>
                    </Helmet>
                    <Container maxWidth="lg">
                        <Navigation siteTitle={data.site.siteMetadata.title}/>
                        <Box component="main">
                            {children}
                        </Box>
                    </Container>
                </>
            )}
        />
    )
}

Layout.propTypes = {
    children: PropTypes.node.isRequired,
}

export default Layout;

        En el ejemplo anterior, estamos importando los componentes Box y Container del paquete @ material-ui / core y luego los usamos para envolver el contenido de cada página que usa el componente Layout.

     Agregue un componente de navegación.

        Agregue el archivo src / components / Navigation / Navigation.js. Usaremos esto como encabezado y navegación para nuestra aplicación:

Drupal 9 headless Gatsby  | www.drupaladicto.com - Consultor especializado en drupal y symfony
import React from 'react';
import { Link } from 'gatsby';
import { makeStyles } from '@mui/styles';
import AppBar from '@mui/material/AppBar';
import Button from '@mui/material/Button';
import Toolbar from '@mui/material/Toolbar';
import Typography from '@mui/material/Typography';

const useStyles = makeStyles({
    root: {
        flexGrow: 1,
    },
    menuButton: {
        marginRight: 2,
    },
    title: {
        flexGrow: 1,
    },
});

function Navigation(props) {
    const classes = useStyles();

    return (
        <AppBar position="static" className={classes.root}>
            <Toolbar>
                <Typography
                    variant="h6"
                    className={classes.title}
                >
                    {props.siteTitle}
                </Typography>
                <div>
                    <Button
                        component={Link}
                        to="/"
                        color="inherit"
                    >
                        Home
                    </Button>
                </div>
            </Toolbar>
        </AppBar>
    );
}

export default Navigation;

     Agregue un poco de estilo a la página de inicio

        Actualice la página de inicio, src / pages / index.js, para usar los componentes Paper y Typography para darle un poco más de estilo:

 

Drupal 9 headless Gatsby  | www.drupaladicto.com - Consultor especializado en drupal y symfony
import React from 'react'
import PropTypes from 'prop-types';
import { Link } from 'gatsby'
import { makeStyles } from '@mui/styles';
import Paper from '@mui/material/Paper';
import Typography from '@mui/material/Typography';
import Layout from '../components/layout'

const useStyles = makeStyles({
    root: {
      spacing: [3, 2],
      padding: '30px',
    },
});

const IndexPage = (props) => {
    const classes = useStyles();

    return (
        <Layout>
            <Paper className={classes.root}>
                <Typography variant="h2">Hi people</Typography>
                <Typography variant="subtitle1" paragraph>
                    Welcome to your new Gatsby site using <a href="https://material-ui.com">Material UI</a> for the UI.
                </Typography>
                <Typography variant="subtitle1" paragraph>
                    Now go build something great.
                </Typography>
                <Link to="/page-2/">Go to page 2</Link>
            </Paper>
        </Layout>
    );
};

IndexPage.propTypes = {
    classes: PropTypes.object.isRequired,
};

export default IndexPage;

        SI arrancamos nuevamente nuestra aplicación de Gatsby, veremos un resultado mucho más moderno y atractivo:

lando gatsby develop --host 0.0.0.0

 

Drupal 9 headless Gatsby  | www.drupaladicto.com - Consultor especializado en drupal y symfony

     Agregar una nueva página estática en Gatsby

        Para añadir una nueva página estática en Gatsby, bastará con crear un archivo con la extensión .js, y colocarlo dentro de la carpeta src/pages/. A continuación, una vez completados los datos que queremos mostrar, gatsby se encargará de generar la url para que podamos acceder a esa pagina.

        Hagamos una prueba, vamos a crear una página llamada Sobre Nosotros, para ello, abriremos nuestro IDE y crearemos el archivo correspondiente, dentro de la carpeta src/pages/sobre-nosotros.js, luego pegaremos el siguiente código y comprobaremos el resultado.

Drupal 9 headless Gatsby  | www.drupaladicto.com - Consultor especializado en drupal y symfony
import React from 'react'
import { Link } from 'gatsby'

import Layout from '../components/layout'

const SobreNosotrosPage = () => (
    <Layout>
        <h1>Sobre Nosotros</h1>
        <p>Esta aplicación es un conjunto seleccionado de las mejores recetas, cuidadosamente
            seleccionados solo para nuestros miembros.</p>
        <Link to="/">Home</Link>
    </Layout>
)

export default SobreNosotrosPage

        Suponiendo que el servidor de desarrollo de gatsby se está ejecutando, cuando guarde este archivo, Gatsby debería encontrarlo automáticamente, compilar la versión actualizada de su aplicación y hacer que la página esté disponible para su visualización.

        Para acceder a esta nueva página estática de Gatsby, escribiremos en el navegador la siguiente url:

http://gatsbymobile.lndo.site/sobre-nosotros

        El resultado, debería ser uno parecido al de la siguiente imagen.

 

Drupal 9 headless Gatsby  | www.drupaladicto.com - Consultor especializado en drupal y symfony

     Mostrar contenido dinámico en Gastby desde Drupal 9

        Ahora que tenemos el "Master en Creación de Páginas Estáticas", podremos pasar al siguiente nivel, que consistirá en la generación de listados con el contenido importado desde Drupal 9. Para esta parte

        Para generar páginas estáticas para el contenido en la base de datos GraphQL de Gatsby, necesitamos hacer un par de cosas. Primero, necesitamos consultar la base de datos y hacer una lista de qué páginas queremos que Gatsby genere en qué ruta. Para hacer esto, implementamos la API de nodo createPages () de Gatsby; consultar la base de datos GraphQL para obtener una lista de las páginas que queremos generar; y luego proporcione a Gatsby información sobre esas páginas. Esta información incluye la ruta en la que queremos que viva la página, la plantilla que queremos usar para representar el HTML de la página y suficiente información contextual para que el componente de la plantilla pueda extraer el resto de la información que necesita de la base de datos en Tiempo de construcción.

        Luego, necesitamos escribir un componente React para usar como plantilla para renderizar cada página individual, así como una consulta de página GraphQL que Gatsby ejecutará para obtener los datos necesarios para construir la página, y luego inyectar automáticamente como props.data en nuestro componente.

     Implementar la API createPages () de Gatsby

        Cuéntale a Gatsby sobre las páginas que quieres que represente implementando la API createPages (). Esto se hace exportando un createPages, con nombre funcional desde el archivo gatsby-node.js,  con nombre especial en la raíz de su proyecto. Continúe y cree el archivo si aún no existe.

Drupal 9 headless Gatsby  | www.drupaladicto.com - Consultor especializado en drupal y symfony

        Una vez abierto, copiaremos el siguiente código y lo añadiremos justo debajo del código actual.

const path = require(`path`);

exports.createPages = ({ graphql, actions }) => {
  const { createPage } = actions;

  return new Promise((resolve, reject) => {
    graphql(`
      {
        allNodeRecipe {
          edges {
            node {
              drupal_id,
              title,
              path {
                alias,
              }
            }
          }
        }
      }
    `).then(result => {
      result.data.allNodeRecipe.edges.forEach(({ node }) => {
        let path_alias;
        if (node.path.alias == null) {
          path_alias = `recipe/${node.drupal_id}`;
        } else {
          path_alias = node.path.alias;
        }

        createPage({
          // This is the path, or route, at which the page will be visible.
          path: path_alias,
          // This the path to the file that contains the React component
          // that will be used to render the HTML for the recipe.
          component: path.resolve(`./src/templates/recipe.js`),
          context: {
            // Data passed to context is available in page queries as GraphQL
            // variables.
            drupal_id: node.drupal_id,
          },
        })
      });

      resolve()
    })
  })
};

     Definir una plantilla para esta "recipe" o receta de Gatsby

        A continuación, debemos definir la plantilla que se utiliza para representar el HTML de una "recipe". Según el código anterior, esto debería estar en src/templates/recipe.js. Necesitamos exportar un componente React y una consulta de página.

        Crearemos el archivo para la plantilla, en la dirección mencionada de la línea anterior y luego pegaremos el siguiente código:

Drupal 9 headless Gatsby  | www.drupaladicto.com - Consultor especializado en drupal y symfony
import React from 'react';
import { graphql } from 'gatsby';
import Helmet from 'react-helmet';
import Paper from '@mui/material/Paper';
import Layout from '../components/layout';
import Recipe from '../components/Recipe/Recipe';
import {makeStyles} from "@mui/styles";

const useStyles = makeStyles({
    root: {
        spacing: [3, 2],
        padding: '30px',
    },
});


const RecipeTemplate = (props) => {
    const classes = useStyles();

    const { nodeRecipe: recipe } = props.data;

    return (
        <Layout>
            <Helmet
                title={`Umami - ${recipe.title}`}
                meta={[
                    {name: 'description', content: recipe.title},
                ]}
            />
            <Paper className={classes.root}>
                <Recipe
                    {...recipe}
                    category={recipe.relationships.category[0].name}
                    tags={recipe.relationships.tags}
                    instructions={recipe.instructions.processed}
                    summary={recipe.summary.processed}
                />
            </Paper>
        </Layout>
    )
};

export default RecipeTemplate;

// The $drupal_id variable here is obtained from the "context" object passed into
// the createPage() API in gatsby-node.js.
//
// Also note the use of field name aliasing in the query. This is done to
// help normalize the shape of the recipe data.
export const query = graphql`
  query RecipeTemplate($drupal_id: String!) {
    nodeRecipe(drupal_id: {eq: $drupal_id}) {
      drupal_id,
      title,
      cooking_time: field_cooking_time,
      difficulty: field_difficulty,
      ingredients: field_ingredients,
      preparation_time: field_preparation_time,
      number_of_servings: field_number_of_servings,
      instructions: field_recipe_instruction {
        processed,
      },
      summary: field_summary {
        processed,
      },
      relationships {
        category: field_recipe_category {
          name,
        }
        tags: field_tags {
          name,
        }
      }
    }
  }
`;

     Por último, crearemos el componente para mostrar el contenido individual de cada receta, dentro del listado que acabamos de construir, para ello, generaremos un nuevo archivo en la siguiente ruta: src/components/Recipe/Recipe.js

Drupal 9 headless Gatsby  | www.drupaladicto.com - Consultor especializado en drupal y symfony
import React from 'react';
import PropTypes from 'prop-types';
import ImageList from '@mui/material/ImageList';
import List from '@mui/material/List';
import ListItem from '@mui/material/ListItem';
import ListItemText from '@mui/material/ListItemText';
import Typography from '@mui/material/Typography';
import RecipeList from "../RecipeList/RecipeList";


const Recipe = (props) => (

    <>
        <Typography variant="h2" paragraph>{props.title}</Typography>
        <ImageList cols={5} cellHeight="auto">
            <ListItem>
                <ListItemText primary="Difficulty" secondary={props.difficulty} />
            </ListItem>
            <ListItem>
                <ListItemText primary="Cooking time" secondary={`${props.cooking_time} minutes`} />
            </ListItem>
            <ListItem>
                <ListItemText primary="Preparation time" secondary={`${props.preparation_time} minutes`} />
            </ListItem>
            <ListItem>
                <ListItemText primary="Category" secondary={props.category} />
            </ListItem>
            {props.tags &&
            <ListItem>
                <ListItemText primary="Tags" secondary={props.tags.map(item => item.name)}/>
            </ListItem>
            }
        </ImageList>

        <Typography variant="subtitle1">Summary:</Typography>
        <Typography variant="body1" paragraph dangerouslySetInnerHTML={{ __html: props.summary }} />

        <Typography variant="subtitle1">Ingredients:</Typography>
        <List dense={true}>
            {
                props.ingredients.map((item, index) => <ListItem key={index}>{item}</ListItem>)
            }
        </List>

        <Typography variant="subtitle1">Preparation:</Typography>
        <Typography variant="body1" paragraph dangerouslySetInnerHTML={{ __html: props.instructions }} />
        <RecipeList />
    </>
);

Recipe.propTypes = {
    title: PropTypes.string.isRequired,
    difficulty: PropTypes.string.isRequired,
    cooking_time: PropTypes.number.isRequired,
    preparation_time: PropTypes.number.isRequired,
    ingredients: PropTypes.arrayOf(PropTypes.string),
    summary: PropTypes.string.isRequired,
    instructions: PropTypes.string.isRequired,
    category: PropTypes.string.isRequired,
    tags: PropTypes.array,
};

export default Recipe;

     Cuando hayamos terminado, ejecutaremos el código para reconstruir Gastby y realizaremos la coprobación accediendo a una págiana no existente, escriendo cualquier cosa después del dominio de nuestra pagina. Con esto deberíamos ver un listado de las Recetas del Drupal 9.

lando gatsby develop --host 0.0.0.0

 

Drupal 9 headless Gatsby  | www.drupaladicto.com - Consultor especializado en drupal y symfony

        Con los cambios anteriores en su lugar, reinicie el servidor de desarrollo de Gatsby con gatsby Develop. Cuando se reconstruye el contenido estático de la aplicación, ahora debería incluir las páginas de recetas obtenidas de Drupal. Pruébelo navegando directamente a la ruta de una receta o navegando a una página 404 conocida como http: // localhost: 8000 / asdf. Gatsby tiene un truco útil en el que las páginas 404 en el servidor de desarrollo le darán una lista de todas las páginas que Gatsby conoce internamente.

Drupal 9 headless Gatsby  | www.drupaladicto.com - Consultor especializado en drupal y symfony