Add woocommerce variation GTIN to structured data - woocommerce

If a webshop has variable products, in some cases the variations have unique GTINs. In our case, it is EAN.
How to add these different GTINS to the structured data? I made a function to insert multiple "offer" in "offers", but the GTIN is not recognized here.
This is the function:
//add structured data to the markup
function set_structured_data( $markup, $product ) {
if ($product->is_type('simple')){
$markup['brand'] = array('#type' => 'Brand', 'name' => get_post_meta( $product->get_id(), '_brand', true ) );
$markup['gtin8'] = get_post_meta( $product->get_id(), '_EAN', true );
}
if ($product->is_type('variable')){
$available_variations = $product->get_available_variations();
foreach ($available_variations as $variation) {
$variation_id = $variation['variation_id'];
$variproduct = wc_get_product($variation_id);
$i++;
$stock = $product->get_stock_status();
$offers[] = array(
'type' => 'Offer',
'price' => $variproduct->get_price(),
'priceValidUntil' => $priceuntil,
'priceSpecification' => array(
'price' => $variproduct->get_price(),
'priceCurrency' => get_woocommerce_currency(),
'valueAddedTaxIncluded' => 'http://schema.org/True'
),
'priceCurrency' => get_woocommerce_currency(),
'availability' => 'http://schema.org/'.$stock.'',
'url' => get_the_permalink(),
'seller' => array (
'type' => 'Organization',
'name' => 'HeatPerformance®',
'url' => get_the_permalink(),
));
}
$markup['offers'] = $offers;
$markup['brand'] = array('#type' => 'Brand', 'name' => get_post_meta( $product->get_id(), '_brand', true ) );
}
return $markup;
}
add_filter( 'woocommerce_structured_data_product', 'set_structured_data', 99, 2 );
Any ideas?

Ok after a couple of days puzzling i found the path to a solution here:
https://support.google.com/merchants/answer/6386198?hl=en
Result is that i corrected the standardized method from woocommerce and made each variation a unique product.
So first remove the markup in case of a variable product like this:
function set_structured_data( $markup, $product ) {
if ($product->is_type('variable')) {
$markup = array();
}
return $markup;
}
add_filter( 'woocommerce_structured_data_product', 'set_structured_data', 99, 2 );
And then build up the ld+json script again, but then the desired way:
function set_structured_data_variable() {
global $product;
if (isset($product)){
if ($product->is_type('variable') && !empty($product)){
$available_variations = $product->get_available_variations();
$date = date("Y-m-d",strtotime(" + 3months"));
$commenttext = '';
$commentauthor = '';
$commentdate = '';
$comments = get_comments(array( 'post_id' => $product->get_id(), 'number' => '1' ));
foreach($comments as $comment) {
$commenttext = $comment->comment_content;
$commentauthor = $comment->comment_author;
$commentdate = $comment->comment_date;
}
foreach ($available_variations as $variation) {
$variation_id = $variation['variation_id'];
$variproduct = wc_get_product($variation_id);
$i++;
$gtin = 0000000000001;
if (!empty(get_post_meta( $variation_id, '_EAN', true ))){
$gtin = get_post_meta( $variation_id, '_EAN', true );
}
$stock = $product->get_stock_status();
$arrays[$i] = array(
'#context' => 'https://schema.org/',
'#type' => 'Product',
'sku' => $variproduct->get_sku(),
'gtin13' => $gtin,
'image' => get_the_post_thumbnail_url($product->get_id()),
'name' => implode(" / ", $variproduct->get_variation_attributes()),
'description' => wp_strip_all_tags(get_the_excerpt($product->get_id())),
'brand' => array('#type' => 'Brand', 'name' => get_post_meta( $product->get_id(), '_brand', true ) ),
'review' => array(
'#type' => 'Review',
'reviewRating' => array (
'#type' => 'Rating',
'ratingValue' => $product->get_review_count(),
'bestRating' => '5',
'worstRating' => '1',
),
'author' => array(
'#type' => 'person',
'name' => $commentauthor,
'reviewBody' => $commenttext,
'datePublished' => $commentdate,
)),
'aggregateRating' => array (
'#type' => 'AggregateRating',
'ratingValue'=> $product->get_average_rating(),
'reviewCount' => $product->get_rating_count(),
),
'inProductGroupWithID' => $product->get_sku(),
'offers' => array(
'#type' => 'Offer',
'price' => $variproduct->get_price(),
'priceValidUntil' => $date,
'priceSpecification' => array(
'price' => $variproduct->get_price(),
'priceCurrency' => get_woocommerce_currency(),
'valueAddedTaxIncluded' => 'http://schema.org/True'
),
'priceCurrency' => get_woocommerce_currency(),
'availability' => 'http://schema.org/'.$stock.'',
'url' => get_the_permalink(),
'seller' => array (
'type' => 'Organization',
'name' => 'HeatPerformance®',
'url' => get_the_permalink(),
)));
}
echo '<script type="application/ld+json" class="buronoort">[';
$i = 0;
foreach ($arrays as $array){
$i++;
echo json_encode($array);
if ($i < array_key_last($arrays)){
echo ',';
}
}
echo ']</script>';
}
}
}
add_action('wp_head','set_structured_data_variable', 19);
Tested in the test tool for structured data and it is working.
A little concern is the product review however, i have to look into that what happens after multiple reviews.
Another concern is the EAN, i have set it standard to "0000000000001" as not all EANS are inputted yet. So this is a debatle solution, but if someone has a better idea, keep me posted.

