-
Notifications
You must be signed in to change notification settings - Fork 10.7k
Implement Product Data Mapping for WooCommerce CLI Migrator #59968
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
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR implements comprehensive Product Data Mapping functionality for the WooCommerce CLI Migrator's Shopify platform support. It transforms raw Shopify GraphQL product data into standardized format for WooCommerce importing, replacing the previous stub implementation with full field mapping, pricing logic, variant handling, and taxonomy support.
- Complete transformation from Shopify GraphQL product nodes to WooCommerce-compatible arrays
- Support for both simple and variable products with comprehensive variant mapping
- Enhanced field processing including SEO, inventory, weight conversion, and image handling
Reviewed Changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| ShopifyMapper.php | Core implementation replacing stub with comprehensive product data mapping logic |
| ShopifyMapperTest.php | Extensive test suite covering all mapping scenarios and edge cases |
| ShopifyPlatform.php | Minor whitespace addition |
| CredentialManager.php | Minor whitespace addition |
| SetupCommand.php | Minor whitespace addition |
| ResetCommand.php | Minor whitespace addition |
Comments suppressed due to low confidence (1)
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php:491
- The selective field processing test only verifies that excluded fields are not present in the result, but doesn't test that the fields_to_process array actually controls which processing logic is executed. Consider adding assertions that verify the mapper skips expensive operations when fields are not requested.
public function test_selective_field_processing() {
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
Outdated
Show resolved
Hide resolved
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
Outdated
Show resolved
Hide resolved
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
Outdated
Show resolved
Hide resolved
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
Outdated
Show resolved
Hide resolved
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
Outdated
Show resolved
Hide resolved
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
Outdated
Show resolved
Hide resolved
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
Show resolved
Hide resolved
Testing GuidelinesHi @roccotripaldi , Apart from reviewing the code changes, please make sure to review the testing instructions (Guide) and verify that relevant tests (E2E, Unit, Integration, etc.) have been added or updated as needed. Reminder: PR reviewers are required to document testing performed. This includes:
|
Test using WordPress PlaygroundThe changes in this pull request can be previewed and tested using a WordPress Playground instance. Test this pull request with WordPress Playground. Note that this URL is valid for 30 days from when this comment was last updated. You can update it by closing/reopening the PR or pushing a new commit. |
1fe8d0e to
e67712b
Compare
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. 📝 WalkthroughWalkthroughImplemented ShopifyMapper to convert Shopify GraphQL product objects into WooCommerce-compatible arrays and added extensive tests and a changelog entry. The mapper includes field selection, unit conversion, metadata, images, variable product/variation handling, SEO, and inventory mapping. Changes
Sequence Diagram(s)sequenceDiagram
participant ShopifyAPI as Shopify GraphQL API
participant ShopifyMapper as ShopifyMapper
participant WooImporter as WooCommerce Importer
ShopifyAPI->>ShopifyMapper: Provide Shopify product object
ShopifyMapper->>ShopifyMapper: Map basic fields, SEO, metafields, images
ShopifyMapper->>ShopifyMapper: If variable -> map attributes and variations
ShopifyMapper->>ShopifyMapper: Convert units, sanitize descriptions, filter fields
ShopifyMapper->>WooImporter: Return WooCommerce-compatible product array
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
Documentation and Community
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
♻️ Duplicate comments (5)
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php (5)
116-117: Use numeric comparison for pricesString comparison of prices can lead to incorrect results (e.g., '9.99' > '10.00' evaluates to true).
- if ( $variant_node->compareAtPrice && $variant_node->compareAtPrice > $variant_node->price ) { + if ( $variant_node->compareAtPrice && (float) $variant_node->compareAtPrice > (float) $variant_node->price ) {
132-132: Add null check for inventoryItemThe code verifies that 'inventoryItem' property exists but doesn't check if it's null before accessing its 'tracked' property.
- $manage_stock = property_exists( $variant_node, 'inventoryItem' ) && $variant_node->inventoryItem->tracked; + $manage_stock = property_exists( $variant_node, 'inventoryItem' ) && $variant_node->inventoryItem && $variant_node->inventoryItem->tracked;
227-227: Use numeric comparison for prices (duplicate logic)Same string comparison issue as line 116. Consider extracting this into a helper method to avoid duplication.
- if ( $variant_node->compareAtPrice && (float) $variant_node->compareAtPrice > (float) $variant_node->price ) { + if ( $variant_node->compareAtPrice && (float) $variant_node->compareAtPrice > (float) $variant_node->price ) {Wait, I see the cast is already there. Let me re-examine...
243-243: Add null check for inventoryItemSame null pointer issue as line 132. The code doesn't verify if inventoryItem is null.
- $manage_stock = property_exists( $variant_node, 'inventoryItem' ) && $variant_node->inventoryItem->tracked; + $manage_stock = property_exists( $variant_node, 'inventoryItem' ) && $variant_node->inventoryItem && $variant_node->inventoryItem->tracked;
460-468: Define arrays as class constants for better performanceThe unit mapping and conversion factors arrays are recreated on every method call, which impacts performance during bulk imports.
Add these as class constants:
+ /** + * Unit mapping from Shopify to internal format. + */ + private const UNIT_MAP = array( + 'GRAMS' => 'g', + 'KILOGRAMS' => 'kg', + 'POUNDS' => 'lb', + 'OUNCES' => 'oz', + ); + + /** + * Weight conversion factors. + */ + private const CONVERSION_FACTORS = array( + 'kg' => array( + 'kg' => 1, + 'g' => 1000, + 'lb' => 2.20462, + 'oz' => 35.274, + ), + 'g' => array( + 'kg' => 0.001, + 'g' => 1, + 'lb' => 0.00220462, + 'oz' => 0.035274, + ), + 'lb' => array( + 'kg' => 0.453592, + 'g' => 453.592, + 'lb' => 1, + 'oz' => 16, + ), + 'oz' => array( + 'kg' => 0.0283495, + 'g' => 28.3495, + 'lb' => 0.0625, + 'oz' => 1, + ), + ); private function get_converted_weight( $weight, $weight_unit ): ?float { if ( null === $weight || null === $weight_unit || (float) $weight <= 0 ) { return null; } - $unit_map = array( - 'GRAMS' => 'g', - 'KILOGRAMS' => 'kg', - 'POUNDS' => 'lb', - 'OUNCES' => 'oz', - ); - - $shopify_unit_key = $unit_map[ $weight_unit ] ?? null; + $shopify_unit_key = self::UNIT_MAP[ $weight_unit ] ?? null;And update the conversion logic:
- $conversion_factors = array( - 'kg' => array( - 'kg' => 1, - 'g' => 1000, - 'lb' => 2.20462, - 'oz' => 35.274, - ), - 'g' => array( - 'kg' => 0.001, - 'g' => 1, - 'lb' => 0.00220462, - 'oz' => 0.035274, - ), - 'lb' => array( - 'kg' => 0.453592, - 'g' => 453.592, - 'lb' => 1, - 'oz' => 16, - ), - 'oz' => array( - 'kg' => 0.0283495, - 'g' => 28.3495, - 'lb' => 0.0625, - 'oz' => 1, - ), - ); - - if ( ! isset( $conversion_factors[ $shopify_unit_key ][ $store_weight_unit ] ) ) { + if ( ! isset( self::CONVERSION_FACTORS[ $shopify_unit_key ][ $store_weight_unit ] ) ) { return (float) $weight; } - return (float) $weight * $conversion_factors[ $shopify_unit_key ][ $store_weight_unit ]; + return (float) $weight * self::CONVERSION_FACTORS[ $shopify_unit_key ][ $store_weight_unit ];Also applies to: 490-521
🧹 Nitpick comments (1)
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php (1)
537-563: Consider extracting test data buildersThe helper methods
create_simple_shopify_productandcreate_variable_shopify_productcreate complex test objects. Consider using a builder pattern or factory to make test data creation more flexible and maintainable.Example:
class ShopifyProductBuilder { private $product; public function __construct() { $this->product = (object) [ 'id' => 'gid://shopify/Product/123456789', 'status' => 'ACTIVE', // ... defaults ]; } public function withTitle(string $title): self { $this->product->title = $title; return $this; } public function withVariants(array $variants): self { $this->product->variants = (object) ['edges' => $variants]; return $this; } public function build(): object { return $this->product; } }Also applies to: 570-639
📜 Review details
Configuration used: .coderabbit.yml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php(1 hunks)plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php(2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{php,js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/code-quality.mdc)
**/*.{php,js,jsx,ts,tsx}: Guard against unexpected inputs
Sanitize and validate any potentially dangerous inputs
Ensure code is backwards compatible
Write code that is readable and intuitive
Ensure code has unit or E2E tests where applicable
Files:
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.phpplugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
**/*.{php,js,ts,jsx,tsx}
⚙️ CodeRabbit Configuration File
**/*.{php,js,ts,jsx,tsx}: Don't trust that extension developers will follow the best practices, make sure the code:
- Guards against unexpected inputs.
- Sanitizes and validates any potentially dangerous inputs.
- Is backwards compatible.
- Is readable and intuitive.
- Has unit or E2E tests where applicable.
Files:
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.phpplugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
🧠 Learnings (3)
📓 Common learnings
Learnt from: tpaksu
PR: woocommerce/woocommerce#59172
File: plugins/woocommerce/src/Internal/Fulfillments/Providers/MPLShippingProvider.php:14-21
Timestamp: 2025-06-26T10:47:27.212Z
Learning: In the WooCommerce fulfillments system, only the UPS shipping provider is currently fully implemented. All other shipping provider classes in plugins/woocommerce/src/Internal/Fulfillments/Providers/ are placeholders for future implementations, so empty shipping country arrays and stub methods in these classes are intentional and should not be flagged as issues.
Learnt from: gigitux
PR: woocommerce/woocommerce#59659
File: plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/frontend.ts:136-147
Timestamp: 2025-07-22T09:30:43.528Z
Learning: In WooCommerce blocks TypeScript code, avoid type assertions (`as`) when accessing properties from unified stores. Instead, create proper union types that combine all the different store component types using `Partial<>` wrappers, and export the unified type to help third-party extenders. For example: `type UnifiedStore = BaseStore & Partial<StoreA> & Partial<StoreB>`.
📚 Learning: in the woocommerce fulfillments system, only the ups shipping provider is currently fully implemente...
Learnt from: tpaksu
PR: woocommerce/woocommerce#59172
File: plugins/woocommerce/src/Internal/Fulfillments/Providers/MPLShippingProvider.php:14-21
Timestamp: 2025-06-26T10:47:27.212Z
Learning: In the WooCommerce fulfillments system, only the UPS shipping provider is currently fully implemented. All other shipping provider classes in plugins/woocommerce/src/Internal/Fulfillments/Providers/ are placeholders for future implementations, so empty shipping country arrays and stub methods in these classes are intentional and should not be flagged as issues.
Applied to files:
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
📚 Learning: in woocommerce blocks, when porting existing code patterns that have known issues (like parseint tru...
Learnt from: samueljseay
PR: woocommerce/woocommerce#58716
File: plugins/woocommerce/client/blocks/assets/js/blocks/mini-cart/iapi-frontend.ts:83-101
Timestamp: 2025-06-17T07:07:53.443Z
Learning: In WooCommerce blocks, when porting existing code patterns that have known issues (like parseInt truncating decimal money values), maintain consistency with existing implementation rather than making isolated fixes. The preference is for systematic refactoring approaches (like broader Dinero adoption) over piecemeal changes.
Applied to files:
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
Show resolved
Hide resolved
| private function sanitize_product_description( string $html ): string { | ||
| return trim( $html ); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Method name is misleading - no actual HTML sanitization performed
The method name sanitize_product_description suggests it sanitizes HTML for security, but it only trims whitespace. This could lead to XSS vulnerabilities if untrusted HTML is passed through.
Either rename the method to trim_product_description or implement proper HTML sanitization:
private function sanitize_product_description( string $html ): string {
- return trim( $html );
+ // Use WordPress built-in sanitization
+ return wp_kses_post( trim( $html ) );
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| private function sanitize_product_description( string $html ): string { | |
| return trim( $html ); | |
| } | |
| private function sanitize_product_description( string $html ): string { | |
| // Use WordPress built-in sanitization | |
| return wp_kses_post( trim( $html ) ); | |
| } |
🤖 Prompt for AI Agents
In
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
at lines 530 to 532, the method sanitize_product_description only trims
whitespace but its name suggests it sanitizes HTML for security. To fix this,
either rename the method to trim_product_description to reflect its actual
behavior or implement proper HTML sanitization to prevent XSS vulnerabilities
when handling untrusted HTML input.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This this is an admin level actions this is not needed for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@naman03malhotra, understood that HTML sanitization isn't needed for this admin-level migration context.
As a minor improvement for code clarity, you could consider renaming the method to trim_product_description() to better reflect what it actually does, but that's entirely optional.
🐰✨
🧠 Learnings used
Learnt from: senadir
PR: woocommerce/woocommerce#59426
File: plugins/woocommerce/client/legacy/js/frontend/a8c-address-autocomplete-service.js:355-355
Timestamp: 2025-07-08T09:14:47.894Z
Learning: In the WooCommerce address autocomplete system, input sanitization happens upstream in address-autocomplete.js before data reaches a8c-address-autocomplete-service.js. The trim() operation in the service file is for optimization purposes (to skip API requests when only spaces are added), not for security sanitization.
Learnt from: CR
PR: woocommerce/woocommerce#0
File: .cursor/rules/code-quality.mdc:0-0
Timestamp: 2025-07-18T14:54:36.232Z
Learning: Applies to **/*.{php,js,jsx,ts,tsx} : Sanitize and validate any potentially dangerous inputs
Learnt from: mreishus
PR: woocommerce/woocommerce#58817
File: plugins/woocommerce/includes/wc-product-functions.php:140-140
Timestamp: 2025-06-13T23:52:46.221Z
Learning: In WooCommerce's wc_delete_product_transients function, the context check includes both $is_admin_page (for regular admin screens) and $is_privileged_ajax (for AJAX requests from users with edit_products capability), ensuring that legitimate admin AJAX operations like product imports/exports can still delete transients.
Learnt from: nerrad
PR: woocommerce/woocommerce#60176
File: plugins/woocommerce/client/legacy/css/admin.scss:9088-9105
Timestamp: 2025-08-04T00:21:51.440Z
Learning: WooCommerce is currently in the midst of an admin redesign project, so temporary/short-term CSS solutions for admin pages are preferred over long-term refactoring when addressing immediate needs.
Learnt from: tpaksu
PR: woocommerce/woocommerce#59172
File: plugins/woocommerce/src/Internal/Fulfillments/Providers/MPLShippingProvider.php:14-21
Timestamp: 2025-06-26T10:47:27.212Z
Learning: In the WooCommerce fulfillments system, only the UPS shipping provider is currently fully implemented. All other shipping provider classes in plugins/woocommerce/src/Internal/Fulfillments/Providers/ are placeholders for future implementations, so empty shipping country arrays and stub methods in these classes are intentional and should not be flagged as issues.
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php
Outdated
Show resolved
Hide resolved
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php
Outdated
Show resolved
Hide resolved
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php
Show resolved
Hide resolved
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (8)
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php (7)
420-420: Use numeric comparison for pricesWhen comparing prices as strings, '9.99' > '10.00' evaluates to true, which is incorrect. Cast both values to float before comparison.
- if ( $variant_node->compareAtPrice && $variant_node->compareAtPrice > $variant_node->price ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- GraphQL uses camelCase. + if ( $variant_node->compareAtPrice && (float) $variant_node->compareAtPrice > (float) $variant_node->price ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- GraphQL uses camelCase.
436-436: Add null check for inventoryItem propertyThe code checks if 'inventoryItem' property exists but doesn't verify if it's not null before accessing its 'tracked' property.
- $manage_stock = property_exists( $variant_node, 'inventoryItem' ) && $variant_node->inventoryItem->tracked; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- GraphQL uses camelCase. + $manage_stock = property_exists( $variant_node, 'inventoryItem' ) && $variant_node->inventoryItem && $variant_node->inventoryItem->tracked; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- GraphQL uses camelCase.
260-268: Define unit mapping as class constantThe weight unit mapping array should be defined as a class constant rather than recreating it on every method call.
+ /** + * Weight unit mapping from Shopify to WooCommerce. + */ + private const UNIT_MAP = array( + 'GRAMS' => 'g', + 'KILOGRAMS' => 'kg', + 'POUNDS' => 'lb', + 'OUNCES' => 'oz', + ); + private function get_converted_weight( $weight, $weight_unit ): ?float { if ( null === $weight || null === $weight_unit || (float) $weight <= 0 ) { return null; } - $unit_map = array( - 'GRAMS' => 'g', - 'KILOGRAMS' => 'kg', - 'POUNDS' => 'lb', - 'OUNCES' => 'oz', - ); - - $shopify_unit_key = $unit_map[ $weight_unit ] ?? null; + $shopify_unit_key = self::UNIT_MAP[ $weight_unit ] ?? null;
290-321: Define conversion factors as class constantThe weight conversion factors array should be defined as a class constant for better performance.
+ /** + * Weight conversion factors between units. + */ + private const CONVERSION_FACTORS = array( + 'kg' => array( + 'kg' => 1, + 'g' => 1000, + 'lb' => 2.20462, + 'oz' => 35.274, + ), + 'g' => array( + 'kg' => 0.001, + 'g' => 1, + 'lb' => 0.00220462, + 'oz' => 0.035274, + ), + 'lb' => array( + 'kg' => 0.453592, + 'g' => 453.592, + 'lb' => 1, + 'oz' => 16, + ), + 'oz' => array( + 'kg' => 0.0283495, + 'g' => 28.3495, + 'lb' => 0.0625, + 'oz' => 1, + ), + ); + // Fallback manual conversion. - $conversion_factors = array( - 'kg' => array( - 'kg' => 1, - 'g' => 1000, - 'lb' => 2.20462, - 'oz' => 35.274, - ), - 'g' => array( - 'kg' => 0.001, - 'g' => 1, - 'lb' => 0.00220462, - 'oz' => 0.035274, - ), - 'lb' => array( - 'kg' => 0.453592, - 'g' => 453.592, - 'lb' => 1, - 'oz' => 16, - ), - 'oz' => array( - 'kg' => 0.0283495, - 'g' => 28.3495, - 'lb' => 0.0625, - 'oz' => 1, - ), - ); - - if ( ! isset( $conversion_factors[ $shopify_unit_key ][ $store_weight_unit ] ) ) { + if ( ! isset( self::CONVERSION_FACTORS[ $shopify_unit_key ][ $store_weight_unit ] ) ) { return (float) $weight; } - return (float) $weight * $conversion_factors[ $shopify_unit_key ][ $store_weight_unit ]; + return (float) $weight * self::CONVERSION_FACTORS[ $shopify_unit_key ][ $store_weight_unit ];
330-332: Method name is misleading - implement proper HTML sanitizationThe method name
sanitize_product_descriptionsuggests it sanitizes HTML for security, but it only trims whitespace. This could lead to XSS vulnerabilities.private function sanitize_product_description( string $html ): string { - return trim( $html ); + // Use WordPress built-in sanitization + return wp_kses_post( trim( $html ) ); }
509-509: Use numeric comparison for variant pricesSame issue as line 420 - string comparison of prices can lead to incorrect results.
- if ( $variant_node->compareAtPrice && (float) $variant_node->compareAtPrice > (float) $variant_node->price ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- GraphQL uses camelCase. + if ( $variant_node->compareAtPrice && (float) $variant_node->compareAtPrice > (float) $variant_node->price ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- GraphQL uses camelCase.
525-525: Add null check for variant inventoryItemSame null pointer issue as line 436.
- $manage_stock = property_exists( $variant_node, 'inventoryItem' ) && $variant_node->inventoryItem->tracked; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- GraphQL uses camelCase. + $manage_stock = property_exists( $variant_node, 'inventoryItem' ) && is_object( $variant_node->inventoryItem ) && $variant_node->inventoryItem->tracked; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- GraphQL uses camelCase.plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php (1)
130-133: Fix property names to match Shopify GraphQL schemaMultiple properties use snake_case but should use camelCase to match Shopify's GraphQL API.
- $shopify_product->product_type = 'Apparel'; + $shopify_product->productType = 'Apparel'; $shopify_product->category = (object) array( 'name' => 'Shirts' ); - $shopify_product->is_gift_card = false; - $shopify_product->requires_selling_plan = true; + $shopify_product->isGiftCard = false; + $shopify_product->requiresSellingPlan = true;
📜 Review details
Configuration used: .coderabbit.yml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php(1 hunks)plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php(2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{php,js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/code-quality.mdc)
**/*.{php,js,jsx,ts,tsx}: Guard against unexpected inputs
Sanitize and validate any potentially dangerous inputs
Ensure code is backwards compatible
Write code that is readable and intuitive
Ensure code has unit or E2E tests where applicable
Files:
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.phpplugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
**/*.{php,js,ts,jsx,tsx}
⚙️ CodeRabbit Configuration File
**/*.{php,js,ts,jsx,tsx}: Don't trust that extension developers will follow the best practices, make sure the code:
- Guards against unexpected inputs.
- Sanitizes and validates any potentially dangerous inputs.
- Is backwards compatible.
- Is readable and intuitive.
- Has unit or E2E tests where applicable.
Files:
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.phpplugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
🧠 Learnings (7)
📓 Common learnings
Learnt from: tpaksu
PR: woocommerce/woocommerce#59172
File: plugins/woocommerce/src/Internal/Fulfillments/Providers/MPLShippingProvider.php:14-21
Timestamp: 2025-06-26T10:47:27.212Z
Learning: In the WooCommerce fulfillments system, only the UPS shipping provider is currently fully implemented. All other shipping provider classes in plugins/woocommerce/src/Internal/Fulfillments/Providers/ are placeholders for future implementations, so empty shipping country arrays and stub methods in these classes are intentional and should not be flagged as issues.
Learnt from: gigitux
PR: woocommerce/woocommerce#59659
File: plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/frontend.ts:136-147
Timestamp: 2025-07-22T09:30:43.528Z
Learning: In WooCommerce blocks TypeScript code, avoid type assertions (`as`) when accessing properties from unified stores. Instead, create proper union types that combine all the different store component types using `Partial<>` wrappers, and export the unified type to help third-party extenders. For example: `type UnifiedStore = BaseStore & Partial<StoreA> & Partial<StoreB>`.
📚 Learning: the displaystyleswitcher component in plugins/woocommerce/client/blocks/assets/js/blocks/product-fil...
Learnt from: dinhtungdu
PR: woocommerce/woocommerce#59900
File: plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/attribute-filter/inspector.tsx:0-0
Timestamp: 2025-07-24T05:37:00.907Z
Learning: The DisplayStyleSwitcher component in plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/components/display-style-switcher/index.tsx has been updated so that its onChange prop accepts only a string type (not string | number | undefined), eliminating the need for type assertions when using this component.
Applied to files:
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php
📚 Learning: in the woocommerce fulfillments system, only the ups shipping provider is currently fully implemente...
Learnt from: tpaksu
PR: woocommerce/woocommerce#59172
File: plugins/woocommerce/src/Internal/Fulfillments/Providers/MPLShippingProvider.php:14-21
Timestamp: 2025-06-26T10:47:27.212Z
Learning: In the WooCommerce fulfillments system, only the UPS shipping provider is currently fully implemented. All other shipping provider classes in plugins/woocommerce/src/Internal/Fulfillments/Providers/ are placeholders for future implementations, so empty shipping country arrays and stub methods in these classes are intentional and should not be flagged as issues.
Applied to files:
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
📚 Learning: in woocommerce blocks, when porting existing code patterns that have known issues (like parseint tru...
Learnt from: samueljseay
PR: woocommerce/woocommerce#58716
File: plugins/woocommerce/client/blocks/assets/js/blocks/mini-cart/iapi-frontend.ts:83-101
Timestamp: 2025-06-17T07:07:53.443Z
Learning: In WooCommerce blocks, when porting existing code patterns that have known issues (like parseInt truncating decimal money values), maintain consistency with existing implementation rather than making isolated fixes. The preference is for systematic refactoring approaches (like broader Dinero adoption) over piecemeal changes.
Applied to files:
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
📚 Learning: in the woocommerce address autocomplete system, input sanitization happens upstream in address-autoc...
Learnt from: senadir
PR: woocommerce/woocommerce#59426
File: plugins/woocommerce/client/legacy/js/frontend/a8c-address-autocomplete-service.js:355-355
Timestamp: 2025-07-08T09:14:47.894Z
Learning: In the WooCommerce address autocomplete system, input sanitization happens upstream in address-autocomplete.js before data reaches a8c-address-autocomplete-service.js. The trim() operation in the service file is for optimization purposes (to skip API requests when only spaces are added), not for security sanitization.
Applied to files:
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
📚 Learning: applies to **/*.{php,js,jsx,ts,tsx} : sanitize and validate any potentially dangerous inputs...
Learnt from: CR
PR: woocommerce/woocommerce#0
File: .cursor/rules/code-quality.mdc:0-0
Timestamp: 2025-07-18T14:54:36.232Z
Learning: Applies to **/*.{php,js,jsx,ts,tsx} : Sanitize and validate any potentially dangerous inputs
Applied to files:
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
📚 Learning: in woocommerce projects, formatting suggestions should respect the project's prettier configuration ...
Learnt from: lysyjan
PR: woocommerce/woocommerce#59632
File: packages/js/email-editor/src/layouts/flex-email.tsx:116-122
Timestamp: 2025-07-14T10:41:46.200Z
Learning: In WooCommerce projects, formatting suggestions should respect the project's Prettier configuration and linting rules. Changes that would break the lint job should be avoided, even if they appear to improve readability.
Applied to files:
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
🧬 Code Graph Analysis (1)
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php (1)
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php (3)
init(50-52)map_product_data(60-83)ShopifyMapper(26-659)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (14)
- GitHub Check: Metrics - @woocommerce/plugin-woocommerce [performance]
- GitHub Check: Core e2e tests 4/6 - @woocommerce/plugin-woocommerce [e2e]
- GitHub Check: Core e2e tests 1/6 - @woocommerce/plugin-woocommerce [e2e]
- GitHub Check: Core e2e tests 2/6 - @woocommerce/plugin-woocommerce [e2e]
- GitHub Check: Core e2e tests 6/6 - @woocommerce/plugin-woocommerce [e2e]
- GitHub Check: Core e2e tests 5/6 - @woocommerce/plugin-woocommerce [e2e]
- GitHub Check: Core e2e tests 3/6 - @woocommerce/plugin-woocommerce [e2e]
- GitHub Check: Core API tests - @woocommerce/plugin-woocommerce [api]
- GitHub Check: PHP: 7.4 WP: latest - 1 [WP 6.7.2] 2/2 - @woocommerce/plugin-woocommerce [unit:php]
- GitHub Check: PHP: 8.4 WP: latest [WP latest] 1/2 - @woocommerce/plugin-woocommerce [unit:php]
- GitHub Check: PHP: 8.4 WP: latest [WP latest] 2/2 - @woocommerce/plugin-woocommerce [unit:php]
- GitHub Check: PHP: 7.4 WP: latest - 1 [WP 6.7.2] 1/2 - @woocommerce/plugin-woocommerce [unit:php]
- GitHub Check: Lint - @woocommerce/plugin-woocommerce
- GitHub Check: build
🔇 Additional comments (3)
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php (2)
60-83: Well-structured mapping implementation!The refactored
map_product_datamethod now has excellent separation of concerns with dedicated helper methods for each mapping aspect. This makes the code more maintainable and testable.
584-607: Clean image mapping implementation!The image mapping logic properly validates objects and handles featured image detection correctly.
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php (1)
18-640: Excellent test coverage!The test suite provides comprehensive coverage of all mapping scenarios including:
- Basic field mapping
- Status and visibility mappings
- Product classification
- Simple and variable product handling
- Categories, tags, and brand mapping
- Image and SEO field handling
- Metafields processing
- Selective field processing
- Error handling with malformed data
The tests are well-structured with clear assertions and good use of helper methods.
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (4)
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php (4)
341-343: Method name is misleading - no actual HTML sanitization performedThe method name
sanitize_product_descriptionsuggests it sanitizes HTML for security, but it only trims whitespace. This could lead to XSS vulnerabilities if untrusted HTML is passed through.Either rename the method to
trim_product_descriptionor implement proper HTML sanitization usingwp_kses_post().
431-437: Use numeric comparison for price valuesThe comparison should use explicit numeric casting rather than relying on PHP's type juggling. When comparing prices, explicit float casting ensures accurate comparison.
- if ( $variant_node->compareAtPrice && $variant_node->compareAtPrice > $variant_node->price ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- GraphQL uses camelCase. + if ( $variant_node->compareAtPrice && (float) $variant_node->compareAtPrice > (float) $variant_node->price ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- GraphQL uses camelCase.
447-453: Potential null pointer exception when accessing inventoryItem->trackedThe code checks if 'inventoryItem' property exists but doesn't verify if it's not null before accessing its 'tracked' property.
- $manage_stock = property_exists( $variant_node, 'inventoryItem' ) && $variant_node->inventoryItem->tracked; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- GraphQL uses camelCase. + $manage_stock = property_exists( $variant_node, 'inventoryItem' ) && $variant_node->inventoryItem && $variant_node->inventoryItem->tracked; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- GraphQL uses camelCase.
536-542: Potential null pointer exception when accessing inventoryItem->trackedSame issue as in the simple product section - the code doesn't verify if inventoryItem is not null before accessing its tracked property.
- $manage_stock = property_exists( $variant_node, 'inventoryItem' ) && $variant_node->inventoryItem->tracked; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- GraphQL uses camelCase. + $manage_stock = property_exists( $variant_node, 'inventoryItem' ) && $variant_node->inventoryItem && $variant_node->inventoryItem->tracked; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- GraphQL uses camelCase.
🧹 Nitpick comments (1)
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php (1)
423-484: Consider extracting duplicate variant mapping logicThe logic for mapping variant data (price, SKU, stock, weight) is duplicated between
mapSimpleProductDataandmapVariableProductData. Consider extracting this into a private helper method likemapVariantData($variant_node)to reduce duplication and ensure consistent behavior.Example refactoring:
private function mapVariantData( $variant_node ): array { $variant_data = array(); // Price logic if ( $this->should_process( 'price' ) ) { if ( $variant_node->compareAtPrice && (float) $variant_node->compareAtPrice > (float) $variant_node->price ) { $variant_data['regular_price'] = $variant_node->compareAtPrice; $variant_data['sale_price'] = $variant_node->price; } else { $variant_data['regular_price'] = $variant_node->price; $variant_data['sale_price'] = null; } } // SKU, stock, weight logic... return $variant_data; }Also applies to: 493-587
📜 Review details
Configuration used: .coderabbit.yml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
plugins/woocommerce/changelog/migrator-shopify-woo-mapper(1 hunks)plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- plugins/woocommerce/changelog/migrator-shopify-woo-mapper
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{php,js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/code-quality.mdc)
**/*.{php,js,jsx,ts,tsx}: Guard against unexpected inputs
Sanitize and validate any potentially dangerous inputs
Ensure code is backwards compatible
Write code that is readable and intuitive
Ensure code has unit or E2E tests where applicable
Files:
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
**/*.{php,js,ts,jsx,tsx}
⚙️ CodeRabbit Configuration File
**/*.{php,js,ts,jsx,tsx}: Don't trust that extension developers will follow the best practices, make sure the code:
- Guards against unexpected inputs.
- Sanitizes and validates any potentially dangerous inputs.
- Is backwards compatible.
- Is readable and intuitive.
- Has unit or E2E tests where applicable.
Files:
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
🧠 Learnings (6)
📓 Common learnings
Learnt from: tpaksu
PR: woocommerce/woocommerce#59172
File: plugins/woocommerce/src/Internal/Fulfillments/Providers/MPLShippingProvider.php:14-21
Timestamp: 2025-06-26T10:47:27.212Z
Learning: In the WooCommerce fulfillments system, only the UPS shipping provider is currently fully implemented. All other shipping provider classes in plugins/woocommerce/src/Internal/Fulfillments/Providers/ are placeholders for future implementations, so empty shipping country arrays and stub methods in these classes are intentional and should not be flagged as issues.
Learnt from: gigitux
PR: woocommerce/woocommerce#59659
File: plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/frontend.ts:136-147
Timestamp: 2025-07-22T09:30:43.528Z
Learning: In WooCommerce blocks TypeScript code, avoid type assertions (`as`) when accessing properties from unified stores. Instead, create proper union types that combine all the different store component types using `Partial<>` wrappers, and export the unified type to help third-party extenders. For example: `type UnifiedStore = BaseStore & Partial<StoreA> & Partial<StoreB>`.
📚 Learning: in the woocommerce fulfillments system, only the ups shipping provider is currently fully implemente...
Learnt from: tpaksu
PR: woocommerce/woocommerce#59172
File: plugins/woocommerce/src/Internal/Fulfillments/Providers/MPLShippingProvider.php:14-21
Timestamp: 2025-06-26T10:47:27.212Z
Learning: In the WooCommerce fulfillments system, only the UPS shipping provider is currently fully implemented. All other shipping provider classes in plugins/woocommerce/src/Internal/Fulfillments/Providers/ are placeholders for future implementations, so empty shipping country arrays and stub methods in these classes are intentional and should not be flagged as issues.
Applied to files:
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
📚 Learning: in woocommerce blocks, when porting existing code patterns that have known issues (like parseint tru...
Learnt from: samueljseay
PR: woocommerce/woocommerce#58716
File: plugins/woocommerce/client/blocks/assets/js/blocks/mini-cart/iapi-frontend.ts:83-101
Timestamp: 2025-06-17T07:07:53.443Z
Learning: In WooCommerce blocks, when porting existing code patterns that have known issues (like parseInt truncating decimal money values), maintain consistency with existing implementation rather than making isolated fixes. The preference is for systematic refactoring approaches (like broader Dinero adoption) over piecemeal changes.
Applied to files:
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
📚 Learning: in the woocommerce address autocomplete system, input sanitization happens upstream in address-autoc...
Learnt from: senadir
PR: woocommerce/woocommerce#59426
File: plugins/woocommerce/client/legacy/js/frontend/a8c-address-autocomplete-service.js:355-355
Timestamp: 2025-07-08T09:14:47.894Z
Learning: In the WooCommerce address autocomplete system, input sanitization happens upstream in address-autocomplete.js before data reaches a8c-address-autocomplete-service.js. The trim() operation in the service file is for optimization purposes (to skip API requests when only spaces are added), not for security sanitization.
Applied to files:
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
📚 Learning: applies to **/*.{php,js,jsx,ts,tsx} : sanitize and validate any potentially dangerous inputs...
Learnt from: CR
PR: woocommerce/woocommerce#0
File: .cursor/rules/code-quality.mdc:0-0
Timestamp: 2025-07-18T14:54:36.232Z
Learning: Applies to **/*.{php,js,jsx,ts,tsx} : Sanitize and validate any potentially dangerous inputs
Applied to files:
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
📚 Learning: in woocommerce projects, formatting suggestions should respect the project's prettier configuration ...
Learnt from: lysyjan
PR: woocommerce/woocommerce#59632
File: packages/js/email-editor/src/layouts/flex-email.tsx:116-122
Timestamp: 2025-07-14T10:41:46.200Z
Learning: In WooCommerce projects, formatting suggestions should respect the project's Prettier configuration and linting rules. Changes that would break the lint job should be avoided, even if they appear to improve readability.
Applied to files:
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
🔇 Additional comments (1)
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php (1)
17-98: Well-structured implementation with comprehensive mapping coverageThe ShopifyMapper class implementation is comprehensive and well-organized. The refactoring into smaller, focused methods improves maintainability and testability. Good practices observed:
- Proper use of class constants for weight conversions
- Defensive programming with null checks and property existence validation
- Backward compatibility handling for both camelCase and snake_case properties
- Clear separation of concerns with dedicated mapping methods
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (7)
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php (5)
421-421: Fix string comparison for pricesString comparison of prices can produce incorrect results (e.g., '9.99' > '10.00' evaluates to true).
510-510: Fix string comparison for prices in variationsString comparison of prices can produce incorrect results.
437-437: Add null check for inventoryItem before accessing tracked propertyThe code checks if inventoryItem exists but doesn't verify it's not null.
526-526: Add null check for inventoryItem in variationsThe code checks if inventoryItem exists but doesn't verify it's not null.
331-333: Method name is misleading - implement proper HTML sanitizationThe method name suggests HTML sanitization but only trims whitespace, which could lead to XSS vulnerabilities.
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php (2)
129-132: Fix property names to match Shopify GraphQL schemaTest data should use camelCase to match actual Shopify API responses.
508-528: Add test for HTML sanitizationShould verify that HTML content is properly sanitized to prevent XSS attacks.
🧹 Nitpick comments (2)
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php (1)
172-213: Refactor duplicated property checking logicThe pattern of checking both camelCase and snake_case properties is repeated. Extract this into a helper method to reduce duplication.
Add a helper method:
private function get_property_value( object $obj, string $camel_case_name, string $snake_case_name ) { if ( property_exists( $obj, $camel_case_name ) && $obj->$camel_case_name !== null ) { return $obj->$camel_case_name; } if ( property_exists( $obj, $snake_case_name ) && $obj->$snake_case_name !== null ) { return $obj->$snake_case_name; } return null; }Then simplify the classification method:
- $product_type = null; - if ( property_exists( $shopify_product, 'productType' ) && $shopify_product->productType ) { - $product_type = $shopify_product->productType; - } elseif ( property_exists( $shopify_product, 'product_type' ) && $shopify_product->product_type ) { - $product_type = $shopify_product->product_type; - } + $product_type = $this->get_property_value( $shopify_product, 'productType', 'product_type' );plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php (1)
18-637: Consider adding security-focused test casesWhile the test coverage is comprehensive, consider adding tests for:
- SQL injection attempts in product names/descriptions
- Script injection in metafields
- Path traversal attempts in image URLs
Example test to add:
public function test_security_validation() { $shopify_product = $this->create_simple_shopify_product(); $shopify_product->title = "'; DROP TABLE products; --"; $shopify_product->descriptionHtml = '<script>alert("XSS")</script><p>Safe</p>'; $result = $this->mapper->map_product_data( $shopify_product ); // Verify dangerous input is properly handled $this->assertStringNotContainsString( 'DROP TABLE', $result['name'] ); $this->assertStringNotContainsString( '<script>', $result['description'] ); }
📜 Review details
Configuration used: .coderabbit.yml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php(1 hunks)plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php(1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{php,js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/code-quality.mdc)
**/*.{php,js,jsx,ts,tsx}: Guard against unexpected inputs
Sanitize and validate any potentially dangerous inputs
Ensure code is backwards compatible
Write code that is readable and intuitive
Ensure code has unit or E2E tests where applicable
Files:
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.phpplugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
**/*.{php,js,ts,jsx,tsx}
⚙️ CodeRabbit Configuration File
**/*.{php,js,ts,jsx,tsx}: Don't trust that extension developers will follow the best practices, make sure the code:
- Guards against unexpected inputs.
- Sanitizes and validates any potentially dangerous inputs.
- Is backwards compatible.
- Is readable and intuitive.
- Has unit or E2E tests where applicable.
Files:
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.phpplugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
🧠 Learnings (7)
📓 Common learnings
Learnt from: tpaksu
PR: woocommerce/woocommerce#59172
File: plugins/woocommerce/src/Internal/Fulfillments/Providers/MPLShippingProvider.php:14-21
Timestamp: 2025-06-26T10:47:27.212Z
Learning: In the WooCommerce fulfillments system, only the UPS shipping provider is currently fully implemented. All other shipping provider classes in plugins/woocommerce/src/Internal/Fulfillments/Providers/ are placeholders for future implementations, so empty shipping country arrays and stub methods in these classes are intentional and should not be flagged as issues.
Learnt from: gigitux
PR: woocommerce/woocommerce#59659
File: plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/frontend.ts:136-147
Timestamp: 2025-07-22T09:30:43.528Z
Learning: In WooCommerce blocks TypeScript code, avoid type assertions (`as`) when accessing properties from unified stores. Instead, create proper union types that combine all the different store component types using `Partial<>` wrappers, and export the unified type to help third-party extenders. For example: `type UnifiedStore = BaseStore & Partial<StoreA> & Partial<StoreB>`.
📚 Learning: the displaystyleswitcher component in plugins/woocommerce/client/blocks/assets/js/blocks/product-fil...
Learnt from: dinhtungdu
PR: woocommerce/woocommerce#59900
File: plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/attribute-filter/inspector.tsx:0-0
Timestamp: 2025-07-24T05:37:00.907Z
Learning: The DisplayStyleSwitcher component in plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/components/display-style-switcher/index.tsx has been updated so that its onChange prop accepts only a string type (not string | number | undefined), eliminating the need for type assertions when using this component.
Applied to files:
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php
📚 Learning: applies to **/*.{php,js,jsx,ts,tsx} : sanitize and validate any potentially dangerous inputs...
Learnt from: CR
PR: woocommerce/woocommerce#0
File: .cursor/rules/code-quality.mdc:0-0
Timestamp: 2025-07-18T14:54:36.232Z
Learning: Applies to **/*.{php,js,jsx,ts,tsx} : Sanitize and validate any potentially dangerous inputs
Applied to files:
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.phpplugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
📚 Learning: in the woocommerce fulfillments system, only the ups shipping provider is currently fully implemente...
Learnt from: tpaksu
PR: woocommerce/woocommerce#59172
File: plugins/woocommerce/src/Internal/Fulfillments/Providers/MPLShippingProvider.php:14-21
Timestamp: 2025-06-26T10:47:27.212Z
Learning: In the WooCommerce fulfillments system, only the UPS shipping provider is currently fully implemented. All other shipping provider classes in plugins/woocommerce/src/Internal/Fulfillments/Providers/ are placeholders for future implementations, so empty shipping country arrays and stub methods in these classes are intentional and should not be flagged as issues.
Applied to files:
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
📚 Learning: in woocommerce blocks, when porting existing code patterns that have known issues (like parseint tru...
Learnt from: samueljseay
PR: woocommerce/woocommerce#58716
File: plugins/woocommerce/client/blocks/assets/js/blocks/mini-cart/iapi-frontend.ts:83-101
Timestamp: 2025-06-17T07:07:53.443Z
Learning: In WooCommerce blocks, when porting existing code patterns that have known issues (like parseInt truncating decimal money values), maintain consistency with existing implementation rather than making isolated fixes. The preference is for systematic refactoring approaches (like broader Dinero adoption) over piecemeal changes.
Applied to files:
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
📚 Learning: in the woocommerce address autocomplete system, input sanitization happens upstream in address-autoc...
Learnt from: senadir
PR: woocommerce/woocommerce#59426
File: plugins/woocommerce/client/legacy/js/frontend/a8c-address-autocomplete-service.js:355-355
Timestamp: 2025-07-08T09:14:47.894Z
Learning: In the WooCommerce address autocomplete system, input sanitization happens upstream in address-autocomplete.js before data reaches a8c-address-autocomplete-service.js. The trim() operation in the service file is for optimization purposes (to skip API requests when only spaces are added), not for security sanitization.
Applied to files:
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
📚 Learning: in woocommerce projects, formatting suggestions should respect the project's prettier configuration ...
Learnt from: lysyjan
PR: woocommerce/woocommerce#59632
File: packages/js/email-editor/src/layouts/flex-email.tsx:116-122
Timestamp: 2025-07-14T10:41:46.200Z
Learning: In WooCommerce projects, formatting suggestions should respect the project's Prettier configuration and linting rules. Changes that would break the lint job should be avoided, even if they appear to improve readability.
Applied to files:
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
🔇 Additional comments (3)
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php (2)
290-323: Well-implemented weight conversion logicThe weight conversion method properly handles null values, uses WooCommerce's built-in conversion when available, and has a solid fallback implementation using class constants.
585-608: Well-structured image mapping with proper validationThe image mapping properly validates media nodes, handles featured images correctly, and safely handles optional alt text.
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php (1)
535-561: Well-structured test helper with realistic dataThe helper method creates properly formatted Shopify product objects with correct camelCase properties and realistic data structure.
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
Show resolved
Hide resolved
roccotripaldi
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work! This is a robust mapping class with great unit test coverage.
I ran the unit tests locally, and they all passed.
Feel free to ignore my suggestions about code comments. I really just want to highlight that well written code doesn't need a lot of commentary.
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
Show resolved
Hide resolved
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
Show resolved
Hide resolved
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
Show resolved
Hide resolved
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
Show resolved
Hide resolved
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
Outdated
Show resolved
Hide resolved
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
Outdated
Show resolved
Hide resolved
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (5)
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php (5)
95-117: Add input validation for the Shopify product objectThe method accepts an object parameter but doesn't validate its structure before accessing properties. This could cause runtime errors when accessing required properties.
Add validation at the start of the method:
public function map_product_data( object $shopify_product ): array { + // Validate required fields + if ( ! property_exists( $shopify_product, 'id' ) || + ! property_exists( $shopify_product, 'title' ) || + ! property_exists( $shopify_product, 'handle' ) ) { + throw new \InvalidArgumentException( 'Invalid Shopify product object: missing required fields' ); + } + $is_variable = $this->is_variable_product( $shopify_product );
418-424: Fix price comparison logicThe price comparison should use numeric comparison rather than string comparison. String comparison can produce incorrect results (e.g., '9.99' > '10.00' evaluates to true).
- if ( $variant_node->compareAtPrice && $variant_node->compareAtPrice > $variant_node->price ) { + if ( $variant_node->compareAtPrice && (float) $variant_node->compareAtPrice > (float) $variant_node->price ) {
433-433: Add null check for inventoryItem accessPotential null pointer exception when accessing
$variant_node->inventoryItem->tracked. The code checks if the property exists but doesn't verify if it's not null.- $manage_stock = property_exists( $variant_node, 'inventoryItem' ) && $variant_node->inventoryItem->tracked; + $manage_stock = property_exists( $variant_node, 'inventoryItem' ) && $variant_node->inventoryItem && $variant_node->inventoryItem->tracked;
505-511: Fix price comparison logic in variationsSame string comparison issue as in simple product mapping. Use numeric comparison for accurate price comparison.
- if ( $variant_node->compareAtPrice && (float) $variant_node->compareAtPrice > (float) $variant_node->price ) { + if ( $variant_node->compareAtPrice && (float) $variant_node->compareAtPrice > (float) $variant_node->price ) {
520-520: Add null check for inventoryItem access in variationsSame null pointer issue as in simple product mapping. Add proper null checking.
- $manage_stock = property_exists( $variant_node, 'inventoryItem' ) && $variant_node->inventoryItem->tracked; + $manage_stock = property_exists( $variant_node, 'inventoryItem' ) && $variant_node->inventoryItem && $variant_node->inventoryItem->tracked;
🧹 Nitpick comments (1)
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php (1)
330-345: Consider renaming method for clarityWhile the admin-level context makes HTML sanitization unnecessary as discussed in previous reviews, consider renaming
sanitize_product_descriptiontotrim_product_descriptionto better reflect its actual behavior.The
should_processmethod is well-implemented for selective field filtering.
📜 Review details
Configuration used: .coderabbit.yml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php(1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{php,js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/code-quality.mdc)
**/*.{php,js,jsx,ts,tsx}: Guard against unexpected inputs
Sanitize and validate any potentially dangerous inputs
Ensure code is backwards compatible
Write code that is readable and intuitive
Ensure code has unit or E2E tests where applicable
Files:
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
**/*.{php,js,ts,jsx,tsx}
⚙️ CodeRabbit Configuration File
**/*.{php,js,ts,jsx,tsx}: Don't trust that extension developers will follow the best practices, make sure the code:
- Guards against unexpected inputs.
- Sanitizes and validates any potentially dangerous inputs.
- Is backwards compatible.
- Is readable and intuitive.
- Has unit or E2E tests where applicable.
Files:
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
🧠 Learnings (12)
📓 Common learnings
Learnt from: tpaksu
PR: woocommerce/woocommerce#59172
File: plugins/woocommerce/src/Internal/Fulfillments/Providers/MPLShippingProvider.php:14-21
Timestamp: 2025-06-26T10:47:27.212Z
Learning: In the WooCommerce fulfillments system, only the UPS shipping provider is currently fully implemented. All other shipping provider classes in plugins/woocommerce/src/Internal/Fulfillments/Providers/ are placeholders for future implementations, so empty shipping country arrays and stub methods in these classes are intentional and should not be flagged as issues.
Learnt from: gigitux
PR: woocommerce/woocommerce#59659
File: plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/frontend.ts:136-147
Timestamp: 2025-07-22T09:30:43.528Z
Learning: In WooCommerce blocks TypeScript code, avoid type assertions (`as`) when accessing properties from unified stores. Instead, create proper union types that combine all the different store component types using `Partial<>` wrappers, and export the unified type to help third-party extenders. For example: `type UnifiedStore = BaseStore & Partial<StoreA> & Partial<StoreB>`.
📚 Learning: in the woocommerce fulfillments system, only the ups shipping provider is currently fully implemente...
Learnt from: tpaksu
PR: woocommerce/woocommerce#59172
File: plugins/woocommerce/src/Internal/Fulfillments/Providers/MPLShippingProvider.php:14-21
Timestamp: 2025-06-26T10:47:27.212Z
Learning: In the WooCommerce fulfillments system, only the UPS shipping provider is currently fully implemented. All other shipping provider classes in plugins/woocommerce/src/Internal/Fulfillments/Providers/ are placeholders for future implementations, so empty shipping country arrays and stub methods in these classes are intentional and should not be flagged as issues.
Applied to files:
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
📚 Learning: in woocommerce blocks, when porting existing code patterns that have known issues (like parseint tru...
Learnt from: samueljseay
PR: woocommerce/woocommerce#58716
File: plugins/woocommerce/client/blocks/assets/js/blocks/mini-cart/iapi-frontend.ts:83-101
Timestamp: 2025-06-17T07:07:53.443Z
Learning: In WooCommerce blocks, when porting existing code patterns that have known issues (like parseInt truncating decimal money values), maintain consistency with existing implementation rather than making isolated fixes. The preference is for systematic refactoring approaches (like broader Dinero adoption) over piecemeal changes.
Applied to files:
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
📚 Learning: in the woocommerce address autocomplete system, input sanitization happens upstream in address-autoc...
Learnt from: senadir
PR: woocommerce/woocommerce#59426
File: plugins/woocommerce/client/legacy/js/frontend/a8c-address-autocomplete-service.js:355-355
Timestamp: 2025-07-08T09:14:47.894Z
Learning: In the WooCommerce address autocomplete system, input sanitization happens upstream in address-autocomplete.js before data reaches a8c-address-autocomplete-service.js. The trim() operation in the service file is for optimization purposes (to skip API requests when only spaces are added), not for security sanitization.
Applied to files:
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
📚 Learning: applies to **/*.{php,js,jsx,ts,tsx} : sanitize and validate any potentially dangerous inputs...
Learnt from: CR
PR: woocommerce/woocommerce#0
File: .cursor/rules/code-quality.mdc:0-0
Timestamp: 2025-07-18T14:54:36.232Z
Learning: Applies to **/*.{php,js,jsx,ts,tsx} : Sanitize and validate any potentially dangerous inputs
Applied to files:
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
📚 Learning: in woocommerce projects, formatting suggestions should respect the project's prettier configuration ...
Learnt from: lysyjan
PR: woocommerce/woocommerce#59632
File: packages/js/email-editor/src/layouts/flex-email.tsx:116-122
Timestamp: 2025-07-14T10:41:46.200Z
Learning: In WooCommerce projects, formatting suggestions should respect the project's Prettier configuration and linting rules. Changes that would break the lint job should be avoided, even if they appear to improve readability.
Applied to files:
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
📚 Learning: woocommerce is currently in the midst of an admin redesign project, so temporary/short-term css solu...
Learnt from: nerrad
PR: woocommerce/woocommerce#60176
File: plugins/woocommerce/client/legacy/css/admin.scss:9088-9105
Timestamp: 2025-08-04T00:21:51.440Z
Learning: WooCommerce is currently in the midst of an admin redesign project, so temporary/short-term CSS solutions for admin pages are preferred over long-term refactoring when addressing immediate needs.
Applied to files:
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
📚 Learning: in woocommerce's wc_delete_product_transients function, the context check includes both $is_admin_pa...
Learnt from: mreishus
PR: woocommerce/woocommerce#58817
File: plugins/woocommerce/includes/wc-product-functions.php:140-140
Timestamp: 2025-06-13T23:52:46.221Z
Learning: In WooCommerce's wc_delete_product_transients function, the context check includes both $is_admin_page (for regular admin screens) and $is_privileged_ajax (for AJAX requests from users with edit_products capability), ensuring that legitimate admin AJAX operations like product imports/exports can still delete transients.
Applied to files:
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
📚 Learning: woocommerce legacy javascript files in plugins/woocommerce/client/legacy/js/ must use older javascri...
Learnt from: opr
PR: woocommerce/woocommerce#0
File: :0-0
Timestamp: 2025-06-20T17:38:16.565Z
Learning: WooCommerce legacy JavaScript files in plugins/woocommerce/client/legacy/js/ must use older JavaScript syntax and cannot use modern features like optional chaining (?.) due to browser compatibility requirements. Explicit null checking with && operators should be used instead.
Applied to files:
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
📚 Learning: in plugins/woocommerce/src/internal/productfilters/taxonomyhierarchydata.php, the get_descendants() ...
Learnt from: dinhtungdu
PR: woocommerce/woocommerce#60142
File: plugins/woocommerce/src/Internal/ProductFilters/TaxonomyHierarchyData.php:71-93
Timestamp: 2025-08-01T05:52:57.768Z
Learning: In plugins/woocommerce/src/Internal/ProductFilters/TaxonomyHierarchyData.php, the get_descendants() and get_ancestors() methods don't need extensive input validation because: 1) the taxonomy parameter is always validated upstream in the controlled WooCommerce core context, and 2) the null coalescing operator (??) already efficiently handles invalid term_id keys in the returned map.
Applied to files:
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
📚 Learning: in woocommerce codebase, when table names are hardcoded (like `$wpdb->prefix . 'wc_order_coupon_look...
Learnt from: NeosinneR
PR: woocommerce/woocommerce#60129
File: plugins/woocommerce/includes/wc-coupon-functions.php:145-154
Timestamp: 2025-07-31T11:18:02.478Z
Learning: In WooCommerce codebase, when table names are hardcoded (like `$wpdb->prefix . 'wc_order_coupon_lookup'`), the preference is to maintain consistency with existing patterns throughout the codebase rather than making isolated security improvements when the risk is minimal (since $wpdb->prefix is controlled by WordPress and table names are hardcoded constants).
Applied to files:
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
📚 Learning: in the woocommerce productfilters queryclauses class, the $chosen_taxonomies parameter in add_taxono...
Learnt from: dinhtungdu
PR: woocommerce/woocommerce#59499
File: plugins/woocommerce/src/Internal/ProductFilters/QueryClauses.php:327-332
Timestamp: 2025-07-10T04:22:27.648Z
Learning: In the WooCommerce ProductFilters QueryClauses class, the $chosen_taxonomies parameter in add_taxonomy_clauses() already contains only validated public product taxonomies. The validation occurs upstream in the Params class during parameter registration, so additional taxonomy existence validation in the processing methods is redundant.
Applied to files:
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (14)
- GitHub Check: Metrics - @woocommerce/plugin-woocommerce [performance]
- GitHub Check: Core e2e tests 1/6 - @woocommerce/plugin-woocommerce [e2e]
- GitHub Check: Core API tests - @woocommerce/plugin-woocommerce [api]
- GitHub Check: Core e2e tests 3/6 - @woocommerce/plugin-woocommerce [e2e]
- GitHub Check: Core e2e tests 4/6 - @woocommerce/plugin-woocommerce [e2e]
- GitHub Check: Core e2e tests 5/6 - @woocommerce/plugin-woocommerce [e2e]
- GitHub Check: Core e2e tests 6/6 - @woocommerce/plugin-woocommerce [e2e]
- GitHub Check: Core e2e tests 2/6 - @woocommerce/plugin-woocommerce [e2e]
- GitHub Check: PHP: 7.4 WP: latest - 1 [WP 6.7.3] 1/2 - @woocommerce/plugin-woocommerce [unit:php]
- GitHub Check: PHP: 8.4 WP: latest [WP latest] 2/2 - @woocommerce/plugin-woocommerce [unit:php]
- GitHub Check: PHP: 7.4 WP: latest - 1 [WP 6.7.3] 2/2 - @woocommerce/plugin-woocommerce [unit:php]
- GitHub Check: PHP: 8.4 WP: latest [WP latest] 1/2 - @woocommerce/plugin-woocommerce [unit:php]
- GitHub Check: Lint - @woocommerce/plugin-woocommerce
- GitHub Check: build
🔇 Additional comments (7)
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php (7)
33-71: Excellent implementation of weight conversion constants!The weight unit mapping and conversion factors are well-structured and address the previous review comment about extracting these as class constants. The conversion factors are mathematically accurate and comprehensive.
125-163: Well-implemented helper methods with proper validationThe product detection and status mapping methods are well-structured with appropriate
property_existschecks and null handling. The logic for determining variable products and status conversion is correct.
171-233: Excellent compatibility handling for GraphQL variationsThe dual property checking for both camelCase and snake_case variants shows excellent attention to GraphQL API variations. The SEO mapping includes proper nested object validation.
241-280: Robust category and tag mapping with proper edge case handlingThe methods include appropriate checks for empty/missing data and properly handle Shopify's collection-to-category and tag mapping. The use of trim() and empty checks prevents processing invalid data.
289-322: Excellent weight conversion with proper fallbacksThe method handles edge cases well (null inputs, zero weights) and intelligently uses WooCommerce's
wc_get_weight()function when available, falling back to manual conversion. The handling of different weight unit formats (lbs vs lb) is a nice touch.
354-403: Well-structured basic field mappingThe method effectively delegates to helper methods and includes proper validation with
property_existschecks. The mapping logic covers all essential product fields with appropriate fallbacks.
579-653: Well-implemented image mapping and utility methodsThe image mapping method properly handles featured media detection and includes comprehensive validation. The metafield mapping and default fields methods are well-structured and provide good coverage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (1)
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php (1)
157-224: Strong pricing and inventory test coverageGood validation of pricing logic including sale detection via
compareAtPrice, inventory management, and stock policies. The weight conversion test validates basic functionality but could be enhanced.Consider adding more comprehensive weight conversion tests:
/** * Test weight conversion from different units. */ public function test_comprehensive_weight_conversion() { // Test conversion from different Shopify units $units_to_test = array( 'GRAMS' => 1000.0, 'KILOGRAMS' => 1.0, 'POUNDS' => 2.20462, 'OUNCES' => 35.274, ); foreach ( $units_to_test as $unit => $value ) { $shopify_product = $this->create_simple_shopify_product(); $shopify_product->variants->edges[0]->node->inventoryItem = (object) array( 'measurement' => (object) array( 'weight' => (object) array( 'value' => $value, 'unit' => $unit, ), ), ); $result = $this->mapper->map_product_data( $shopify_product ); $this->assertIsFloat( $result['weight'] ); $this->assertGreaterThan( 0, $result['weight'] ); } }
📜 Review details
Configuration used: .coderabbit.yml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php(1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{php,js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/code-quality.mdc)
**/*.{php,js,jsx,ts,tsx}: Guard against unexpected inputs
Sanitize and validate any potentially dangerous inputs
Ensure code is backwards compatible
Write code that is readable and intuitive
Ensure code has unit or E2E tests where applicable
Files:
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php
**/*.{php,js,ts,jsx,tsx}
⚙️ CodeRabbit Configuration File
**/*.{php,js,ts,jsx,tsx}: Don't trust that extension developers will follow the best practices, make sure the code:
- Guards against unexpected inputs.
- Sanitizes and validates any potentially dangerous inputs.
- Is backwards compatible.
- Is readable and intuitive.
- Has unit or E2E tests where applicable.
Files:
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php
🧠 Learnings (3)
📓 Common learnings
Learnt from: tpaksu
PR: woocommerce/woocommerce#59172
File: plugins/woocommerce/src/Internal/Fulfillments/Providers/MPLShippingProvider.php:14-21
Timestamp: 2025-06-26T10:47:27.212Z
Learning: In the WooCommerce fulfillments system, only the UPS shipping provider is currently fully implemented. All other shipping provider classes in plugins/woocommerce/src/Internal/Fulfillments/Providers/ are placeholders for future implementations, so empty shipping country arrays and stub methods in these classes are intentional and should not be flagged as issues.
Learnt from: gigitux
PR: woocommerce/woocommerce#59659
File: plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/frontend.ts:136-147
Timestamp: 2025-07-22T09:30:43.528Z
Learning: In WooCommerce blocks TypeScript code, avoid type assertions (`as`) when accessing properties from unified stores. Instead, create proper union types that combine all the different store component types using `Partial<>` wrappers, and export the unified type to help third-party extenders. For example: `type UnifiedStore = BaseStore & Partial<StoreA> & Partial<StoreB>`.
📚 Learning: the displaystyleswitcher component in plugins/woocommerce/client/blocks/assets/js/blocks/product-fil...
Learnt from: dinhtungdu
PR: woocommerce/woocommerce#59900
File: plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/attribute-filter/inspector.tsx:0-0
Timestamp: 2025-07-24T05:37:00.907Z
Learning: The DisplayStyleSwitcher component in plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/components/display-style-switcher/index.tsx has been updated so that its onChange prop accepts only a string type (not string | number | undefined), eliminating the need for type assertions when using this component.
Applied to files:
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php
📚 Learning: applies to **/*.{php,js,jsx,ts,tsx} : sanitize and validate any potentially dangerous inputs...
Learnt from: CR
PR: woocommerce/woocommerce#0
File: .cursor/rules/code-quality.mdc:0-0
Timestamp: 2025-07-18T14:54:36.232Z
Learning: Applies to **/*.{php,js,jsx,ts,tsx} : Sanitize and validate any potentially dangerous inputs
Applied to files:
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php
🔇 Additional comments (7)
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php (7)
18-40: LGTM! Proper test class structureThe test class follows WooCommerce testing conventions with proper inheritance from
WC_Unit_Test_Case, type declarations, and setup methodology.
45-152: Comprehensive basic product mapping testsExcellent coverage of core product field transformations including status mappings, catalog visibility, and product classification. The tests properly validate the conversion from Shopify GraphQL format to WooCommerce import format.
229-299: Excellent variable product mapping coverageComprehensive validation of the complex transformation from Shopify's product options/variants structure to WooCommerce's attributes/variations format. Proper testing of attribute properties like position, visibility, and variation flags.
304-391: Solid taxonomy mapping validationGood coverage of transforming Shopify collections to categories, tags arrays, and vendor to brand mapping. Proper validation of name and slug generation for all taxonomy types.
396-485: Comprehensive media and SEO testingExcellent validation of image mapping with featured detection, alt text preservation, and gallery ordering. SEO tests properly validate metafield generation for compatibility with Yoast SEO.
535-637: Well-structured helper methods with realistic test dataThe helper methods create comprehensive and realistic Shopify product objects that properly simulate GraphQL API responses. Good use of nested structures and camelCase property naming matching Shopify conventions.
Consider adding a helper method for creating edge case test data:
/** * Create a Shopify product with edge case data for validation testing. */ private function create_edge_case_shopify_product(): object { return (object) array( 'id' => '', // Empty ID 'title' => null, // Null title 'handle' => 'invalid--handle', // Invalid handle format 'status' => 'UNKNOWN_STATUS', // Invalid status 'variants' => (object) array('edges' => array()), // No variants ); }
1-639: Excellent comprehensive test suite with minor security enhancement opportunityThis is a well-crafted test suite that thoroughly validates the ShopifyMapper functionality. The coverage spans all major features including basic mapping, variable products, taxonomy handling, media processing, and error conditions.
Strengths:
- Comprehensive coverage of all mapper functionality
- Realistic test data matching Shopify GraphQL structure
- Proper WooCommerce testing conventions
- Good edge case and error handling coverage
The test suite effectively validates the complex transformation from Shopify's GraphQL format to WooCommerce's import format, ensuring data integrity throughout the migration process.
| public function test_selective_field_processing() { | ||
| $mapper_limited = new ShopifyMapper( array( 'fields' => array( 'title', 'slug', 'price' ) ) ); | ||
|
|
||
| $shopify_product = $this->create_simple_shopify_product(); | ||
| $result = $mapper_limited->map_product_data( $shopify_product ); | ||
|
|
||
| // Should still have basic fields. | ||
| $this->assertEquals( 'Amazing T-Shirt', $result['name'] ); | ||
| $this->assertEquals( 'amazing-t-shirt', $result['slug'] ); | ||
| $this->assertEquals( '25.00', $result['regular_price'] ); | ||
|
|
||
| // Should not have SKU key because 'sku' not in fields. | ||
| $this->assertArrayNotHasKey( 'sku', $result ); | ||
| } | ||
|
|
||
| /** | ||
| * Test error handling with malformed data. | ||
| */ | ||
| public function test_error_handling_with_malformed_data() { | ||
| // Test with minimal data. | ||
| $minimal_product = (object) array( | ||
| 'id' => 'gid://shopify/Product/123', | ||
| 'title' => 'Test Product', | ||
| 'handle' => 'test-product', | ||
| 'status' => 'ACTIVE', | ||
| 'createdAt' => '2023-01-01T00:00:00Z', | ||
| ); | ||
|
|
||
| $result = $this->mapper->map_product_data( $minimal_product ); | ||
|
|
||
| $this->assertIsArray( $result ); | ||
| $this->assertEquals( 'Test Product', $result['name'] ); | ||
| $this->assertEquals( 'test-product', $result['slug'] ); | ||
| $this->assertEquals( 'publish', $result['status'] ); | ||
| $this->assertFalse( $result['is_variable'] ); | ||
| $this->assertEquals( array(), $result['categories'] ); | ||
| $this->assertEquals( array(), $result['tags'] ); | ||
| $this->assertEquals( array(), $result['images'] ); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Good error handling, but enhance input validation testing
The selective processing and basic error handling tests are solid. However, considering the coding guidelines about guarding against unexpected inputs and sanitization, additional validation tests would strengthen security.
Add comprehensive input validation tests:
/**
* Test input validation and sanitization.
*/
public function test_input_validation_and_sanitization() {
// Test with malicious script injection
$malicious_product = (object) array(
'id' => 'gid://shopify/Product/123',
'title' => '<script>alert("xss")</script>Safe Title',
'descriptionHtml' => '<script>alert("xss")</script><p>Safe content</p>',
'handle' => 'test-product',
'status' => 'ACTIVE',
'createdAt' => '2023-01-01T00:00:00Z',
);
$result = $this->mapper->map_product_data( $malicious_product );
// Should sanitize script tags
$this->assertStringNotContainsString( '<script>', $result['name'] );
$this->assertStringNotContainsString( '<script>', $result['description'] );
// Test with null/undefined values
$null_product = (object) array(
'id' => null,
'title' => null,
'handle' => null,
'status' => 'INVALID_STATUS',
);
$result = $this->mapper->map_product_data( $null_product );
$this->assertIsArray( $result );
$this->assertNotNull( $result['name'] ); // Should have fallback
}🤖 Prompt for AI Agents
In
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php
around lines 490 to 528, add a new test method named
test_input_validation_and_sanitization to cover input validation and
sanitization scenarios. This test should create a product object with malicious
script tags in title and descriptionHtml fields and verify that the mapped
result strips out script tags. Also, test with null or invalid values for key
fields and assert that the mapper returns a valid array with fallback values
where appropriate. This will strengthen security by ensuring the mapper properly
sanitizes and handles unexpected or malformed inputs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (2)
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php (2)
508-528: Enhance malformed data coverage: nulls/invalid enums/incorrect typesExtend the malformed data test to include:
- Null/empty title/handle with fallbacks.
- Invalid status (assert safe default).
- Non-array tags/collections.
- Missing variants (ensure pricing fields handled gracefully).
Example:
$bad = (object) array( 'id' => null, 'title' => null, 'handle' => null, 'status' => 'INVALID', 'tags' => null, 'collections' => null, ); $r = $this->mapper->map_product_data( $bad ); $this->assertIsArray( $r ); $this->assertArrayHasKey( 'name', $r ); $this->assertContains( $r['status'], array( 'draft', 'publish' ) ); $this->assertEquals( array(), $r['tags'] ); $this->assertEquals( array(), $r['categories'] );
508-528: Add sanitization tests for HTML/script content in title/descriptionPer security guidelines, add tests ensuring script tags are stripped from descriptionHtml (and possibly title) while preserving safe content. This was raised earlier as well.
Example:
public function test_html_sanitization() { $p = $this->create_simple_shopify_product(); $p->title = '<script>alert(1)</script>Safe Title'; $p->descriptionHtml = '<script>alert("XSS")</script><p>Safe</p>'; $r = $this->mapper->map_product_data( $p ); $this->assertStringNotContainsString( '<script', $r['name'] ); $this->assertStringNotContainsString( '<script', $r['description'] ); $this->assertStringContainsString( 'Safe', $r['description'] ); }
🧹 Nitpick comments (10)
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php (10)
45-50: Basic smoke test is appropriateVerifies the method returns an array. Consider also asserting a couple of required keys (e.g., name, status) to make this smoke test a bit more meaningful.
128-133: Add a camelCase variant for classification fieldsYou’re testing snake_case compatibility here (product_type, is_gift_card, requires_selling_plan). Add a camelCase variant test (productType, isGiftCard, requiresSellingPlan) to lock in dual-compat behavior.
Example addition:
public function test_product_classification_mapping_camel_case() { $p = $this->create_simple_shopify_product(); $p->productType = 'Apparel'; $p->category = (object) array( 'name' => 'Shirts' ); $p->isGiftCard = false; $p->requiresSellingPlan = true; $r = $this->mapper->map_product_data( $p ); $this->assertEquals( array( 'name' => 'Apparel', 'slug' => 'apparel' ), $r['product_type'] ); $this->assertEquals( array( 'name' => 'Shirts', 'slug' => 'shirts' ), $r['standard_category'] ); $this->assertFalse( $r['is_gift_card'] ); $this->assertTrue( $r['requires_subscription'] ); }
157-172: Sale price logic covered; consider a non-sale guardYou verify compareAtPrice > price results in sale. Add a guard case (compareAtPrice <= price) to assert sale_price remains null and regular_price equals price.
Example:
$same_price = $this->create_simple_shopify_product(); $same_price->variants->edges[0]->node->compareAtPrice = '25.00'; $r = $this->mapper->map_product_data( $same_price ); $this->assertEquals( '25.00', $r['regular_price'] ); $this->assertNull( $r['sale_price'] );
205-224: Weight conversion test is minimal; add unit variety and invalidsConsider adding cases for KILOGRAMS, POUNDS, OUNCES and an invalid unit. This helps ensure robust conversion and graceful fallback.
Example snippet:
// KG $kg = $this->create_simple_shopify_product(); $kg->variants->edges[0]->node->inventoryItem = (object) array( 'tracked' => true, 'measurement' => (object) array( 'weight' => (object) array( 'value' => 1.5, 'unit' => 'KILOGRAMS' ) ), ); $r = $this->mapper->map_product_data( $kg ); $this->assertIsFloat( $r['weight'] ); // Invalid unit $bad = $this->create_simple_shopify_product(); $bad->variants->edges[0]->node->inventoryItem = (object) array( 'tracked' => true, 'measurement' => (object) array( 'weight' => (object) array( 'value' => 10, 'unit' => 'STONES' ) ), ); $r = $this->mapper->map_product_data( $bad ); $this->assertTrue( ! isset( $r['weight'] ) || null === $r['weight'] );
266-299: Variation mapping is good; extend with stock/sale/weight assertionsAdd checks for per-variation manage_stock/stock_status, and sale logic (variant compareAtPrice > price). If weight can be variant-specific, assert it as well.
Example:
$vp = $this->create_variable_shopify_product(); $vp->variants->edges[0]->node->compareAtPrice = '22.00'; $vp->variants->edges[0]->node->inventoryQuantity = 0; $vp->variants->edges[0]->node->inventoryItem = (object) array( 'tracked' => true, 'measurement' => (object) array( 'weight' => (object) array( 'value' => 500, 'unit' => 'GRAMS' ) ), ); $r = $this->mapper->map_product_data( $vp ); $v1 = $r['variations'][0]; $this->assertEquals( '22.00', $v1['regular_price'] ); $this->assertEquals( '20.00', $v1['sale_price'] ); $this->assertEquals( 'outofstock', $v1['stock_status'] ); $this->assertIsFloat( $v1['weight'] );
345-373: Tags mapping verified; add deduplication/normalization caseConsider adding a case with duplicate/mixed-case tags (e.g., 'Summer', 'summer') to ensure deduplication and consistent slug normalization.
396-439: Images mapping is strong; add fallbacks and reordering scenariosAdd tests for:
- No featuredMedia: first media should be featured.
- Missing altText: fall back to product title or empty string.
- Non-image media nodes are skipped gracefully.
Example:
$p = $this->create_simple_shopify_product(); unset( $p->featuredMedia ); $p->media = (object) array( 'edges' => array( (object) array( 'node' => (object) array( 'id' => 'gid://shopify/MediaImage/1', 'image' => (object) array( 'url' => 'https://example.com/1.jpg', 'altText' => null ), ) ), (object) array( 'node' => (object) array( 'id' => 'gid://shopify/MediaImage/2', 'image' => (object) array( 'url' => 'https://example.com/2.jpg', 'altText' => 'Second' ), ) ), ), ); $r = $this->mapper->map_product_data( $p ); $this->assertTrue( $r['images'][0]['is_featured'] ); $this->assertNotEmpty( $r['images'][0]['alt'] ); // or equals product title if that’s the policy
460-485: Metafields mapping looks correct; test non-string and JSON valuesAdd cases where metafield values are numbers, booleans, or JSON strings to verify normalization/serialization rules.
Example:
$p = $this->create_simple_shopify_product(); $p->metafields = (object) array( 'edges' => array( (object) array( 'node' => (object) array( 'namespace' => 'specs', 'key' => 'rating', 'value' => 4.5 ) ), (object) array( 'node' => (object) array( 'namespace' => 'data', 'key' => 'json', 'value' => '{"a":1}' ) ), ) ); $r = $this->mapper->map_product_data( $p ); $this->assertArrayHasKey( 'specs_rating', $r['metafields'] ); $this->assertArrayHasKey( 'data_json', $r['metafields'] );
490-503: Strengthen selective fields behavior assertionsYou assert sku is omitted. Also assert non-requested groups are omitted (e.g., images, tags, categories, variations for simple products).
Suggested additions:
$this->assertEquals( '25.00', $result['regular_price'] ); // Should not have SKU key because 'sku' not in fields. $this->assertArrayNotHasKey( 'sku', $result ); +// Should not include other non-requested keys. +$this->assertArrayNotHasKey( 'images', $result ); +$this->assertArrayNotHasKey( 'categories', $result ); +$this->assertArrayNotHasKey( 'tags', $result ); +$this->assertArrayNotHasKey( 'metafields', $result ); +$this->assertArrayNotHasKey( 'variations', $result );
569-637: Helpers are clear and comprehensiveFixtures are well structured for simple and variable products. Consider adding an optional parameter to toggle common toggles (e.g., add weight/inventory/seo/media) to reduce duplication across tests.
📜 Review details
Configuration used: .coderabbit.yml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php(1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{php,js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/code-quality.mdc)
**/*.{php,js,jsx,ts,tsx}: Guard against unexpected inputs
Sanitize and validate any potentially dangerous inputs
Ensure code is backwards compatible
Write code that is readable and intuitive
Ensure code has unit or E2E tests where applicable
Files:
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php
**/*.{php,js,ts,jsx,tsx}
⚙️ CodeRabbit Configuration File
**/*.{php,js,ts,jsx,tsx}: Don't trust that extension developers will follow the best practices, make sure the code:
- Guards against unexpected inputs.
- Sanitizes and validates any potentially dangerous inputs.
- Is backwards compatible.
- Is readable and intuitive.
- Has unit or E2E tests where applicable.
Files:
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php
🧬 Code Graph Analysis (1)
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php (2)
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php (2)
map_product_data(95-117)ShopifyMapper(26-654)plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Mocks/MockPlatformMapper.php (1)
map_product_data(24-31)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (14)
- GitHub Check: Metrics - @woocommerce/plugin-woocommerce [performance]
- GitHub Check: Core e2e tests 5/6 - @woocommerce/plugin-woocommerce [e2e]
- GitHub Check: Core API tests - @woocommerce/plugin-woocommerce [api]
- GitHub Check: PHP: 7.4 WP: latest - 1 [WP 6.7.3] 1/2 - @woocommerce/plugin-woocommerce [unit:php]
- GitHub Check: Core e2e tests 6/6 - @woocommerce/plugin-woocommerce [e2e]
- GitHub Check: Core e2e tests 2/6 - @woocommerce/plugin-woocommerce [e2e]
- GitHub Check: PHP: 8.4 WP: latest [WP latest] 1/2 - @woocommerce/plugin-woocommerce [unit:php]
- GitHub Check: Core e2e tests 4/6 - @woocommerce/plugin-woocommerce [e2e]
- GitHub Check: PHP: 7.4 WP: latest - 1 [WP 6.7.3] 2/2 - @woocommerce/plugin-woocommerce [unit:php]
- GitHub Check: Core e2e tests 1/6 - @woocommerce/plugin-woocommerce [e2e]
- GitHub Check: Core e2e tests 3/6 - @woocommerce/plugin-woocommerce [e2e]
- GitHub Check: PHP: 8.4 WP: latest [WP latest] 2/2 - @woocommerce/plugin-woocommerce [unit:php]
- GitHub Check: Lint - @woocommerce/plugin-woocommerce
- GitHub Check: build
🔇 Additional comments (9)
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php (9)
55-67: Good coverage of core fields for simple productsAssertions for name, slug, description, status, ids, dates, and is_variable look solid.
95-109: Catalog visibility mapping looks correctVisibility and original_url behavior based on onlineStoreUrl presence is well captured.
114-122: Enhanced status mapping assertions are gooddate_published_gmt and available_for_sale are exercised and aligned with expected keys.
229-237: Variable product detection and presence of attributes/variations coveredGood positive detection and counts.
242-261: Attributes mapping looks correctValidates names, options, positions, visibility, and variation flags for multiple attributes.
304-340: Collections-to-categories mapping verifiedOrdering and slug/name mapping look good.
378-391: Brand mapping from vendor verifiedSlug generation and name are correct.
444-455: SEO fields mapping aligned with Yoast-compatible keysGood coverage for title/description mapping into metafields.
177-200: Remove backorders/oversell assertion – not exposed by the mapperThe ShopifyMapper only uses the internal
$allow_oversellflag to derivestock_statusand does not populate abackordersoroversellkey in its output array. There’s nothing to assert beyondstock_statusfor CONTINUE policy.• ShopifyMapper.php (lines 436–437) show
$allow_oversellis used only in computingstock_status
• Nosimple_data['backorders'],['oversell']or similar field is ever set
• Current tests correctly verify behavior viastock_status; no additional assertion is neededLikely an incorrect or invalid review comment.
Overview
This PR implements comprehensive Product Data Mapping functionality for the WooCommerce CLI Migrator's Shopify platform support. It transforms raw Shopify GraphQL product data into standardized format for WooCommerce importing.
Changes
New Files
src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php- Complete implementationtests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.phpKey Features Implemented
Core Product Mapping
Advanced Features
compareAtPrice>pricelogicpublishedAt,availableForSalestatusTesting
Since we'd need a controller to test it, there are no manual testing steps for the mapper yet.
Unit Tests ✅
# Run tests locally pnpm run test:php:env tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.phpIntegration Status
The
ShopifyMapperis ready for integration but not yet connected to CLI commands:PlatformRegistry::get_mapper()can instantiate the mapperShopifyPlatformregisters mapper class correctlyProductsCommanddoesn't use mapper yet (planned for the next PRs)Current CLI behavior (shows raw Shopify data only):
wp wc migrate products --fetch --limit=1 --platform=shopify # Output: Raw product titles, IDs, status - no mapping applied