rburgst / wp-graphql-wpml

a wordpress plugin that improves wpgraphql usage together with wpml
GNU General Public License v3.0
49 stars 19 forks source link

Have not found a way to fetch Posts or Post by `locale` #13

Closed retzion closed 2 years ago

retzion commented 3 years ago

Thank you to anyone who can help with this. Maybe my approach is wrong and you can tell me how you are getting the results I'm looking for.

fragment AuthorFields on User {
  name
  firstName
  lastName
  avatar {
    url
  }
}

fragment PostFields on Post {
  title
  excerpt
  slug
  date
  featuredImage {
    node {
      sourceUrl
    }
  }
  author {
    node {
      ...AuthorFields
    }
  }
  categories {
    edges {
      node {
        name
      }
    }
  }
  tags {
    edges {
      node {
        name
      }
    }
  }
}

fragment AdditionalFields on Post {
  content
  locale {
    locale
  }
  translations {
    post_title
    locale
    href
  }
  seo {
    metaDesc
    metaKeywords
    metaRobotsNofollow
    metaRobotsNoindex
    opengraphAuthor
    opengraphDescription
    focuskw
    cornerstone
    canonical
    breadcrumbs {
      text
      url
    }
    opengraphImage {
      altText
      link
      uri
      title
      sourceUrl
    }
    opengraphModifiedTime
    opengraphPublishedTime
    opengraphPublisher
    opengraphSiteName
    opengraphTitle
    opengraphType
    opengraphUrl
    title
    twitterDescription
    twitterTitle
    twitterImage {
      altText
      caption
      uri
      sourceUrl
    }
  }
}

query PostBySlug($id: ID!, $idType: PostIdType!) {
  post(id: $id, idType: $idType) {
    ...PostFields
    ...AdditionalFields
  }
}

query AllPosts($length: Int!, $after: String) {
  posts(first: $length, after: $after, where: {orderby: {field: DATE, order: DESC}}) {
    edges {
      node {
        title
        excerpt
        slug
        date
        featuredImage {
          node {
            sourceUrl
          }
        }
        author {
          node {
            name
            firstName
            lastName
            avatar {
              url
            }
          }
        }
      }
      cursor
    }
    pageInfo {
      hasNextPage
      hasPreviousPage
      startCursor
    }
  }
}
kblizeck commented 2 years ago

@rburgst Pinging this too - currently querying posts returns all posts in all languages - I need a way to query just English, or just Spanish, to allow for pagination.

rburgst commented 2 years ago

I know this is an urgent request however, at the moment I am very busy with other things.

bonellicious commented 2 years ago

following this thread

federicocappellotto97 commented 2 years ago

follow

florianschommertz commented 2 years ago

You can do this: https://DOMAIN.com/graphql?locale=en

burakkilic commented 2 years ago

You can do this: https://DOMAIN.com/graphql?locale=en

Is it really possible?

macharest commented 2 years ago

If this can help anyone, i've developped a code that allows you to filter content by WPML locale by adding a new "wpmlLanguage" in the WHERE clause of the following post types:

Keep in mind that i've developped this for my specific use case so it might not be the most optimized. It also hasn't been tested on a website with lots of content, but it works for me right now.

This code can be added to a custom plugin or in a theme's functions.php. It has been developped with WP GraphQL 1.6.12 and WPGraphQL WPML 1.0.8.

wpml-filter

/**
 * Function called by the graphql_register_types action. Used to register a new "wpmlLanguage" where clause in specific post types. This function only registers the field, it is not in charge of filtering by language.
 * Adds a wpmlLanguage field where clause to: Pages, Posts, Categories, Comments, any custom post type and any custom taxonomy.
 * @return void
 */
function leonard_graphql_register_language_where_option(){
  $connections_where_name = [
    'RootQueryToPostConnectionWhereArgs',
    'RootQueryToPageConnectionWhereArgs',
    'RootQueryToCategoryConnectionWhereArgs',
    'RootQueryToCommentConnectionWhereArgs',
  ];

  $language_field_params = [
    'wpmlLanguage' => [
      'type' => 'String',
      'description' => 'Filter by WPML language code',
    ],
  ];

  //Get all new custom post types that are available in the GraphQL schema
  $gql_valid_custom_post_types = get_post_types([
    'show_in_graphql' => true, 
    '_builtin' => false
  ], 'objects');

  //Get all new taxonomies post types that are available in the GraphQL schema
  $gql_valid_taxonomies = get_taxonomies([
    'show_in_graphql' => true, 
    '_builtin' => false
  ], 'objects');

  //Add the custom post types to the connections that require the language filter option
  foreach ($gql_valid_custom_post_types as $custom_post_type) {
    $connections_where_name[] = 'RootQueryTo' . ucwords($custom_post_type->graphql_single_name) . 'ConnectionWhereArgs';
  }

  //Add the custom taxonomies to the connections that require the language filter option
  foreach ($gql_valid_taxonomies as $custom_taxonomy) {
    $connections_where_name[] = 'RootQueryTo' . ucwords($custom_taxonomy->graphql_single_name) . 'ConnectionWhereArgs';
  }

  foreach ($connections_where_name as $connection) {
    register_graphql_fields($connection, $language_field_params);
  }
}

