Tutorial del esquema
Tutorial del esquemaLección 16: Enviando una notificación cuando hay una nueva entrada

Lección 16: Enviando una notificación cuando hay una nueva entrada

Gato GraphQL puede ayudarnos a automatizar tareas en la aplicación, como enviar un email de notificación al admin cuando hay una nueva entrada.

En esta lección del tutorial exploraremos dos formas de lograr esto.

Consulta GraphQL para enviar un email de notificación al admin

Esta consulta GraphQL envía un email al usuario admin, notificándole de la creación de una nueva entrada en el sitio:

query GetEmailData(
  $postTitle: String!,
  $postContent: String!
  $postURL: URL!
) {
  adminEmail: optionValue(name: "admin_email")
    @export(as: "adminEmail")
 
  emailMessageTemplate: _strConvertMarkdownToHTML(
    text: """
 
There is a [new post on the site]({$postURL}):
 
**{$postTitle}**:
 
{$postContent}
 
    """
  )
  emailMessage: _strReplaceMultiple(
    search: ["{$postTitle}", "{$postContent}", "{$postURL}"],
    replaceWith: [$postTitle, $postContent, $postURL],
    in: $__emailMessageTemplate
  )
    @export(as: "emailMessage")
 
  emailSubject: _sprintf(
    string: "New post: \"%s\"",
    values: [$postTitle]
  )
    @export(as: "emailSubject")
}
 
mutation SendEmail @depends(on: "GetEmailData") {
  _sendEmail(
    input: {
      to: $adminEmail
      subject: $emailSubject
      messageAs: {
        html: $emailMessage
      }
    }
  ) {
    status
  }
}

Para enviar el email en texto plano:

  • Usa la entrada messageAs: { text: ... } en la mutación _sendEmail
  • Elimina las etiquetas HTML del contenido de la entrada usando el campo global _htmlStripTags (proporcionado por la extensión PHP Functions via Schema)

Veamos a continuación cómo disparar la ejecución de la consulta GraphQL.

Opción 1: Disparar siempre reaccionando a hooks de WordPress

Nos enganchamos a la acción de WordPress core new_to_publish, recuperamos los datos de la entrada recién creada, y ejecutamos la consulta GraphQL definida arriba contra el servidor GraphQL interno (proporcionado mediante la extensión Internal GraphQL Server):

use GatoGraphQL\InternalGraphQLServer\GraphQLServer;
use WP_Post;
 
// The GraphQL query, under var `$query`, is the one defined above
// $query = '...';
add_action(
  'new_to_publish',
  function (WP_Post $post) use ($query) {
    $variables = [
      'postTitle' => $post->post_title,
      'postContent' => $post->post_content,
      'postURL' => get_permalink($post->ID),
    ]
    GraphQLServer::executeQuery($query, $variables, 'SendEmail');
  }
);

La clase GatoGraphQL\InternalGraphQLServer\GraphQLServer no es accesible como una API externa. En su lugar, está pensada para ser usada por la aplicación mediante código PHP, para ejecutar/automatizar tareas administrativas mediante consultas GraphQL.

Esta clase proporciona 3 métodos estáticos para ejecutar consultas:

  • executeQuery: Ejecuta una consulta GraphQL
  • executeQueryInFile: Ejecuta una consulta GraphQL contenida en un archivo (.gql)
  • executePersistedQuery: Ejecuta una persisted GraphQL query (proporcionando su ID como int, o slug como string)

Esta consulta GraphQL se ejecutará cada vez que se cree una nueva entrada o, para ser más precisos, cada vez que se invoque la función wp_insert_post de WordPress (ya que esta función dispara el hook new_to_publish):

$postID = wp_insert_post([
  'post_title' => 'Hello world!'
]);

Este es también el caso cuando se ejecuta otra consulta GraphQL que ejecuta la mutación createPost (ya que su resolver, en código PHP, invoca la función wp_insert_post):

mutation CreatePost {
  createPost(input: {
    title: "Hello world!"
  }) {
    status
    postID
  }
}

El GraphQL Server (que es "externo", accedido como una API mediante HTTP) y el Internal GraphQL Server ejecutarán sus consultas aplicando su propia Schema Configuration, incluso cuando su ejecución se entrelaza.

Por ejemplo, supongamos que estamos ejecutando una consulta GraphQL contra el endpoint único, y crea una entrada ejecutando la mutación createPost. Entonces tiene lugar la siguiente secuencia de pasos:

(Externo) GraphQL ServerInternal GraphQL Server
Ejecuta la consulta GraphQL contra el endpoint único, usando su propia Schema Configuration(no activo)
Crea una entrada; esto dispara new_to_publish(no activo)
(esperando...)Reacciona al hook new_to_publish: Arranca el Internal GraphQL server, usando su propia Schema Configuration
(esperando...)Ejecuta la consulta para enviar un email
(esperando...)Envía email, fin de esa consulta
(esperando...)Apaga el servidor
Continúa la ejecución de la consulta, fin de esa consulta(no activo)
Apaga el servidor(no activo)

Opción 2: Disparar encadenando consultas GraphQL

La extensión Automatización hace que el GraphQL Server dispare un hook después de completar la ejecución de una consulta GraphQL. Esto nos permite encadenar consultas GraphQL.

Este código PHP ejecuta la operación SendEmail (consulta GraphQL definida arriba), después de que el servidor GraphQL haya ejecutado alguna otra consulta con la operación CreatePost (consulta GraphQL definida arriba):

// The GraphQL query, under var `$query`, is the one defined above
// $query = '...';
add_action(
  "gatographql__executed_query:CreatePost",
  function (Response $response) use ($query) {
    // @var string
    $responseContent = $response->getContent();
    // @var array<string,mixed>
    $responseJSON = json_decode($responseContent, true);
    $postID = $responseJSON['data']['createPost']['postID'] ?? null;
    if ($postID === null) {
      // Do nothing
      return;
    }
 
    $post = get_post($postID);
    $variables = [
      'postTitle' => $post->post_title,
      'postContent' => $post->post_content,
      'postURL' => get_permalink($post->ID),
    ]
    GraphQLServer::executeQuery($query, $variables, 'SendEmail');
  }
);

Encadenar consultas GraphQL nos permite ejecutar una sola consulta, incluso cuando se mutaron muchos recursos.

Por ejemplo, esta consulta GraphQL actualiza muchas entradas:

mutation ReplaceDomains {
  posts {
    id
    rawContent
    adaptedRawContent: _strReplace(
      search: "https://my-old-domain.com"
      replaceWith: "https://my-new-domain.com"
      in: $__rawContent
    )
    update(input: {
      contentAs: { html: $__adaptedRawContent }
    }) {
      status
      postID
    }
  }
}

Dependiendo de nuestra estrategia, podemos disparar la ejecución de una o múltiples consultas GraphQL adicionales:

Enganchándose a...Dispara número de consultas GraphQL...
post_updated (por WordPress core)Una por cada entrada actualizada
gatographql__executed_query:ReplaceDomains (por la extensión Automatización)Una en total (recibirá los datos para todas las entradas actualizadas)