Persisting meta data on item, and retrieving at checkout complete - php

I'm attempting to set meta data on an item when it's added to the cart, persist it through each page reload and when checkout is successful retrieve the meta data to pass to a seperate API.
The below code is successful in the ense that it echos the meta data after a page reload so I'm considering that to be a success.
add_action('woocommerce_order_status_completed', 'order_system');
add_filter('woocommerce_add_cart_item_data', 'nick_add_cart_item_data', 10, 2);
add_filter('woocommerce_get_cart_item_from_session', 'nick_get_cart_item_from_session', 10, 2);
function nick_add_cart_item_data($cart_item_meta, $product_id) {
global $woocommerce;
$nick_form_data = get_post_meta($product_id, '_nick_form_data', true);
$cart_item_meta['_nick_form_data'] = $nick_form_data;
$cart_item_meta['_nick_credential_data']['username'] = ( isset( $_POST['username'] ) && $_POST['username'] != '') ? $_POST['username'] : '';
$cart_item_meta['_nick_credential_data']['password'] = ( isset( $_POST['password'] ) && $_POST['password'] != '') ? $_POST['password'] : '';
return $cart_item_meta;
}
function nick_get_cart_item_from_session($cart_item, $values) {
if (isset($values['_nick_form_data'])) {
$cart_item['_nick_form_data'] = $values['_nick_form_data'];
}
if (isset($values['_nick_credential_data'])) {
$cart_item['_nick_credential_data'] = $values['_nick_credential_data'];
echo $values['_nick_credential_data']['username'] . $values['_nick_credential_data']['password'] ;
}
return $cart_item;
}
When I call this function, which fires when the order is completed I'm unable to retain any of the item meta data.
function order_system($order_id)
{
global $woocommerce;
try {
// do something that can go wrong
// instantiate new order from WooCommerce
$order = new WC_Order($order_id);
$userid = $order->user_id;
foreach ($order->get_items() as $item_id => $values) :
if (isset($values['_nick_form_data'])) :
// Not getting inside this case statement
endif;
endforeach;
....
I'm unsure of what I'm doing wrong, any help is appreciated. Thank you!!

It's a little late, but: although you're attaching your metadata to the cart items, they're not subsequently being added to the order items when the order is created. To do that:
function nick_add_order_item_meta($itemID, $values) {
$nick_form_data = $values['_nick_form_data'];
if (!empty($nick_form_data)) {
wc_add_order_item_meta($itemID, '_nick_form_data', $nick_form_data);
}
}
add_action('woocommerce_add_order_item_meta','nick_add_order_item_meta', 1, 2);
At the point your order is created, your cart item meta _nick_form_data will be attached to each item. You'll then be able to see it in the backend of WooCommerce when viewing an order: each order item should have a [?] next to it which will show you the attached metadata.

I believe you are hooking in too late with the 'order_system' function.
Try hooking in to here instead:
add_action('woocommerce_checkout_process', array(&$this, 'nick_checkout_field_process') );
function nick_checkout_field_process() {
global $woocommerce;
try {
// do something that can go wrong
// instantiate new order from WooCommerce
$order = new WC_Order($order_id);
$userid = $order->user_id;
foreach ($order->get_items() as $item_id => $values) :
if (isset($values['_nick_form_data'])) :
// Do Something here
// echo $values['_nick_credential_data']['username'] . $values['_nick_credential_data']['password'] ;
endif;
endforeach;
....

Related

Woocommerce : Generate a unique key per item and send it in the order email

I would like that when a user orders items, a special unique key is generated for each item. These keys / This key should then be visible in ALL order mails (admin and customer). Here is what I tried to do after some research around the web :
add_action('woocommerce_order_status_processing', 'process_membership_order');
//Trying to generate my keys for each item
function process_membership_order($order_id) {
$order = new WC_Order($order_id);
$items = $order->get_items();
foreach ($items as $item_id => $product) {
$key_id = "TESTING123"; //testing purpose of course, will call a function later
wc_add_order_item_meta($item_id, 'custom_key', $key_id);
}
}
add_action( 'woocommerce_email_order_meta', 'add_email_order_meta', 10, 3 );
//Trying to edit the email order infos
function add_email_order_meta($order_obj, $sent_to_admin, $plain_text){
$key = get_post_meta( $order_obj->get_order_number(), 'custom_key', true );
if ( $plain_text === false ) {
echo "<h2>Custom Information</h2><p>$key</p>";
} else {
echo "CUSTOM INFORMATION : $key";
}
}
It doesn't seem to work, my mails have the new title but it's always empty instead of showing my TESTING123 as many times as there is an item. I'm more a tinkerer than a dev, I've used an entire day on this, a little push on the right direction would be really appreciated, I think I'm close but something's missing ! Anyway, any advice would be great, thank you.
I think you need to get order items from the order object and then you can get your custom meta by using wc_get_order_item_meta. check below code.
add_action('woocommerce_order_status_processing', 'process_membership_order');
//Trying to generate my keys for each item
function process_membership_order($order_id) {
$order = new WC_Order($order_id);
$items = $order->get_items();
foreach ($items as $item_id => $product) {
$key_id = "TESTING123"; //testing purpose of course, will call a function later
wc_add_order_item_meta($item_id, 'custom_key', $key_id);
}
}
add_action( 'woocommerce_email_order_meta', 'add_email_order_meta', 10, 3 );
//Trying to edit the email order infos
function add_email_order_meta($order_obj, $sent_to_admin, $plain_text){
$order = wc_get_order( $order_obj->get_order_number() );
$items = $order->get_items();
foreach ( $order->get_items() as $item_id => $item ) {
$key = wc_get_order_item_meta( $item_id, 'custom_key', true );
if ( $plain_text === false ) {
echo "<h2>Custom Information</h2><p>$key</p>";
} else {
echo "CUSTOM INFORMATION : $key";
}
}
}

User redirect after login, depending on its role in WooCommerce

In Woocommerce, I have code that changes the role if the user buys a certain product.
add_action( 'woocommerce_order_status_processing', 'change_role_on_purchase' );
function change_role_on_purchase( $order_id ) {
$order = new WC_Order( $order_id );
$items = $order->get_items();
foreach ( $items as $item ) {
$product_name = $item['name'];
$product_id = $item['product_id'];
$product_variation_id = $item['variation_id'];
if ( $order->user_id > 0 && $product_id == '3422, 3423, 3424' ) {
update_user_meta( $order->user_id, 'paying_customer', 1 );
$user = new WP_User( $order->user_id );
// Remove role
$user->remove_role( 'customer' );
// Add role
$user->add_role( 'subscriber' );
}
}
}
I need to, after logging in to the site, if the user "Subscriber" and he has a certain "Subscription" product in the cart, then the system redirects it to the checkout page.
I shall be very glad of your help!
Update: I will keep this variant. It can help other users.
add_filter('login_redirect', 'my_login_redirect', 10, 3);
function my_login_redirect($redirect_to, $request, $user)
{
global $woocommerce;
$items = $woocommerce->cart->get_cart();
$ids_to_check = array(3422, 3423, 3424);
foreach ($items as $item => $values) {
$product_id = wc_get_product($values['data']->get_id());
if (in_array($product_id, $id_to_check)) {
//is there a user to check?
if (isset($user->roles) && is_array($user->roles)) {
//check for subscribers
if (in_array('subscriber', $user->roles)) {
// redirect them to another URL, in this case, the homepage
$url = get_permalink(get_option('woocommerce_checkout_page_id'));
$redirect_to = $url;
}
}
}
}
return $redirect_to;
}
Your if statement will never return true, because you checking the product id if they are equal to '3422, 3423, 3424' which your product will never be equal to this what you need to do is store those ids in array and check if the product id in the array and if yes execute your code.
try the following:
add_action('woocommerce_order_status_processing', 'change_role_on_purchase');
function change_role_on_purchase($order_id)
{
$order = new WC_Order($order_id);
$items = $order->get_items();
$ids_to_check = array(3422, 3423, 3424, 15);
foreach ($items as $item) {
$product_name = $item['name'];
$product_id = $item['product_id'];
$product_variation_id = $item['variation_id'];
if (in_array($product_id, $id_to_check) && $order->user_id > 0) {
update_user_meta($order->user_id, 'paying_customer', 1);
$user = new WP_User($order->user_id);
// Remove role
$user->remove_role('customer');
// Add role
$user->add_role('subscriber');
}
}
}
To redirect the user if they have certain role you can use the following if you are using Woocommerce login page:
add_filter('woocommerce_login_redirect', 'my_login_redirect', 20, 2);
function my_login_redirect($redirect, $user)
{
if (in_array('subscriber', $user->roles)) {
// redirect them to another URL, in this case, the homepage
$url = get_permalink(get_option('woocommerce_checkout_page_id'));
$redirect = $url;
}
return $redirect;
}
if you are using WordPress Default login form you can use the following function:
add_filter('login_redirect', 'my_login_redirect', 20, 3);
function my_login_redirect($redirect_to, $request, $user)
{
//is there a user to check?
if (isset($user->roles) && is_array($user->roles)) {
//check for subscribers
if (in_array('subscriber', $user->roles)) {
// redirect them to another URL, in this case, the homepage
$url = get_permalink(get_option('woocommerce_checkout_page_id'));
$redirect_to = $url;
}
}
return $redirect_to;
}
Please keep in mind that woocommerce_order_status_processing does NOT guarantee that the order has been paid for. It simply means what it says. If you change the user role based on an open order, you open yourself to the risk that customers whose payment hasn't gone through do have access to whatever you are offering by changing the user role.

How to update order item meta data from everywhere?

I am developing a cycle renting site in which user select the date range (in days) from start date to end. And I am storing that data using wc_add_order_item_meta() function as a meta data of the order, right.
Then, I wanted to give user a functionality like he could change or extend the days by going through the orders page. This is also done, I have created a modal and added a ajax request on change of dates on modal.
Now, I sent data to functions.php using AJAX and I used wc_update_order_item_meta() to update the meta data.
Here's my function looks like :
add_action( 'wp_ajax_update_date_picker', 'update_date_picker' );
function update_date_picker() {
$p_da = $_POST['p_date'];
$r_da = $_POST['r_date'];
$t_dy = $_POST['t_days'];
$order_id = $_POST['order_id'];
$order = wc_get_order($order_id);
foreach ($order->get_items() as $item_id => $item_obj) {
$pd = wc_update_order_item_meta($item_id, 'pickup_hidden_datetime', $p_da);
$rd = wc_update_order_item_meta($item_id, 'return_hidden_datetime', $r_da);
$td = wc_update_order_item_meta($item_id, 'return_hidden_days', $t_dy);
if($pd&&$rd&&$td){
echo "Nice work, Bilal";
}
die();
}
}
This is updating too but not at all the places. I mean, it's giving me right response, when I refresh, it's also giving the updated data but when I go through the view item page it's showing there the old data only instead of updated one. Even when I check the orders in the admin, it's showing the old meta data there too.
Any idea would be appreciated, Thanks in Advance.
I think it's because of the cache that WooCommerce has... try something like this.
add_action( 'wp_ajax_update_date_picker', 'update_date_picker' );
function update_date_picker() {
$p_da = $_POST['p_date'];
$r_da = $_POST['r_date'];
$t_dy = $_POST['t_days'];
$order_id = $_POST['order_id'];
$order = wc_get_order($order_id);
foreach ($order->get_items() as $item_id => $item_obj) {
$pd = wc_update_order_item_meta($item_id, 'pickup_hidden_datetime', $p_da);
$rd = wc_update_order_item_meta($item_id, 'return_hidden_datetime', $r_da);
$td = wc_update_order_item_meta($item_id, 'return_hidden_days', $t_dy);
if($pd&&$rd&&$td){
echo "Nice work, Bilal";
}
}
clean_post_cache( $order->get_id() );
wc_delete_shop_order_transients( $order );
wp_cache_delete( 'order-items-' . $order->get_id(), 'orders' );
die();
}

Get a custom field value saved in Order items meta in a hooked function

I am able to add, validate, display on cart and checkout page a custom field on the Product Page.Please can someone tell me how can I retrieve the custom field values using woocommerce_order_status_completed hook?
I want to send an additional email including the custom field data after the confirmation email is sent to the user
static function sendCustomData($order_id) {
$order = wc_get_order($order_id);
$items = $order->get_items();
foreach ($items as $item) {
$product_id = $item['product_id'];
$Id = get_post_meta($product_id, '_wpws_ID', true);
$first_name = $order->get_billing_first_name();
$billing_email = $order->get_billing_email();
if (empty($Id))
continue;
$mail = new CustomMails();
$mail->SendMailtoReaderOnWCOrderComplete($first_name, $billing_email, $Id);
}
}
add_action('woocommerce_order_status_completed','sendCustomData');
Saving custom order meta value
public static function tshirt_order_meta_handler( $item_id, $values, $cart_item_key ) {
if( isset( $values['name_on_tshirt'] ) ) {
wc_add_order_item_meta( $item_id, "name_on_tshirt", $values['name_on_tshirt'] );
}
}
add_action( 'woocommerce_new_order_item', 'tshirt_order_meta_handler', 1, 3 );
To get "name_on_tshirt" custom field, you need to get the order Item ID and you need to use wc_get_order_item_meta() function this way:
foreach ($order->get_items() as $item_id => $item) {
## HERE ==> Get your custom field value
$name_on_tshirt wc_get_order_item_meta( $item_id, "name_on_tshirt", true );
}
change if (empty($Id)) .. to if (empty($wbnId))

restrict product page with product category 'x' WooCommerce

I wan't to restrict certain product pages from logged-out users or users with a specific role. The easiest way would probably be to check the category ID of the product page and if current_user_can('') than redirect to the main shop page.
However I don't really know where to start.. Should I add an action on init? And how do I check for the current page product ID?
I thought I could get some data with a var_dump() But that resulted in nothing.
I did this:
add_action('init', 'get_all_post_meta');
function get_all_post_meta() {
//$meta = get_post_meta( get_the_ID() );
global $post;
var_dump('$post');
$metavar = get_the_terms($post->ID);
var_dump('$metavar');
}
But no results in my console.
Edit: I found my var_dump() was incorrect as it should be like var_dump($post); Continuing my quest now.
This is my solution so far, now I need to figure out how to remove a single product instead of them all.
add_action('wp_head', 'get_all_post_meta', 1 );
function get_all_post_meta() {
global $post;
$banned_cid = array(8);
$current_cid = array();
$metavar = get_the_terms($post->ID, 'product_cat');
global $woocommerce;
$redirect_url = 'http://www.example.nl/';
if(current_user_can('subscriber') || current_user_can('manage_options')){}else if( is_product()){
foreach ( $metavar as $term ) {
$cat_id .= $term->term_id.',';
array_push($current_cid, $term->term_id);
}
var_dump($current_cid);
$c = array_intersect($banned_cid, $current_cid);
if (count($c) > 0) {
$woocommerce->cart->empty_cart();
wp_redirect( $redirect_url, 302 );
exit;
}
}
}

Categories