Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
* copyrights andrew killen 2017
* Attribution-ShareAlike 4.0 International.
* This will get the contents from the db about the current values, optionally if you have setup
* a custom taxonomy you can say "I want from supplier ID n" this helps as SKU's can often be the same
* for many suppliers, it will then load/download an XML file and convert it to an Array,
* and finally loop through the DB current values, looking for them in the feed array by SKU, if they exist
* then it will try to update the meta, logging ths changes made.
class xmlUpdateWooCommerce {
// place on the system where CRON downloads the file. this is normally 10 --> 20X faster than php
protected $fileSystemFile = '/path/to/cron/downloaded/file.xml';
// fall back to remote url
protected $feedUrl = "";
// where the database current values are held
protected $result;
// the XML that is loaded
protected $xml;
* How to run the whole thing.
* @param $supplier int, optional tax term id that can be used to define all products from one supplier
public function init($supplier = 0){
* Use $wpdb and a generated select statement to return an object with all
* of the needed produt meta + product ID
* @param $supplier int taxonomy term id
* @return object
protected function getCurrentDbValues($supplier = 0){
global $wpdb;
// set the post type to search through
$post_type = 'product';
// postmeta data that will be joined to the result
$metas = ['_sku','_stock', '_regular_price','_price'];
// look the metas to add the sql statement to make the joins
foreach ($metas as $i => $meta_key) {
$meta_fields[] = 'm' . $i . '.meta_value as ' . $meta_key;
$meta_joins[] = ' left join ' . $wpdb->postmeta . ' as m' . $i . ' on m' . $i . '.post_id=' . $wpdb->posts . '.ID and m' . $i . '.meta_key="' . $meta_key . '"';
// begin the SQL statemnt adding in the meta fields to ID
$request = "SELECT ID, " . join(',', $meta_fields) . " FROM $wpdb->posts ";
// add the joins
$request .= join(' ', $meta_joins);
// make sure its using the right post status and post type
$request .= " WHERE {$wpdb->posts}.post_status='publish' AND {$wpdb->posts}.post_type='{$post_type}'";
* Optional, use supplier taxonomy to reduce the total number of records
* returned. This is often needed as SKU's can be the same from many suppliers.
* It should really double check to see if the term tax id exists first...
if($supplier != 0){
$request .= " AND ID IN(
select object_id FROM {$wpdb->prefix}term_relationships where term_taxonomy_id ='{$supplier}'
$this->result = $wpdb->get_results($request);
* tries to get CRON downloaded file first
* Idealy this should check the file datetime to see if it out of date
* @return void
protected function openFeedAsXML(){
$this->xml = simplexml_load_file($this->fileSystemFile, NULL, LIBXML_NOCDATA);
} else {
$content = file_get_contents($this->feedUrl);
$this->xml = simplexml_load_string($content, NULL, LIBXML_NOCDATA);
* Loop through the feed XML converting it to an assoicative array
* where the sku is the key, and the other values are in an array assoicated
* with it.
* this will convert the entire feed, not just the items that are in the db.
* becuase of the use late of isset(), it will make it uber fast.
* If I was making this for anyone else I would have spilt this up a bit into separate methods
* and not nested loops, but I love doing it this way. nested loops make me happy :)
protected function feedLoopAndUpdate(){
// VidaXL use /products/product to hold the stock items. change this as needed
$nodes = $this->xml->xpath("/products/product");
$output = [];
// This is part of the magic here. make sure the SKU is the key
foreach($nodes as $node ){
$output [(int) $node->SKU] = [
'stock' => (int) $node->Stock,
'rrp' => (string) $node->RRP,
// loop through the result
foreach( $this->result as $row ){
$check = [
'_stock' => 'stock',
'_price' => 'rrp' ,
'_regular_price' => 'rrp',
// check the feed array if the sku exists. if it does work on it
// loop through the fiels/post meta you can to check
foreach($check as $meta => $field) {
// continue to next meta is this meta does not exist
// If the current meta value is different to the one in the feed, update.
if($output[$row->_sku][$field] != $row->{$meta}){
* some error logging so you can see what happend
error_log( "ID: {$row->ID} SKU: {$row->_sku} - changing $meta to be: {$output[$row->_sku][$field]} , $meta was: {$row->{$meta}}" );
echo "ID: {$row->ID} SKU: {$row->_sku} - changing $meta to be: {$output[$row->_sku][$field]} , $meta was: {$row->{$meta}} ";
echo "<a href='".get_permalink($row->ID)."'>". get_the_title($row->ID) ."</a> <br/>";
* update the post meta for that value
update_post_meta($row->ID, $meta, $output[$row->_sku][$field]);
} else {
protected function whatToDoIfSKUdoesNotExistInFeed($id) {
// do something like send an email, or create a report that tells
// the system owner that the product is no longer available in the feed
// this will stop orphaned products, or products that say they have stock
// when the supplier cannot deliver.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.