wp-graphql-polylang icon indicating copy to clipboard operation
wp-graphql-polylang copied to clipboard

add_post_type_fields for language is not working for woocomerce products

Open PeteKoda opened this issue 3 years ago • 12 comments

I am trying to access the translations of products and the current language but its not working. I searched for the get_allowed_post_types and I think there is a timing error so the product post type is not fetched. I don't know if this is a wp-graphql-polylang problem or a wp-graphql-woocomerce but I had to call again the add_post_type_fields in my function theme to make the language and the translation work for them.

PeteKoda avatar Feb 01 '22 10:02 PeteKoda

@PeteKoda can you explain a bit more about the workaround? I'm also facing the same issue.

brianpereradap avatar Jun 01 '22 11:06 brianpereradap

Hey @brianpereradap , i tried adding the translations field to the product connection but it stopped working for me so I just created an API call that fetches the available translations for a single product. So it's a workaround but not a proper one, I will investigate this in more depth for a new project of mine, if I find a better solution I will let you know!

PeteKoda avatar Jun 01 '22 11:06 PeteKoda

@PeteKoda thanks for the insight

brianpereradap avatar Jun 01 '22 11:06 brianpereradap

Hi @PeteKoda, I'm trying to use this plugins to fetch all the products in one particular language. The WPgraphql does not support this query by default. Can you help me?

H-Nguyenx avatar Oct 31 '22 12:10 H-Nguyenx

Hi @H-Nguyenx. You can fetch all the products of a particular language with this plugin by using the product category query and the language as the where parameter. If you want to fetch it from the root product query you need to register the language fields to it. If you describe exactly the problem you are facing I might be able to help you! I am attaching a screenshot of what I am referring in the fist comment of the issue, if it is what you need let me know! image

PeteKoda avatar Oct 31 '22 13:10 PeteKoda

