How to access ACF fields and the featured_media for /blog page

I have created some custom fields for /blog page and want to show those fields and featured_media for on this page.

I just do eg…


const data = state.source.get(state.router.link);
const post = state.source[data.type][data.id];  
const foo = post.acf.foo

console.log data and post and you’ll see what information is available

For featured media I’m sure there’s an examples in Mars theme… look for the Boolean in the settings file and check how that’s implemented in the code

for individual post and pages data.id exists but for /blog page (by making the page as blog page in wordpress settings) data.id doesn’t exist

const data = state.source.get('/blog');
const page = state.source[data.type][data.id];  

so I can’t get page or acf prop from it

This is what I get from state.source.get('/blog')

isArchive: true
isFetching: false
isPostArchive: true
isPostTypeArchive: true
isReady: true
items: [post objects]
link: "/blog/"
next: "/blog/page/2/"
page: 1
query: {}
route: "/blog/"
total: 51
totalPages: 6
type: "post"

So what are your acf fields attached to? Surely individual items in the blog?

The problem you’re having is caused by a limitation in the REST API of WordPress (or actually, a limitation of WordPress in general).

When querying /blog/ you will not get a post or page, but instead a collection of posts. WordPress does not know that it’s an actual page (even though it exists), so instead will return the contents of the archive.

I guess the only solution is to do a second fetch to ACF directly to retrieve the data from that page.

You could maybe do something like what I did here and create a page called eg /blog_page and manually 404 the route in the browser, but use it to get data over REST

Yes frontity only gives the collection of posts when doing state.source[data.type]. I am not sure why data.id is missing for /blog only.

As the wordpress rest endpoint is giving both the featured_media and acf fields but are missing when getting via frontity.

/blog is set to be postsPage but it is a page. Frontity should also return id for this page so we can get the page details just like other pages.

Because an archive page is not a real page, so it doesn’t have a page/post id. Even when you have set a page in WP.

So it’s normal behavior in WordPress.

But when listing the pages using wp-rest apis, I’m getting the id, featured_media, acf for the /blog, like this

{
   "id":64,
   "slug":"blog",
   "status":"publish",
   "type":"page",
   "title":{
      "rendered":"Blog"
   },
   "author":1,
   "featured_media":1637,
   "acf":{
      "title":"Blog"
   }
}

My doubt is that why is it missing in frontity source?

Because, as I explained before, when you get the posts archive (/blog) WordPress will not return the page.

The request you sent is:
/wp-json/wp/v2/pages?slug=/blog
This will obviously return the content of the ā€˜blog’ page, but does not return anything else (like the posts it should show.

The request that is actually done:
/wp-json/wp/v2/posts
This will return all the posts which is the same as WordPress does on the archive. And since you set ā€˜/blog’ as the posts archive URI, it will do this request by default (as it should).

So it’s not a bug or missing feature in Frontity, it’s how WordPress works and how Frontity implemented it to return the exact same results.

Got it. Thank you for the explanation :+1:

There is a solution though and that is through creating a custom handler.

Here are the general steps:

  1. Change the postsPage in frontity.settings to something else
  2. Create a handler for /blog/
  3. Get /blog by using
        endpoint: 'pages',
        params: {
          slug: 'blog',
          _embed: true,
        },
      });
  1. Get all your blog posts by
await api.get({
      endpoint: 'posts',
      params: {
        per_page: 100,
        _embed: true,
      },
    });
  1. Then build your own state.source.data by setting isPostArchive to true etc.

I can share more in detail if anyone is curious.

Here are some links that might help:

Here is the handler. Not tested!

const blogHandler = {
  pattern: '/blog/',
  priority: 5,
  func: async ({link, params, state, libraries}) => {

    const {api, populate, getTotalPages} = libraries.source;

    // Get the Blog Page
    // 1. search id in state or get the entity from WP REST API
    const {route, query} = libraries.source.parse(link);
    const routeData = state.source.get(route);
    if (!routeData.id) {
      //console.log(routeData);
      // Get the page
      let response = await api.get({
        endpoint: 'pages',
        params: {
          slug: 'blog',
          _embed: true,
        },
      });
      const blogPage = await populate({
        response,
        state,
      });
      console.log("Blog Page", blogPage);
    }

    // 2. Get the Blog List

    let response = await api.get({
      endpoint: 'posts',
      params: {
        per_page: 100,
        _embed: true,
      },
    });
    const firstItems = await populate({
      response,
      state,
    });
    const pages = getTotalPages(response);
    const requests = [];

    // Get the next pages
    for (let page = 2; page <= pages; page++) {
      requests.push(
          api.get({
            endpoint: 'posts',
            params: {
              per_page: 100,
              page,
              _embed: true,
            },
          }),
      );
    }
    const responses = await Promise.all(requests);

    const newItems = await Promise.all(
        responses.map((response) => populate({state, response})),
    );
    const items = firstItems.concat(newItems);

    // 4. add data to source
    const currentPageData = state.source.data['/blog/'];
    const postData = {
      isPostType: true,
      isPostArchive: true,
      type: 'page',
      items,
    };

    Object.assign(currentPageData, postData);
  },
};

export default blogHandler;