Skip to content

Commit

Permalink
Expose Brand & MPN to Woocommerce UI (#2842)
Browse files Browse the repository at this point in the history
Summary:
Changes proposed in this Pull Request:

This PR introduces a new feature to the Facebook WooCommerce Plugin, allowing users to add a brand field to their products. With this update, we can seamlessly synchronize the brand field with the Facebook Commerce Manager platform.
<img width="679" alt="Screenshot 2025-01-10 at 18 57 18" src="https://github.com/user-attachments/assets/9d941a1f-48cb-4106-9672-1620aa926be6" />

<img width="527" alt="Screenshot 2024-10-30 at 18 08 36" src="https://github.com/user-attachments/assets/d0cf9ae0-293b-4685-a033-643dc6214a1f">

Pull Request resolved: #2842

Reviewed By: vinkmeta

Differential Revision: D68720240

Pulled By: devbodaghe

fbshipit-source-id: 0112d2063d9d9358c9f08efde51b6bf5d04b2d59
  • Loading branch information
David Evbodaghe authored and facebook-github-bot committed Feb 5, 2025
1 parent 08afb68 commit f7a6c3b
Show file tree
Hide file tree
Showing 6 changed files with 275 additions and 16 deletions.
22 changes: 22 additions & 0 deletions facebook-commerce.php
Original file line number Diff line number Diff line change
Expand Up @@ -793,6 +793,7 @@ public function on_product_save( int $wp_id ) {

$products_to_delete_from_facebook = $this->get_removed_from_sync_products_to_delete();
if ( $product->is_type( 'variable' ) ) {
$this->save_variable_product_settings( $product );
// check variations for deletion
foreach ( $products_to_delete_from_facebook as $delete_product_id ) {
$delete_product = wc_get_product( $delete_product_id );
Expand Down Expand Up @@ -841,6 +842,19 @@ public function on_product_save( int $wp_id ) {
}
}

/**
* Saves the submitted Facebook settings for a variable product.
*
*
* @param \WC_Product $product The variable product object.
*/
private function save_variable_product_settings( WC_Product $product ) {
$woo_product = new WC_Facebook_Product( $product->get_id() );
if ( isset( $_POST[ WC_Facebook_Product::FB_VARIABLE_BRAND ] ) ) {
$woo_product->set_fb_brand( sanitize_text_field( wp_unslash( $_POST[ WC_Facebook_Product::FB_VARIABLE_BRAND ] ) ) );
}
}

/**
* Saves the submitted Facebook settings for a product.
*
Expand Down Expand Up @@ -868,6 +882,14 @@ private function save_product_settings( WC_Product $product ) {
if ( isset( $_POST[ WC_Facebook_Product::FB_PRODUCT_IMAGE ] ) ) {
$woo_product->set_product_image( sanitize_text_field( wp_unslash( $_POST[ WC_Facebook_Product::FB_PRODUCT_IMAGE ] ) ) );
}

if ( isset( $_POST[ WC_Facebook_Product::FB_BRAND ] ) ) {
$woo_product->set_fb_brand( sanitize_text_field( wp_unslash( $_POST[ WC_Facebook_Product::FB_BRAND ] ) ) );
}

if ( isset( $_POST[ WC_Facebook_Product::FB_MPN ] ) ) {
$woo_product->set_fb_mpn( sanitize_text_field( wp_unslash( $_POST[ WC_Facebook_Product::FB_MPN ] ) ) );
}
// phpcs:enable WordPress.Security.NonceVerification.Missing
}

Expand Down
50 changes: 50 additions & 0 deletions includes/Admin.php
Original file line number Diff line number Diff line change
Expand Up @@ -1185,6 +1185,8 @@ public function add_product_settings_tab_content() {
$price = get_post_meta( $post->ID, \WC_Facebook_Product::FB_PRODUCT_PRICE, true );
$image_source = get_post_meta( $post->ID, Products::PRODUCT_IMAGE_SOURCE_META_KEY, true );
$image = get_post_meta( $post->ID, \WC_Facebook_Product::FB_PRODUCT_IMAGE, true );
$fb_brand = get_post_meta( $post->ID, \WC_Facebook_Product::FB_BRAND, true ) ? get_post_meta( $post->ID, \WC_Facebook_Product::FB_BRAND, true ) : get_post_meta( $post->ID, '_wc_facebook_enhanced_catalog_attributes_brand', true );
$fb_mpn = get_post_meta( $post->ID, \WC_Facebook_Product::FB_MPN, true );

if ( $sync_enabled ) {
$sync_mode = $is_visible ? self::SYNC_MODE_SYNC_AND_SHOW : self::SYNC_MODE_SYNC_AND_HIDE;
Expand Down Expand Up @@ -1270,6 +1272,24 @@ public function add_product_settings_tab_content() {
)
);

woocommerce_wp_text_input(
array(
'id' => \WC_Facebook_Product::FB_BRAND,
'label' => __( 'Brand', 'facebook-for-woocommerce' ),
'value' => $fb_brand,
'class' => 'enable-if-sync-enabled',
)
);

woocommerce_wp_text_input(
array(
'id' => \WC_Facebook_Product::FB_MPN,
'label' => __( 'Manufacturer Parts Number (MPN)', 'facebook-for-woocommerce' ),
'value' => $fb_mpn,
'class' => 'enable-if-sync-enabled',
)
);

woocommerce_wp_hidden_input(
array(
'id' => \WC_Facebook_Product::FB_REMOVE_FROM_SYNC,
Expand All @@ -1278,6 +1298,21 @@ public function add_product_settings_tab_content() {
);
?>
</div>

<div class='options_group show_if_variable'>
<?php
woocommerce_wp_text_input(
array(
'id' => \WC_Facebook_Product::FB_VARIABLE_BRAND,
'label' => __( 'Brand', 'facebook-for-woocommerce' ),
'value' => $fb_brand,
'class' => 'enable-if-sync-enabled',
)
);
?>
</div>


<div class='wc-facebook-commerce-options-group options_group'>
<?php \WooCommerce\Facebook\Admin\Products::render_google_product_category_fields_and_enhanced_attributes( $product ); ?>
</div>
Expand Down Expand Up @@ -1318,6 +1353,7 @@ public function add_product_variation_edit_fields( $index, $variation_data, $pos
$price = $this->get_product_variation_meta( $variation, \WC_Facebook_Product::FB_PRODUCT_PRICE, $parent );
$image_url = $this->get_product_variation_meta( $variation, \WC_Facebook_Product::FB_PRODUCT_IMAGE, $parent );
$image_source = $variation->get_meta( Products::PRODUCT_IMAGE_SOURCE_META_KEY );
$fb_mpn = $this->get_product_variation_meta( $variation, \WC_Facebook_Product::FB_MPN, $parent );

if ( $sync_enabled ) {
$sync_mode = $is_visible ? self::SYNC_MODE_SYNC_AND_SHOW : self::SYNC_MODE_SYNC_AND_HIDE;
Expand Down Expand Up @@ -1405,6 +1441,17 @@ public function add_product_variation_edit_fields( $index, $variation_data, $pos
'wrapper_class' => 'form-row form-full',
)
);

woocommerce_wp_text_input(
array(
'id' => sprintf( 'variable_%s%s', \WC_Facebook_Product::FB_MPN, $index ),
'name' => sprintf( "variable_%s[$index]", \WC_Facebook_Product::FB_MPN ),
'label' => __( 'Manufacturer Parts Number (MPN)', 'facebook-for-woocommerce' ),
'value' => $fb_mpn,
'class' => 'enable-if-sync-enabled',
)
);

}


Expand Down Expand Up @@ -1456,6 +1503,8 @@ public function save_product_variation_edit_fields( $variation_id, $index ) {
Products::set_product_visibility( $variation, self::SYNC_MODE_SYNC_AND_HIDE !== $sync_mode );
$posted_param = 'variable_' . \WC_Facebookcommerce_Integration::FB_PRODUCT_DESCRIPTION;
$description = isset( $_POST[ $posted_param ][ $index ] ) ? sanitize_text_field( wp_unslash( $_POST[ $posted_param ][ $index ] ) ) : null;
$posted_param = 'variable_' . \WC_Facebook_Product::FB_MPN;
$fb_mpn = isset( $_POST[ $posted_param ][ $index ] ) ? sanitize_text_field( wp_unslash( $_POST[ $posted_param ][ $index ] ) ) : null;
$posted_param = 'variable_fb_product_image_source';
$image_source = isset( $_POST[ $posted_param ][ $index ] ) ? sanitize_key( wp_unslash( $_POST[ $posted_param ][ $index ] ) ) : '';
$posted_param = 'variable_' . \WC_Facebook_Product::FB_PRODUCT_IMAGE;
Expand All @@ -1464,6 +1513,7 @@ public function save_product_variation_edit_fields( $variation_id, $index ) {
$price = isset( $_POST[ $posted_param ][ $index ] ) ? wc_format_decimal( wc_clean( wp_unslash( $_POST[ $posted_param ][ $index ] ) ) ) : '';
$variation->update_meta_data( \WC_Facebookcommerce_Integration::FB_PRODUCT_DESCRIPTION, $description );
$variation->update_meta_data( Products::PRODUCT_IMAGE_SOURCE_META_KEY, $image_source );
$variation->update_meta_data( \WC_Facebook_Product::FB_MPN, $fb_mpn );
$variation->update_meta_data( \WC_Facebook_Product::FB_PRODUCT_IMAGE, $image_url );
$variation->update_meta_data( \WC_Facebook_Product::FB_PRODUCT_PRICE, $price );
$variation->save_meta_data();
Expand Down
2 changes: 1 addition & 1 deletion includes/Admin/Enhanced_Catalog_Attribute_Fields.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class Enhanced_Catalog_Attribute_Fields {
*/
private $category_handler;

public function __construct( $page_type, \WP_Term $term = null, \WC_Product $product = null ) {
public function __construct( $page_type, ?\WP_Term $term = null, ?\WC_Product $product = null ) {
$this->page_type = $page_type;
$this->term = $term;
$this->product = $product;
Expand Down
11 changes: 10 additions & 1 deletion includes/Products/FBCategories.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
*/
class FBCategories {

private $keys_to_exclude = ['brand' => true];

/**
* Fetches the attribute from a category using attribute key.
*
Expand Down Expand Up @@ -129,7 +131,14 @@ public function get_attributes( $category_id ) {
$return_attributes = array();
foreach ( $category['attributes'] as $attribute_hash ) {
// Get attribute array from the stored hash version
$return_attributes[] = $this->get_attribute_field_by_hash( $attribute_hash );
$attribute = $this->get_attribute_field_by_hash( $attribute_hash );

// Skip if attribute is invalid or its key is in the exclude list
if ( ! is_array( $attribute ) || empty( $attribute['key'] ) || isset( $this->keys_to_exclude[ $attribute['key'] ] ) ) {
continue;
}

$return_attributes[] = $attribute;
}

return $return_attributes;
Expand Down
127 changes: 113 additions & 14 deletions includes/fbproduct.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ class WC_Facebook_Product {
const FB_VARIANT_IMAGE = 'fb_image';
const FB_VISIBILITY = 'fb_visibility';
const FB_REMOVE_FROM_SYNC = 'fb_remove_from_sync';
const FB_BRAND = 'fb_brand';
const FB_VARIABLE_BRAND = 'fb_variable_brand';
const FB_MPN = 'fb_mpn';

const MIN_DATE_1 = '1970-01-29';
const MIN_DATE_2 = '1970-01-30';
Expand Down Expand Up @@ -358,6 +361,28 @@ public function set_product_image( $image ) {
}
}

public function set_fb_brand( $fb_brand ) {
$fb_brand = stripslashes(
WC_Facebookcommerce_Utils::clean_string( $fb_brand )
);
update_post_meta(
$this->id,
self::FB_BRAND,
$fb_brand
);
}

public function set_fb_mpn( $fb_mpn ) {
$fb_mpn = stripslashes(
WC_Facebookcommerce_Utils::clean_string( $fb_mpn )
);
update_post_meta(
$this->id,
self::FB_MPN,
$fb_mpn
);
}

public function set_price( $price ) {
if ( is_numeric( $price ) ) {
update_post_meta(
Expand Down Expand Up @@ -391,6 +416,38 @@ public function set_use_parent_image( $setting ) {
);
}

public function get_fb_brand() {
// Get brand directly from post meta
$fb_brand = get_post_meta(
$this->id,
self::FB_BRAND,
true
);

// If empty and this is a variation, get the parent brand
if ( empty( $fb_brand ) && $this->is_type('variation') ) {
$parent_id = $this->get_parent_id();
if ( $parent_id ) {
$fb_brand = get_post_meta($parent_id, self::FB_BRAND, true);
}
}

// Fallback to brand attribute or store name if no brand found
if ( empty( $fb_brand ) ) {
$brand = get_post_meta( $this->id, Products::ENHANCED_CATALOG_ATTRIBUTES_META_KEY_PREFIX . 'brand', true );
$brand_taxonomy = get_the_term_list( $this->id, 'product_brand', '', ', ' );
if ( $brand ) {
$fb_brand = $brand;
} elseif ( !is_wp_error( $brand_taxonomy ) && $brand_taxonomy ) {
$fb_brand = $brand_taxonomy;
} else {
$fb_brand = wp_strip_all_tags( WC_Facebookcommerce_Utils::get_store_name() );
}
}

return WC_Facebookcommerce_Utils::clean_string( $fb_brand );
}

public function get_fb_description() {
$description = '';

Expand Down Expand Up @@ -498,6 +555,23 @@ public function add_sale_price( $product_data, $for_items_batch = false ) {
return $product_data;
}

public function get_fb_mpn() {
$fb_mpn = get_post_meta(
$this->id,
self::FB_MPN,
true
);

// If empty and this is a variation, get the parent MPN
if ( empty( $fb_mpn ) && $this->is_type('variation') ) {
$parent_id = $this->get_parent_id();
if ( $parent_id ) {
$fb_mpn = get_post_meta($parent_id, self::FB_MPN, true);
}
}

return WC_Facebookcommerce_Utils::clean_string( $fb_mpn );
}

public function get_price_plus_tax( $price ) {
$woo_product = $this->woo_product;
Expand Down Expand Up @@ -650,18 +724,6 @@ public function prepare_product( $retailer_id = null, $type_to_prepare_for = sel
$categories =
WC_Facebookcommerce_Utils::get_product_categories( $id );

// Get brand attribute.
$brand = get_post_meta( $id, Products::ENHANCED_CATALOG_ATTRIBUTES_META_KEY_PREFIX . 'brand', true );
$brand_taxonomy = get_the_term_list( $id, 'product_brand', '', ', ' );

if ( $brand ) {
$brand = WC_Facebookcommerce_Utils::clean_string( $brand );
} elseif ( !is_wp_error( $brand_taxonomy ) && $brand_taxonomy ) {
$brand = WC_Facebookcommerce_Utils::clean_string( $brand_taxonomy );
} else {
$brand = wp_strip_all_tags( WC_Facebookcommerce_Utils::get_store_name() );
}

$custom_fields = $this->get_facebook_specific_fields();

if ( self::PRODUCT_PREP_TYPE_ITEMS_BATCH === $type_to_prepare_for ) {
Expand All @@ -672,7 +734,8 @@ public function prepare_product( $retailer_id = null, $type_to_prepare_for = sel
'additional_image_link' => $this->get_additional_image_urls( $image_urls ),
'link' => $product_url,
'product_type' => $categories['categories'],
'brand' => Helper::str_truncate( $brand, 100 ),
'brand' => Helper::str_truncate( $this->get_fb_brand(), 100 ),
'mpn' => Helper::str_truncate( $this->get_fb_mpn(), 100 ),
'retailer_id' => $retailer_id,
'price' => $this->get_fb_price( true ),
'availability' => $this->is_in_stock() ? 'in stock' : 'out of stock',
Expand Down Expand Up @@ -703,7 +766,8 @@ public function prepare_product( $retailer_id = null, $type_to_prepare_for = sel
*/
'category' => $categories['categories'],
'product_type' => $categories['categories'],
'brand' => Helper::str_truncate( $brand, 100 ),
'brand' => Helper::str_truncate( $this->get_fb_brand(), 100 ),
'mpn' => Helper::str_truncate( $this->get_fb_mpn(), 100 ),
'retailer_id' => $retailer_id,
'price' => $this->get_fb_price(),
'currency' => get_woocommerce_currency(),
Expand Down Expand Up @@ -829,6 +893,36 @@ private function apply_enhanced_catalog_fields_from_attributes( $product_data, $
}


/**
* Filters list of attributes to only those available for a given product
*
* @param \WC_Product $product WooCommerce Product
* @param array $all_attributes List of Enhanced Catalog attributes to match
* @return array
*/
public function get_matched_attributes_for_product( $product, $all_attributes ) {
$matched_attributes = array();
$sanitized_keys = array_map(
function( $key ) {
return \WC_Facebookcommerce_Utils::sanitize_variant_name( $key, false );
},
array_keys( $product->get_attributes() )
);

$matched_attributes = array_filter(
$all_attributes,
function( $attribute ) use ( $sanitized_keys ) {
if ( is_array( $attribute ) && isset( $attribute['key'] ) ) {
return in_array( $attribute['key'], $sanitized_keys );
}
return false; // Return false if $attribute is not valid
}
);

return $matched_attributes;
}


/**
* Normalizes variant data for Facebook.
*
Expand Down Expand Up @@ -857,6 +951,11 @@ public function prepare_variants_for_item( &$product_data ) {
// For each product field type, pull the single variant
foreach ( $variant_names as $original_variant_name ) {

// Ensure that the attribute exists before accessing it
if ( !isset( $attributes[ $original_variant_name ] ) ) {
continue; // Skip if the attribute is not set
}

// don't handle any attributes that are designated as Commerce attributes
if ( in_array( str_replace( 'attribute_', '', strtolower( $original_variant_name ) ), Products::get_distinct_product_attributes( $this->woo_product ), true ) ) {
continue;
Expand Down
Loading

0 comments on commit f7a6c3b

Please sign in to comment.