Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Language prefixes do not work in idType: URI queries #35

Open
4 of 8 tasks
adriandmitroca opened this issue Oct 15, 2020 · 14 comments
Open
4 of 8 tasks

Language prefixes do not work in idType: URI queries #35

adriandmitroca opened this issue Oct 15, 2020 · 14 comments
Labels
bug Something isn't working

Comments

@adriandmitroca
Copy link

adriandmitroca commented Oct 15, 2020

Hey,

I think at the current time it is literally impossible to fetch home page of non default language. Everything works fine for pages. Take look at full report listed below.

Fetching default home page

query Page {
  pageBy(uri: "/") {
    title
  }
  page(id: "/", idType: URI) {
    title
  }
}
  • Fetching using pageBy - works as expected
  • Fetching using page - works as expected

Fetching home page for selected language

Disclaimer: Expected page exists, uses directory based permalinks and is available through WordPress through URL like: wordpress-instance.com/pl

query Page {
  pageBy(uri: "/pl") {
    title
  }
  page(id: "/pl", idType: URI) {
    title
  }
}
  • Fetching using pageBy - not working at all
  • Fetching using page - not working at all

Fetching normal page for selected language

query Page {
  pageBy(uri: "/pl/kontakt") {
    title
  }
  page(id: "/pl/kontakt", idType: URI) {
    title
  }
}
  • Fetching using pageBy - works as expected
  • Fetching using page - works as expected

Fetching normal page for selected language without language prefix

query Page {
  pageBy(uri: "/kontakt") {
    title
  }
  page(id: "/kontakt", idType: URI) {
    title
  }
}
  • Fetching using pageBy - works unexpected, as Page's permalink is /pl/kontakt not /kontakt
  • Fetching using page - works unexpected, as Page's permalink is /pl/kontakt not /kontakt

Unless I am doing something wrong, I think there could be a great benefit by exposing additional language parameter under pageBy entity, alongside already existing id, pageId, and uri so that individual page could be safely retrieved in expected language. This should also fix fetching home page for different language issue at the same time.

What do you think?

PS. Excellent package by the way!

@gu-stav
Copy link

gu-stav commented Jan 5, 2021

@esamattis Could you provide a pointer here, where to start/ fix that? 🙏🏽

@adriandmitroca
Copy link
Author

@gustavpursche

Hey, I ultimately use REST API altogether with GraphQL (yes, that means multiple network requests) to fetch page IDs.

@esamattis
Copy link
Member

esamattis commented Feb 15, 2021

Thanks for the report!

The id: "/kontakt", idType: URI args are fairly new in wp-graphql and this plugin has been written before those, so there's no build-in support yet.

Quick look at the wp-graphql code points the issue here:

https://github.com/wp-graphql/wp-graphql/blob/f15bbb5044066c3d09dc96cd5c23acbade1a5727/src/Data/NodeResolver.php#L303

The frontpage logic is hard coded. We need to inject our own custom logic to detect the translated frontpages. Unfortunately the resolve_uri() does not seem have any filters which we could use. There might be a way to process the id field earlier and transform it to a format supported by the resolve_uri().

But @jasonbahl of wp-graphql has been very open to adding filters to wp-graphql to support things like this.

For example a short circuiting filter like graphql_pre_resolver_uri' at the very begining of resolve_uri would allow us to solve this like this:

	    $front_page_post = get_option('page_on_front');
        foreach (pll_languages_list('slug') as $lang_slug) {
            if ($parsed_url['path'] === "/$lang_slug") {
                $post = pll_get_post($front_page_post, $lang_slug);
                if ( $post ) {
                    return $post;
                }
            }
        }

@esamattis
Copy link
Member

esamattis commented Feb 15, 2021

We might be able to wrap the existing page resolver function with the graphql_Page_fields filter and first check if the the request is for a translated front page and then call the original resolver if not 🤔

Here's an example how to override it #38

@simplenotezy
Copy link

Ah, this would be nice. For now I will have to hard-code the frontpage slug in the code, but full support for the URI would be great.