Related

CF7 validate and store data

I am trying to make custom validation to cf7 form and then store data in a custom post type if everything is valid. My problem is that even when form inputs are not valid form still sends email and store its data. Can you help me with that. Below is my code. What should I use to validate inputs first and only then store data.
Store posted data:
function save_posted_data_ios( $posted_data ) {
if( isset($posted_data['menu-377']) == "IOS" ){
$args2 = array(
'post_type' => 'np_coupon_code',
'post_status' => 'publish',
'posts_per_page' => 1,
'meta_query' => array(
array(
'key' => 'np_ccode__email',
'value' => 'null',
'compare' => 'NOT EXISTS'
),
array(
'key' => 'np_ccode__type',
'value' => 'IOS'
)
)
);
$posts_query2 = new WP_Query($args2);
$the_count_ios = $posts_query2->found_posts;
$query = new WP_Query($args2);
while ( $query->have_posts() ) : $query->the_post();
$id = get_the_ID();
$code = get_the_title();
endwhile;
if( isset($posted_data['your-email']) ){
update_post_meta($id, 'np_ccode__email', $posted_data['your-email']);
}
$to = $posted_data['your-email'];
$subject = 'Subject';
$body = $code;
$headers = array('Content-Type: text/html; charset=UTF-8');
wp_mail( $to, $subject, $body, $headers );
return $posted_data;
wp_reset_query();
}
}
add_filter( 'wpcf7_posted_data', 'save_posted_data_ios' );```
Custom error:
function my_wpcf7_validate_ios( $result_ios, $tag ) {
$type = $tag['type'];
$name = $tag['menu-377'];
$args2 = array(
'post_type' => 'np_coupon_code',
'post_status' => 'publish',
'posts_per_page' => 1,
'meta_query' => array(
array(
'key' => 'np_ccode__email',
'value' => 'null',
'compare' => 'NOT EXISTS'
),
array(
'key' => 'np_ccode__type',
'value' => 'IOS'
)
)
);
$posts_query2 = new WP_Query($args2);
$the_count_ios = $posts_query2->found_posts;
if ( $the_count_ios == 0 && $_POST['menu-377'] == "IOS"){
$result_ios->invalidate( $tag, wpcf7_get_message( 'invalid_ios' ) );
}
return $result_ios;
}
add_filter( 'wpcf7_validate_select*', 'my_wpcf7_validate_ios' , 10, 2 );
add_filter( 'wpcf7_messages', 'mywpcf7_ios_messages' );
function mywpcf7_ios_messages( $messages ) {
return array_merge( $messages, array(
'invalid_ios' => array(
'description' => __( "No IOS Coupons Left", 'contact-form-7' ),
'default' => __( 'No IOS Coupons Left.', 'contact-form-7' )
)
));
}

Custom Checkout Field for Woocommerce with dynamic options

I want to have dynamic options for my checkout dropdown field. The options must be users of specific roles that are in WordPress users. How can I achieve it? Below is the code that I am working with. In the first function, we can get all administrator users. I want users to be in options of the dropdown in the select field of checkout.
**User Roles**
function get_users_by_role($role, $orderby, $order) {
$args = array(
'role' => 'administrator',
'orderby' => 'user_nicename',
'order' => 'ASC'
);
$users = get_users( $args );
return $users;
}
**Dropdown Field**
function everest_custom_dropdown_fields( $fields ) {
$fields['everestmerchant_extra_dropdown_fields']['dropdown'] = array(
'label' => __('Leader Name', 'woocommerce'),
'placeholder' => _x('Leader Name', 'placeholder', 'woocommerce'),
'required' => true,
'class' => array( 'wps-drop' ),
'clear' => true,
'type' => 'select',
'options' => array(
'option 1' => __('option 1', 'woocommerce' ),
'option 2' => __('option 2', 'woocommerce'
)//end of options
);
return $fields;
}
add_filter( 'woocommerce_checkout_fields', 'everest_custom_dropdown_fields' );
Get All Leaders
function get_leaders($role) {
$args = array(
'role' => $role,
'orderby' => 'user_nicename',
'order' => 'ASC'
);
$leaders = ['' => 'Please Select'];
$users = get_users( $args );
if($users){
foreach($users as $user){
$leaders[$user->data->ID] = $user->data->display_name;
}
}
return $leaders;
}
Add Leader field in checkout page
function leader_name_select_field( $checkout ){
woocommerce_form_field( 'leader_name', array(
'type' => 'select',
'required' => true,
'class' => array('form-row-wide'),
'label' => 'Leader Name',
'options' => get_leaders('administrator')
), $checkout->get_value( 'leader_name' ) );
}
add_action( 'woocommerce_after_checkout_billing_form', 'leader_name_select_field' );
Save Selected field in database.
function save_leader_name_of_order( $order_id ){
if( !empty( $_POST['leader_name'] ) )
update_post_meta( $order_id, 'leader_name', sanitize_text_field( $_POST['leader_name'] ) );
}
add_action( 'woocommerce_checkout_update_order_meta', 'save_leader_name_of_order' );
Show saved field in order page in dashboard
function leader_name_show_on_order_detail_page($order){
$leader_id = get_post_meta($order->get_id(), 'leader_name', true );
$leader = get_user_by( 'ID', $leader_id );
if($leader_id && $leader){
echo '<p class="form-field form-field-wide">';
echo '<label>Leader Name</label>';
echo '<p> ' . $leader->data->display_name . '</p>';
echo '</p>';
}
}
add_action( 'woocommerce_admin_order_data_after_order_details', 'leader_name_show_on_order_detail_page', 10, 1 );

WooCommerce set attributes programmatically based on variations

Need some help with WooCommerce adding variations to product attributes.
I created an attribute called Data ( pa_data ), this attribute stores terms from a custom field added with ACF, this field is a date picker.
I want to create an event that is recurring, and for every recurrence, I want to add a variation with price and stock.
So the process is like this:
Add all ACF fields in an array.
Create Variations with this array.
Add variations to attributes.
The problems is that i don't know how to add variations to the attribute ( pa_data );
Here is how is displaying my attributes
https://i.imgur.com/YmbDFlO.png
Here is how i want to be displayed
https://i.imgur.com/oDNvuOD.png
$days = array
array(
'date' => 'April 02, 2020',
'price' => '10',
),
array(
'date' => 'April 03, 2020',
'price' => '20',
),
array(
'date' => 'April 04, 2020',
'price' => '10',
),
);
// This method inserts new data into the post after the post is saved
add_action('post_updated', 'ProductUpdate', 10, 3);
// Callback function after the post is saved
function ProductUpdate($post_id, $post_after, $post_before) {
if ( $post_after && get_post_type($post_id) == 'product' ) {
ProductSetVariations( $post_id, $postDataVariations );
}
};
function ProductSetVariations($productID, $days ) {
// Get product object
$product = wc_get_product($productID);
foreach ($days as $day ) {
$date = $day['date'];
$price = $day['price'];
$variationPost = array(
'post_title' => $product->get_title(),
'post_name' => 'product-' . $productID . '-variation',
'post_status' => 'publish',
'post_parent' => $productID,
'post_type' => 'product_variation',
'guid' => $product->get_permalink()
);
// Insert the new variation post;
$variationID = wp_insert_post($variationPost);
// Create the new post based on the id
$variation = new WC_Product_Variation($variationID);
$taxonomy = 'pa_data';
// If term dosent exsist in taxonomy than add it
if ( !term_exists( $date, $taxonomy )) {
wp_insert_term( $date, $taxonomy );
};
$term_slug = get_term_by('name', $date, $taxonomy)->slug; // Get the term slug
$post_term_names = wp_get_post_terms( $productID, $taxonomy, array('fields' => 'names') );
if( ! in_array( $date, $post_term_names ) ) {
wp_set_post_terms( $productID, $date, $taxonomy, true );
};
$term_slug = get_term_by('name', $date, $taxonomy)->slug; // Get the term slug
update_post_meta( $variationID, 'attribute_'.$taxonomy, $term_slug );
$variation->set_price($price);
$variation->save(); // Save the data
}
};
Resolved!
Solution:
The problem was with WordPress hook 'post_updated', it does save in the database the changes but it doesn't change it in admin.
Here is the solution, for someone who has to update posts from rest api or just post update.
This is only a simple demonstration.
add_action('woocommerce_update_product', 'ProductUpdate', 10, 3);
function ProductUpdate($post_id) {
// Insert Product Attributes
function insert_product_attributes ($post_id, $variations) {
$product = wc_get_product($post_id);
// Set up an array to store the current attributes values.
$values = array();
foreach ($variations as $variation) {
array_push($values, $variation['date']);
}
wp_set_object_terms($post_id, $values, 'pa_data', true );
$product_attributes_data = array('pa_data' =>
array(
'name' => 'pa_data',
'position' => '1',
'is_visible' => '1',
'is_variation' => '1',
'is_taxonomy' => '1',
);
);
update_post_meta($post_id, '_product_attributes', $product_attributes_data);
};
function insert_product_variations ($post_id, $variations) {
foreach ($variations as $index => $variation) {
$attribute_term = get_term_by('name', $variation['date'], 'pa_data');
if ( $variation['date'] == $attribute_term ) {
return;
}
$variation_post = array( // Setup the post data for the variation
'post_title' => 'Variation #'.$index.' of data for product#'. $post_id,
'post_name' => 'product-'.$post_id.'-variation-'.$index,
'post_status' => 'publish',
'post_parent' => $post_id,
'post_type' => 'product_variation',
'guid' => home_url() . '/?product_variation=product-' . $post_id . '-variation-' . $index
);
$variation_post_id = wp_insert_post($variation_post); // Insert the variation
// We need to insert the slug not the name into the variation post meta
update_post_meta($variation_post_id, 'attribute_pa_data', $attribute_term->slug);
update_post_meta($variation_post_id, '_price', $variation['price']);
update_post_meta($variation_post_id, '_regular_price', $variation['price']);
}
function ProductSetVariations($post_id ) {
// Get product object
$product = wc_get_product($post_id);
// Verify if the product is variable or simple product
if ( !$product->is_type( 'variable' ) ) {
return;
};
$product_data = array(
'days' => array(
array(
'sku' => '123SKU',
'date' => '1 April 2020',
'price' => '10',
'stock' => '20'
),
array(
'sku' => '456SKU',
'date' => '2 April 2020',
'price' => '10',
'stock' => '20'
),
array(
'sku' => '789SKU',
'date' => '3 April 2020',
'price' => '10',
'stock' => '20'
)
)
);
insert_product_attributes($post_id, $product_data['days']); // Insert variations passing the new post id & variations
insert_product_variations($post_id, $product_data['days']);
}
};

Create a page with child pages and have each child have it's own page template

I have some code that creates pages with child pages in WordPress. What i would like to do is have each child have its own page template.
As you see below, i am creating child pages of Script and Assets. Currently the child page is created with one page_Full.php added to everything but i would like to have Script and Assets have their own page template. Ideas would be helpful.
Thanks in advance.
function CreatePage(){
$post_title = $_POST['ProjectName'];
$post_excerpt = $_POST['ProjectDiscript'];
$tags = $_POST['TypeOption'];
$pages = array(
array(
'name' => $post_title,
'title' => $post_title,
'child' => array(
'script' => $post_title.'_Script',
'assets' => $post_title.'_Assets'
)
)
);
$template = array(
'post_type' => 'page',
'post_status' => 'publish',
'post_author' => $user_id,
'post_excerpt' => $post_excerpt,
'tags_input' => array($tags),
);
foreach( $pages as $page ) {
$exists = get_page_by_title( $page['title'] );
$my_page = array(
'post_name' => $page['name'],
'post_title' => $page['title'],
'page_template' => 'page_Full.php'
);
$my_page = array_merge( $my_page, $template );
$id = ( $exists ? $exists->ID : wp_insert_post( $my_page ) );
if( isset( $page['child'] ) ) {
foreach( $page['child'] as $key => $value ) {
$child_id = get_page_by_title( $value );
$child_page = array(
'post_name' => $key,
'post_title' => $value,
'post_parent' => $id,
'page_template' => 'page_Full.php'
);
$child_page = array_merge( $child_page, $template );
if( !isset( $child_id ) ) wp_insert_post( $child_page );
}
}
}
wp_die();
}
add_action( 'wp_ajax_CreatePage','CreatePage' );
Had the idea to create an array for every child page - and it seams to work. Perhaps there is still a better way but for the time being - This does the trick!
$pages = array(
array(
'name' => $post_title,
'title' => $post_title,
'child' => array(
'script' => array(
'Pname' => $post_title.'_Script',
'Ptemplate' => 'page_Full_Script.php'
),
'assets' => array(
'Pname' => $post_title.'_Assets',
'Ptemplate' => 'page_Full_Assets.php'
),
'settings' => array(
'Pname' => $post_title.'_Settings',
'Ptemplate' => 'page_Full_Settings.php'
)
)
)
);
Here is the child with sub arrays.
if( isset( $page['child'] ) ) {
foreach( $page['child'] as $key[] => $value ) {
$child_id = get_page_by_title( $value );
$child_page = array(
'post_name' => $value['Pname'],
'post_title' => $value['Pname'],
'post_parent' => $id,
'page_template' => $value['Ptemplate'],
);
$child_page = array_merge( $child_page, $template );
if( !isset( $child_id ) ) wp_insert_post( $child_page );
}
}
And here is where the child array is set as var to be iterated through so that the info can be inserted into the child_page var to be merged and the post inserted.
Thanks to any who took the time to review.
Cheers

Understanding genesis_markup() in Genesis Framework

I don't understand how the genesis_markup function turns:
array(
'open' => '<aside class="widget-areainfo-onder-content">',
'context' => 'widget-area-wrap',
'echo' => false,
'params' => array(
'id' => $id,
)
into
"<div class="widget-area">"
I do not understand why the id is not used in the output at all and the aside class is also not used....I would have thought that it should be.....
When I do this in the code:
$before_markup_args = array(
'open' => '<aside class="widget-areainfo-onder-content">',
'context' => 'widget-area-wrap',
'echo' => false,
'params' => array(
'id' => $id,
)
);
d($before_markup_args);
d(genesis_markup( $before_markup_args ));
First, I would like to confirm the missing space:
```
$before_markup_args = array(
'open' => '<aside class="widget-area info-onder-content">',
'context' => 'widget-area-wrap',
'echo' => false,
'params' => array(
'id' => $id,
)
);
```
"Onder" is Dutch for "Below". Thus, "info-onder-content" means "info-below-content". Possibly a title for the widget area.
This is a code example that is used for the genesis_widget_area function, typically found in a functions.php file in a WordPress Genesis child theme.
$widget_area_args = array('before' => genesis_markup($before_markup_args),
);
genesis_widget_area($id, $widget_area_args);
We can understand how this works by looking up the definition of the genesis_widget_area function in the /var/www/html/wp-content/themes/genesis/lib/functions/widgetize.php file.
function genesis_widget_area( $id, $args = array() ) {
if ( ! $id ) {
return false;
}
$defaults = apply_filters(
'genesis_widget_area_defaults',
array(
'before' => genesis_markup(
array(
'open' => '<aside class="widget-area">' . genesis_sidebar_title( $id ),
'context' => 'widget-area-wrap',
'echo' => false,
'params' => array(
'id' => $id,
),
)
),
'after' => genesis_markup(
array(
'close' => '</aside>',
'context' => 'widget-area-wrap',
'echo' => false,
)
),
'default' => '',
'show_inactive' => 0,
'before_sidebar_hook' => 'genesis_before_' . $id . '_widget_area',
'after_sidebar_hook' => 'genesis_after_' . $id . '_widget_area',
),
$id,
$args
);
$args = wp_parse_args( $args, $defaults );
if ( ! $args['show_inactive'] && ! is_active_sidebar( $id ) ) {
return false;
}

Resources