mirror of
https://github.com/wp-graphql/wp-graphql-woocommerce.git
synced 2026-08-14 12:53:44 +02:00
fix: product variation duplicate fields removed (#957)
* fix: product variation duplicate fields removed * chore: Linter compliances met * chore: Linter compliances met * chore: Linter compliances met * chore: Linter compliances met * chore: Linter compliances met * chore: Linter compliances met * devops: CI compliances met * devops: unneeded access function removed * devops: unneeded access function removed * devops: unneeded access function removed
This commit is contained in:
@@ -52,6 +52,19 @@ wp config create \
|
||||
--quiet \
|
||||
--allow-root
|
||||
|
||||
# Install WP if not yet installed
|
||||
if ! $( wp core is-installed --allow-root ); then
|
||||
echo "Installing WordPress..."
|
||||
wp core install \
|
||||
--path="${WP_ROOT_FOLDER}" \
|
||||
--url="${WORDPRESS_URL}" \
|
||||
--title='Test' \
|
||||
--admin_user="${ADMIN_USERNAME}" \
|
||||
--admin_password="${ADMIN_PASSWORD}" \
|
||||
--admin_email="${ADMIN_EMAIL}" \
|
||||
--allow-root
|
||||
fi
|
||||
|
||||
# Use alternative database export for WordPress 6.8+ to avoid MariaDB SSL issues
|
||||
if [[ "${WP_VERSION}" == "6.8"* ]]; then
|
||||
echo "Using alternative database export method for WordPress 6.8+"
|
||||
|
||||
Generated
+6
-6
@@ -2046,16 +2046,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/string",
|
||||
"version": "v6.4.26",
|
||||
"version": "v6.4.30",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/string.git",
|
||||
"reference": "5621f039a71a11c87c106c1c598bdcd04a19aeea"
|
||||
"reference": "50590a057841fa6bf69d12eceffce3465b9e32cb"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/string/zipball/5621f039a71a11c87c106c1c598bdcd04a19aeea",
|
||||
"reference": "5621f039a71a11c87c106c1c598bdcd04a19aeea",
|
||||
"url": "https://api.github.com/repos/symfony/string/zipball/50590a057841fa6bf69d12eceffce3465b9e32cb",
|
||||
"reference": "50590a057841fa6bf69d12eceffce3465b9e32cb",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2111,7 +2111,7 @@
|
||||
"utf8"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/string/tree/v6.4.26"
|
||||
"source": "https://github.com/symfony/string/tree/v6.4.30"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -2131,7 +2131,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-09-11T14:32:46+00:00"
|
||||
"time": "2025-11-21T18:03:05+00:00"
|
||||
},
|
||||
{
|
||||
"name": "szepeviktor/phpstan-wordpress",
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
<!--
|
||||
title: "WPGraphQL for WooCommerce"
|
||||
description: "Comprehensive documentation for WPGraphQL for WooCommerce (WooGraphQL) - the WPGraphQL extension that integrates WooCommerce with GraphQL for headless e-commerce solutions."
|
||||
author: "Geoff Taylor"
|
||||
keywords: "WooGraphQL, WPGraphQL, WooCommerce, GraphQL, headless, e-commerce"
|
||||
-->
|
||||
|
||||
# WPGraphQL for WooCommerce
|
||||
|
||||
WPGraphQL for WooCommerce (WooGraphQL) is a free, open-source WordPress plugin that extends the WPGraphQL plugin, allowing you to access WooCommerce data through GraphQL queries and mutations.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
### Getting Started
|
||||
|
||||
- [Installation](./installation.md)
|
||||
- [Settings](./settings.md)
|
||||
- [Configuring GraphQL Client For the User Session](./configuring-graphql-client-for-user-session.md)
|
||||
- [Handling User Authentication](./handling-user-authentication.md)
|
||||
|
||||
### WooGraphQL
|
||||
|
||||
- [Routing by URI](./routing-by-uri.md)
|
||||
- [Using Product Data](./using-product-data.md)
|
||||
- [Creating Session Provider and using Cart Mutations](./handling-user-session-and-using-cart-mutations.md)
|
||||
- [Using Cart Data](./using-cart-data.md)
|
||||
- [Harmonizing with WordPress](./harmonizing-with-wordpress.md)
|
||||
- [Using Checkout Mutation + Order Mutations](./using-checkout-mutation-and-order-mutations.md)
|
||||
- [Using Order Data](./using-order-data.md)
|
||||
- [Using Customer Data + Mutations](./using-customer-data-and-mutations.md)
|
||||
|
||||
### WooGraphQL Pro
|
||||
|
||||
For premium WooCommerce extension support (Subscriptions, Composite Products, Bundles, Add-ons), see the [WooGraphQL Pro Documentation](https://woographql.com/docs/woographql-pro).
|
||||
|
||||
### Contributing
|
||||
|
||||
- [Testing Quick-Start Guide](./testing-quick-start.md)
|
||||
- [Local CLI Testing](./local-testing.md)
|
||||
- [Development with Docker](./development-w-docker.md)
|
||||
- [CLI Testing with Docker](./testing-w-docker.md)
|
||||
|
||||
## Introduction
|
||||
|
||||
WooGraphQL brings the power of GraphQL to WooCommerce, enabling developers to build modern, performant headless e-commerce applications. With WooGraphQL, you can:
|
||||
|
||||
- Query products, categories, tags, and attributes
|
||||
- Manage shopping carts with session-based mutations
|
||||
- Handle user authentication and customer accounts
|
||||
- Process checkouts and manage orders
|
||||
- Access WooCommerce settings and configurations
|
||||
|
||||
Whether you're building a React, Vue, or any other frontend application, WooGraphQL provides a flexible and efficient API to interact with your WooCommerce store.
|
||||
|
||||
### Key Features
|
||||
|
||||
- **Complete WooCommerce Integration**: Access all WooCommerce data types including products, orders, customers, coupons, and more
|
||||
- **Session Management**: Built-in support for guest and authenticated user sessions
|
||||
- **Cart Operations**: Full cart functionality with add, remove, update, and clear operations
|
||||
- **Checkout Process**: Complete checkout mutation with payment gateway integration
|
||||
- **Extensible**: Hooks and filters to customize the schema and responses
|
||||
|
||||
### Requirements
|
||||
|
||||
- WordPress 5.6+
|
||||
- WooCommerce 6.0+
|
||||
- WPGraphQL 1.14+
|
||||
- PHP 7.4+
|
||||
|
||||
Get started by following the [Installation Guide](./installation.md).
|
||||
@@ -1,9 +1,9 @@
|
||||
---
|
||||
<!--
|
||||
title: "Configuring GraphQL Client for User Session"
|
||||
description: "A step-by-step guide on how to configure your GraphQL client to handle user sessions, authentication, and more when working with WPGraphQL for WooCommerce and WPGraphQL."
|
||||
keywords: "WooGraphQL, WPGraphQL, WooCommerce, GraphQL, user session, authentication, configuration, client"
|
||||
author: "Geoff Taylor"
|
||||
---
|
||||
-->
|
||||
|
||||
# Configuring a GraphQL Client for WooCommerce User Session Management
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
---
|
||||
<!--
|
||||
title: "Development w/ Docker"
|
||||
author: "Geoff Taylor"
|
||||
description: "A simple to follow guide on using the WPGraphQL for WooCommerce App Docker Image."
|
||||
keywords: ""
|
||||
---
|
||||
-->
|
||||
|
||||
# Coming Soon
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
---
|
||||
<!--
|
||||
title: "Handling User Authentication with WooGraphQL"
|
||||
description: "Learn how to handle user authentication in your headless WooCommerce application using WPGraphQL for WooCommerce and WPGraphQL for secure and seamless user experiences."
|
||||
keywords: "WooGraphQL, WPGraphQL, WooCommerce, GraphQL, user authentication, login, register, secure, headless"
|
||||
author: "Geoff Taylor"
|
||||
---
|
||||
-->
|
||||
|
||||
# Handling User Authentication
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
---
|
||||
<!--
|
||||
title: "Handling User Session and Using Cart Mutations with WooGraphQL"
|
||||
description: "Discover how to manage user sessions and perform cart mutations using WPGraphQL for WooCommerce and WPGraphQL in your headless WooCommerce application for a smooth shopping experience."
|
||||
keywords: "WooGraphQL, WPGraphQL, WooCommerce, GraphQL, user session, cart mutations, headless, shopping experience"
|
||||
author: "Geoff Taylor"
|
||||
---
|
||||
-->
|
||||
|
||||
# Handling User Session and Using Cart Mutations
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
---
|
||||
<!--
|
||||
title: "Harmonizing with WordPress"
|
||||
description: "Learn how to create a secure checkout button and understand the security behind the URL validation."
|
||||
keywords: "WooGraphQL, WooCommerce, checkout functionality, secure checkout, session transferring URLs, nonce generation, session meta, URL validation, Client Session ID, UpdateSession mutation, GraphQL, cart page, WordPress, frontend security"
|
||||
author: "Geoff Taylor"
|
||||
---
|
||||
-->
|
||||
|
||||
# Harmonizing with WordPress
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
---
|
||||
<!--
|
||||
title: "WPGraphQL for WooCommerce Installation Guide"
|
||||
author: "Geoff Taylor"
|
||||
description: "Step-by-step instructions to install and set up WooGraphQL, the WPGraphQL extension that integrates WooCommerce with GraphQL for headless e-commerce solutions."
|
||||
keywords: "WooGraphQL, WPGraphQL, WooCommerce, GraphQL, installation, setup, headless e-commerce"
|
||||
---
|
||||
-->
|
||||
|
||||
# Installation
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
---
|
||||
<!--
|
||||
title: "Local Testing"
|
||||
author: "Geoff Taylor"
|
||||
description: "An extensive guide on local testing w/ WooGraphQL.",
|
||||
description: "An extensive guide on local testing w/ WooGraphQL."
|
||||
keywords: ""
|
||||
---
|
||||
-->
|
||||
|
||||
# Local CLI Testing
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
---
|
||||
<!--
|
||||
title: "Routing by URI with WooGraphQL"
|
||||
description: "Discover how to implement routing by URI in your headless WooCommerce application using WPGraphQL for WooCommerce and WPGraphQL for efficient and user-friendly navigation."
|
||||
keywords: "WooGraphQL, WPGraphQL, WooCommerce, GraphQL, routing, URI, headless, navigation"
|
||||
author: "Geoff Taylor"
|
||||
---
|
||||
-->
|
||||
|
||||
# Routing By URI
|
||||
|
||||
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
---
|
||||
<!--
|
||||
title: "WPGraphQL for WooCommerce Settings Guide"
|
||||
description: "Learn how to configure and manage WPGraphQL for WooCommerce settings to optimize the integration of WooCommerce with WPGraphQL for your headless e-commerce solution."
|
||||
keywords: "WooGraphQL, WPGraphQL, WooCommerce, GraphQL, settings, configuration, headless e-commerce"
|
||||
author: "Geoff Taylor"
|
||||
---
|
||||
-->
|
||||
|
||||
# WPGraphQL for WooCommerce Settings
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
---
|
||||
<!--
|
||||
title: "Testing (Quick-Start Guide)"
|
||||
author: "Geoff Taylor"
|
||||
description: "A simple guide to get started testing with WooGraphQL."
|
||||
keywords: ""
|
||||
---
|
||||
-->
|
||||
|
||||
# Testing (Quick-Start Guide)
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
---
|
||||
<!--
|
||||
title: "Testing w/ Docker"
|
||||
author: "Geoff Taylor"
|
||||
description: "An extensive guide on testing w/ WPGraphQL for WooCommerce + Docker."
|
||||
keywords: ""
|
||||
---
|
||||
-->
|
||||
|
||||
# Coming Soon
|
||||
|
||||
|
||||
+1
-4
@@ -20,10 +20,7 @@
|
||||
|
||||
## WooGraphQL Pro
|
||||
|
||||
- [Using Subscription Data + Mutations](using-subscription-data-and-mutations.md)
|
||||
- [Using Composite Product Data + Mutations](using-composite-product-data-and-mutations.md)
|
||||
- [Using Product Bundle Data + Mutations](using-product-bundle-data-and-mutations.md)
|
||||
- [Using Product Add-ons Data + Mutations](using-product-addons-data-and-mutations.md)
|
||||
For documentation on using premium WooCommerce extensions with WooGraphQL, see the [WooGraphQL Pro Documentation](https://woographql.com/docs/woographql-pro).
|
||||
|
||||
## Extending WooGraphQL
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
---
|
||||
<!--
|
||||
title: "Using Cart Data with WooGraphQL"
|
||||
description: "Learn how to retrieve and utilize cart data in your headless WooCommerce application with WPGraphQL for WooCommerce and WPGraphQL, enabling seamless shopping experiences for your customers."
|
||||
keywords: "WooGraphQL, WPGraphQL, WooCommerce, GraphQL, cart data, headless, shopping experience"
|
||||
author: "Geoff Taylor"
|
||||
---
|
||||
-->
|
||||
|
||||
# Using Cart Data
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
---
|
||||
<!--
|
||||
title: "Using Checkout Mutation + Order Mutations with WooGraphQL"
|
||||
description: "Learn how to handle repeat shoppers with existing payment methods using the `checkout` mutation, and side-stepping WooCommerce's management of the session completely by using the `createOrder` mutation."
|
||||
keywords: "WooGraphQL, WPGraphQL, WooCommerce, GraphQL, checkout mutation, createOrder mutation, session management"
|
||||
author: "Geoff Taylor"
|
||||
---
|
||||
-->
|
||||
|
||||
# Using Checkout Mutation and Order Mutations
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
---
|
||||
<!--
|
||||
title: "Using Composite Product Data + Mutations with WooGraphQL"
|
||||
description: "Learn how to use the Composite Product functionality with WPGraphQL for WooCommerce by building upon the code from `Using Product Data` and `Creating Session Provider and using Cart Mutations`."
|
||||
keywords: "WooGraphQL, WPGraphQL, WooCommerce, GraphQL, Composite Product functionality, Product Data, Session Provider, Cart Mutations"
|
||||
author: "Geoff Taylor"
|
||||
---
|
||||
-->
|
||||
|
||||
# Using Composite Product Data + Mutations
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
---
|
||||
<!--
|
||||
title: "Using Customer Data + Mutations with WooGraphQL"
|
||||
description: "Learn how to utilize the `customer` query and mutations by building a clone of a WooCommerce's user account pages in React.js."
|
||||
keywords: "WooGraphQL, WPGraphQL, WooCommerce, GraphQL, customer query, customer mutations, React.js, user account pages"
|
||||
author: "Geoff Taylor"
|
||||
---
|
||||
-->
|
||||
|
||||
# Using Customer Data + Mutations
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
---
|
||||
<!--
|
||||
title: "Using Order Data with WooGraphQL"
|
||||
description: "Learn how to utilize the Order queries and type by building a basic order status page in React.js that works by taking an `email` and returning a list of all the orders connected to that `email`."
|
||||
keywords: "WooGraphQL, WPGraphQL, WooCommerce, GraphQL, Order queries, React.js, order status page"
|
||||
author: "Geoff Taylor"
|
||||
---
|
||||
-->
|
||||
|
||||
# Using Order Data
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
---
|
||||
<!--
|
||||
title: "Using Product Add-ons Data + Mutations with WooGraphQL"
|
||||
author: "Geoff Taylor"
|
||||
description: "Learn how to use the Product Add-on functionality with WPGraphQL for WooCommerce by building upon the code from `Using Product Data` and `Creating Session Provider and using Cart Mutations`."
|
||||
keywords: "WooGraphQL, WPGraphQL, WooCommerce, GraphQL, Product Add-on functionality, Product Data, Session Provider, Cart Mutations"
|
||||
---
|
||||
-->
|
||||
|
||||
# Coming Soon
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
---
|
||||
<!--
|
||||
title: "Using Product Bundle Data + Mutations with WooGraphQL"
|
||||
description: "Learn how to use the Product Bundle functionality with WPGraphQL for WooCommerce by building upon the code from `Using Product Data` and `Creating Session Provider and using Cart Mutations`."
|
||||
keywords: "WooGraphQL, WPGraphQL, WooCommerce, GraphQL, Product Bundle functionality, Product Data, Session Provider, Cart Mutations"
|
||||
author: "Geoff Taylor"
|
||||
---
|
||||
-->
|
||||
|
||||
# Using Product Bundle Data + Mutations
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
---
|
||||
<!--
|
||||
title: "Using Product Data with WooGraphQL"
|
||||
description: "Learn how to efficiently fetch and use product data in your headless WooCommerce application using WPGraphQL for WooCommerce and WPGraphQL for a seamless shopping experience."
|
||||
keywords: "WooGraphQL, WPGraphQL, WooCommerce, GraphQL, product data, headless, shopping experience"
|
||||
author: "Geoff Taylor"
|
||||
---
|
||||
-->
|
||||
|
||||
# Using Product Data
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
---
|
||||
<!--
|
||||
title: "Using Subscription Data + Mutations with WooGraphQL"
|
||||
description: "Learn how to use the Subscription functionality provided by WooGraphQL Pro and build upon the code written in `Using Product Data` and `Routing by URI` by rewriting the `ProductListing` and `SingleProduct` components to support `SubscriptionProduct` types."
|
||||
keywords: "WooGraphQL, WPGraphQL, WooCommerce, GraphQL, Subscription functionality, ProductListing, SingleProduct, SubscriptionProduct types"
|
||||
author: "Geoff Taylor"
|
||||
---
|
||||
-->
|
||||
|
||||
# Using Subscription Data + Mutations
|
||||
|
||||
|
||||
@@ -188,6 +188,8 @@ class JWT_Auth_Schema_Filters {
|
||||
);
|
||||
|
||||
if ( ! WooCommerce_Filters::is_session_handler_disabled() ) {
|
||||
$token_type = woographql_setting( 'set_session_token_type', 'legacy' );
|
||||
if ( in_array( $token_type, [ 'legacy', 'both' ], true ) ) {
|
||||
register_graphql_field(
|
||||
'LoginPayload',
|
||||
'sessionToken',
|
||||
@@ -207,5 +209,26 @@ class JWT_Auth_Schema_Filters {
|
||||
]
|
||||
);
|
||||
}
|
||||
if ( in_array( $token_type, [ 'store-api', 'both' ], true ) ) {
|
||||
register_graphql_field(
|
||||
'LoginPayload',
|
||||
'cartToken',
|
||||
[
|
||||
'type' => 'String',
|
||||
'description' => __( 'A JWT token that can be used in future requests to for WooCommerce session identification', 'wp-graphql-woocommerce' ),
|
||||
'resolve' => static function () {
|
||||
/**
|
||||
* Session Handler.
|
||||
*
|
||||
* @var \WPGraphQL\WooCommerce\Utils\QL_Session_Handler $session
|
||||
*/
|
||||
$session = \WC()->session;
|
||||
|
||||
return apply_filters( 'graphql_customer_session_token', $session->build_cart_token() );
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ class Type_Registry {
|
||||
Type\WPInterface\Attribute::register_interface();
|
||||
Type\WPInterface\Product_Attribute::register_interface();
|
||||
Type\WPInterface\Cart_Error::register_interface();
|
||||
Type\WPInterface\Payment_Token::register_interface();
|
||||
Type\WPInterface\Payment_Token_Interface::register_interface();
|
||||
Type\WPInterface\Product_Union::register_interface();
|
||||
Type\WPInterface\Cart_Item::register_interface();
|
||||
Type\WPInterface\Downloadable_Product::register_interface();
|
||||
|
||||
@@ -34,6 +34,9 @@ class WooCommerce_Filters {
|
||||
add_filter( 'woocommerce_session_handler', [ self::class, 'woocommerce_session_handler' ] );
|
||||
add_filter( 'graphql_response_headers_to_send', [ self::class, 'add_session_header_to_expose_headers' ] );
|
||||
add_filter( 'graphql_access_control_allow_headers', [ self::class, 'add_session_header_to_allow_headers' ] );
|
||||
|
||||
// Initialize cart/session after JWT authentication has had a chance to run.
|
||||
add_action( 'graphql_process_http_request', [ self::class, 'initialize_session_and_cart' ] );
|
||||
}
|
||||
|
||||
// Add better support for Stripe payment gateway.
|
||||
@@ -50,7 +53,31 @@ class WooCommerce_Filters {
|
||||
* @return boolean
|
||||
*/
|
||||
public static function is_session_handler_disabled() {
|
||||
return defined( 'NO_QL_SESSION_HANDLER' ) || 'on' === woographql_setting( 'disable_ql_session_handler', 'off' );
|
||||
return \defined( 'NO_QL_SESSION_HANDLER' ) || 'on' === woographql_setting( 'disable_ql_session_handler', 'off' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize WooCommerce session and cart for GraphQL requests.
|
||||
*
|
||||
* This is hooked to 'graphql_before_execute' to ensure JWT authentication has
|
||||
* had a chance to set the current user before the session is initialized.
|
||||
* This fixes an issue where guest sessions weren't being updated when a user
|
||||
* provides both a Cart-Token (session) and Authorization (JWT) header.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function initialize_session_and_cart() {
|
||||
// Clear any existing WooCommerce objects to ensure fresh initialization
|
||||
// with the correct user context after JWT authentication.
|
||||
|
||||
// @phpstan-ignore-next-line
|
||||
\WC()->customer = null;
|
||||
// @phpstan-ignore-next-line
|
||||
\WC()->cart = null;
|
||||
// @phpstan-ignore-next-line
|
||||
\WC()->session = null;
|
||||
|
||||
wc_load_cart();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -261,7 +261,7 @@ if ( ! class_exists( '\WPGraphQL\WooCommerce\WP_GraphQL_WooCommerce' ) ) :
|
||||
require $include_directory_path . 'type/interface/class-product-attribute.php';
|
||||
require $include_directory_path . 'type/interface/class-product.php';
|
||||
require $include_directory_path . 'type/interface/class-product-variation.php';
|
||||
require $include_directory_path . 'type/interface/class-payment-token.php';
|
||||
require $include_directory_path . 'type/interface/class-payment-token-interface.php';
|
||||
require $include_directory_path . 'type/interface/class-product-union.php';
|
||||
require $include_directory_path . 'type/interface/class-cart-item.php';
|
||||
require $include_directory_path . 'type/interface/class-downloadable-product.php';
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace WPGraphQL\WooCommerce\Connection;
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use WPGraphQL\AppContext;
|
||||
use WPGraphQL\WooCommerce\Data\Connection\Order_Connection_Resolver;
|
||||
use WPGraphQL\WooCommerce\Model\Customer;
|
||||
|
||||
/**
|
||||
* Class - Orders
|
||||
@@ -93,104 +94,6 @@ class Orders {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns order connection filter by customer.
|
||||
*
|
||||
* @param \WPGraphQL\WooCommerce\Data\Connection\Order_Connection_Resolver $resolver Connection resolver.
|
||||
* @param \WC_Customer $customer Customer object of querying user.
|
||||
*
|
||||
* @return array|\GraphQL\Deferred
|
||||
*/
|
||||
private static function get_customer_order_connection( $resolver, $customer ) {
|
||||
// If not "billing email" or "ID" set bail early by returning an empty connection.
|
||||
if ( empty( $customer->get_billing_email() ) && empty( $customer->get_id() ) ) {
|
||||
return [
|
||||
'nodes' => [],
|
||||
'edges' => [],
|
||||
];
|
||||
}
|
||||
|
||||
$customer_id = $customer->get_id();
|
||||
$billing_email = $customer->get_billing_email();
|
||||
if ( ! empty( $customer_id ) ) {
|
||||
$resolver->set_query_arg( 'customer_id', $customer_id );
|
||||
$resolver->set_should_execute( \WC()->customer->get_id() === $customer_id );
|
||||
} elseif ( ! empty( $billing_email ) ) {
|
||||
$resolver->set_query_arg( 'billing_email', $billing_email );
|
||||
$resolver->set_should_execute( \WC()->customer->get_billing_email() === $billing_email );
|
||||
}
|
||||
|
||||
return $resolver->get_connection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns refund connection filter by customer.
|
||||
*
|
||||
* @param \WPGraphQL\WooCommerce\Data\Connection\Order_Connection_Resolver $resolver Connection resolver.
|
||||
* @param \WC_Customer $customer Customer object of querying user.
|
||||
*
|
||||
* @return array|\GraphQL\Deferred
|
||||
*/
|
||||
private static function get_customer_refund_connection( $resolver, $customer ) {
|
||||
$empty_results = [
|
||||
'pageInfo' => null,
|
||||
'nodes' => [],
|
||||
'edges' => [],
|
||||
];
|
||||
// If not "billing email" or "ID" set bail early by returning an empty connection.
|
||||
if ( empty( $customer->get_billing_email() ) && empty( $customer->get_id() ) ) {
|
||||
return $empty_results;
|
||||
}
|
||||
|
||||
$order_ids = [];
|
||||
$customer_id = $customer->get_id();
|
||||
$billing_email = $customer->get_billing_email();
|
||||
if ( ! empty( $customer_id ) ) {
|
||||
$args = [
|
||||
'customer_id' => $customer_id,
|
||||
'return' => 'ids',
|
||||
];
|
||||
/** @var array<int> $order_ids_by_customer_id */
|
||||
$order_ids_by_customer_id = wc_get_orders( $args );
|
||||
|
||||
if ( is_array( $order_ids_by_customer_id ) ) {
|
||||
$order_ids = $order_ids_by_customer_id;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $billing_email ) ) {
|
||||
$args = [
|
||||
'billing_email' => $billing_email,
|
||||
'return' => 'ids',
|
||||
];
|
||||
/** @var array<int> $order_ids_by_email */
|
||||
$order_ids_by_email = wc_get_orders( $args );
|
||||
// Merge the arrays of order IDs.
|
||||
if ( is_array( $order_ids_by_email ) ) {
|
||||
$order_ids = array_merge( $order_ids, $order_ids_by_email );
|
||||
}
|
||||
}
|
||||
|
||||
// If no orders found, return empty connection.
|
||||
if ( empty( $order_ids ) ) {
|
||||
return $empty_results;
|
||||
}
|
||||
|
||||
// Remove duplicates.
|
||||
$order_ids = array_unique( $order_ids );
|
||||
|
||||
// Set connection args.
|
||||
$resolver->set_should_execute(
|
||||
( 0 !== $customer_id && \WC()->customer->get_id() === $customer_id )
|
||||
|| \WC()->customer->get_billing_email() === $billing_email
|
||||
);
|
||||
|
||||
$resolver->set_query_arg( 'post_parent__in', array_map( 'absint', $order_ids ) );
|
||||
|
||||
// Execute and return connection.
|
||||
return $resolver->get_connection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an array of $args, this returns the connection config, merging the provided args
|
||||
* with the defaults.
|
||||
@@ -233,8 +136,8 @@ class Orders {
|
||||
*/
|
||||
if ( $not_manager ) {
|
||||
return 'shop_order_refund' === $post_object->name
|
||||
? self::get_customer_refund_connection( $resolver, \WC()->customer )
|
||||
: self::get_customer_order_connection( $resolver, \WC()->customer );
|
||||
? self::get_customer_refund_connection( $resolver, new Customer( 'session', ! is_user_logged_in() ) )
|
||||
: self::get_customer_order_connection( $resolver, new Customer( 'session', ! is_user_logged_in() ) );
|
||||
}
|
||||
|
||||
return $resolver->get_connection();
|
||||
@@ -330,4 +233,102 @@ class Orders {
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns order connection filter by customer.
|
||||
*
|
||||
* @param \WPGraphQL\WooCommerce\Data\Connection\Order_Connection_Resolver $resolver Connection resolver.
|
||||
* @param \WPGraphQL\WooCommerce\Model\Customer $customer Customer object of querying user.
|
||||
*
|
||||
* @return array|\GraphQL\Deferred
|
||||
*/
|
||||
private static function get_customer_order_connection( $resolver, Customer $customer ) {
|
||||
// If not "billing email" or "ID" set bail early by returning an empty connection.
|
||||
if ( empty( $customer->billing['email'] ) && ( empty( absint( $customer->ID ) ) ) ) {
|
||||
return [
|
||||
'nodes' => [],
|
||||
'edges' => [],
|
||||
];
|
||||
}
|
||||
|
||||
$target_customer_id = absint( $customer->ID );
|
||||
$current_customer_id = absint( \WC()->customer->get_id() );
|
||||
if ( ! empty( $target_customer_id ) ) {
|
||||
$resolver->set_query_arg( 'customer_id', $target_customer_id );
|
||||
$resolver->set_should_execute( $current_customer_id === $target_customer_id );
|
||||
} elseif ( ! empty( $customer->billing['email'] ) ) {
|
||||
$resolver->set_query_arg( 'billing_email', $customer->billing['email'] );
|
||||
$resolver->set_should_execute( \WC()->customer->get_billing_email() === $customer->billing['email'] );
|
||||
}
|
||||
|
||||
return $resolver->get_connection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns refund connection filter by customer.
|
||||
*
|
||||
* @param \WPGraphQL\WooCommerce\Data\Connection\Order_Connection_Resolver $resolver Connection resolver.
|
||||
* @param \WPGraphQL\WooCommerce\Model\Customer $customer Customer object of querying user.
|
||||
*
|
||||
* @return array|\GraphQL\Deferred
|
||||
*/
|
||||
private static function get_customer_refund_connection( Order_Connection_Resolver $resolver, Customer $customer ) {
|
||||
$empty_results = [
|
||||
'pageInfo' => null,
|
||||
'nodes' => [],
|
||||
'edges' => [],
|
||||
];
|
||||
// If not "billing email" or "ID" set bail early by returning an empty connection.
|
||||
if ( empty( $customer->billing['email'] ) && empty( $customer->ID ) ) {
|
||||
return $empty_results;
|
||||
}
|
||||
|
||||
$order_ids = [];
|
||||
$customer_id = $customer->ID;
|
||||
$billing_email = $customer->billing['email'];
|
||||
if ( ! empty( $customer_id ) ) {
|
||||
$args = [
|
||||
'customer_id' => $customer_id,
|
||||
'return' => 'ids',
|
||||
];
|
||||
/** @var array<int> $order_ids_by_customer_id */
|
||||
$order_ids_by_customer_id = wc_get_orders( $args );
|
||||
|
||||
if ( is_array( $order_ids_by_customer_id ) ) {
|
||||
$order_ids = $order_ids_by_customer_id;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $billing_email ) ) {
|
||||
$args = [
|
||||
'billing_email' => $billing_email,
|
||||
'return' => 'ids',
|
||||
];
|
||||
/** @var array<int> $order_ids_by_email */
|
||||
$order_ids_by_email = wc_get_orders( $args );
|
||||
// Merge the arrays of order IDs.
|
||||
if ( is_array( $order_ids_by_email ) ) {
|
||||
$order_ids = array_merge( $order_ids, $order_ids_by_email );
|
||||
}
|
||||
}
|
||||
|
||||
// If no orders found, return empty connection.
|
||||
if ( empty( $order_ids ) ) {
|
||||
return $empty_results;
|
||||
}
|
||||
|
||||
// Remove duplicates.
|
||||
$order_ids = array_unique( $order_ids );
|
||||
|
||||
// Set connection args.
|
||||
$resolver->set_should_execute(
|
||||
( 0 !== $customer_id && \WC()->customer->get_id() === $customer_id )
|
||||
|| \WC()->customer->get_billing_email() === $billing_email
|
||||
);
|
||||
|
||||
$resolver->set_query_arg( 'post_parent__in', array_map( 'absint', $order_ids ) );
|
||||
|
||||
// Execute and return connection.
|
||||
return $resolver->get_connection();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,8 +17,6 @@ use WPGraphQL\Model\Model;
|
||||
/**
|
||||
* Class Customer
|
||||
*
|
||||
* @property \WC_Customer $wc_data
|
||||
*
|
||||
* @property int $ID
|
||||
* @property string $id
|
||||
* @property int $databaseId
|
||||
@@ -86,7 +84,7 @@ class Customer extends Model {
|
||||
return ( ! empty( $this->data->get_id() ) ) ? $this->data->get_id() : \WC()->session->get_customer_id();
|
||||
},
|
||||
'id' => function () {
|
||||
return ( ! empty( $this->data->get_id() ) )
|
||||
return ! empty( $this->data->get_id() )
|
||||
? Relay::toGlobalId( 'user', $this->data->get_id() )
|
||||
: 'guest';
|
||||
},
|
||||
|
||||
@@ -62,7 +62,7 @@ class Payment_Method_Set_Default {
|
||||
},
|
||||
],
|
||||
'token' => [
|
||||
'type' => 'PaymentToken',
|
||||
'type' => 'PaymentTokenInterface',
|
||||
'description' => __( 'Preferred payment method token', 'wp-graphql-woocommerce' ),
|
||||
'resolve' => static function ( $payload ) {
|
||||
return ! empty( $payload['token'] ) ? $payload['token'] : null;
|
||||
|
||||
+5
-28
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* WPInterface Type - Payment_Token
|
||||
* WPInterface Type - Payment_Token_Interface
|
||||
*
|
||||
* @package WPGraphQL\WooCommerce\Type\WPInterface
|
||||
* @since 0.10.1
|
||||
@@ -8,13 +8,10 @@
|
||||
|
||||
namespace WPGraphQL\WooCommerce\Type\WPInterface;
|
||||
|
||||
use GraphQL\Error\UserError;
|
||||
use GraphQLRelay\Relay;
|
||||
|
||||
/**
|
||||
* Class Payment_Token
|
||||
* Class Payment_Token_Interface
|
||||
*/
|
||||
class Payment_Token {
|
||||
class Payment_Token_Interface {
|
||||
/**
|
||||
* Registers the "PaymentToken" interface.
|
||||
*
|
||||
@@ -22,7 +19,7 @@ class Payment_Token {
|
||||
*/
|
||||
public static function register_interface() {
|
||||
register_graphql_interface_type(
|
||||
'PaymentToken',
|
||||
'PaymentTokenInterface',
|
||||
[
|
||||
'description' => __( 'Payment token object', 'wp-graphql-woocommerce' ),
|
||||
'interfaces' => [ 'Node' ],
|
||||
@@ -36,13 +33,7 @@ class Payment_Token {
|
||||
case 'eCheck':
|
||||
return $type_registry->get_type( 'PaymentTokenECheck' );
|
||||
default:
|
||||
throw new UserError(
|
||||
sprintf(
|
||||
/* translators: %s: Payment token type */
|
||||
__( 'The "%s" token type is not supported by the core WPGraphQL for WooCommerce (WooGraphQL) schema.', 'wp-graphql-woocommerce' ),
|
||||
$type
|
||||
)
|
||||
);
|
||||
return $type_registry->get_type( 'PaymentToken' );
|
||||
}
|
||||
},
|
||||
]
|
||||
@@ -58,20 +49,6 @@ class Payment_Token {
|
||||
public static function get_fields( $other_fields = [] ) {
|
||||
return array_merge(
|
||||
[
|
||||
'id' => [
|
||||
'type' => [ 'non_null' => 'ID' ],
|
||||
'description' => __( 'Token ID unique identifier', 'wp-graphql-woocommerce' ),
|
||||
'resolve' => static function ( $source ) {
|
||||
return ! empty( $source->get_id() ) ? Relay::toGlobalId( 'token', $source->get_id() ) : null;
|
||||
},
|
||||
],
|
||||
'tokenId' => [
|
||||
'type' => [ 'non_null' => 'Integer' ],
|
||||
'description' => __( 'Token database ID.', 'wp-graphql-woocommerce' ),
|
||||
'resolve' => static function ( $source ) {
|
||||
return ! empty( $source->get_id() ) ? $source->get_id() : null;
|
||||
},
|
||||
],
|
||||
'type' => [
|
||||
'type' => [ 'non_null' => 'String' ],
|
||||
'description' => __( 'Token type', 'wp-graphql-woocommerce' ),
|
||||
@@ -12,7 +12,6 @@ use GraphQL\Type\Definition\ResolveInfo;
|
||||
use WPGraphQL\AppContext;
|
||||
use WPGraphQL\WooCommerce\Data\Connection\Product_Connection_Resolver;
|
||||
use WPGraphQL\WooCommerce\Data\Connection\Variation_Attribute_Connection_Resolver;
|
||||
use WPGraphQL\WooCommerce\Type\WPObject\Meta_Data_Type;
|
||||
|
||||
|
||||
/**
|
||||
@@ -78,208 +77,14 @@ class Product_Variation {
|
||||
*/
|
||||
public static function get_fields() {
|
||||
return [
|
||||
'id' => [
|
||||
'type' => [ 'non_null' => 'ID' ],
|
||||
'description' => __( 'Product or variation global ID', 'wp-graphql-woocommerce' ),
|
||||
],
|
||||
'databaseId' => [
|
||||
'type' => [ 'non_null' => 'Int' ],
|
||||
'description' => __( 'Product or variation ID', 'wp-graphql-woocommerce' ),
|
||||
],
|
||||
'name' => [
|
||||
'type' => 'String',
|
||||
'description' => __( 'Product name', 'wp-graphql-woocommerce' ),
|
||||
],
|
||||
'date' => [
|
||||
'type' => 'String',
|
||||
'description' => __( 'Date variation created', 'wp-graphql-woocommerce' ),
|
||||
],
|
||||
'modified' => [
|
||||
'type' => 'String',
|
||||
'description' => __( 'Date variation last updated', 'wp-graphql-woocommerce' ),
|
||||
],
|
||||
'description' => [
|
||||
'type' => 'String',
|
||||
'description' => __( 'Product description', 'wp-graphql-woocommerce' ),
|
||||
],
|
||||
'sku' => [
|
||||
'type' => 'String',
|
||||
'description' => __( 'Product variation SKU (Stock-keeping unit)', 'wp-graphql-woocommerce' ),
|
||||
],
|
||||
'price' => [
|
||||
'type' => 'String',
|
||||
'description' => __( 'Product variation\'s active price', 'wp-graphql-woocommerce' ),
|
||||
'args' => [
|
||||
'format' => [
|
||||
'type' => 'PricingFieldFormatEnum',
|
||||
'description' => __( 'Format of the price', 'wp-graphql-woocommerce' ),
|
||||
],
|
||||
],
|
||||
'resolve' => static function ( $source, $args ) {
|
||||
if ( isset( $args['format'] ) && 'raw' === $args['format'] ) {
|
||||
// @codingStandardsIgnoreLine.
|
||||
return $source->priceRaw;
|
||||
} else {
|
||||
return $source->price;
|
||||
}
|
||||
},
|
||||
],
|
||||
'regularPrice' => [
|
||||
'type' => 'String',
|
||||
'description' => __( 'Product variation\'s regular price', 'wp-graphql-woocommerce' ),
|
||||
'args' => [
|
||||
'format' => [
|
||||
'type' => 'PricingFieldFormatEnum',
|
||||
'description' => __( 'Format of the price', 'wp-graphql-woocommerce' ),
|
||||
],
|
||||
],
|
||||
'resolve' => static function ( $source, $args ) {
|
||||
if ( isset( $args['format'] ) && 'raw' === $args['format'] ) {
|
||||
// @codingStandardsIgnoreLine.
|
||||
return $source->regularPriceRaw;
|
||||
} else {
|
||||
// @codingStandardsIgnoreLine.
|
||||
return $source->regularPrice;
|
||||
}
|
||||
},
|
||||
],
|
||||
'salePrice' => [
|
||||
'type' => 'String',
|
||||
'description' => __( 'Product variation\'s sale price', 'wp-graphql-woocommerce' ),
|
||||
'args' => [
|
||||
'format' => [
|
||||
'type' => 'PricingFieldFormatEnum',
|
||||
'description' => __( 'Format of the price', 'wp-graphql-woocommerce' ),
|
||||
],
|
||||
],
|
||||
'resolve' => static function ( $source, $args ) {
|
||||
if ( isset( $args['format'] ) && 'raw' === $args['format'] ) {
|
||||
// @codingStandardsIgnoreLine.
|
||||
return $source->salePriceRaw;
|
||||
} else {
|
||||
// @codingStandardsIgnoreLine.
|
||||
return $source->salePrice;
|
||||
}
|
||||
},
|
||||
],
|
||||
'dateOnSaleFrom' => [
|
||||
'type' => 'String',
|
||||
'description' => __( 'Date on sale from', 'wp-graphql-woocommerce' ),
|
||||
],
|
||||
'dateOnSaleTo' => [
|
||||
'type' => 'String',
|
||||
'description' => __( 'Date on sale to', 'wp-graphql-woocommerce' ),
|
||||
],
|
||||
'onSale' => [
|
||||
'type' => 'Boolean',
|
||||
'description' => __( 'Is variation on sale?', 'wp-graphql-woocommerce' ),
|
||||
],
|
||||
'status' => [
|
||||
'type' => 'String',
|
||||
'description' => __( 'Variation status', 'wp-graphql-woocommerce' ),
|
||||
],
|
||||
'purchasable' => [
|
||||
'type' => 'Boolean',
|
||||
'description' => __( 'If product variation can be bought', 'wp-graphql-woocommerce' ),
|
||||
],
|
||||
'virtual' => [
|
||||
'type' => 'Boolean',
|
||||
'description' => __( 'Is product virtual?', 'wp-graphql-woocommerce' ),
|
||||
],
|
||||
'downloadable' => [
|
||||
'type' => 'Boolean',
|
||||
'description' => __( 'Is downloadable?', 'wp-graphql-woocommerce' ),
|
||||
],
|
||||
'downloads' => [
|
||||
'type' => [ 'list_of' => 'ProductDownload' ],
|
||||
'description' => __( 'Product downloads', 'wp-graphql-woocommerce' ),
|
||||
],
|
||||
'downloadLimit' => [
|
||||
'type' => 'Int',
|
||||
'description' => __( 'Download limit', 'wp-graphql-woocommerce' ),
|
||||
],
|
||||
'downloadExpiry' => [
|
||||
'type' => 'Int',
|
||||
'description' => __( 'Download expiry', 'wp-graphql-woocommerce' ),
|
||||
],
|
||||
'taxStatus' => [
|
||||
'type' => 'TaxStatusEnum',
|
||||
'description' => __( 'Tax status', 'wp-graphql-woocommerce' ),
|
||||
],
|
||||
'taxClass' => [
|
||||
'type' => 'TaxClassEnum',
|
||||
'description' => __( 'Product variation tax class', 'wp-graphql-woocommerce' ),
|
||||
],
|
||||
'manageStock' => [
|
||||
'type' => 'ManageStockEnum',
|
||||
'description' => __( 'if/how product variation stock is managed', 'wp-graphql-woocommerce' ),
|
||||
],
|
||||
'stockQuantity' => [
|
||||
'type' => 'Int',
|
||||
'description' => __( 'Product variation stock quantity', 'wp-graphql-woocommerce' ),
|
||||
],
|
||||
'stockStatus' => [
|
||||
'type' => 'StockStatusEnum',
|
||||
'description' => __( 'Product stock status', 'wp-graphql-woocommerce' ),
|
||||
],
|
||||
'backorders' => [
|
||||
'type' => 'BackordersEnum',
|
||||
'description' => __( 'Product variation backorders', 'wp-graphql-woocommerce' ),
|
||||
],
|
||||
'backordersAllowed' => [
|
||||
'type' => 'Boolean',
|
||||
'description' => __( 'Can product be backordered?', 'wp-graphql-woocommerce' ),
|
||||
],
|
||||
'weight' => [
|
||||
'type' => 'String',
|
||||
'description' => __( 'Product variation weight', 'wp-graphql-woocommerce' ),
|
||||
],
|
||||
'length' => [
|
||||
'type' => 'String',
|
||||
'description' => __( 'Product variation length', 'wp-graphql-woocommerce' ),
|
||||
],
|
||||
'width' => [
|
||||
'type' => 'String',
|
||||
'description' => __( 'Product variation width', 'wp-graphql-woocommerce' ),
|
||||
],
|
||||
'height' => [
|
||||
'type' => 'String',
|
||||
'description' => __( 'Product variation height', 'wp-graphql-woocommerce' ),
|
||||
],
|
||||
'menuOrder' => [
|
||||
'type' => 'Int',
|
||||
'description' => __( 'Menu order', 'wp-graphql-woocommerce' ),
|
||||
],
|
||||
'purchaseNote' => [
|
||||
'type' => 'String',
|
||||
'description' => __( 'Product variation purchase_note', 'wp-graphql-woocommerce' ),
|
||||
],
|
||||
'shippingClass' => [
|
||||
'type' => 'String',
|
||||
'description' => __( 'Product variation shipping class', 'wp-graphql-woocommerce' ),
|
||||
],
|
||||
'catalogVisibility' => [
|
||||
'type' => 'CatalogVisibilityEnum',
|
||||
'description' => __( 'Product variation catalog visibility', 'wp-graphql-woocommerce' ),
|
||||
],
|
||||
'hasAttributes' => [
|
||||
'type' => 'Boolean',
|
||||
'description' => __( 'Does product variation have any visible attributes', 'wp-graphql-woocommerce' ),
|
||||
],
|
||||
'type' => [
|
||||
'type' => 'ProductTypesEnum',
|
||||
'description' => __( 'Product type', 'wp-graphql-woocommerce' ),
|
||||
],
|
||||
'image' => [
|
||||
'type' => 'MediaItem',
|
||||
'description' => __( 'Product variation main image', 'wp-graphql-woocommerce' ),
|
||||
'resolve' => static function ( $source, array $args, AppContext $context ) {
|
||||
return ! empty( $source->image_id )
|
||||
? $context->get_loader( 'post' )->load_deferred( $source->image_id )
|
||||
: null;
|
||||
},
|
||||
],
|
||||
'metaData' => Meta_Data_Type::get_metadata_field_definition(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,11 +25,8 @@ class Product {
|
||||
* @return void
|
||||
*/
|
||||
public static function register_interface() {
|
||||
|
||||
// Register the fields to the Product Interface
|
||||
// the product interface is defined by the post_type registration.
|
||||
register_graphql_fields( 'Product', self::get_fields() );
|
||||
|
||||
register_graphql_field(
|
||||
'RootQuery',
|
||||
'product',
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
namespace WPGraphQL\WooCommerce\Type\WPObject;
|
||||
|
||||
use GraphQL\Deferred;
|
||||
use GraphQL\Error\UserError;
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use WPGraphQL\AppContext;
|
||||
@@ -221,7 +222,7 @@ class Customer_Type {
|
||||
'Customer',
|
||||
[
|
||||
'availablePaymentMethods' => [
|
||||
'type' => [ 'list_of' => 'PaymentToken' ],
|
||||
'type' => [ 'list_of' => 'PaymentTokenInterface' ],
|
||||
'description' => __( 'Customer\'s stored payment tokens.', 'wp-graphql-woocommerce' ),
|
||||
'resolve' => static function ( $source ) {
|
||||
if ( get_current_user_id() === $source->ID ) {
|
||||
@@ -285,6 +286,8 @@ class Customer_Type {
|
||||
* @return void
|
||||
*/
|
||||
public static function register_session_handler_fields() {
|
||||
$token_type = woographql_setting( 'set_session_token_type', 'legacy' );
|
||||
if ( in_array( $token_type, [ 'legacy', 'both' ], true ) ) {
|
||||
/**
|
||||
* Register the "sessionToken" field to the "Customer" type.
|
||||
*/
|
||||
@@ -296,6 +299,8 @@ class Customer_Type {
|
||||
'description' => __( 'A JWT token that can be used in future requests to for WooCommerce session identification', 'wp-graphql-woocommerce' ),
|
||||
'resolve' => static function ( $source ) {
|
||||
if ( \get_current_user_id() === $source->ID || 'guest' === $source->id ) {
|
||||
return new Deferred(
|
||||
static function () {
|
||||
/**
|
||||
* Session handler.
|
||||
*
|
||||
@@ -305,11 +310,14 @@ class Customer_Type {
|
||||
|
||||
return apply_filters( 'graphql_customer_session_token', $session->build_token() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
/**
|
||||
* Register the "wooSessionToken" field to the "User" type.
|
||||
*/
|
||||
@@ -321,6 +329,8 @@ class Customer_Type {
|
||||
'description' => __( 'A JWT token that can be used in future requests to for WooCommerce session identification', 'wp-graphql-woocommerce' ),
|
||||
'resolve' => static function ( $source ) {
|
||||
if ( \get_current_user_id() === $source->userId ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
|
||||
return new Deferred(
|
||||
static function () {
|
||||
/**
|
||||
* Session handler
|
||||
*
|
||||
@@ -330,6 +340,8 @@ class Customer_Type {
|
||||
|
||||
return apply_filters( 'graphql_customer_session_token', $session->build_token() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
@@ -337,6 +349,69 @@ class Customer_Type {
|
||||
);
|
||||
}
|
||||
|
||||
if ( in_array( $token_type, [ 'store-api', 'both' ], true ) ) {
|
||||
/**
|
||||
* Register the "cartToken" field to the "Customer" type.
|
||||
*/
|
||||
register_graphql_field(
|
||||
'Customer',
|
||||
'cartToken',
|
||||
[
|
||||
'type' => 'String',
|
||||
'description' => __( 'A JWT token that can be used in future requests to for WooCommerce session identification', 'wp-graphql-woocommerce' ),
|
||||
'resolve' => static function ( $source ) {
|
||||
if ( \get_current_user_id() === $source->ID || 'guest' === $source->id ) {
|
||||
return new Deferred(
|
||||
static function () {
|
||||
/**
|
||||
* Session handler.
|
||||
*
|
||||
* @var \WPGraphQL\WooCommerce\Utils\QL_Session_Handler $session
|
||||
*/
|
||||
$session = \WC()->session;
|
||||
|
||||
return apply_filters( 'graphql_cart_token', $session->build_cart_token() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
/**
|
||||
* Register the "cartToken" field to the "User" type.
|
||||
*/
|
||||
register_graphql_field(
|
||||
'User',
|
||||
'cartToken',
|
||||
[
|
||||
'type' => 'String',
|
||||
'description' => __( 'A JWT token that can be used in future requests to for WooCommerce session identification', 'wp-graphql-woocommerce' ),
|
||||
'resolve' => static function ( $source ) {
|
||||
if ( \get_current_user_id() === $source->userId ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
|
||||
return new Deferred(
|
||||
static function () {
|
||||
/**
|
||||
* Session handler
|
||||
*
|
||||
* @var \WPGraphQL\WooCommerce\Utils\QL_Session_Handler $session
|
||||
*/
|
||||
$session = \WC()->session;
|
||||
|
||||
return apply_filters( 'graphql_cart_token', $session->build_cart_token() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers selected authorizing_url_fields
|
||||
*
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace WPGraphQL\WooCommerce\Type\WPObject;
|
||||
|
||||
use WPGraphQL\AppContext;
|
||||
use WPGraphQL\Data\Connection\PostObjectConnectionResolver;
|
||||
use WPGraphQL\WooCommerce\Data\Connection\Product_Connection_Resolver;
|
||||
use WPGraphQL\WooCommerce\Data\Factory;
|
||||
|
||||
/**
|
||||
@@ -211,7 +212,7 @@ class Order_Item_Type {
|
||||
'oneToOne' => true,
|
||||
'resolve' => static function ( $source, array $args, AppContext $context, $info ) {
|
||||
$id = $source->productId; // @phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
|
||||
$resolver = new PostObjectConnectionResolver( $source, $args, $context, $info, 'product' );
|
||||
$resolver = new Product_Connection_Resolver( $source, $args, $context, $info );
|
||||
|
||||
return $resolver
|
||||
->one_to_one()
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
namespace WPGraphQL\WooCommerce\Type\WPObject;
|
||||
|
||||
use WPGraphQL\WooCommerce\Type\WPInterface\Payment_Token;
|
||||
use WPGraphQL\WooCommerce\Type\WPInterface\Payment_Token_Interface;
|
||||
|
||||
/**
|
||||
* Class Payment_Token_Types
|
||||
@@ -22,21 +22,29 @@ class Payment_Token_Types {
|
||||
* @return void
|
||||
*/
|
||||
public static function register() {
|
||||
register_graphql_object_type(
|
||||
'PaymentToken',
|
||||
[
|
||||
'description' => __( 'A payment token', 'wp-graphql-woocommerce' ),
|
||||
'interfaces' => [ 'PaymentTokenInterface' ],
|
||||
'fields' => [],
|
||||
]
|
||||
);
|
||||
register_graphql_object_type(
|
||||
'PaymentTokenCC',
|
||||
[
|
||||
'description' => __( 'A credit cart payment token', 'wp-graphql-woocommerce' ),
|
||||
'interfaces' => [ 'PaymentToken' ],
|
||||
'fields' => Payment_Token::get_fields( self::get_credit_card_fields() ),
|
||||
'description' => __( 'A credit card payment token', 'wp-graphql-woocommerce' ),
|
||||
'interfaces' => [ 'PaymentTokenInterface' ],
|
||||
'fields' => Payment_Token_Interface::get_fields( self::get_credit_card_fields() ),
|
||||
]
|
||||
);
|
||||
|
||||
register_graphql_object_type(
|
||||
'PaymentTokenECheck',
|
||||
[
|
||||
'description' => __( 'A electronic check payment token', 'wp-graphql-woocommerce' ),
|
||||
'interfaces' => [ 'PaymentToken' ],
|
||||
'fields' => Payment_Token::get_fields( self::get_e_check_fields() ),
|
||||
'description' => __( 'An electronic check payment token', 'wp-graphql-woocommerce' ),
|
||||
'interfaces' => [ 'PaymentTokenInterface' ],
|
||||
'fields' => Payment_Token_Interface::get_fields( self::get_e_check_fields() ),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -54,10 +54,10 @@ class Shipping_Rate_Type {
|
||||
},
|
||||
],
|
||||
'cost' => [
|
||||
'type' => 'String',
|
||||
'type' => 'Float',
|
||||
'description' => __( 'Shipping rate cost', 'wp-graphql-woocommerce' ),
|
||||
'resolve' => static function ( $source ) {
|
||||
return ! empty( $source->get_cost() ) ? $source->get_cost() : null;
|
||||
return ! is_null( $source->get_cost() ) ? $source->get_cost() : null;
|
||||
},
|
||||
],
|
||||
],
|
||||
|
||||
@@ -118,6 +118,8 @@ class QL_Session_Handler extends WC_Session_Handler {
|
||||
add_action( 'woocommerce_set_cart_cookies', [ $this, 'set_customer_session_token' ], 10 );
|
||||
add_action( 'woographql_update_session', [ $this, 'set_customer_session_token' ], 10 );
|
||||
add_action( 'shutdown', [ $this, 'save_data' ] );
|
||||
add_filter( 'graphql_jwt_auth_after_authenticate', [ $this, 'reinitialize_session_token' ], 10 );
|
||||
add_filter( 'graphql_login_payload', [ $this, 'reinitialize_session_token' ], 10 );
|
||||
} else {
|
||||
add_action( 'woocommerce_set_cart_cookies', [ $this, 'set_customer_session_cookie' ], 10 );
|
||||
add_action( 'shutdown', [ $this, 'save_data' ], 20 );
|
||||
@@ -185,11 +187,11 @@ class QL_Session_Handler extends WC_Session_Handler {
|
||||
if ( $token->exp < $this->_session_expiration ) {
|
||||
$this->update_session_timestamp( (string) $this->_customer_id, $this->_session_expiration );
|
||||
}
|
||||
} elseif ( is_wp_error( $token ) ) {
|
||||
} elseif ( Router::is_graphql_http_request() && is_wp_error( $token ) ) {
|
||||
add_filter(
|
||||
'graphql_woocommerce_session_token_errors',
|
||||
static function ( $errors ) use ( $token ) {
|
||||
$errors = $token->get_error_message();
|
||||
$errors = $token->get_error_code() . ': ' . $token->get_error_message();
|
||||
return $errors;
|
||||
}
|
||||
);
|
||||
@@ -214,6 +216,29 @@ class QL_Session_Handler extends WC_Session_Handler {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reinitialize session token in response after authentication in GraphQL.
|
||||
*
|
||||
* @param array $response The authentication response.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function reinitialize_session_token( $response ) {
|
||||
$this->init_session_token();
|
||||
|
||||
$token = $this->build_token();
|
||||
if ( $token ) {
|
||||
$response['session_token'] = $token;
|
||||
}
|
||||
|
||||
// Add Store API Cart-Token if enabled.
|
||||
$cart_token = $this->build_cart_token();
|
||||
if ( ! empty( $cart_token ) ) {
|
||||
$response['cart_token'] = $cart_token;
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve and decrypt the session data from session, if set. Otherwise return false.
|
||||
*
|
||||
@@ -480,7 +505,7 @@ class QL_Session_Handler extends WC_Session_Handler {
|
||||
*
|
||||
* @return string|null Cart-Token JWT or null if feature disabled or unavailable.
|
||||
*/
|
||||
protected function build_cart_token() {
|
||||
public function build_cart_token() {
|
||||
// Check if Store API token generation is enabled.
|
||||
$token_type = woographql_setting( 'set_session_token_type', 'legacy' );
|
||||
if ( ! in_array( $token_type, [ 'store-api', 'both' ], true ) ) {
|
||||
@@ -633,10 +658,9 @@ class QL_Session_Handler extends WC_Session_Handler {
|
||||
*/
|
||||
public function set_session_expiration() {
|
||||
$this->_session_issued = time();
|
||||
// 47 hours.
|
||||
$this->_session_expiring = apply_filters( 'wc_session_expiring', $this->_session_issued + ( 60 * 60 * 47 ) ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
|
||||
// 48 hours.
|
||||
$this->_session_expiration = apply_filters( 'wc_session_expiration', $this->_session_issued + ( 60 * 60 * 48 ) ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
|
||||
|
||||
parent::set_session_expiration();
|
||||
|
||||
$this->_session_expiration = apply_filters_deprecated(
|
||||
'graphql_woocommerce_cart_session_expire',
|
||||
[ $this->_session_expiration ],
|
||||
|
||||
@@ -939,6 +939,14 @@ class GraphQLE2E extends \Codeception\Module {
|
||||
$zone->add_shipping_method( 'flat_rate' );
|
||||
$zone->add_shipping_method( 'free_shipping' );
|
||||
|
||||
$zone = new \WC_Shipping_Zone();
|
||||
$zone->set_zone_name( 'US' );
|
||||
$zone->set_zone_order( 5 );
|
||||
$zone->add_location( 'US:NY', 'state' );
|
||||
$zone->save();
|
||||
$zone->add_shipping_method( 'flat_rate' );
|
||||
$zone->add_shipping_method( 'free_shipping' );
|
||||
|
||||
global $wp_rewrite;
|
||||
|
||||
//Write the rule
|
||||
|
||||
@@ -319,7 +319,7 @@ $authToken = $success['data']['login']['authToken'];
|
||||
// any existing sessions.
|
||||
$new_session_token = $success['data']['login']['sessionToken'];
|
||||
|
||||
$I->wantTo( 'Check the cart and should be empty' );
|
||||
$I->wantTo( 'Check the cart and should contain the contents from our guest session' );
|
||||
|
||||
$cart_query = '
|
||||
query {
|
||||
@@ -346,14 +346,5 @@ $response = $I->sendGraphQLRequest(
|
||||
'woocommerce-session' => "Session {$new_session_token}",
|
||||
]
|
||||
);
|
||||
$expected_results = [
|
||||
'data' => [
|
||||
'cart' => [
|
||||
'contents' => [
|
||||
'nodes' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$I->assertEquals( $expected_results, $response );
|
||||
|
||||
@@ -488,6 +488,7 @@ class QLSessionHandlerCest {
|
||||
$actual,
|
||||
[
|
||||
$I->expectField( 'cart.contents.nodes.#.key', $cart_item_key ),
|
||||
$I->expectField( 'cart.availableShippingMethods', Signal::NOT_FALSY ),
|
||||
$I->expectNode(
|
||||
'cart.availableShippingMethods',
|
||||
[
|
||||
@@ -496,14 +497,14 @@ class QLSessionHandlerCest {
|
||||
$I->expectNode(
|
||||
'rates',
|
||||
[
|
||||
$I->expectField( 'cost', '0.00' ),
|
||||
$I->expectField( 'cost', 0 ),
|
||||
$I->expectField( 'label', 'Flat rate' ),
|
||||
]
|
||||
),
|
||||
$I->expectNode(
|
||||
'rates',
|
||||
[
|
||||
$I->expectField( 'cost', '0.00' ),
|
||||
$I->expectField( 'cost', 0 ),
|
||||
$I->expectField( 'label', 'Free shipping' ),
|
||||
]
|
||||
),
|
||||
|
||||
@@ -496,8 +496,6 @@ class CustomerQueriesTest extends \Tests\WPGraphQL\WooCommerce\TestCase\WooGraph
|
||||
customer(id: $id) {
|
||||
id
|
||||
availablePaymentMethods {
|
||||
id
|
||||
tokenId
|
||||
... on PaymentTokenCC {
|
||||
last4
|
||||
expiryMonth
|
||||
@@ -509,16 +507,12 @@ class CustomerQueriesTest extends \Tests\WPGraphQL\WooCommerce\TestCase\WooGraph
|
||||
}
|
||||
}
|
||||
availablePaymentMethodsCC {
|
||||
id
|
||||
tokenId
|
||||
last4
|
||||
expiryMonth
|
||||
expiryYear
|
||||
cardType
|
||||
}
|
||||
availablePaymentMethodsEC {
|
||||
id
|
||||
tokenId
|
||||
last4
|
||||
}
|
||||
}
|
||||
@@ -554,8 +548,6 @@ class CustomerQueriesTest extends \Tests\WPGraphQL\WooCommerce\TestCase\WooGraph
|
||||
$this->expectedNode(
|
||||
'customer.availablePaymentMethods',
|
||||
[
|
||||
$this->expectedField( 'id', $this->toRelayId( 'token', $token_cc->get_id() ) ),
|
||||
$this->expectedField( 'tokenId', $token_cc->get_id() ),
|
||||
$this->expectedField( 'last4', 1234 ),
|
||||
$this->expectedField( 'expiryMonth', $expiry_month ),
|
||||
$this->expectedField( 'expiryYear', $expiry_year ),
|
||||
@@ -565,8 +557,6 @@ class CustomerQueriesTest extends \Tests\WPGraphQL\WooCommerce\TestCase\WooGraph
|
||||
$this->expectedNode(
|
||||
'customer.availablePaymentMethodsCC',
|
||||
[
|
||||
$this->expectedField( 'id', $this->toRelayId( 'token', $token_cc->get_id() ) ),
|
||||
$this->expectedField( 'tokenId', $token_cc->get_id() ),
|
||||
$this->expectedField( 'last4', 1234 ),
|
||||
$this->expectedField( 'expiryMonth', $expiry_month ),
|
||||
$this->expectedField( 'expiryYear', $expiry_year ),
|
||||
@@ -576,8 +566,6 @@ class CustomerQueriesTest extends \Tests\WPGraphQL\WooCommerce\TestCase\WooGraph
|
||||
$this->expectedNode(
|
||||
'customer.availablePaymentMethodsEC',
|
||||
[
|
||||
$this->not()->expectedField( 'id', $this->toRelayId( 'token', $token_cc->get_id() ) ),
|
||||
$this->not()->expectedField( 'tokenId', $token_cc->get_id() ),
|
||||
$this->not()->expectedField( 'last4', 1234 ),
|
||||
$this->not()->expectedField( 'expiryMonth', $expiry_month ),
|
||||
$this->not()->expectedField( 'expiryYear', $expiry_year ),
|
||||
@@ -587,24 +575,18 @@ class CustomerQueriesTest extends \Tests\WPGraphQL\WooCommerce\TestCase\WooGraph
|
||||
$this->expectedNode(
|
||||
'customer.availablePaymentMethods',
|
||||
[
|
||||
$this->expectedField( 'id', $this->toRelayId( 'token', $token_ec->get_id() ) ),
|
||||
$this->expectedField( 'tokenId', $token_ec->get_id() ),
|
||||
$this->expectedField( 'last4', 4567 ),
|
||||
]
|
||||
),
|
||||
$this->expectedNode(
|
||||
'customer.availablePaymentMethodsCC',
|
||||
[
|
||||
$this->not()->expectedField( 'id', $this->toRelayId( 'token', $token_ec->get_id() ) ),
|
||||
$this->not()->expectedField( 'tokenId', $token_ec->get_id() ),
|
||||
$this->not()->expectedField( 'last4', 4567 ),
|
||||
]
|
||||
),
|
||||
$this->expectedNode(
|
||||
'customer.availablePaymentMethodsEC',
|
||||
[
|
||||
$this->expectedField( 'id', $this->toRelayId( 'token', $token_ec->get_id() ) ),
|
||||
$this->expectedField( 'tokenId', $token_ec->get_id() ),
|
||||
$this->expectedField( 'last4', 4567 ),
|
||||
]
|
||||
),
|
||||
|
||||
@@ -33,8 +33,14 @@ class PaymentMethodMutationsTest extends \Tests\WPGraphQL\WooCommerce\TestCase\W
|
||||
setDefaultPaymentMethod(input: { tokenId: $tokenId }) {
|
||||
status
|
||||
token {
|
||||
id
|
||||
type
|
||||
isDefault
|
||||
... on PaymentTokenCC {
|
||||
last4
|
||||
}
|
||||
... on PaymentTokenECheck {
|
||||
last4
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -67,7 +73,7 @@ class PaymentMethodMutationsTest extends \Tests\WPGraphQL\WooCommerce\TestCase\W
|
||||
$this->expectedObject(
|
||||
'setDefaultPaymentMethod.token',
|
||||
[
|
||||
$this->expectedField( 'id', $this->toRelayId( 'token', $token_ec->get_id() ) ),
|
||||
$this->expectedField( 'type', 'eCheck' ),
|
||||
$this->expectedField( 'isDefault', true ),
|
||||
]
|
||||
),
|
||||
@@ -83,7 +89,7 @@ class PaymentMethodMutationsTest extends \Tests\WPGraphQL\WooCommerce\TestCase\W
|
||||
$this->expectedObject(
|
||||
'setDefaultPaymentMethod.token',
|
||||
[
|
||||
$this->expectedField( 'id', $this->toRelayId( 'token', $token_cc->get_id() ) ),
|
||||
$this->expectedField( 'type', 'CC' ),
|
||||
$this->expectedField( 'isDefault', true ),
|
||||
]
|
||||
),
|
||||
|
||||
@@ -163,6 +163,28 @@ function init_auth_router() {
|
||||
}
|
||||
add_action( 'plugins_loaded', 'WPGraphQL\WooCommerce\init_auth_router' );
|
||||
|
||||
/**
|
||||
* Prevent WooCommerce from loading cart during 'init' for GraphQL requests.
|
||||
*
|
||||
* WooCommerce calls wc_load_cart() during the 'init' action for frontend requests.
|
||||
* This happens before JWT authentication can set the current user, causing the
|
||||
* session to be initialized with the wrong user context.
|
||||
*
|
||||
* By making WooCommerce think GraphQL requests are REST API requests, we prevent
|
||||
* the early cart loading. The cart is then loaded later during 'graphql_before_execute'
|
||||
* after JWT authentication has had a chance to run.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function prevent_early_wc_cart_loading() {
|
||||
if ( ! is_graphql_http_request() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
add_filter( 'woocommerce_is_rest_api_request', '__return_true' );
|
||||
}
|
||||
add_action( 'plugins_loaded', 'WPGraphQL\WooCommerce\prevent_early_wc_cart_loading', 0 );
|
||||
|
||||
// Load constants.
|
||||
constants();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user