Skip to content

Instantly share code, notes, and snippets.

@bitardev
Created November 21, 2024 21:15
Show Gist options
  • Select an option

  • Save bitardev/2e13acbe8e143f1360915b5c63fed000 to your computer and use it in GitHub Desktop.

Select an option

Save bitardev/2e13acbe8e143f1360915b5c63fed000 to your computer and use it in GitHub Desktop.
Fetch Posts/Post by Name From Wordpress REST API using GraphQL
<?php
function generate_post_json($post_id) {
// Check if this is an autosave, if so, return
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
return;
}
$data = array(
'cache_revalidation_number' => rand(1,100)
);
// Define the path for the JSON file
$json_file_path = get_template_directory() . '/post-data.json';
// Generate the JSON file
file_put_contents($json_file_path, json_encode($data));
}
add_action('save_post', 'generate_post_json');
// Log the start of the fetching process
console.log("start fetching...");

// Define the base URL of the headless blog
const siteURL = "http://localhost/headless-blog";

// Define HTML tag IDs for post title, content, and date
const postTitleHtmltagID = "#documentation-section--result__post_title";
const postContentHtmltagID = "#documentation-section--result__post_content";
const postDateHtmltagID = "#documentation-section--result__post_date";

// Extract the slug from the current URL
const slug = getUrlParameter(window.location.href);

// Fetch the post data using the slug
const post = await fetchPost(siteURL, slug);

// Log the fetched post data
console.log(post);

// Update the header title with the post title
document.getElementById("headerTitle").textContent = `${post.title} | Optim Finance`;

// Create a new div element to hold the post content
const postDivHtmlTemplate = document.createElement("div");
postDivHtmlTemplate.innerHTML = post.content;

// Hide the preloader once the content is ready
document.querySelector("#preloader").classList.add('d-none');

// Append the post content to the designated HTML element
document.querySelector(postContentHtmltagID).append(postDivHtmlTemplate);

// Update the post title in the designated HTML element
document.querySelector(postTitleHtmltagID).textContent = post.title;

// Format and display the post publication date
document.querySelector(postDateHtmltagID).textContent = `Publié le ${convertDateFormat(post.date)}`;

// Log the completion of the fetching process
console.log("done");

// Function to extract the blog name from the URL
function getUrlParameter(url) {
  const urlObject = new URL(url);
  const path = urlObject.pathname;
  const segments = path.split("/");
  const blogName = segments[segments.length - 1];
  return blogName;
}

// Asynchronous function to fetch the post data from the GraphQL API
async function fetchPost(siteURL, slug) {
  const myQuery = `
        query getPostBySlug {
            postBy(slug: "${slug}") {
                featuredImage {
                    node {
                        title
                        sourceUrl
                    }
                }
                date
                title
                content
                tags {
                    nodes {
                        name
                    }
                }
            }
        }
      `;

  // Send a POST request to the GraphQL endpoint
  const res = await fetch(`${siteURL}/graphql`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      query: myQuery,
    }),
  });

  // Parse the JSON response
  const json = await res.json();

  // Return the post data
  return json.data.postBy;
}

// Function to create a template for displaying post tags
function template(tags) {
    return `
        <div class="detail d-flex justify-content-between align-items-center gap-3">
            <ul class="list-unstyled mb-0 d-flex gap-2">
                ${tags.nodes
                    .map(({ name }) => {
                    return `<li><span class="bg-D5D6D7 fs-14px px-2 py-1">${name}</span></li>`;
                    })
                    .join(" ")}
            </ul>
            <div class="d-flex gap-4 justify-content-end align-items-center">
                <a href="#" class="text-danger text-decoration-none inter-700 fs-14px d-flex justify-content-between align-items-center gap-2"><img src="assets/icon_share.svg" alt="" class="19px"> Partager</a>
                <a href="#" class="text-primary text-decoration-none inter-700 fs-14px d-flex justify-content-between align-items-center gap-2"><img src="assets/icon_save.svg" alt="" class="19px"> Enregistrer</a>
            </div>
        </div>
    `;
}

// Function to convert date string into a formatted string in French
function convertDateFormat(dateString) {
    // Step 1: Parse the date string
    const date = new Date(dateString);
    
    // Step 2: Extract date components
    const day = date.getDate();
    const monthIndex = date.getMonth(); // 0-11
    const year = date.getFullYear();
    
    // Step 3: Format the month in French
    const monthsInFrench = [
        "Janvier", "Février", "Mars", "Avril", "Mai", "Juin",
        "Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre"
    ];
    const month = monthsInFrench[monthIndex];
    
    // Step 4: Format the time
    let hours = date.getHours();
    const minutes = date.getMinutes();
    const ampm = hours >= 12 ? 'pm' : 'am';
    hours = hours % 12; // Convert to 12-hour format
    hours = hours ? hours : 12; // the hour '0' should be '12'
    const formattedTime = `${hours}:${minutes < 10 ? '0' + minutes : minutes} ${ampm}`;
    
    // Step 5: Construct the final string
    return `${day} ${month} ${year} ${formattedTime}`;
}
// Start fetching process
console.log("start fetching...");