Hi all, it's true that fetching the products via the Product Category query with a Language parameter works, however this shouldn't be the preferred way of querying by language in my opinion. As this brings a few complications:

  • The output now returns the products classified per product category
  • This makes server side (via GraphQL) filtering/sorting of products not work as intended anymore, as the products are now filtered and sorted per product category. When the products of these seperated product categories are concat together, they will not be sorted correctly (as they're only sorted per product category). This makes sorting via GraphQL not viable anymore and requires client side sorting on top of it.
  • In my particular environment most parameters for the Query > Product Category > Products > where { ... } clause do not work at all anymore. Where as in the regular Query > Products > where { ... } clause they work fine. I'm not sure if this is a specific problem in my environment. The parameters that don't work anymore are First:, After: etc.

hofewo avatar Oct 31 '22 17:10 hofewo

@PeteKoda @brianpereradap @H-Nguyenx

I just inspected the code and basicly the plugin makes the "where: (language: XX)" clause available for the post types that are supported by default by the WPGraphQL plugin (via the get_allowed_post_types() function or "graphql_post_entities_allowed_post_types" filter hook). We basicly just have to hook the "product" post type in there and the "where: language = xx" clause will be available in the Products query without having to query Product Category first!

A very simple solution would be to install the "WPGraphQL Enable All Post Types" plugin! https://github.com/DalkMania/wp-graphql-cpt

This allows for querying products via the following scheme:

products(where: {
    taxonomyFilter: {
        filters: {
            taxonomy: LANGUAGE,
            terms: "EN"
        }
    }
})

hofewo avatar Oct 31 '22 18:10 hofewo

Hi @H-Nguyenx. You can fetch all the products of a particular language with this plugin by using the product category query and the language as the where parameter. If you want to fetch it from the root product query you need to register the language fields to it. If you describe exactly the problem you are facing I might be able to help you! I am attaching a screenshot of what I am referring in the fist comment of the issue, if it is what you need let me know! image

I do not know how to register the language fields to root query products. Please show me how to do it!

H-Nguyenx avatar Nov 03 '22 10:11 H-Nguyenx

@PeteKoda @brianpereradap @H-Nguyenx

I just inspected the code and basicly the plugin makes the "where: (language: XX)" clause available for the post types that are supported by default by the WPGraphQL plugin (via the get_allowed_post_types() function or "graphql_post_entities_allowed_post_types" filter hook). We basicly just have to hook the "product" post type in there and the "where: language = xx" clause will be available in the Products query without having to query Product Category first!

A very simple solution would be to install the "WPGraphQL Enable All Post Types" plugin! https://github.com/DalkMania/wp-graphql-cpt

This allows for querying products via the following scheme:

products(where: {
    taxonomyFilter: {
        filters: {
            taxonomy: LANGUAGE,
            terms: "EN"
        }
    }
})

Thank you! It works. I wonder if installing this plugin is going to have an impact on wordpress server.

H-Nguyenx avatar Nov 03 '22 10:11 H-Nguyenx

add_post_type_fields(get_post_type_object('product'));

function add_post_type_fields(\WP_Post_Type $post_type_object) { if (!pll_is_translated_post_type($post_type_object->name)) { return; }

    $type = ucfirst($post_type_object->graphql_single_name);

    register_graphql_fields("RootQueryTo${type}ConnectionWhereArgs", [
        'language' => [
            'type' => 'LanguageCodeFilterEnum',
            'description' => "Filter by ${type}s by language code (Polylang)",
        ],
        'languages' => [
            'type' => [
                'list_of' => [
                    'non_null' => 'LanguageCodeEnum',
                ],
            ],
            'description' => "Filter ${type}s by one or more languages (Polylang)",
        ],
    ]);

    register_graphql_fields("Create${type}Input", [
        'language' => [
            'type' => 'LanguageCodeEnum',
        ],
    ]);

    register_graphql_fields("Update${type}Input", [
        'language' => [
            'type' => 'LanguageCodeEnum',
        ],
    ]);

    register_graphql_field(
        $post_type_object->graphql_single_name,
        'language',
        [
            'type' => 'Language',
            'description' => __('Polylang language', 'wpnext'),
            'resolve' => function (
                \WPGraphQL\Model\Post $post,
                $args,
                $context,
                $info
            ) {
                $fields = $info->getFieldSelection();
                $language = [
                    'name' => null,
                    'slug' => null,
                    'code' => null,
                ];

                $post_id = $post->ID;

                // The language of the preview post is not set at all so we
                // must get the language using the original post id
                if ($post->isPreview) {
                    $post_id = wp_get_post_parent_id($post->ID);
                }

                $slug = pll_get_post_language($post_id, 'slug');

                if (!$slug) {
                    return null;
                }

                $language['code'] = $slug;
                $language['slug'] = $slug;
                $language['id'] = Relay::toGlobalId('Language', $slug);

                if (isset($fields['name'])) {
                    $language['name'] = pll_get_post_language(
                        $post_id,
                        'name'
                    );
                }

                if (isset($fields['locale'])) {
                    $language['locale'] = pll_get_post_language(
                        $post_id,
                        'locale'
                    );
                }

                return $language;
            },
        ]
    );

}

This is what I am doing to expose the language to the product query, it is basically a copy paste from the plugin

PeteKoda avatar Nov 03 '22 10:11 PeteKoda

@PeteKoda thank you. It works.

H-Nguyenx avatar Nov 04 '22 16:11 H-Nguyenx

@PeteKoda I found a solution for this. So as you can see you added a type to support the post type "product" but you also need to query into products so here is an example : `<?php use GraphQLRelay\Relay; //On init to add post type product add_action('init', 'addProductLanguage');

function addProductLanguage(){ //add query args to the product query add_filter( 'graphql_map_input_fields_to_product_query', 'map_language_to_product_query_args', 10, 2 );

add_post_type_fields(get_post_type_object( 'product' ));

} //same logic as the plugin function map_language_to_product_query_args( array $query_args, array $where_args ) { $lang = ''; if ( isset($where_args['languages']) && is_array($where_args['languages']) && !empty($where_args['languages']) ) { $langs = $where_args['languages']; unset($where_args['languages']); $lang = implode(',', $langs); } elseif (isset($where_args['language'])) { $lang = $where_args['language']; unset($where_args['language']);

			if ('all' === $lang) {
					// No need to do anything. We show all languages by default
					return $query_args;
			}

			if ('default' === $lang) {
					$lang = pll_default_language('slug');
			}
	} else {
			return $query_args;
	}

	$query_args['lang'] = $lang;

	return $query_args;

} //same logic as plugin //Only change \WPGraphQL\Model\Post to \WPGraphQL\WooCommerce\Model\Product function add_post_type_fields(\WP_Post_Type $post_type_object) { if (!pll_is_translated_post_type($post_type_object->name)) { return; }

$type = ucfirst($post_type_object->graphql_single_name);

register_graphql_fields("RootQueryTo${type}ConnectionWhereArgs", [
	'language' => [
		'type' => 'LanguageCodeFilterEnum',
		'description' => "Filter by ${type}s by language code (Polylang)",
	],
	'languages' => [
		'type' => [
			'list_of' => [
				'non_null' => 'LanguageCodeEnum',
			],
		],
		'description' => "Filter ${type}s by one or more languages (Polylang)",
	],
]);

register_graphql_fields("Create${type}Input", [
	'language' => [
		'type' => 'LanguageCodeEnum',
	],
]);

register_graphql_fields("Update${type}Input", [
	'language' => [
		'type' => 'LanguageCodeEnum',
	],
]);

register_graphql_field(
	$post_type_object->graphql_single_name,
	'language',
	[
		'type' => 'Language',
		'description' => __('Polylang language', 'wpnext'),
		'resolve' => function (
			\WPGraphQL\WooCommerce\Model\Product $post,
			$args,
			$context,
			$info
		) {
			$fields = $info->getFieldSelection();
			$language = [
				'name' => null,
				'slug' => null,
				'code' => null,
			];

			$post_id = $post->ID;

			// The language of the preview post is not set at all so we
			// must get the language using the original post id
			if ($post->isPreview) {
				$post_id = wp_get_post_parent_id($post->ID);
			}

			$slug = pll_get_post_language($post_id, 'slug');

			if (!$slug) {
				return null;
			}

			$language['code'] = $slug;
			$language['slug'] = $slug;
			$language['id'] = Relay::toGlobalId('Language', $slug);

			if (isset($fields['name'])) {
				$language['name'] = pll_get_post_language(
					$post_id,
					'name'
				);
			}

			if (isset($fields['locale'])) {
				$language['locale'] = pll_get_post_language(
					$post_id,
					'locale'
				);
			}

			return $language;
		},
	]
);

register_graphql_field(
		$post_type_object->graphql_single_name,
		'translation',
		[
				'type' => $type,
				'description' => __(
						'Get specific translation version of this object',
						'wp-graphql-polylang'
				),
				'args' => [
						'language' => [
								'type' => [
										'non_null' => 'LanguageCodeEnum',
								],
						],
				],
				'resolve' => function (
						\WPGraphQL\WooCommerce\Model\Product $post,
						array $args
				) {
						$translations = pll_get_post_translations($post->ID);
						$post_id = $translations[$args['language']] ?? null;

						if (!$post_id) {
								return null;
						}

						return new \WPGraphQL\WooCommerce\Model\Product(
							$post_id
						);
				},
		]
);

register_graphql_field(
	$post_type_object->graphql_single_name,
	'translations',
	[
		'type' => [
			'list_of' => $type,
		],
		'description' => __(
			'List all translated versions of this post',
			'wp-graphql-polylang'
		),
		'resolve' => function (\WPGraphQL\WooCommerce\Model\Product $post) {
			$posts = [];

			if ($post->isPreview) {
					$parent = wp_get_post_parent_id($post->ID);
					$translations = pll_get_post_translations($parent);
			} else {
					$translations = pll_get_post_translations($post->ID);
			}

			foreach ($translations as $lang => $post_id) {
				$translation = \WP_Post::get_instance($post_id);

				if (!$translation) {
						continue;
				}

				if (is_wp_error($translation)) {
						continue;
				}

				if ($post->ID === $translation->ID) {
						continue;
				}

				// If fetching preview do not add the original as a translation
				if ($post->isPreview && $parent === $translation->ID) {
						continue;
				}

				$model = new \WPGraphQL\WooCommerce\Model\Product($translation->ID);

				// If we do not filter out privates here wp-graphql will
				// crash with 'Cannot return null for non-nullable field
				// Post.id.'. This might be a wp-graphql bug.
				// Interestingly only fetching the id of the translated
				// post caused the crash. For example title is ok even
				// without this check
				if ($model->is_private()) {
						continue;
				}

				$posts[] = $model;
			}

			return $posts;
		},
	]
);

} `

reqica7 avatar Mar 22 '23 00:03 reqica7