@esamattis esamattis added the bug Something isn't working label Nov 11, 2021
@esamattis esamattis changed the title Fetching home page (and individual pages) for different language Languages prefixes do not work in idType: URI queries Nov 11, 2021
@esamattis
Copy link
Member

esamattis commented Nov 11, 2021

There's an user land based workaround by @gorkalaucirica here: #46 (comment)

Maybe we can build something like that in to this plugin.

@esamattis esamattis changed the title Languages prefixes do not work in idType: URI queries Language prefixes do not work in idType: URI queries Nov 11, 2021
@lewebsimple
Copy link

Any update on this ?

@simplenotezy
Copy link

simplenotezy commented Jan 13, 2022

A temporary workaround. This fixed it for my use-case:

/**
 * Bugfix: prevent fetching pages from other languages. Without below filter, /de/about-us would resolve
 * to the english post called /en/about-us, which will give SEO issues. It also fixes an issue if two
 * posts in two different languages had the same post_name, the wrong one would/could be resolved
 * depending on what language you was requesting
 *
 * https://github.com/valu-digital/wp-graphql-polylang/issues/35
 */

add_filter( 'request', function ( $query_args) {
    if(isset($query_args['uri']) && $query_args['uri'] && isset($query_args['lang'])) {
        $explodedUri = explode('/', $query_args['uri']);
        if($explodedUri[0] !== $query_args['lang']) {
            // query was made without language slug in URI
            return $query_args;
        }

        unset($explodedUri[0]); // remove language slug from URL
        $uriWithoutLang = implode('/', $explodedUri); // rebuild the URI without the slug in front

        if(function_exists('pll_get_post_language')) {
            $post = get_page_by_path($uriWithoutLang);

            if(!$post || $post && pll_get_post_language($post->ID) !== $query_args['lang']) {
                $query_args = [];
            }
        }
    }
    return $query_args;
}, 	10, 2);

It's not the prettiest, but it does it's job. We had an issue if two pages was called the same, e.g. /de/about us and /en/about-us, when requesting the /de/about-us, it would resolve the english version.

Also we had an issue where if you requested an english slug, on the german prefix, if a german page did not exist with that name, it would return the english page - on the german site. Above filter also fixes that.

@MKlblangenois
Copy link

Hey !

Any update about this issue ? I'm facing the same issue, with postQuery and contentNodeQuery

With "Remove ID from slug home" settings checked in Polylang :
Capture d’écran 2022-07-08 à 14 06 58
Capture d’écran 2022-07-08 à 14 07 11

Without "Remove ID from slug home" settings checked in Polylang :
Capture d’écran 2022-07-08 à 14 05 45
Capture d’écran 2022-07-08 à 14 06 00

@maximilliangeorge
Copy link

I am also experiencing this issue. It is not possible to request the translated homepage via a /<lang> uri, however /<lang>/some/path works just fine.

@maximilliangeorge
Copy link

maximilliangeorge commented May 22, 2023

Needs some polish but I think this will work for querying nodeByUri.

<?php

add_filter('graphql_resolve_field', function ($result, $source, $args, $context, $info, $type_name, $field_key, $field, $field_resolver) {

  if ($type_name === 'RootQuery' && $field_key === 'nodeByUri') {

    // explode uri and get only the middle part

    $exploded = explode('/', $args['uri']);
    $lang = $exploded[1];

    // if $exploded length is more than 3 it means that the uri is not a front page

    if (count($exploded) > 3) {
      return $result;
    }

    // if $exploded length is less than 2 it means that the uri is not a front page 

    if (count($exploded) < 2) {
      return $result;
    }

    // check if language exists in pll

    $valid_languages = pll_languages_list();

    if (in_array($lang, $valid_languages)) {
      
      // get translated post object

      $homepage_id = get_option('page_on_front');
      $frontpage = pll_get_post($homepage_id, $lang);
      $page = get_post($frontpage);

      // 💖

      $result = new \WPGraphQL\Model\Post($page);

    }

  }

  return $result;

}, 10, 9);

@assef
Copy link

assef commented Nov 19, 2023

I've improved @maximilliangeorge code, this way I was able to solve all my problems. To make this work you have to query using NodeByUri and pass the URL always with a leading slash ( / ).

