-
Notifications
You must be signed in to change notification settings - Fork 184
Fix Product Syncing via Feeds #2841
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
Closed
Closed
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
d85dda8
WIP to fix feeds
3f62a54
Added GTIN to feed file.
1c52118
Implemented creation of a new feed.
74ee4c6
Added local cache feed ID invalidation logic.
ff7c5dc
Added descriptions to new functions.
9f13ab3
Add quantity_to_sell_on_facebook and woo_product_id to the feed.
caeaef1
Added tests to ApiTest.php
61b762e
Added tests to fbproductTest.php
cba18b9
Removed woo_product_id field, will be handled as seperate PR if neede…
39b304d
Fixed product_type and google_product_category
mshymon 9a8dfe7
Addressed PR comments.
mshymon 5a81179
Remove test after rebase.
mshymon e47eae3
Fixed test test_enhanced_catalog_fields_from_attributes after rebase.
mshymon 098d24a
Added to rich_text_description feed.
mshymon 51b031e
Fixed broken description via feeds for variable products.
mshymon File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
25 changes: 25 additions & 0 deletions
25
includes/API/ProductCatalog/ProductFeedUploads/Create/Request.php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| <?php | ||
| declare( strict_types=1 ); | ||
|
|
||
| namespace WooCommerce\Facebook\API\ProductCatalog\ProductFeedUploads\Create; | ||
|
|
||
| use WooCommerce\Facebook\API\Request as ApiRequest; | ||
|
|
||
| defined( 'ABSPATH' ) || exit; | ||
|
|
||
| /** | ||
| * Request object for Product Catalog > Product Feed Upload > Create Graph Api. | ||
| * | ||
| * @link https://developers.facebook.com/docs/marketing-api/reference/product-feed/uploads/#Creating | ||
| */ | ||
| class Request extends ApiRequest { | ||
|
|
||
| /** | ||
| * @param string $product_feed_id Facebook Product Feed ID. | ||
| * @param array $data Facebook Product Feed Data. | ||
| */ | ||
| public function __construct( string $product_feed_id, array $data ) { | ||
| parent::__construct( "/{$product_feed_id}/uploads", 'POST' ); | ||
| parent::set_data( $data ); | ||
| } | ||
| } |
16 changes: 16 additions & 0 deletions
16
includes/API/ProductCatalog/ProductFeedUploads/Create/Response.php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| <?php | ||
| declare( strict_types=1 ); | ||
|
|
||
| namespace WooCommerce\Facebook\API\ProductCatalog\ProductFeedUploads\Create; | ||
|
|
||
| use WooCommerce\Facebook\API\Response as ApiResponse; | ||
|
|
||
| defined( 'ABSPATH' ) || exit; | ||
|
|
||
| /** | ||
| * Response object for Product Catalog > Product Feed Upload > Create Graph Api. | ||
| * | ||
| * @link https://developers.facebook.com/docs/marketing-api/reference/product-feed/uploads/#Creating | ||
| * @property-read array $data Facebook Product Feeds Upload. | ||
| */ | ||
| class Response extends ApiResponse {} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,6 +13,9 @@ | |
|
|
||
| defined( 'ABSPATH' ) || exit; | ||
|
|
||
| use Error; | ||
| use Exception; | ||
| use WC_Facebookcommerce_Utils; | ||
| use WooCommerce\Facebook\Framework\Helper; | ||
| use WooCommerce\Facebook\Utilities\Heartbeat; | ||
| use WooCommerce\Facebook\Framework\Plugin\Exception as PluginException; | ||
|
|
@@ -36,6 +39,8 @@ class Feed { | |
| /** @var string the WordPress option name where the secret included in the feed URL is stored */ | ||
| const OPTION_FEED_URL_SECRET = 'wc_facebook_feed_url_secret'; | ||
|
|
||
| /** @var string the feed name for creating a new feed by this plugin */ | ||
| const FEED_NAME = 'Product Feed by Facebook for WooCommerce plugin. DO NOT DELETE.'; | ||
|
|
||
| /** | ||
| * Feed constructor. | ||
|
|
@@ -62,6 +67,9 @@ private function add_hooks() { | |
|
|
||
| // handle the feed data request | ||
| add_action( 'woocommerce_api_' . self::REQUEST_FEED_ACTION, array( $this, 'handle_feed_data_request' ) ); | ||
|
|
||
| // Send request for feed one time upload after feed file generated | ||
| add_action( 'wc_facebook_feed_generation_completed', array( $this, 'send_request_to_upload_feed' ) ); | ||
| } | ||
|
|
||
|
|
||
|
|
@@ -170,7 +178,7 @@ public function schedule_feed_generation() { | |
| * @since 1.11.0 | ||
| * @since 2.5.0 Feed generation interval increased to 24h. | ||
| * | ||
| * @param int $interval the frequency with which the product feed data is generated, in seconds. Defaults to every 15 minutes. | ||
| * @param int $interval the frequency with which the product feed data is generated, in seconds. | ||
| */ | ||
| $interval = apply_filters( 'wc_facebook_feed_generation_interval', DAY_IN_SECONDS ); | ||
| if ( ! as_next_scheduled_action( self::GENERATE_FEED_ACTION ) ) { | ||
|
|
@@ -179,6 +187,191 @@ public function schedule_feed_generation() { | |
| } | ||
|
|
||
|
|
||
| /** | ||
| * Sends request to Meta to start a one-time feed file upload session. | ||
| * | ||
| * @internal | ||
| */ | ||
| public function send_request_to_upload_feed() { | ||
| $feed_id = self::retrieve_or_create_integration_feed_id(); | ||
|
mshymon marked this conversation as resolved.
|
||
| if ( empty( $feed_id ) ) { | ||
| WC_Facebookcommerce_Utils::log( 'Feed: integration feed ID is null or empty, feed will not be uploaded.' ); | ||
| return; | ||
| } | ||
|
|
||
| $data = [ | ||
| 'url' => Feed::get_feed_data_url(), | ||
| ]; | ||
|
|
||
| try { | ||
| facebook_for_woocommerce()->get_api()->create_upload( $feed_id, $data ); | ||
| } catch ( Exception $exception ) { | ||
| facebook_for_woocommerce()->log( 'Failed to create feed upload request: ' . $exception->getMessage() ); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Retrieves or creates an integration feed ID | ||
| * | ||
| * @return string the integration feed ID | ||
| * | ||
| * @internal | ||
| */ | ||
| public function retrieve_or_create_integration_feed_id() { | ||
| // Step 1 - Get feed ID if it is already available in local cache | ||
| $feed_id = facebook_for_woocommerce()->get_integration()->get_feed_id(); | ||
| if ( $feed_id ) { | ||
| if ( self::validate_feed_exists($feed_id) ) { | ||
| WC_Facebookcommerce_Utils::log( 'Feed: feed_id = '.$feed_id.', from local cache was validated.'); | ||
| return $feed_id; | ||
| } else { | ||
| WC_Facebookcommerce_Utils::log( 'Feed: feed_id = '.$feed_id.', from local cache was invalidated.'); | ||
| } | ||
| } | ||
|
|
||
| // Step 2 - Query feeds data from Meta and filter the right one | ||
| $feed_id = self::query_and_filter_integration_feed_id(); | ||
|
mshymon marked this conversation as resolved.
|
||
| if ( $feed_id ) { | ||
| facebook_for_woocommerce()->get_integration()->update_feed_id($feed_id); | ||
| WC_Facebookcommerce_Utils::log( 'Feed: feed_id = '.$feed_id.', queried and filtered from Meta API.'); | ||
| return $feed_id; | ||
| } | ||
|
|
||
| // Step 3 - Create a new feed | ||
| $feed_id = self::create_feed_id(); | ||
| if ( $feed_id ) { | ||
| facebook_for_woocommerce()->get_integration()->update_feed_id($feed_id); | ||
| WC_Facebookcommerce_Utils::log( 'Feed: feed_id = '.$feed_id.', created a new feed via Meta API.'); | ||
| return $feed_id; | ||
| } | ||
|
|
||
|
mshymon marked this conversation as resolved.
|
||
| return ''; | ||
| } | ||
|
|
||
| /** | ||
| * Validates that provided feed ID still exists on the Meta side | ||
| * | ||
| * @param string $feed_id the feed ID | ||
| * | ||
| * @return bool true if the feed ID is valid | ||
| * | ||
| * @internal | ||
| */ | ||
| private function validate_feed_exists($feed_id) { | ||
| try { | ||
| $catalog_id = facebook_for_woocommerce()->get_integration()->get_product_catalog_id(); | ||
| if ( '' === $catalog_id ) { | ||
| throw new Error( 'No catalog ID' ); | ||
| } | ||
| $feed_nodes = facebook_for_woocommerce()->get_api()->read_feeds( $catalog_id )->data; | ||
| } catch ( Exception $e ) { | ||
| $message = sprintf( 'There was an error trying to get feed nodes for catalog: %s', $e->getMessage() ); | ||
| WC_Facebookcommerce_Utils::log( $message ); | ||
| return ''; | ||
| } | ||
|
|
||
| foreach ( $feed_nodes as $feed ) { | ||
| if ($feed['id'] == $feed_id) { | ||
| return true; | ||
| } | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| /** | ||
| * Queries existing feeds for the integration catalog and filters | ||
| * the plugin integration feed ID | ||
| * | ||
| * @return string the integration feed ID | ||
| * | ||
| * @internal | ||
| */ | ||
| private function query_and_filter_integration_feed_id() { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This method seems like a lot of logic |
||
| try { | ||
| $catalog_id = facebook_for_woocommerce()->get_integration()->get_product_catalog_id(); | ||
| if ( '' === $catalog_id ) { | ||
| throw new Error( 'No catalog ID' ); | ||
| } | ||
| $feed_nodes = facebook_for_woocommerce()->get_api()->read_feeds( $catalog_id )->data; | ||
| } catch ( Exception $e ) { | ||
| $message = sprintf( 'There was an error trying to get feed nodes for catalog: %s', $e->getMessage() ); | ||
| WC_Facebookcommerce_Utils::log( $message ); | ||
| return ''; | ||
| } | ||
|
|
||
| if ( empty( $feed_nodes ) ) { | ||
| return ''; | ||
| } | ||
|
|
||
| try { | ||
| $catalog = facebook_for_woocommerce()->get_api()->get_catalog( $catalog_id ); | ||
| } catch ( Exception $e ) { | ||
| $message = sprintf( 'There was an error trying to get a catalog: %s', $e->getMessage() ); | ||
| WC_Facebookcommerce_Utils::log( $message ); | ||
| } | ||
|
|
||
| /* | ||
| We need to detect which feed is the one that was created for Facebook for WooCommerce plugin usage. | ||
|
|
||
| We are detecting based on the name. | ||
| - Option 1. Plugin can create this feed name currently. | ||
| - Option 2 and 3. FBE creates a catalog with feed name '{catalog name} - Feed' or '{catalog name} – Feed' (short vs long dash) | ||
| - Option 4. Plugin used to create a feed name 'Initial product sync from WooCommerce. DO NOT DELETE.' | ||
| */ | ||
| foreach ( $feed_nodes as $feed ) { | ||
| try { | ||
| $feed_metadata = facebook_for_woocommerce()->get_api()->read_feed( $feed['id'] ); | ||
| } catch ( Exception $e ) { | ||
| $message = sprintf( 'There was an error trying to get feed metadata: %s', $e->getMessage() ); | ||
| WC_Facebookcommerce_Utils::log( $message ); | ||
| continue; | ||
| } | ||
|
|
||
| $woo_feed_name_option_1 = self::FEED_NAME; | ||
| $woo_feed_name_option_2 = sprintf( '%s - Feed', $catalog['name'] ); | ||
| $woo_feed_name_option_3 = sprintf( '%s – Feed', $catalog['name'] ); | ||
| $woo_feed_name_option_4 = 'Initial product sync from WooCommerce. DO NOT DELETE.'; | ||
|
|
||
| if ( $feed_metadata['name'] === $woo_feed_name_option_1 || | ||
| $feed_metadata['name'] === $woo_feed_name_option_2 || | ||
| $feed_metadata['name'] === $woo_feed_name_option_3 || | ||
| $feed_metadata['name'] === $woo_feed_name_option_4 ) { | ||
| return $feed['id']; | ||
| } | ||
| } | ||
|
|
||
| return ''; | ||
| } | ||
|
|
||
| /** | ||
| * Makes a request to Meta to create a new feed | ||
| * | ||
| * @return string the integration feed ID | ||
| * | ||
| * @internal | ||
| */ | ||
| private function create_feed_id() { | ||
| try { | ||
| $catalog_id = facebook_for_woocommerce()->get_integration()->get_product_catalog_id(); | ||
| if ( '' === $catalog_id ) { | ||
| throw new Error( 'No catalog ID' ); | ||
| } | ||
|
|
||
| $data = [ | ||
| 'name' => self::FEED_NAME, | ||
| ]; | ||
|
|
||
| $feed = facebook_for_woocommerce()->get_api()->create_feed( $catalog_id, $data ); | ||
| return $feed['id']; | ||
| } catch ( Exception $exception ) { | ||
| facebook_for_woocommerce()->log( 'Could not create a feed: ' . $exception->getMessage() ); | ||
| } | ||
|
|
||
| return ''; | ||
| } | ||
|
|
||
|
|
||
| /** | ||
| * Checks whether fpassthru has been disabled in PHP. | ||
| * | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.