valu-digital / wp-graphql-polylang

WPGraphQL Polylang Extension for WordPress
https://packagist.org/packages/valu/wp-graphql-polylang
GNU General Public License v2.0
129 stars 22 forks source link

add_post_type_fields for language is not working for woocomerce products #70

Open PeteKoda opened 2 years ago

PeteKoda commented 2 years ago

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.

brianpereradap commented 2 years ago

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

PeteKoda commented 2 years ago

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!

brianpereradap commented 2 years ago

@PeteKoda thanks for the insight

H-Nguyenx commented 2 years ago

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?

PeteKoda commented 2 years ago

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

hofewo commented 2 years ago

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:

hofewo commented 2 years ago

@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"
        }
    }
})
H-Nguyenx commented 2 years ago

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 commented 2 years ago

@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.

PeteKoda commented 2 years ago

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

H-Nguyenx commented 2 years ago

@PeteKoda thank you. It works.

reqica7 commented 1 year ago

@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;
        },
    ]
);

} `