My use case was:

  • Home Page:
    • /
    • /en
  • Other Pages (E.g.: About Us)
    • /slug
    • /en/slug

Also, after getting the basic infos I needed from the page, I've used it's ID to query all the other infos (I'm querying each components props with its own query), which allowed me to use the Page query type.

add_filter('graphql_resolve_field', function ($result, $source, $args, $context, $info, $type_name, $field_key, $field, $field_resolver) {
  if ($type_name === 'RootQuery' && $field_key === 'nodeByUri') {
		// if is defaultLanguage home returns
		if($args["uri"] === "/") {
			return $result;
		}
		
		// explode uri and get only the first string
    $exploded = explode('/', substr($args['uri'], 1));
    $lang = $exploded[0];

    // if $exploded length is different than 1 (["slug"]) there's no chance to be a different language home
    if (count($exploded) != 1) {
      return $result;
    }

    // check if $lang is really a polylng aviable language (if not, it can be a normal page)
    $valid_languages = pll_languages_list();

    if (in_array($lang, $valid_languages)) {
      // get homepage ID in default language
      $homepage_id = get_option('page_on_front');
			// get all translations for homepage
			$translations = pll_get_post_translations($homepage_id);

			// get requested lang page ID or null if not available
			$post_id = $translations[$lang] ?? null;

			// checks if requested lang homePage exists and is published
			if (!$post_id || get_post_status($post_id) !== "publish") {
					return null;
			}

			// returns pageObject
			return new \WPGraphQL\Model\Post(
					\WP_Post::get_instance($post_id)
			);
    }
  }

  return $result;
}, 10, 9);

Edit:
Improved the code to check if page is published because I had problems with draft pages.

@maximilliangeorge
Copy link

maximilliangeorge commented Mar 11, 2024

@assef Thanks, I was having trouble with my aforementioned solution after upgrading WPGraphQL. However, your altered solution above works! Thanks for saving me a bunch of time.

@LarsEjaas
Copy link

I've improved @maximilliangeorge code, this way I was able to solve all my problems. To make this work you have to query using NodeByUri and pass the URL always with a leading slash ( / ).

My use case was:

  • Home Page:

    • /
    • /en
  • Other Pages (E.g.: About Us)

    • /slug
    • /en/slug

Also, after getting the basic infos I needed from the page, I've used it's ID to query all the other infos (I'm querying each components props with its own query), which allowed me to use the Page query type.

add_filter('graphql_resolve_field', function ($result, $source, $args, $context, $info, $type_name, $field_key, $field, $field_resolver) {
  if ($type_name === 'RootQuery' && $field_key === 'nodeByUri') {
		// if is defaultLanguage home returns
		if($args["uri"] === "/") {
			return $result;
		}
		
		// explode uri and get only the first string
    $exploded = explode('/', substr($args['uri'], 1));
    $lang = $exploded[0];

    // if $exploded length is different than 1 (["slug"]) there's no chance to be a different language home
    if (count($exploded) != 1) {
      return $result;
    }

    // check if $lang is really a polylng aviable language (if not, it can be a normal page)
    $valid_languages = pll_languages_list();

    if (in_array($lang, $valid_languages)) {
      // get homepage ID in default language
      $homepage_id = get_option('page_on_front');
			// get all translations for homepage
			$translations = pll_get_post_translations($homepage_id);

			// get requested lang page ID or null if not available
			$post_id = $translations[$lang] ?? null;

			// checks if requested lang homePage exists and is published
			if (!$post_id || get_post_status($post_id) !== "publish") {
					return null;
			}

			// returns pageObject
			return new \WPGraphQL\Model\Post(
					\WP_Post::get_instance($post_id)
			);
    }
  }

  return $result;
}, 10, 9);

Edit: Improved the code to check if page is published because I had problems with draft pages.

I really love your solution: Thanks so much for sharing! 🙏

I sometimes get empty strings in the $exploded variable, so I rewrote the code like this:

// explode uri and get only the first string
$exploded = explode('/', substr($args['uri'], 1));
// Filter out empty strings
$exploded = array_filter($exploded, function ($value) {
return strlen($value) > 0;
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

9 participants