function leonard_graphql_action_init()
{
  add_action(
    'graphql_register_types',
    'leonard_graphql_register_language_where_option',
    10,
    0
  );
}
add_action('graphql_init', 'leonard_graphql_action_init');

/**
 * Function used to filter the query args of specific graphql request.
 * Checks if the wpmlLanguage exists in the GraphQL query WHERE clause. If so, switch the language to the specified locale.
 *
 * @param array $query_args: Array used as arguments in a WP Query request
 * @param array $args: Array containing the GraphQL query filter params
 * @return $query_args
 */
function leonard_handle_lang_filter_request($query_args, $source, $args, $context, $info)
{
  $lang = $args['where']['wpmlLanguage'];
  //If the wpmlLanguage argument exists
  if(isset($lang)){
    global $sitepress;
    //If WPML is installed
    if($sitepress){
      //Switch the current locale
      $sitepress->switch_lang($lang);
      //Remove the argument added earlier that removes all language filtering
      $query_args['suppress_wpml_where_and_join_filter'] = false;
    }
  }

  return $query_args;
}
add_filter('graphql_post_object_connection_query_args', 'leonard_handle_lang_filter_request', 110, 5);
add_filter('graphql_term_object_connection_query_args', 'leonard_handle_lang_filter_request', 110, 5);
add_filter('graphql_comment_object_connection_query_args', 'leonard_handle_lang_filter_request', 110, 5);
rburgst commented 2 years ago

@macharest this looks really good. Would you mind creating a PR so we can integrate this into this plugin?

macharest commented 2 years ago

@rburgst Sure I could probably integrate this as a PR in the next few days or so.

revnelson commented 2 years ago

@macharest This works well, thank you! In many front end clients (such as apollo or urql) this would require destroying the client instance and re-creating a new client on locale change. It seems like a more elegant solution may be to support reading a request header from the client. Something like "locale: en" for example. Then we can simply invalidate cache on locale change to get fresh queries. Is this something that's possible?

rburgst commented 2 years ago

@RevNelson I am not sure I understand, the PR https://github.com/rburgst/wp-graphql-wpml/pull/32 adds a new filter option where you can choose the language via your normal Query.

eliawk commented 2 years ago

@rburgst @macharest is it possible to implement the where also on singular item like Page and Post? for example if I want to query a page by slug I need to filter also for language in case I have the same slug on two different languages, es test and test?lang=en

andyjamesn commented 2 years ago

@rburgst @macharest is it possible to implement the where also on singular item like Page and Post? for example if I want to query a page by slug I need to filter also for language in case I have the same slug on two different languages, es test and test?lang=en

I am also looking for this solution to return a single translated post

eg: I need to retrieve a single post by its slug. In WPGraphQL there is no way to retrieve a single post by ID type slug when using Posts. This only works on singular Post

Since where and wpmlLanguage: "es" only works on pages I am unable to retrieve a single post in the specific language

Something like this would be ideal

post(id: "some/post", idType: URI, wpmlLanguage: "es") {

alancwoo commented 1 year ago

@rburgst also running into the same problem as @andyjamesn, being unable to just query a post by its slug and the desired language. I sort of see why, since slugs are meant to be unique, but in the context of wpml where slugs may be the same, it would be incredibly useful.

I'm unable to think of a solution that doesn't require two requests (first to get post and the id of its translations, second to get the desired translation).

Ririshi commented 1 year ago

I'm using NextJS with SSR, and trying to use pretty permalinks. To identify a unique post, I would like to use its URI or slug. However, using post(id: $slug, idType: SLUG) (or a CPT's equivalent) returns null for every slug that doesn't match a default language post.

In my use-case, the slugs for different languages will be derived from the translated post's title, so they will (almost certainly) be unique across languages. Having support for querying single posts based on their (translated) slug, would be great.

I could extract the language from the language code from the request URI and use it to set a language filter, if a solution like the one @andyjamesn suggested, was implemented.

Edit: I don't know how I didn't think of this earlier, but a very easy workaround is to use the multiple object version of the query and using the where filter, combined with first, which allows to get only one post, according to name (slug) and language.

posts(where: {name: $slug, wpmlLanguage: $languageCode}, first: 1) {
  nodes {
    id
  }
}
rburgst commented 1 year ago

@Ririshi this sounds like a reasonable approach for now, thanks for sharing