// Define the URL of the site and the container for displaying results
const siteURL = "http://localhost/headless-blog";
const containerID = "#documentation-section--result__list";

// Define the URL for revalidation data
const revalidationURL = "http://localhost/headless-blog/wp-content/themes/twentytwentyfive/post-data.json";

// Fetch the revalidation number
const revalidation = await getRevalidationNumber(revalidationURL);

// Retrieve cached posts and revalidation number from local storage
const cachedPosts = localStorage.getItem("posts");
const cachedRevalidation = parseInt(localStorage.getItem("revalidation"));
let posts = [];

// Check if cached posts are valid
if (cachedPosts && cachedRevalidation === revalidation) {
  // Use cached posts if revalidation number matches
  posts = JSON.parse(cachedPosts);
  console.log("cached posts => ", posts);
} else {
  // Fetch fresh posts if cache is invalid
  posts = await fetchPosts(siteURL);
  const postsString = JSON.stringify(posts);
  
  // Store fetched posts and revalidation number in local storage
  localStorage.setItem("posts", postsString);
  localStorage.setItem("revalidation", parseInt(revalidation));
  console.log("fresh posts => ", posts);
}

// Render posts to the webpage
posts.posts.nodes.map((post) => {
  const postTemplate = postTemplateCard(post);
  const postDivHtmlTemplate = document.createElement('div');
  postDivHtmlTemplate.innerHTML = postTemplate;
  document.querySelector(containerID).append(postDivHtmlTemplate);
});

// Indicate that fetching is complete
console.log("fetching done...");

// Function to fetch posts from the GraphQL endpoint
async function fetchPosts(siteURL) {
  const myQuery = `
        query getAllPosts {
            posts {
                nodes {
                    slug
                    title
                    categories {
                        nodes {
                            name
                            slug
                        }
                    }
                    excerpt
                    featuredImage {
                        node {
                            title
                            sourceUrl
                        }
                    }
                    status
                    tags {
                        nodes {
                            name
                        }
                    }
                    date
                }
            }
        }
    `;

  // Make a POST request to fetch posts
  const res = await fetch(`${siteURL}/graphql`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      query: myQuery,
    }),
  });

  // Parse the response JSON
  const json = await res.json();

  // Return the posts data
  return {
    posts: json.data.posts,
  };
}

// Function to get the revalidation number from the specified URL
async function getRevalidationNumber(revalidationURL) {
  const res = await fetch(`${revalidationURL}`, {
    method: "GET",
    headers: { "Content-Type": "application/json" },
  });

  // Parse the response JSON
  const json = await res.json();

  // Return the cache revalidation number
  return json.cache_revalidation_number;
}

// Function to create a post template card
function postTemplateCard({
  categories,
  date,
  excerpt,
  featuredImage,
  slug,
  status,
  tags,
  title,
}) {
  return `
    <div class="documentation-section--result__list--item d-flex flex-column align-items-start gap-3">
        <div class="background w-100">
            <a href="blog/${slug}"><img src="${featuredImage.node.sourceUrl}" alt="${featuredImage.node.title}" class="w-100 border"></a>
        </div>
        <a href="blog/${slug}" class="fs-24px text-424242 text-decoration-none inter-700 d-flex justify-content-between flex-wrap gap-2 align-items-center">${title} <span class="fs-14px text-gray opacity-50 inter-400">${convertDateFormat(date)}</span></a>
        <p class="fs-20px text-757575 inter-400">${excerpt}</p>
        <div class="detail d-flex justify-content-between align-items-center gap-3">
            <ul class="list-unstyled mb-0 d-flex gap-2">
                ${tags.nodes.map(({ name }) => `<li><span class="bg-D5D6D7 fs-14px px-2 py-1">${name}</span></li>`).join(" ")}
            </ul>
            <div class="d-flex gap-4 justify-content-end align-items-center">
                <a href="#" class="text-danger text-decoration-none inter-700 fs-14px d-flex justify-content-between align-items-center gap-2"><img src="assets/icon_share.svg" alt="" class="19px"> Partager</a>
                <a href="#" class="text-primary text-decoration-none inter-700 fs-14px d-flex justify-content-between align-items-center gap-2"><img src="assets/icon_save.svg" alt="" class="19px"> Enregistrer</a>
            </div>
        </div>
    </div>
    `;
}

// Function to convert date format to a more readable string
function convertDateFormat(dateString) {
  const date = new Date(dateString);
  const day = date.getDate();
  const monthIndex = date.getMonth();
  const year = date.getFullYear();
  
  const monthsInFrench = [
      "Janvier", "Février", "Mars", "Avril", "Mai", "Juin",
      "Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre"
  ];
  const month = monthsInFrench[monthIndex];
  
  let hours = date.getHours();
  const minutes = date.getMinutes();
  const ampm = hours >= 12 ? 'pm' : 'am';
  hours = hours % 12;
  hours = hours ? hours : 12;
  const formattedTime = `${hours}:${minutes < 10 ? '0' + minutes : minutes} ${ampm}`;
  
  return `${day} ${month} ${year} ${formattedTime}`;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment