$Order ACCESSING PROTECTED ARRAY - php

I am attempting to access the meta data in an $order through a woocommerce order system. The system has extra order fields . It is these fields I am attempting to acess. Every variation I try, just end up with with a server error OR the data is blank on output. I have tried $MetaData->id, $MetaData[0]['id'], using a foreach loop for each record...etc etc. It makes no difference, the data, in string form, does not display. I suspect it has something to do with it being a protected array, which I have no clue as to how to manage...obviously. It is not working.
Here is the code and the some results of the test.
$order = new WC_Order( $order_id );
$order_data = $order->get_data();
$MetaData = $order_data['meta_data'];
DebugLog(json_encode($MetaData));
// OUTPUT of JSON_ENCODE:
// [{"id":2658,"key":"business-name","value":"BUSINESS NAME HERE"},{"id":2659,"key":"full-business-address","value":"1028 STREET Dr"},{"id":2660,"key":"city","value":"CITY NAME"},{"id":2661,"key":"state","value":"California"},{"id":2662,"key":"zip","value":"900XX"},{"id":2663,"key":"_subscription_switch_data","value":[]},{"id":2723,"key":"_stripe_customer_id","value":"XXXX"},{"id":2724,"key":"_stripe_source_id","value":"XXXX"},{"id":2727,"key":"_stripe_charge_captured","value":"yes"},{"id":2728,"key":"_stripe_fee","value":"0.45"},{"id":2729,"key":"_stripe_net","value":"4.55"},{"id":2730,"key":"_stripe_currency","value":"USD"},{"id":2736,"key":"_wc_memberships_access_granted","value":{"215":{"already_granted":"yes","granting_order_status":"processing"}}},{"id":2748,"key":"_wc_memberships_access_granted","value":{"215":{"already_granted":"yes","granting_order_status":"processing"}}}]
foreach($MetaData as $index => $feature)
{
$MetaValue = $MetaData[$index ]['key'];
DebugLog($MetaValue);
}
The final foreach causes a server error. I just don't see it and I have tried all sorts of combinations.
I have also tried:
foreach($MetaData as $feature)
{
$MetaValue = $feature['key'];
DebugLog($MetaValue);
}
and
$MetaValue = $MetaData[0]['key']
Same results. Any idea? What am I missing in this most basic of programming tools?

UPDATE! GOT IT
Wild ride. You have to create the pointer to the row (MetaData, but then treat each row as it's own array with indexes.
THE SOLUTION
$order = new WC_Order( $order_id );
$order_data = $order->get_data();
$MetaData = $order_data['meta_data'];
foreach($MetaData as $SubRow)
{
DebugLog($SubRow->id);
DebugLog($SubRow->key);
DebugLog($SubRow->value);
}
Thanks to all who wrote and got me to think out of the box a bit.

Related

Migrate a php function from one case to another in the same file

My developer left me out to dry and while im able to learn pretty quickly, actually adding code to excisiting piece of code is a struggle while reading the code makes sort of sense. My business got hit by corona and this piece of code is part of my core business. I have to solve this and sadly on my own account as im willing to pay if i had the recources.
I guess this is a very basic question for all but beginners but i'd love to understand it. Been spending alot of time just reading the code and now its time for help.
This piece of code
case 'Summary list':
foreach ( $order_ids as $order_id ) {
$user_id = get_post_meta($order_id, '_customer_user', true);
$groups_user = new Groups_User( $user_id );
$orders_group[$groups_user->groups[0]->group->name][$user_id][] = $order_id;
}
$i = 1;
foreach ($orders_group as $group => $users) {
// Group name
$formater->custom_header($group);
// Table header
$formater->table_header();
Displays the group that the user is in on a PDF. Now i know that the foreach part defines the formatter part. the part in particulair is the
// Group name
$formater->custom_header($group);
Now in the same file i need to display that same group name in the following sequence
case 'Delivery note':
foreach ( $order_ids as $order_id ) {
$order = wc_get_order( $order_id );
$user_id = $order->get_user_id();
if(array_key_exists($order->get_user_id(), $orders_group)) {
$orders_group[$user_id]['order_ids'][] = $order_id;
} else {
$orders_group[$user_id]['order'] = $order;
$orders_group[$user_id]['order_ids'][] = $order_id;
}
}
$og_count = count($orders_group);
$og_i = 1;
foreach ($orders_group as $user_id => $data) {
// Delivery header
$formater->delivery_header();
// Client name
$formater->custom_header($data['order']
->get_formatted_shipping_full_name(), 5);
// Client address
$address = join( ", ", array_filter( array(
$data['order']->get_shipping_address_1(),
$data['order']->get_shipping_address_2(),
$data['order']->get_shipping_city(),
$data['order']->get_shipping_postcode() )
)
);
$formater->custom_header($address, 10);
// Group name
$formater->custom_header($group);
// Table header
$formater->table_header();
Now ive already put the
// Group name
$formater->custom_header($group);
part in there but i am unable to define it in the upper part.
Is someone willing to show me how i can get this to work and possibly explain it since i really like understanding it aswelll.
Edit:
https://imgur.com/a/wKPDaM7
The "Registerd" part is what the
// Group name
$formater->custom_header($group); prints.
In the 2nd image, the blue line, thats where i want that to appear aswell.

Is file_put_contents a reliable way to check value of some variables?

My website is built with wordpress+woocommerce.
For learning purpose, I am trying to check an order's detail once it is completed using file_put_contents. Here is the code:
add_action( 'woocommerce_order_status_completed', 'change_role_on_first_purchase' );
function change_role_on_first_purchase( $order_id ) {
$order = new WC_Order( $order_id );
// $user = new WP_user($order->user_id);
file_put_contents('order.json',json_encode($order));
file_put_contents('userid.json',json_encode($order->user_id));
}
}
While the second file_put_contents does write the correct user ID into userid.json, order.json's content is just {}. Obviously $order is not empty, then why file_put_contents is outputting an empty json object?
Short Answer
Before you can json_encode($order), you need to get the order's data as a plain unprotected array:
$order = new WC_Order( $order_id );
$order_data = $order->get_data(); // ADD THIS
file_put_contents('order.json',json_encode($order_data));
Long Answer
Read: json_encode empty with no error
TLDR: Basically, json_encode works best when an object has public properties. WC_Order doesn't have any, so it results in an empty object and that's why they created the get_data() method (to expose the data as a plain "unprotected" associative array) (perfect food for json_encode).

Search by order item SKU or ID in WooCommerce Orders Admin page

What I am trying to do is to be able to search by order item SKU or ID in the WooCommerce Orders Admin page.
What I have found/done till now, but with no success is the following at functions.php file.
add_filter( 'woocommerce_shop_order_search_fields', 'woocommerce_shop_order_search_sku' );
function woocommerce_shop_order_search_sku( $search_fields ) {
$args = array( 'post_type' => 'shop_order' );
$orders = new WP_Query( $args );
if ( $orders->have_posts() ) {
while( $orders->have_posts() ) {
$post = $orders->the_post();
$order_id = get_the_ID();
$order = new WC_Order( $order_id );
$items = $order->get_items();
foreach( $items as $item ) {
$search_order_item_sku = wp_get_post_terms( $item['product_id'], 'search_sku' );
foreach( $search_order_item_sku as $search_sku ) {
add_post_meta( $order_id, "_search_sku", $search_sku->sku );
}
}
}
};
$search_fields[] = '_search_sku';
return $search_fields;
}
I suppose the issue is the value of $search_sku at the line with the add_post_meta.
I have also tried it with get_sku(), $item['sku'] with no luck.
You have the right idea about saving extra metadata to the order. As jbby and helgatheviking suggest, there is no built-in postmeta for product_id or sku available by default in the woocommerce orders api. Your methodology for accessing and saving the metadata wasn't quite right, however. wp_get_post_terms will access custom taxonomy information, not metadata (use get_post_meta for that). You will be able to do what you were trying to do with this filter:
add_filter( 'woocommerce_shop_order_search_fields', function ($search_fields ) {
$posts = get_posts(array('post_type' => 'shop_order'));
foreach ($posts as $post) {
$order_id = $post->ID;
$order = new WC_Order($order_id);
$items = $order->get_items();
foreach($items as $item) {
$product_id = $item['product_id'];
$search_sku = get_post_meta($product_id, "_sku", true);
add_post_meta($order_id, "_product_sku", $search_sku);
add_post_meta($order_id, "_product_id", $product_id);
}
}
return array_merge($search_fields, array('_product_sku', '_product_id'));
});
Strictly speaking you should probably move the calls to add_post_meta into a hook that runs when the order is originally saved to the database--this will prevent unnecessary legwork whenever you search through order.
#blacksquare, #jibby, #helgatheviking you are the men! This is the code that works, due to your help.
//Search by product SKU in Admin Woocommerce Orders
add_filter( 'woocommerce_shop_order_search_fields', function ($search_fields ) {
$posts = get_posts(array('post_type' => 'shop_order'));
foreach ($posts as $post) {
$order_id = $post->ID;
$order = new WC_Order($order_id);
$items = $order->get_items();
foreach($items as $item) {
$product_id = $item['product_id'];
$search_sku = get_post_meta($product_id, "_sku", true);
add_post_meta($order_id, "_product_sku", $search_sku);
}
}
return array_merge($search_fields, array('_product_sku'));
});
While #Nikos and #blacksquare 's answers work, new post metas are added to every order on every search. If you have 100 orders and make 10 searches, there will be at least 100*10 = 1000 _product_sku entries in the wp_postmeta table. If some orders contain multiple products, there will be even more.
As #blacksquare suggested, add_post_meta should be called when the order is saved. That said, if the site is small and backend search performance isn't too much of a concern, the following code would work without creating redundant _product_sku entries.
add_filter( 'woocommerce_shop_order_search_fields', 'my_shop_order_search_fields') );
public function my_shop_order_search_fields( $search_fields ) {
$orders = get_posts( array(
'post_type' => 'shop_order',
'post_status' => wc_get_order_statuses(), //get all available order statuses in an array
'posts_per_page' => 999999, // query all orders
'meta_query' => array(
array(
'key' => '_product_sku',
'compare' => 'NOT EXISTS'
)
) // only query orders without '_product_sku' postmeta
) );
foreach ($orders as $order) {
$order_id = $order->ID;
$wc_order = new WC_Order($order_id);
$items = $wc_order->get_items();
foreach($items as $item) {
$product_id = $item['product_id'];
$search_sku = get_post_meta($product_id, '_sku', true);
add_post_meta( $order_id, '_product_sku', $search_sku );
}
}
return array_merge($search_fields, array('_product_sku')); // make '_product_sku' one of the meta keys we are going to search for.
}
While a better solution might be calling add_post_meta when an order is created, extra efforts are needed to create _product_sku for existing orders, and you have to create the _product_sku for orders made while the code isn't activated. For simplicity sake, I'd just use the solution suggested above.
p.s. #Nikos 's solution does have one (debatable) advantage - if you change a product's SKU after orders are made, Nikos's solution will find those orders using the new SKU, while the solution above will not. That said, a product's SKU should NOT be changed anyway, and it's debatable whether searching new SKUs should show old orders.
You may also want to look into https://uk.wordpress.org/plugins/woo-search-order-by-sku/ which does not overloads post meta, but alters the where clause.
As stated in answer by Ming Yeung, any solution which uses the add_post_meta() function will increase your database size, which might not be ideal for larger stores. That sort of approach will also potentially make the searching of Woocommerce orders in admin quite slow.
An alternative solution is to make use of one of the existing "index" post meta records that Woocommerce uses, and append your data to those records (but only append once).
The following code does exactly that. It does not add SKU (as asked in question) but instead allows for Woocommerce order search to include Country name (as opposed to country code). However this code could be easily adapted to also include SKU if needed:
// when doing a search within Woocommerce orders screen, include the Country name as part of the search criteria
// this code has been optimised to do as little DB work as possible (looks for any index values which haven't been updated yet, and updates them)
// this code also makes use of existing post meta "index" records, instead of creating additional new post meta rows (which would fill up database unnecessarily)
add_filter('woocommerce_shop_order_search_fields', function($search_fields) {
global $wpdb;
$records_to_update = $wpdb->get_results("SELECT * FROM $wpdb->postmeta WHERE (meta_key LIKE '%_address_index%') AND (meta_value NOT LIKE '%[HAS_COUNTRY]%')");
foreach ($records_to_update as $record) {
$order = new WC_Order($record->post_id);
if ($record->meta_key == '_billing_address_index') {
$country_name = WC()->countries->countries[ $order->get_billing_country() ];
} else {
$country_name = WC()->countries->countries[ $order->get_shipping_country() ];
}
update_post_meta($record->post_id, $record->meta_key, $record->meta_value . ' ' . $country_name . ' [HAS_COUNTRY]');
}
return $search_fields;
});

How can I get Quote data outside Magento API?

I am trying to write a script to process all the orders from my magento store. I am able to get Shipping,Billing address and customer's name but now I want the list of items they have bought which i think will be in "quote" object.
I am trying this 2 lines to get quote object but I am getting an empty array.
Please tell me what's wrong with this code.
$salesCollection = Mage::getModel("sales/order")->getCollection()->addAttributeToFilter('state', array('eq' => Mage_Sales_Model_Order::STATE_PROCESSING));
foreach($salesCollection as $order)
{
$quote_id = $order->getQuoteId();
$quote = Mage::getModel('sales/quote')->load($quote_id);
print_r($quote->getData());
}
No, they are not in "quote" object. You can get using this sample code -
$order_id = 1234; //use your own order id
$order = Mage::getModel("sales/order")->load($order_id);
$ordered_items = $order->getAllItems();
foreach($ordered_items as $item){
echo $item->getName();
}
Now using foreach loop on $ordered_items, you can get item data.
To get order item you need order item and not quote
$salesCollection = Mage::getModel("sales/order")->getCollection()
->addAttributeToFilter('state', array('eq' => Mage_Sales_Model_Order::STATE_PROCESSING));
foreach($salesCollection as $order){
$shipping = $order->getShippingAddress();
....
$items = $order->getAllVisibleItems();
foreach($items as $item){
echo $item->getProductId();
}
}

Manipulate Zend_Paginator Data

I want to add product variations to my product view table which uses the Zend_Paginator.
With this code I get my products.
$select = $productModel->select() ... (so on)
With this code I create the paginator
$adapter = new Zend_Paginator_Adapter_DbSelect($select);
$paginator = new Zend_Paginator($adapter);
And now I'm trying to add the product_variationsto the product data. I was trying to do this:
foreach($paginator as $key => $product) {
// get variations
$variations = $productModel->getProductVariants($product['ID']);
// overwrite $product add variations
$product['Variations'] = $variations;
$paginator->$key = $product;
}
But in my view controller only the product_data will be shown. The array (Variations) is missing.
How can I handle this?
TIA
FRGTV10
See this: Adding items to a paginator already created.
foreach($paginator as $key => &$product) {
// get variations
$variations = $productModel->getProductVariants($product['ID']);
// overwrite $product add variations
$product['Variations'] = $variations;
}
unset($product);
Notice the & in foreach() - pass by reference. Then you change the referenced $product and don't need to assign anything back to $paginator.

Categories