-
-
Save jamiepittock/007d3869ba05c86e899e01fe051e7b6a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Craft; | |
class B_DeleteGoogleProductTask extends BaseTask | |
{ | |
public function getDescription () | |
{ | |
return 'Updating Google Shopping'; | |
} | |
public function getTotalSteps () | |
{ | |
return count($this->getSettings()->productIds); | |
} | |
public function runStep ($step) | |
{ | |
$product = craft()->commerce_products->getProductById($this->getSettings()->productIds[$step]); | |
try { | |
craft()->b_google->onProductDelete($product); | |
} catch (\Exception $e) {} | |
return true; | |
} | |
// Protected | |
// ========================================================================= | |
protected function defineSettings () | |
{ | |
return [ | |
'productIds' => AttributeType::Mixed | |
]; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Craft; | |
set_error_handler(function($errno, $errstr, $errfile, $errline ){ | |
throw new \ErrorException($errstr, $errno, 0, $errfile, $errline); | |
}); | |
class B_GoogleService extends BaseApplicationComponent | |
{ | |
private $_client; | |
private $_service; | |
private $_keyFilename = '[NameOfKeyFile]'; | |
private $_merchantId = '[MerchantId]'; | |
private function _initGoogleClient () | |
{ | |
if (!empty($this->_client) && !empty($this->_service)) return; | |
// This assumes the keyfile is in the `craft/config/` directory | |
// (i.e. `craft/config/myKeyFile.json`) | |
$path = CRAFT_CONFIG_PATH; | |
putenv("GOOGLE_APPLICATION_CREDENTIALS={$path}{$this->_keyFilename}.json"); | |
$this->_client = new \Google_Client(); | |
$this->_client->useApplicationDefaultCredentials(); | |
$this->_client->setScopes(['https://www.googleapis.com/auth/content']); | |
$this->_service = new \Google_Service_ShoppingContent($this->_client); | |
} | |
/** | |
* @param Commerce_ProductModel $product | |
* | |
* @return bool | |
*/ | |
public function onProductSave (Commerce_ProductModel $product) | |
{ | |
$this->_initGoogleClient(); | |
if ( | |
!filter_var($product->availableOnline, FILTER_VALIDATE_BOOLEAN) | |
|| !$product->enabled | |
) return true; | |
foreach ($product->variants as $variant) { | |
$gProduct = new \Google_Service_ShoppingContent_Product(); | |
$gProduct->setId($product->slug . '_' . $variant->id); | |
// If we don't have a valid GTIN number, skip since Google won't allow it. | |
if (!$this->_checkGTIN($variant->sku)) | |
continue; | |
$gProduct->setGtin($variant->sku); | |
$gProduct->setTitle($product->getTitle()); | |
$gProduct->setDescription(strip_tags($product->productDetails)); | |
$gProduct->setProductType($product->productCategory->last()->getTitle()); | |
$url = $product->getUrl(); | |
if (!$variant->isDefault) | |
$url .= '/' . $variant->id; | |
$gProduct->setLink($url); | |
$gProduct->setImageLink($variant->gallery->first()->getUrl('gallery')); | |
$gProduct->setCondition('new'); | |
$gProduct->setAvailability($variant->stock > 0 || $variant->unlimitedStock ? 'in stock' : 'out of stock'); | |
// If we don't have a brand, skip since Google won't allow it. | |
if (!$product->brand->first()) | |
continue; | |
$gProduct->setBrand($product->brand->first()->getTitle()); | |
$gProduct->setChannel($product->availableOnline ? 'online' : 'local'); | |
$gProduct->setContentLanguage('EN'); | |
$gProduct->setOfferId("P{$product->id}V{$variant->id}"); | |
$gProduct->setTargetCountry('GB'); | |
// TODO: Find a way to not have to hard-code shipping values | |
$shipping_price = new \Google_Service_ShoppingContent_Price(); | |
if ($product->getShippingCategory()->handle == 'aquaOak') { | |
$shipping_price->setValue(50.00); | |
} else { | |
$shipping_price->setValue( | |
$variant->price > 49.99 || $product->freeShipping | |
? '0.00' | |
: '5.95' | |
); | |
} | |
$shipping_price->setCurrency('GBP'); | |
$shipping = new \Google_Service_ShoppingContent_ProductShipping(); | |
$shipping->setPrice($shipping_price); | |
$shipping->setCountry('GB'); | |
$shipping->setService('Standard shipping'); | |
$gProduct->setShipping([$shipping]); | |
$price = new \Google_Service_ShoppingContent_Price(); | |
$price->setCurrency('GBP'); | |
$price->setValue($variant->price); | |
$gProduct->setPrice($price); | |
if ($variant->getOnSale()) { | |
$salePrice = new \Google_Service_ShoppingContent_Price(); | |
$salePrice->setCurrency('GBP'); | |
$salePrice->setValue($variant->getSalePrice()); | |
$gProduct->setSalePrice($salePrice); | |
} | |
try { | |
$this->_service->products->insert( | |
$this->_merchantId, | |
$gProduct, | |
['dryRun' => craft()->config->get('devMode')] | |
); | |
} | |
catch (\Google_Service_Exception $e) | |
{ | |
$message = "[Product ID {$product->id}] [Variant ID {$variant->id}] "; | |
if (!empty($e->getErrors()) && array_key_exists('message', $e->getErrors()[0])) | |
$message .= $e->getErrors()[0]['message']; | |
BPlugin::log(print_r($message, true), LogLevel::Error, true); | |
} | |
} | |
return true; | |
} | |
/** | |
* @param Commerce_ProductModel $product | |
* | |
* @return bool | |
*/ | |
public function onProductDelete (Commerce_ProductModel $product) | |
{ | |
$this->_initGoogleClient(); | |
if (!$product->availableOnline) return true; | |
foreach ($product->variants as $variant) { | |
try { | |
$this->_service->products->delete($this->_merchantId, $product->slug . '_' . $variant->id); | |
} | |
catch (\Exception $e) | |
{ | |
BPlugin::log(print_r($e, true), LogLevel::Error, true); | |
return true; // Fail silently to stop clogging up tasks | |
} | |
} | |
return true; | |
} | |
// Private | |
// ========================================================================= | |
# Check length of barcode for validity | |
function _checkGTIN ($gtin) { | |
# Check that GTIN provided is a certain length | |
if (!$this->_checkBasics($gtin)) | |
return false; | |
# Define fixed variables | |
$CheckDigitArray = []; | |
$gtinMaths = [3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3]; | |
$modifier = 17 - (strlen($gtin) - 1); // Gets the position to place first digit in array | |
$gtinCheckDigit = substr($gtin, -1); // Get provided check digit | |
$BarcodeArray = str_split($gtin); // Split barcode at each digit into array | |
$gtinLength = strlen($gtin); | |
$tmpCheckDigit = 0; | |
$tmpCheckSum = 0; | |
$tmpMath = 0; | |
# Run through and put digits into multiplication table | |
for ($i=0; $i < ($gtinLength - 1); $i++) { | |
$CheckDigitArray[$modifier + $i] = $BarcodeArray[$i]; // Add barcode digits to Multiplication Table | |
// Catch if part of GTIN is not a number | |
if (!is_numeric($CheckDigitArray[$modifier + $i])) | |
return false; | |
} | |
# Calculate "Sum" of barcode digits | |
for ($i=$modifier; $i < 17; $i++) { | |
$tmpCheckSum += ($CheckDigitArray[$i] * $gtinMaths[$i]); | |
} | |
# Difference from Rounded-Up-To-Nearest-10 - Fianl Check Digit Calculation | |
$tmpCheckDigit = (ceil($tmpCheckSum / 10) * 10) - $tmpCheckSum; | |
# Check if last digit is same as calculated check digit | |
if ($gtinCheckDigit == $tmpCheckDigit) | |
return true; | |
return false; | |
} | |
# Checks the length of the GTIN | |
function _checkBasics ($gtin) { | |
# Check length is ok | |
if (strlen($gtin) < 8 || strlen($gtin) > 14) | |
return false; | |
# Check whether is a number | |
preg_match("/\d+/", $gtin, $m, PREG_OFFSET_CAPTURE, 0); | |
if (empty($m)) | |
return false; | |
# Is valid, return true | |
return true; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Craft; | |
class B_UpdateGoogleProductTask extends BaseTask | |
{ | |
public function getDescription () | |
{ | |
return 'Updating Google Shopping'; | |
} | |
public function getTotalSteps () | |
{ | |
return count($this->getSettings()->productIds); | |
} | |
public function runStep ($step) | |
{ | |
$product = craft()->commerce_products->getProductById($this->getSettings()->productIds[$step]); | |
try { | |
craft()->b_google->onProductSave($product); | |
} catch (\Exception $e) {} | |
return true; | |
} | |
// Protected | |
// ========================================================================= | |
protected function defineSettings () | |
{ | |
return [ | |
'productIds' => AttributeType::Mixed | |
]; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
// Stripped down for brevity | |
class BPlugin { | |
public function init () { | |
// Trigger Google Shopping updates | |
// ===================================================================== | |
if (!craft()->config->get('devMode')) { | |
craft()->on('commerce_products.onSaveProduct', | |
function (Event $event) { | |
craft()->tasks->createTask('B_UpdateGoogleProduct', null, [ | |
'productIds' => [$event->params['product']->id], | |
]); | |
}); | |
craft()->on('commerce_products.onBeforeDeleteProduct', | |
function (Event $event) { | |
craft()->tasks->createTask('B_DeleteGoogleProduct', null, [ | |
'productIds' => [$event->params['product']->id], | |
]); | |
}); | |
craft()->on('commerce_variants.onOrderVariant', | |
function (Event $event) { | |
craft()->tasks->createTask('B_UpdateGoogleProduct', null, [ | |
'productIds' => [$event->params['variant']->productId], | |
]); | |
}); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment