| @@ -6,5 +6,6 @@ require __DIR__ . '/IsotopeDatabaseHandler.php'; | |||
| $apiExact = new ApiExact(); | |||
| $databaseHandler = new IsotopeDatabaseHandler(); | |||
| $products = $apiExact->getProducts(); | |||
| $databaseHandler->processProducts($products); | |||
| var_dump($products); | |||
| //$databaseHandler->processProducts($products); | |||
| @@ -0,0 +1,271 @@ | |||
| <?php | |||
| /** | |||
| * @author Daniel Knudsen <d.knudsen@spawntree.de> | |||
| * @date 11.07.22 | |||
| */ | |||
| class Product | |||
| { | |||
| const VAT = 0.19; | |||
| private $id; | |||
| private $standardSalesPrice; | |||
| private $salesVatCode; | |||
| private $salesVatCodeDescription; | |||
| private $description; | |||
| private $extraDescription; | |||
| private $isWebShopItem; | |||
| private $itemGroup; | |||
| private $itemGroupCode; | |||
| private $stock; | |||
| private $netWeight; | |||
| private $grossWeight; | |||
| private $code; | |||
| public static function getFields() | |||
| { | |||
| return [ | |||
| 'ID', | |||
| 'StandardSalesPrice', | |||
| 'SalesVatCode', | |||
| 'SalesVatCodeDescription', | |||
| 'Description', | |||
| 'ExtraDescription', | |||
| 'IsWebshopItem', | |||
| 'ItemGroup', | |||
| 'ItemGroupCode', | |||
| 'Stock', | |||
| 'NetWeight', | |||
| 'GrossWeight', | |||
| 'Code', | |||
| ]; | |||
| } | |||
| public static function createProductFromApiItem(array $item) | |||
| { | |||
| $res = new self(); | |||
| $res->setId($item['ID']); | |||
| $res->setStandardSalesPrice($item['StandardSalesPrice']); | |||
| $res->setSalesVatCode($item['SalesVatCode']); | |||
| $res->setSalesVatCodeDescription($item['SalesVatCodeDescription']); | |||
| $res->setDescription($item['Description']); | |||
| $res->setExtraDescription($item['ExtraDescription']); | |||
| $res->setIsWebShopItem($item['IsWebshopItem']); | |||
| $res->setItemGroup($item['ItemGroup']); | |||
| $res->setItemGroupCode($item['ItemGroupCode']); | |||
| $res->setStock($item['Stock']); | |||
| $res->setNetWeight($item['NetWeight']); | |||
| $res->setGrossWeight($item['GrossWeight']); | |||
| $res->setCode($item['Code']); | |||
| return $res; | |||
| } | |||
| /** | |||
| * @return mixed | |||
| */ | |||
| public function getId() | |||
| { | |||
| return $this->id; | |||
| } | |||
| /** | |||
| * @param mixed $id | |||
| */ | |||
| public function setId($id) | |||
| { | |||
| $this->id = $id; | |||
| } | |||
| /** | |||
| * @return mixed | |||
| */ | |||
| public function getStandardSalesPrice() | |||
| { | |||
| return $this->standardSalesPrice; | |||
| } | |||
| /** | |||
| * @param mixed $standardSalesPrice | |||
| */ | |||
| public function setStandardSalesPrice($standardSalesPrice) | |||
| { | |||
| $this->standardSalesPrice = $standardSalesPrice; | |||
| } | |||
| /** | |||
| * @return mixed | |||
| */ | |||
| public function getSalesVatCode() | |||
| { | |||
| return $this->salesVatCode; | |||
| } | |||
| /** | |||
| * @param mixed $salesVatCode | |||
| */ | |||
| public function setSalesVatCode($salesVatCode) | |||
| { | |||
| $this->salesVatCode = $salesVatCode; | |||
| } | |||
| /** | |||
| * @return mixed | |||
| */ | |||
| public function getSalesVatCodeDescription() | |||
| { | |||
| return $this->salesVatCodeDescription; | |||
| } | |||
| /** | |||
| * @param mixed $salesVatCodeDescription | |||
| */ | |||
| public function setSalesVatCodeDescription($salesVatCodeDescription) | |||
| { | |||
| $this->salesVatCodeDescription = $salesVatCodeDescription; | |||
| } | |||
| /** | |||
| * @return mixed | |||
| */ | |||
| public function getDescription() | |||
| { | |||
| return $this->description; | |||
| } | |||
| /** | |||
| * @param mixed $description | |||
| */ | |||
| public function setDescription($description) | |||
| { | |||
| $this->description = $description; | |||
| } | |||
| /** | |||
| * @return mixed | |||
| */ | |||
| public function getExtraDescription() | |||
| { | |||
| return $this->extraDescription; | |||
| } | |||
| /** | |||
| * @param mixed $extraDescription | |||
| */ | |||
| public function setExtraDescription($extraDescription) | |||
| { | |||
| $this->extraDescription = $extraDescription; | |||
| } | |||
| /** | |||
| * @return mixed | |||
| */ | |||
| public function getIsWebShopItem() | |||
| { | |||
| return $this->isWebShopItem; | |||
| } | |||
| /** | |||
| * @param mixed $isWebShopItem | |||
| */ | |||
| public function setIsWebShopItem($isWebShopItem) | |||
| { | |||
| $this->isWebShopItem = $isWebShopItem; | |||
| } | |||
| /** | |||
| * @return mixed | |||
| */ | |||
| public function getItemGroup() | |||
| { | |||
| return $this->itemGroup; | |||
| } | |||
| /** | |||
| * @param mixed $itemGroup | |||
| */ | |||
| public function setItemGroup($itemGroup) | |||
| { | |||
| $this->itemGroup = $itemGroup; | |||
| } | |||
| /** | |||
| * @return mixed | |||
| */ | |||
| public function getItemGroupCode() | |||
| { | |||
| return $this->itemGroupCode; | |||
| } | |||
| /** | |||
| * @param mixed $itemGroupCode | |||
| */ | |||
| public function setItemGroupCode($itemGroupCode) | |||
| { | |||
| $this->itemGroupCode = $itemGroupCode; | |||
| } | |||
| /** | |||
| * @return mixed | |||
| */ | |||
| public function getStock() | |||
| { | |||
| return $this->stock; | |||
| } | |||
| /** | |||
| * @param mixed $stock | |||
| */ | |||
| public function setStock($stock) | |||
| { | |||
| $this->stock = $stock; | |||
| } | |||
| /** | |||
| * @return mixed | |||
| */ | |||
| public function getNetWeight() | |||
| { | |||
| return $this->netWeight; | |||
| } | |||
| /** | |||
| * @param mixed $netWeight | |||
| */ | |||
| public function setNetWeight($netWeight) | |||
| { | |||
| $this->netWeight = $netWeight; | |||
| } | |||
| /** | |||
| * @return mixed | |||
| */ | |||
| public function getGrossWeight() | |||
| { | |||
| return $this->grossWeight; | |||
| } | |||
| /** | |||
| * @param mixed $grossWeight | |||
| */ | |||
| public function setGrossWeight($grossWeight) | |||
| { | |||
| $this->grossWeight = $grossWeight; | |||
| } | |||
| /** | |||
| * @return mixed | |||
| */ | |||
| public function getCode() | |||
| { | |||
| return $this->code; | |||
| } | |||
| /** | |||
| * @param mixed $code | |||
| */ | |||
| public function setCode($code) | |||
| { | |||
| $this->code = $code; | |||
| } | |||
| } | |||
| @@ -1 +1 @@ | |||
| {"access_token":"stampDE001.gAAAAATOqLu_FvySDkT1YxBYQOSbyTqVC-jQWfCBmfomFxKEuiSUp2H5XYFwHOjuZ_cbURYObp_CuoijjuXPHYAvgR5GjxZVLpnI33116cIBqeKkdnQ2617l-DfjsiPlSO9ilA5CtrV75A1ZhxtWJqJ5uZynH0eT0ZB8LytI81Fg2p4tVAIAAIAAAAAqO6Xcq6ab64TEJDE2QyPHpfueoxrCkLlFVkxbiXgoANGrrSTu9qQtOTYyszkuc3sGZSdg1zD8eJLx8sL0zlTy7BYRYgTmiUhLZhECjG_7PfpObMuaz_Q4NOlIeyGOX23B6p-lbSZ_HHcVx1XzDgSry7MSIcFZGIqUWqr4jWW6EDqU0Q9A1ypbkKAPxh77V68LrHAzIm8yjoQell4GBnNSVOwNWKcLgmqkznUIsywr-Kq6dvfs_Ziy0nu5DR6PbpYLIVY4g1eCE4ncfgtPYsndKKuSIOisf8pYn_7NaLfSxvYWVwFxuTHw3roRufszbE-toAsUzfBVv2mQjKdyop9xRri3Adh3vfsEAKEyksZdsduCjFEPu6KoSv_pgQfExSURkHKj0gOX8_GjPtcbT4OWZ3JuZ7n9-4U5uSV-HHOvd7T2ojgQ0lFcVukuwHq086KTMNGzcBPfM-_vcBYAx1nRRpupQL2Np0fm85iycZwycSKAFydpcMeB1VaP5PYkVLiLOsXBPy4SMM0_-c51NykUm338m8SwAVeVQXN3VYcT0kJwUsyMdOtYoDvXUL-5p_NafKwwPwdrgpIwnjjO3s37O43RsGWaY8kp42y2I4lQbMwcqTde_bOcPCRQWium4OB__fkFaiCJ5eE5EGcbPUVzjEfW_Lhr8yrkRacFfut9YqCTVuGxZepi0eyX3FHk_-qAtXTBav11EZ40G5slokVlyPu63pQfhpSrsNIGZv-j5YIsN5MqjHn7HgtbS0UMRUAQERHvDkz6ddfbkqwjOgYE","token_type":"bearer","expires_in":"600","refresh_token":"stampDE001.vhK0!IAAAAIMZSdkYxeTLXpkXHeNZCQAgV_DZ3JM_sqN9Y81kYggu8QEAAAE1Ecbn6_tJNnyZQn4ktg02ypEuBII4NQc3bgVgWB1lM0IZaxRyKB5C9AVOCqh9ewKECN3eNMP0C0P2QVzNyoBsOHrK_QHqqypFPBfs2fPG2psKUngqf4nTWfGWpjnaeevGqll88v4_Cka58o-Kmlrv2Ho_uc3Tjl4gE6GeLF870gyWnW1Qx3AJUTx4730yswUr1YwEODVTl0WBSJ2LEqELKvx4MQ603b642QkevFCrH6ttZBef-ufEKcRpT7Fic9JHcSdUEJCGrhsGe__Ah0dS2WtxNfhD3m7YgGpxBqSjCJXWvXzBpBd9UsZ6vCuF0CP2EVvSZ_HS3iTgD5UOBDoQkTn8kt65TixrpQWZE5j2-vfgoQy9CbPv8aO75XT4gZGPtjij7GAdXNc17ZH-P_3N5CFmLnycGyfPNdnS58PP4nkYcl6LzVF5pBOBj2mIZa9Ic7W1fcSWDePaGC_WjVQvuV18tjvsMxp738XNTPdZfta5V769cSq1tuRblIyLYIJhhYhmprqg1KADYkcNC7RHYSVOHbds83VORW9gvgjFLCWt15TLhJaGbydgaMMUGHlZ4gb8YFMCzbRiq_20_K8iP_J5BdP2omIeHCNnH6360wmJybbgcFlYv0FZNDXUwkRxaTsREpfSvDUzZHCsk0Od","expiry_time":1657818416} | |||
| {"access_token":"stampDE001.gAAAALz6N4YQffUT_PsUt5T0HjUWldTzhSYiO3CUjBSEayLfCTPdWlFQZ7houvHkiJ5jDlRFLGflu6K_7VnRNtYmkBNRTz3P30sOtLmqGX2B7J3EJ3uSoApS-AEw4kzKk-FitabCwiTgdP9UbXaAnbfqqZEaXWmLmXQLlLBEOv4AJEvsVAIAAIAAAADSS-GjXrYGVJnWsv-jussxX5iQvsELp6Pj0JfWjxMd7AZaTFhADLGSvQndUodg2EeGmQ65dcgxE-gu6ZVdRwUg3u8yFpwDyoGTxqRojOA0pUDz1NGYifXvU_uw49jFYIjT5NhDwUeW-7LtZAnkU-GXqTJXeUy20cPXX7pw5uI6h-DAimSj9rrQTOZVOVAIfLhT-ojj0HFmUQsON0FJ_4ZulHoUggehSj4ECTdNM1sE9HP9cUMwuT46ccuqfyTu01HxkBxuwbPwUFf6VbyenqMjx8kuPrDNhhDD7h3XBnEYnUB5S9gobBGzkBxowi3PE83rhgpHQ1cyq6q76yXK0UOKzYVqyTGlQX6fbH95Ftv8CO7XhI5hQVOHDvadJxKZLtkVTrj9c0VlyduBi6s_VtmMhNw8dWDJqBrDSJOnzmEsXzvcvXMVXj0rLVHOGK54V_RPgFL9ieLLFvHs80X15xl90FvmILpm8BUp1J8hC5ruCGK_LZNsjIzJkPDjTmWCu3ys7WkvMMG79a28S4bgQDXmrgBhLH4PwLYz_r-XLvp7FeFXmL4NFsyQUQULCZjLkHZ45IcrlbLNKSO69QSDCS2orVj5_Ta7koRQ2zTDL34dLmXfk9RP1zVinc7fFN6RTPc_HYFqEjAHiu14Wsq1fMcNPmA9mWIVFXIVJBK6E9t-OxUkHXnvGy_wBIGZvK4qMakFisAeqmbQ61RlFYvzcE-8gUbCx0KU1ZG0TbutmJPr8mFEMWdVBk83Xlm4oaItp6BYA9pKgMciBucPq156qQ0G","token_type":"bearer","expires_in":"600","refresh_token":"stampDE001.vhK0!IAAAACdtr4d9DYfTnyyeJaJvOI0bouLOlyz4z5SU6-U1dG7D8QEAAAEjZ3c-4haAJg1MGAzHhNBKsuCqEJS71izk6qHzC8cEgxzDi5yTB1-UwsRWD8gUZEqepx55CP426XFUBQQzs8C7C_he3pRWXMZ3-qNAKAFcGtL4xJUEjnhHT-8oTswEZUo95U1WBXeAlY2jvQlL2TWlRfXwWWyE1qbcPLI6EHDQfjFnyMk-qFzmwUMjRtxv42FQODq3kkyqJWQm9yzqCJcYF1VpPKOn4NuEvoOgR0PEia3MccvSsRP9EParcpcWIEe45bEZ3jizx6G1vwSmHVNOvKfI45gQksw4EYWZl9TsS2d1AtGAuqGxkJ8JWD6fQHU5aOx0IuSAbcghZbMj25hMenJOCEAx8mehcg-QwDfAFMV0zAoSIhoZ0FZAV25y_fAEiGEz7nZUMkkZkO3wsxTjLkQslA5YrGNHz1VZyHNbQBgsNm2iP-piYpmYcSV2Z91f6oj_N-jLYB5T4vyhQSsw9Fm0Zy97kl0A59NCjkM3EHw3Z6twDF_rQkpqdes40y5PLp2TZrmkmW8moIIYU5L0SMQ0T0t7MnsrHp3GhaKSvFKNcQNUNU8eZfQDr04diEauKmQnGvGy-rXYbhap3kGtLmRZ3jZ8EhC5YXnn1cpejmIacBloOFZ-DdbUG3m86N78LJRVUv06Ca5b-MZjmbtB","expiry_time":1657899700} | |||
| @@ -1,809 +0,0 @@ | |||
| <?php | |||
| /* | |||
| * Isotope eCommerce for Contao Open Source CMS | |||
| * | |||
| * Copyright (C) 2009 - 2019 terminal42 gmbh & Isotope eCommerce Workgroup | |||
| * | |||
| * @link https://isotopeecommerce.org | |||
| * @license https://opensource.org/licenses/lgpl-3.0.html | |||
| */ | |||
| namespace Isotope\Model; | |||
| use Contao\Database; | |||
| use Contao\Date; | |||
| use Contao\DcaExtractor; | |||
| use Contao\Model; | |||
| use Isotope\Interfaces\IsotopeProduct; | |||
| use Isotope\Model\Attribute; | |||
| use Isotope\RequestCache\Filter; | |||
| use Model\Collection; | |||
| /** | |||
| * The basic Isotope product model | |||
| * | |||
| * @property int $id | |||
| * @property int $pid | |||
| * @property int $gid | |||
| * @property int $tstamp | |||
| * @property string $language | |||
| * @property int $dateAdded | |||
| * @property int $type | |||
| * @property array $pages | |||
| * @property array $orderPages | |||
| * @property array $inherit | |||
| * @property bool $fallback | |||
| * @property string $alias | |||
| * @property string $sku | |||
| * @property string $name | |||
| * @property string $teaser | |||
| * @property string $description | |||
| * @property string $meta_title | |||
| * @property string $meta_description | |||
| * @property string $meta_keywords | |||
| * @property bool $shipping_exempt | |||
| * @property array $images | |||
| * @property bool $protected | |||
| * @property array $groups | |||
| * @property bool $guests | |||
| * @property array $cssID | |||
| * @property bool $published | |||
| * @property string $start | |||
| * @property string $stop | |||
| */ | |||
| abstract class Product extends TypeAgent implements IsotopeProduct | |||
| { | |||
| /** | |||
| * Table name | |||
| * @var string | |||
| */ | |||
| protected static $strTable = 'tl_iso_product'; | |||
| /** | |||
| * Interface to validate attribute | |||
| * @var string | |||
| */ | |||
| protected static $strInterface = '\Isotope\Interfaces\IsotopeProduct'; | |||
| /** | |||
| * List of types (classes) for this model | |||
| * @var array | |||
| */ | |||
| protected static $arrModelTypes = array(); | |||
| /** | |||
| * Currently active product (LIFO queue) | |||
| * @var array | |||
| */ | |||
| protected static $arrActive = array(); | |||
| /** | |||
| * Get categories (pages) assigned to this product | |||
| * | |||
| * @param bool $blnPublished Only return published categories (pages) | |||
| * | |||
| * @return array | |||
| */ | |||
| abstract public function getCategories($blnPublished = false); | |||
| /** | |||
| * Get product that is currently active (needed e.g. for insert tag replacement) | |||
| * | |||
| * @return IsotopeProduct|null | |||
| */ | |||
| public static function getActive() | |||
| { | |||
| return 0 === \count(static::$arrActive) ? null : end(static::$arrActive); | |||
| } | |||
| /** | |||
| * Set product that is currently active (needed e.g. for insert tag replacement) | |||
| * | |||
| * @param IsotopeProduct $objProduct | |||
| */ | |||
| public static function setActive(IsotopeProduct $objProduct) | |||
| { | |||
| static::$arrActive[] = $objProduct; | |||
| } | |||
| /** | |||
| * Unset product that is currently active (prevent later use of it) | |||
| */ | |||
| public static function unsetActive() | |||
| { | |||
| array_pop(static::$arrActive); | |||
| } | |||
| /** | |||
| * Find all published products | |||
| * | |||
| * @param array $arrOptions | |||
| * | |||
| * @return Collection|Product[]|null | |||
| */ | |||
| public static function findPublished(array $arrOptions = array()) | |||
| { | |||
| return static::findPublishedBy(array(), array(), $arrOptions); | |||
| } | |||
| /** | |||
| * Find published products by condition | |||
| * | |||
| * @param mixed $arrColumns | |||
| * @param mixed $arrValues | |||
| * @param array $arrOptions | |||
| * | |||
| * @return Collection|Product[]|null | |||
| */ | |||
| public static function findPublishedBy($arrColumns, $arrValues, array $arrOptions = array()) | |||
| { | |||
| $t = static::$strTable; | |||
| $arrValues = (array) $arrValues; | |||
| if (!\is_array($arrColumns)) { | |||
| $arrColumns = array(static::$strTable . '.' . $arrColumns . '=?'); | |||
| } | |||
| // Add publish check to $arrColumns as the first item to enable SQL keys | |||
| if (BE_USER_LOGGED_IN !== true) { | |||
| $time = Date::floorToMinute(); | |||
| array_unshift( | |||
| $arrColumns, | |||
| "$t.published='1' AND ($t.start='' OR $t.start<'$time') AND ($t.stop='' OR $t.stop>'" . ($time + 60) . "')" | |||
| ); | |||
| } | |||
| return static::findBy($arrColumns, $arrValues, $arrOptions); | |||
| } | |||
| /** | |||
| * Find a single product by primary key | |||
| * | |||
| * @param int $intId | |||
| * @param array $arrOptions | |||
| * | |||
| * @return static|null | |||
| */ | |||
| public static function findPublishedByPk($intId, array $arrOptions = array()) | |||
| { | |||
| $arrOptions = array_merge( | |||
| array( | |||
| 'return' => 'Model' | |||
| ), | |||
| $arrOptions | |||
| ); | |||
| return static::findPublishedBy(static::$strPk, (int) $intId, $arrOptions); | |||
| } | |||
| /** | |||
| * Find a single product by its ID or alias | |||
| * | |||
| * @param mixed $varId The ID or alias | |||
| * @param array $arrOptions An optional options array | |||
| * | |||
| * @return static|null The model or null if the result is empty | |||
| */ | |||
| public static function findPublishedByIdOrAlias($varId, array $arrOptions = array()) | |||
| { | |||
| $t = static::$strTable; | |||
| $arrColumns = array("($t.id=? OR $t.alias=?)"); | |||
| $arrValues = array(is_numeric($varId) ? $varId : 0, $varId); | |||
| $arrOptions = array_merge( | |||
| array( | |||
| 'limit' => 1, | |||
| 'return' => 'Model' | |||
| ), | |||
| $arrOptions | |||
| ); | |||
| return static::findPublishedBy($arrColumns, $arrValues, $arrOptions); | |||
| } | |||
| /** | |||
| * Find products by IDs | |||
| * | |||
| * @param array $arrIds | |||
| * @param array $arrOptions | |||
| * | |||
| * @return Product[]|Collection | |||
| */ | |||
| public static function findPublishedByIds(array $arrIds, array $arrOptions = array()) | |||
| { | |||
| if (0 === \count($arrIds)) { | |||
| return null; | |||
| } | |||
| return static::findPublishedBy( | |||
| array(static::$strTable . '.id IN (' . implode(',', array_map('intval', $arrIds)) . ')'), | |||
| null, | |||
| $arrOptions | |||
| ); | |||
| } | |||
| /** | |||
| * Return collection of published product variants by product PID | |||
| * | |||
| * @param int $intPid | |||
| * @param array $arrOptions | |||
| * | |||
| * @return Collection|Product[]|null | |||
| */ | |||
| public static function findPublishedByPid($intPid, array $arrOptions = array()) | |||
| { | |||
| return static::findPublishedBy('pid', (int) $intPid, $arrOptions); | |||
| } | |||
| /** | |||
| * Return collection of published products by categories | |||
| * | |||
| * @param array $arrCategories | |||
| * @param array $arrOptions | |||
| * | |||
| * @return Collection|Product[]|null | |||
| */ | |||
| public static function findPublishedByCategories(array $arrCategories, array $arrOptions = array()) | |||
| { | |||
| return static::findPublishedBy( | |||
| array('c.page_id IN (' . implode(',', array_map('intval', $arrCategories)) . ')'), | |||
| null, | |||
| $arrOptions | |||
| ); | |||
| } | |||
| /** | |||
| * Find a single frontend-available product by primary key | |||
| * | |||
| * @param int $intId | |||
| * @param array $arrOptions | |||
| * | |||
| * @return static|null | |||
| */ | |||
| public static function findAvailableByPk($intId, array $arrOptions = array()) | |||
| { | |||
| $objProduct = static::findPublishedByPk($intId, $arrOptions); | |||
| if (null === $objProduct || !$objProduct->isAvailableInFrontend()) { | |||
| return null; | |||
| } | |||
| return $objProduct; | |||
| } | |||
| /** | |||
| * Find a single frontend-available product by its ID or alias | |||
| * | |||
| * @param mixed $varId The ID or alias | |||
| * @param array $arrOptions An optional options array | |||
| * | |||
| * @return Product|null The model or null if the result is empty | |||
| */ | |||
| public static function findAvailableByIdOrAlias($varId, array $arrOptions = array()) | |||
| { | |||
| $objProduct = static::findPublishedByIdOrAlias($varId, $arrOptions); | |||
| if (null === $objProduct || !$objProduct->isAvailableInFrontend()) { | |||
| return null; | |||
| } | |||
| return $objProduct; | |||
| } | |||
| /** | |||
| * Find frontend-available products by IDs | |||
| * | |||
| * @param array $arrIds | |||
| * @param array $arrOptions | |||
| * | |||
| * @return Collection|Product[]|null | |||
| */ | |||
| public static function findAvailableByIds(array $arrIds, array $arrOptions = array()) | |||
| { | |||
| $objProducts = static::findPublishedByIds($arrIds, $arrOptions); | |||
| if (null === $objProducts) { | |||
| return null; | |||
| } | |||
| $arrProducts = []; | |||
| foreach ($objProducts as $objProduct) { | |||
| if ($objProduct->isAvailableInFrontend()) { | |||
| $arrProducts[] = $objProduct; | |||
| } | |||
| } | |||
| if (0 === \count($arrProducts)) { | |||
| return null; | |||
| } | |||
| return new Collection($arrProducts, static::$strTable); | |||
| } | |||
| /** | |||
| * Find frontend-available products by condition | |||
| * | |||
| * @param mixed $arrColumns | |||
| * @param mixed $arrValues | |||
| * @param array $arrOptions | |||
| * | |||
| * @return Collection | |||
| */ | |||
| public static function findAvailableBy($arrColumns, $arrValues, array $arrOptions = array()) | |||
| { | |||
| $objProducts = static::findPublishedBy($arrColumns, $arrValues, $arrOptions); | |||
| if (null === $objProducts) { | |||
| return null; | |||
| } | |||
| $arrProducts = []; | |||
| foreach ($objProducts as $objProduct) { | |||
| if ($objProduct->isAvailableInFrontend()) { | |||
| $arrProducts[] = $objProduct; | |||
| } | |||
| } | |||
| if (0 === \count($arrProducts)) { | |||
| return null; | |||
| } | |||
| return new Collection($arrProducts, static::$strTable); | |||
| } | |||
| /** | |||
| * Find variant of a product | |||
| * | |||
| * @param IsotopeProduct $objProduct | |||
| * @param array $arrVariant | |||
| * @param array $arrOptions | |||
| * | |||
| * @return Model|null | |||
| */ | |||
| public static function findVariantOfProduct( | |||
| IsotopeProduct $objProduct, | |||
| array $arrVariant, | |||
| array $arrOptions = array() | |||
| ) { | |||
| $t = static::$strTable; | |||
| $arrColumns = array( | |||
| "$t.id IN (" . implode(',', $objProduct->getVariantIds()) . ')', | |||
| "$t." . implode("=? AND $t.", array_keys($arrVariant)) . '=?' | |||
| ); | |||
| $arrOptions = array_merge( | |||
| array( | |||
| 'limit' => 1, | |||
| 'column' => $arrColumns, | |||
| 'value' => $arrVariant, | |||
| 'return' => 'Model' | |||
| ), | |||
| $arrOptions | |||
| ); | |||
| return static::find($arrOptions); | |||
| } | |||
| /** | |||
| * Finds the default variant of a product. | |||
| * | |||
| * @param IsotopeProduct $objProduct | |||
| * @param array $arrOptions | |||
| * | |||
| * @return static|null | |||
| */ | |||
| public static function findDefaultVariantOfProduct(IsotopeProduct $objProduct, array $arrOptions = array()) | |||
| { | |||
| static $cache; | |||
| if (null === $cache) { | |||
| $cache = []; | |||
| $data = Database::getInstance()->execute( | |||
| "SELECT id, pid FROM tl_iso_product WHERE pid>0 AND language='' AND fallback='1'" | |||
| ); | |||
| while ($data->next()) { | |||
| $cache[$data->pid] = $data->id; | |||
| } | |||
| } | |||
| $defaultId = $cache[$objProduct->getProductId()]; | |||
| if ($defaultId < 1 || !\in_array($defaultId, $objProduct->getVariantIds())) { | |||
| return null; | |||
| } | |||
| return static::findByPk($defaultId, $arrOptions); | |||
| } | |||
| /** | |||
| * Returns the number of published products. | |||
| * | |||
| * @param array $arrOptions | |||
| * | |||
| * @return int | |||
| */ | |||
| public static function countPublished(array $arrOptions = array()) | |||
| { | |||
| return static::countPublishedBy(array(), array(), $arrOptions); | |||
| } | |||
| /** | |||
| * Return the number of products matching certain criteria | |||
| * | |||
| * @param mixed $arrColumns | |||
| * @param mixed $arrValues | |||
| * @param array $arrOptions | |||
| * | |||
| * @return int | |||
| */ | |||
| public static function countPublishedBy($arrColumns, $arrValues, array $arrOptions = array()) | |||
| { | |||
| $t = static::$strTable; | |||
| $arrValues = (array) $arrValues; | |||
| if (!\is_array($arrColumns)) { | |||
| $arrColumns = array(static::$strTable . '.' . $arrColumns . '=?'); | |||
| } | |||
| // Add publish check to $arrColumns as the first item to enable SQL keys | |||
| if (BE_USER_LOGGED_IN !== true) { | |||
| $time = Date::floorToMinute(); | |||
| array_unshift( | |||
| $arrColumns, | |||
| " | |||
| $t.published='1' | |||
| AND ($t.start='' OR $t.start<'$time') | |||
| AND ($t.stop='' OR $t.stop>'" . ($time + 60) . "') | |||
| " | |||
| ); | |||
| } | |||
| return static::countBy($arrColumns, $arrValues, $arrOptions); | |||
| } | |||
| /** | |||
| * Gets the number of translation records in the product table. | |||
| * Mostly useful to see if there are any translations at all to optimize queries. | |||
| * | |||
| * @return int | |||
| */ | |||
| public static function countTranslatedProducts() | |||
| { | |||
| static $result; | |||
| if (null === $result) { | |||
| $result = Database::getInstance()->query( | |||
| "SELECT COUNT(*) AS total FROM tl_iso_product WHERE language!=''" | |||
| )->total; | |||
| } | |||
| return $result; | |||
| } | |||
| /** | |||
| * Return a model or collection based on the database result type | |||
| * | |||
| * @param array $arrOptions | |||
| * | |||
| * @return Product|Product[]|Collection|null | |||
| */ | |||
| protected static function find(array $arrOptions) | |||
| { | |||
| $arrOptions['group'] = static::getTable() . '.id' . (null === ($arrOptions['group'] ?? null) ? '' : ', '.$arrOptions['group']); | |||
| $objProducts = parent::find($arrOptions); | |||
| if (null === $objProducts) { | |||
| return null; | |||
| } | |||
| /** @var Filter[] $arrFilters */ | |||
| $arrFilters = $arrOptions['filters'] ?? null; | |||
| $arrSorting = $arrOptions['sorting'] ?? null; | |||
| $hasFilters = \is_array($arrFilters) && 0 !== \count($arrFilters); | |||
| $hasSorting = \is_array($arrSorting) && 0 !== \count($arrSorting); | |||
| if ($hasFilters || $hasSorting) { | |||
| /** @var static[] $arrProducts */ | |||
| $arrProducts = $objProducts->getModels(); | |||
| if ($hasFilters) { | |||
| $arrProducts = array_filter($arrProducts, function ($objProduct) use ($arrFilters) { | |||
| $arrGroups = []; | |||
| foreach ($arrFilters as $objFilter) { | |||
| $blnMatch = $objFilter->matches($objProduct); | |||
| if ($objFilter->hasGroup()) { | |||
| $arrGroups[$objFilter->getGroup()] = $arrGroups[$objFilter->getGroup()] ? : $blnMatch; | |||
| } elseif (!$blnMatch) { | |||
| return false; | |||
| } | |||
| } | |||
| return !\in_array(false, $arrGroups, true); | |||
| }); | |||
| } | |||
| // $arrProducts can be empty if the filter removed all records | |||
| if ($hasSorting && 0 !== \count($arrProducts)) { | |||
| $arrParam = array(); | |||
| $arrData = array(); | |||
| foreach ($arrSorting as $strField => $arrConfig) { | |||
| foreach ($arrProducts as $objProduct) { | |||
| // Both SORT_STRING and SORT_REGULAR are case sensitive, strings starting with a capital letter | |||
| // will come before strings starting with a lowercase letter. To perform a case insensitive | |||
| // search, force the sorting order to be determined by a lowercase copy of the original value. | |||
| // Temporary fix for price attribute (see #945) | |||
| if ('price' === $strField) { | |||
| if (null !== $objProduct->getPrice()) { | |||
| $arrData[$strField][$objProduct->id] = $objProduct->getPrice()->getAmount(); | |||
| } else { | |||
| $arrData[$strField][$objProduct->id] = 0; | |||
| } | |||
| } else { | |||
| $arrData[$strField][$objProduct->id] = strtolower( | |||
| str_replace('"', '', $objProduct->$strField) | |||
| ); | |||
| } | |||
| } | |||
| $arrParam[] = &$arrData[$strField]; | |||
| $arrParam[] = $arrConfig[0]; | |||
| $arrParam[] = $arrConfig[1]; | |||
| } | |||
| // Add product array as the last item. | |||
| // This will sort the products array based on the sorting of the passed in arguments. | |||
| $arrParam[] = &$arrProducts; | |||
| \call_user_func_array('array_multisort', $arrParam); | |||
| } | |||
| $objProducts = new Collection($arrProducts, static::$strTable); | |||
| } | |||
| return $objProducts; | |||
| } | |||
| /** | |||
| * Return select statement to load product data including multilingual fields | |||
| * | |||
| * @param array $arrOptions an array of columns | |||
| * | |||
| * @return string | |||
| */ | |||
| protected static function buildFindQuery(array $arrOptions) | |||
| { | |||
| $objBase = DcaExtractor::getInstance($arrOptions['table']); | |||
| $hasTranslations = (static::countTranslatedProducts() > 0); | |||
| $hasVariants = (ProductType::countByVariants() > 0); | |||
| $arrJoins = array(); | |||
| $arrFields = array( | |||
| $arrOptions['table'] . '.*', | |||
| "'" . str_replace('-', '_', $GLOBALS['TL_LANGUAGE']) . "' AS language", | |||
| ); | |||
| if ($hasVariants) { | |||
| $arrFields[] = sprintf( | |||
| 'IF(%s.pid>0, parent.type, %s.type) AS type', | |||
| $arrOptions['table'], | |||
| $arrOptions['table'] | |||
| ); | |||
| } | |||
| if ($hasTranslations) { | |||
| foreach (Attribute::getMultilingualFields() as $attribute) { | |||
| $arrFields[] = "IFNULL(translation.$attribute, " . $arrOptions['table'] . ".$attribute) AS $attribute"; | |||
| } | |||
| } | |||
| foreach (Attribute::getFetchFallbackFields() as $attribute) { | |||
| $arrFields[] = "{$arrOptions['table']}.$attribute AS {$attribute}_fallback"; | |||
| } | |||
| if ($hasTranslations) { | |||
| $arrJoins[] = sprintf( | |||
| " LEFT OUTER JOIN %s translation ON %s.id=translation.pid AND translation.language='%s'", | |||
| $arrOptions['table'], | |||
| $arrOptions['table'], | |||
| str_replace('-', '_', $GLOBALS['TL_LANGUAGE']) | |||
| ); | |||
| $arrOptions['group'] = (null === $arrOptions['group'] ? '' : $arrOptions['group'].', ') . 'translation.id'; | |||
| } | |||
| if ($hasVariants) { | |||
| $arrJoins[] = sprintf( | |||
| ' LEFT OUTER JOIN %s parent ON %s.pid=parent.id', | |||
| $arrOptions['table'], | |||
| $arrOptions['table'] | |||
| ); | |||
| } | |||
| $arrFields[] = 'GROUP_CONCAT(c.page_id) AS product_categories'; | |||
| $arrJoins[] = sprintf( | |||
| ' LEFT OUTER JOIN %s c ON %s=c.pid', | |||
| ProductCategory::getTable(), | |||
| ($hasVariants ? "IFNULL(parent.id, {$arrOptions['table']}.id)" : "{$arrOptions['table']}.id") | |||
| ); | |||
| if ('c.sorting' === ($arrOptions['order'] ?? '')) { | |||
| $arrFields[] = 'c.sorting'; | |||
| $arrOptions['group'] = (null === $arrOptions['group'] ? '' : $arrOptions['group'].', ') . 'c.id'; | |||
| } | |||
| if ($objBase->hasRelations()) { | |||
| $intCount = 0; | |||
| foreach ($objBase->getRelations() as $strKey => $arrConfig) { | |||
| // Automatically join the single-relation records | |||
| if (('eager' === $arrConfig['load'] || ($arrOptions['eager'] ?? false)) | |||
| && ('hasOne' === $arrConfig['type'] || 'belongsTo' === $arrConfig['type']) | |||
| ) { | |||
| if (\is_array($arrOptions['joinAliases'] ?? null) | |||
| && ($key = array_search($arrConfig['table'], $arrOptions['joinAliases'], true)) !== false | |||
| ) { | |||
| $strJoinAlias = $key; | |||
| unset($arrOptions['joinAliases'][$key]); | |||
| } else { | |||
| ++$intCount; | |||
| $strJoinAlias = 'j' . $intCount; | |||
| } | |||
| $objRelated = DcaExtractor::getInstance($arrConfig['table']); | |||
| foreach ($objRelated->getFields() as $strField => $config) { | |||
| $arrFields[] = $strJoinAlias . '.' . $strField . ' AS ' . $strKey . '__' . $strField; | |||
| } | |||
| $arrJoins[] = sprintf( | |||
| ' LEFT JOIN %s %s ON %s.%s=%s.id', | |||
| $arrConfig['table'], | |||
| $strJoinAlias, | |||
| $arrOptions['table'], | |||
| $strKey, | |||
| $strJoinAlias | |||
| ); | |||
| } | |||
| } | |||
| } | |||
| // Generate the query | |||
| $strQuery = 'SELECT ' . implode(', ', $arrFields) . ' FROM ' . $arrOptions['table'] . implode('', $arrJoins); | |||
| // Where condition | |||
| if (!\is_array($arrOptions['column'] ?? null)) { | |||
| $arrOptions['column'] = array($arrOptions['table'] . '.' . $arrOptions['column'] . '=?'); | |||
| } | |||
| // The model must never find a language record | |||
| $strQuery .= " WHERE {$arrOptions['table']}.language='' AND " . implode(' AND ', $arrOptions['column']); | |||
| // Group by | |||
| if (($arrOptions['group'] ?? null) !== null) { | |||
| $strQuery .= ' GROUP BY ' . $arrOptions['group']; | |||
| } | |||
| // Order by | |||
| if (($arrOptions['order'] ?? null) !== null) { | |||
| $strQuery .= ' ORDER BY ' . $arrOptions['order']; | |||
| } | |||
| return $strQuery; | |||
| } | |||
| /** | |||
| * Build a query based on the given options to count the number of products. | |||
| * | |||
| * @param array $arrOptions The options array | |||
| * | |||
| * @return string | |||
| */ | |||
| protected static function buildCountQuery(array $arrOptions) | |||
| { | |||
| $hasTranslations = (static::countTranslatedProducts() > 0); | |||
| $hasVariants = (ProductType::countByVariants() > 0); | |||
| $arrJoins = array(); | |||
| $arrFields = array( | |||
| $arrOptions['table'] . '.id', | |||
| "'" . str_replace('-', '_', $GLOBALS['TL_LANGUAGE']) . "' AS language", | |||
| ); | |||
| if ($hasVariants) { | |||
| $arrFields[] = sprintf( | |||
| 'IF(%s.pid>0, parent.type, %s.type) AS type', | |||
| $arrOptions['table'], | |||
| $arrOptions['table'] | |||
| ); | |||
| } | |||
| if ($hasTranslations) { | |||
| foreach (Attribute::getMultilingualFields() as $attribute) { | |||
| $arrFields[] = "IFNULL(translation.$attribute, " . $arrOptions['table'] . ".$attribute) AS $attribute"; | |||
| } | |||
| } | |||
| if ($hasTranslations) { | |||
| $arrJoins[] = sprintf( | |||
| " LEFT OUTER JOIN %s translation ON %s.id=translation.pid AND translation.language='%s'", | |||
| $arrOptions['table'], | |||
| $arrOptions['table'], | |||
| str_replace('-', '_', $GLOBALS['TL_LANGUAGE']) | |||
| ); | |||
| $arrOptions['group'] = (null === $arrOptions['group'] ? '' : $arrOptions['group'].', ') . 'translation.id, tl_iso_product.id'; | |||
| } | |||
| if ($hasVariants) { | |||
| $arrJoins[] = sprintf( | |||
| ' LEFT OUTER JOIN %s parent ON %s.pid=parent.id', | |||
| $arrOptions['table'], | |||
| $arrOptions['table'] | |||
| ); | |||
| } | |||
| $arrJoins[] = sprintf( | |||
| ' LEFT OUTER JOIN %s c ON %s=c.pid', | |||
| ProductCategory::getTable(), | |||
| ($hasVariants ? "IFNULL(parent.id, {$arrOptions['table']}.id)" : "{$arrOptions['table']}.id") | |||
| ); | |||
| // Generate the query | |||
| $strWhere = ''; | |||
| $strQuery = ' | |||
| SELECT | |||
| ' . implode(', ', $arrFields) . ' | |||
| FROM ' . $arrOptions['table'] . implode('', $arrJoins); | |||
| // Where condition | |||
| if (!empty($arrOptions['column'])) { | |||
| if (!\is_array($arrOptions['column'])) { | |||
| $arrOptions['column'] = array($arrOptions['table'] . '.' . $arrOptions['column'] . '=?'); | |||
| } | |||
| $strWhere = ' AND ' . implode(' AND ', $arrOptions['column']); | |||
| } | |||
| // The model must never find a language record | |||
| $strQuery .= " WHERE {$arrOptions['table']}.language=''" . $strWhere; | |||
| // Group by | |||
| if ($arrOptions['group'] !== null) { | |||
| $strQuery .= ' GROUP BY ' . $arrOptions['group']; | |||
| } | |||
| return 'SELECT COUNT(*) AS count FROM ('.$strQuery.') c1'; | |||
| } | |||
| /** | |||
| * Return select statement to load product data including multilingual fields | |||
| * | |||
| * @param array $arrOptions an array of columns | |||
| * @param array $arrJoinAliases an array of table join aliases | |||
| * | |||
| * @return string | |||
| * | |||
| * @deprecated use buildFindQuery introduced in Contao 3.3 | |||
| */ | |||
| protected static function buildQueryString($arrOptions, $arrJoinAliases = array('t' => 'tl_iso_producttype')) | |||
| { | |||
| $arrOptions['joinAliases'] = $arrJoinAliases; | |||
| return static::buildFindQuery((array) $arrOptions); | |||
| } | |||
| } | |||
| @@ -1,489 +0,0 @@ | |||
| <?php | |||
| /* | |||
| * Isotope eCommerce for Contao Open Source CMS | |||
| * | |||
| * Copyright (C) 2009 - 2019 terminal42 gmbh & Isotope eCommerce Workgroup | |||
| * | |||
| * @link https://isotopeecommerce.org | |||
| * @license https://opensource.org/licenses/lgpl-3.0.html | |||
| */ | |||
| namespace Isotope\Model; | |||
| use Contao\Database; | |||
| use Contao\Model; | |||
| use Contao\StringUtil; | |||
| use Contao\System; | |||
| use Haste\Data\Plain; | |||
| use Isotope\Interfaces\IsotopeProduct; | |||
| use Isotope\Interfaces\IsotopeProductWithOptions; | |||
| use Isotope\Isotope; | |||
| /** | |||
| * ProductCollectionItem represents an item in a product collection. | |||
| * | |||
| * @property int $id | |||
| * @property int $pid | |||
| * @property int $tstamp | |||
| * @property int $product_id | |||
| * @property string $type | |||
| * @property string $sku | |||
| * @property string $name | |||
| * @property mixed $configuration | |||
| * @property int $quantity | |||
| * @property float $price | |||
| * @property float $tax_free_price | |||
| * @property string $tax_id | |||
| * @property int $jumpTo | |||
| */ | |||
| class ProductCollectionItem extends \Model | |||
| { | |||
| /** | |||
| * Name of the current table | |||
| * @var string | |||
| */ | |||
| protected static $strTable = 'tl_iso_product_collection_item'; | |||
| /** | |||
| * Cache the current product | |||
| * @var IsotopeProduct|IsotopeProductWithOptions|false | |||
| */ | |||
| protected $objProduct = false; | |||
| /** | |||
| * Cache downloads for the collection item | |||
| * @var array | |||
| */ | |||
| protected $arrDownloads; | |||
| /** | |||
| * Errors | |||
| * @var array | |||
| */ | |||
| protected $arrErrors = []; | |||
| /** | |||
| * True if product collection is locked | |||
| * @var bool | |||
| */ | |||
| protected $blnLocked = false; | |||
| /** | |||
| * Check if collection item is available | |||
| * | |||
| * @return bool | |||
| */ | |||
| public function isAvailable() | |||
| { | |||
| if ($this->isLocked()) { | |||
| return true; | |||
| } | |||
| if (isset($GLOBALS['ISO_HOOKS']['itemIsAvailable']) && \is_array($GLOBALS['ISO_HOOKS']['itemIsAvailable'])) { | |||
| foreach ($GLOBALS['ISO_HOOKS']['itemIsAvailable'] as $callback) { | |||
| $available = System::importStatic($callback[0])->{$callback[1]}($this); | |||
| // If return value is boolean then we accept it as result | |||
| if (true === $available || false === $available) { | |||
| return $available; | |||
| } | |||
| } | |||
| } | |||
| if (!$this->hasProduct() || !$this->getProduct()->isAvailableForCollection($this->getRelated('pid'))) { | |||
| return false; | |||
| } | |||
| $arrConfig = $this->getOptions(); | |||
| foreach ($this->getProduct()->getOptions() as $k => $v) { | |||
| if ($arrConfig[$k] !== $v) { | |||
| return false; | |||
| } | |||
| } | |||
| return true; | |||
| } | |||
| /** | |||
| * Return true if product collection item is locked | |||
| */ | |||
| public function isLocked() | |||
| { | |||
| return $this->blnLocked; | |||
| } | |||
| /** | |||
| * Lock item, necessary if product collection is locked | |||
| */ | |||
| public function lock() | |||
| { | |||
| $this->blnLocked = true; | |||
| } | |||
| /** | |||
| * Delete downloads when deleting product collection item | |||
| * | |||
| * @return int | |||
| */ | |||
| public function delete() | |||
| { | |||
| $intId = $this->id; | |||
| $intAffected = parent::delete(); | |||
| if ($intAffected) { | |||
| Database::getInstance()->query("DELETE FROM tl_iso_product_collection_download WHERE pid=$intId"); | |||
| } | |||
| return $intAffected; | |||
| } | |||
| /** | |||
| * Get the product related to this item | |||
| * | |||
| * @param bool $blnNoCache | |||
| * | |||
| * @return IsotopeProduct|null | |||
| */ | |||
| public function getProduct($blnNoCache = false) | |||
| { | |||
| if (false === $this->objProduct || true === $blnNoCache) { | |||
| $this->objProduct = null; | |||
| /** @var string|\Isotope\Model\Product $strClass */ | |||
| $strClass = Product::getClassForModelType($this->type); | |||
| if ($strClass == '' || !class_exists($strClass)) { | |||
| System::log('Error creating product object of type "' . $this->type . '"', __METHOD__, TL_ERROR); | |||
| return null; | |||
| } | |||
| try { | |||
| $this->objProduct = $strClass::findByPk($this->product_id); | |||
| } catch (\Exception $e) { | |||
| $this->objProduct = null; | |||
| $this->addError($e->getMessage()); | |||
| } | |||
| if (null !== $this->objProduct && $this->objProduct instanceof IsotopeProductWithOptions) { | |||
| try { | |||
| if ($this->objProduct instanceof Model) { | |||
| $this->objProduct = clone $this->objProduct; | |||
| $this->objProduct->preventSaving(false); | |||
| $this->objProduct->id = $this->product_id; | |||
| } | |||
| $this->objProduct->setOptions($this->getOptions()); | |||
| } catch (\RuntimeException $e) { | |||
| $this->addError($GLOBALS['TL_LANG']['ERR']['collectionItemNotAvailable']); | |||
| } | |||
| } | |||
| } | |||
| return $this->objProduct; | |||
| } | |||
| /** | |||
| * Return boolean flag if product could be loaded | |||
| * | |||
| * @return bool | |||
| */ | |||
| public function hasProduct() | |||
| { | |||
| return (null !== $this->getProduct()); | |||
| } | |||
| /** | |||
| * Get product SKU. Automatically falls back to the collection item table if product is not found. | |||
| * | |||
| * @return string | |||
| */ | |||
| public function getSku() | |||
| { | |||
| return (string) ($this->isLocked() || !$this->hasProduct()) ? $this->sku : $this->getProduct()->getSku(); | |||
| } | |||
| /** | |||
| * Get product name. Automatically falls back to the collection item table if product is not found. | |||
| * | |||
| * @return string | |||
| */ | |||
| public function getName() | |||
| { | |||
| return (string) ($this->isLocked() || !$this->hasProduct()) ? $this->name : $this->getProduct()->getName(); | |||
| } | |||
| /** | |||
| * Returns key-value array for variant-enabled and customer editable attributes. | |||
| * | |||
| * @return array | |||
| * | |||
| * @deprecated Use getOptions() | |||
| */ | |||
| public function getAttributes() | |||
| { | |||
| return $this->getOptions(); | |||
| } | |||
| /** | |||
| * Returns key-value array for variant-enabled and customer editable attributes. | |||
| * | |||
| * @return array | |||
| */ | |||
| public function getOptions() | |||
| { | |||
| $arrConfig = StringUtil::deserialize($this->configuration); | |||
| return \is_array($arrConfig) ? $arrConfig : []; | |||
| } | |||
| /** | |||
| * Get product configuration | |||
| * | |||
| * @return array | |||
| * | |||
| * @deprecated Deprecated since Isotope 2.4, to be removed in Isotope 3.0. Use getOptions() instead. | |||
| */ | |||
| public function getConfiguration() | |||
| { | |||
| $arrConfig = StringUtil::deserialize($this->configuration); | |||
| if (empty($arrConfig) || !\is_array($arrConfig)) { | |||
| return array(); | |||
| } | |||
| if ($this->hasProduct()) { | |||
| return Isotope::formatProductConfiguration($arrConfig, $this->getProduct()); | |||
| } else { | |||
| foreach ($arrConfig as $k => $v) { | |||
| $arrConfig[$k] = new Plain($v, $k); | |||
| } | |||
| return $arrConfig; | |||
| } | |||
| } | |||
| /** | |||
| * Get product price. Automatically falls back to the collection item table if product is not found. | |||
| * | |||
| * @return string | |||
| */ | |||
| public function getPrice() | |||
| { | |||
| if ($this->isLocked() || !$this->hasProduct()) { | |||
| return $this->price; | |||
| } | |||
| $objPrice = $this->getProduct()->getPrice($this->getRelated('pid')); | |||
| if (null === $objPrice) { | |||
| return ''; | |||
| } | |||
| return $objPrice->getAmount((int) $this->quantity, $this->getOptions()); | |||
| } | |||
| /** | |||
| * Get tax free product price. Automatically falls back to the collection item table if product is not found. | |||
| * | |||
| * @return string | |||
| */ | |||
| public function getTaxFreePrice() | |||
| { | |||
| if ($this->isLocked() || !$this->hasProduct()) { | |||
| return $this->tax_free_price; | |||
| } | |||
| $objPrice = $this->getProduct()->getPrice($this->getRelated('pid')); | |||
| if (null === $objPrice) { | |||
| return ''; | |||
| } | |||
| return $objPrice->getNetAmount((int) $this->quantity, $this->getOptions()); | |||
| } | |||
| /** | |||
| * Get original product price. Automatically falls back to the collection item table if product is not found. | |||
| * | |||
| * @return string | |||
| */ | |||
| public function getOriginalPrice() | |||
| { | |||
| if ($this->isLocked() || !$this->hasProduct()) { | |||
| return $this->price; | |||
| } | |||
| $objPrice = $this->getProduct()->getPrice($this->getRelated('pid')); | |||
| if (null === $objPrice) { | |||
| return ''; | |||
| } | |||
| return $objPrice->getOriginalAmount((int) $this->quantity, $this->getOptions()); | |||
| } | |||
| /** | |||
| * Get product price multiplied by the requested product quantity | |||
| * | |||
| * @return string | |||
| */ | |||
| public function getTotalPrice() | |||
| { | |||
| return (string) ($this->getPrice() * (int) $this->quantity); | |||
| } | |||
| /** | |||
| * Get original product price multiplied by the requested product quantity | |||
| * | |||
| * @return string | |||
| */ | |||
| public function getTotalOriginalPrice() | |||
| { | |||
| return (string) ($this->getOriginalPrice() * (int) $this->quantity); | |||
| } | |||
| /** | |||
| * Get tax free product price multiplied by the requested product quantity | |||
| * | |||
| * @return string | |||
| */ | |||
| public function getTaxFreeTotalPrice() | |||
| { | |||
| return (string) ($this->getTaxFreePrice() * (int) $this->quantity); | |||
| } | |||
| /** | |||
| * Return downloads associated with this product collection item | |||
| * | |||
| * @return ProductCollectionDownload[] | |||
| */ | |||
| public function getDownloads() | |||
| { | |||
| if (null === $this->arrDownloads) { | |||
| $this->arrDownloads = array(); | |||
| $objDownloads = ProductCollectionDownload::findBy('pid', $this->id); | |||
| if (null !== $objDownloads) { | |||
| while ($objDownloads->next()) { | |||
| $this->arrDownloads[] = $objDownloads->current(); | |||
| } | |||
| } | |||
| } | |||
| return $this->arrDownloads; | |||
| } | |||
| /** | |||
| * Increase quantity of product collection item | |||
| * | |||
| * @param int $intQuantity | |||
| * | |||
| * @return self | |||
| */ | |||
| public function increaseQuantityBy($intQuantity) | |||
| { | |||
| $time = time(); | |||
| Database::getInstance()->query(" | |||
| UPDATE tl_iso_product_collection_item | |||
| SET tstamp=$time, quantity=(quantity+" . (int) $intQuantity . ') | |||
| WHERE id=' . $this->id | |||
| ); | |||
| $this->tstamp = $time; | |||
| $this->quantity = Database::getInstance() | |||
| ->query("SELECT quantity FROM tl_iso_product_collection_item WHERE id=" . $this->id) | |||
| ->quantity | |||
| ; | |||
| return $this; | |||
| } | |||
| /** | |||
| * Decrease quantity of product collection item | |||
| * | |||
| * @param int $intQuantity | |||
| * | |||
| * @return self | |||
| */ | |||
| public function decreaseQuantityBy($intQuantity) | |||
| { | |||
| if (($this->quantity - $intQuantity) < 1) { | |||
| throw new \UnderflowException('Quantity of product collection item cannot be less than 1.'); | |||
| } | |||
| $time = time(); | |||
| Database::getInstance()->query(" | |||
| UPDATE tl_iso_product_collection_item | |||
| SET tstamp=$time, quantity=(quantity-" . (int) $intQuantity . ') | |||
| WHERE id=' . $this->id | |||
| ); | |||
| $this->tstamp = $time; | |||
| $this->quantity = Database::getInstance() | |||
| ->query('SELECT quantity FROM tl_iso_product_collection_item WHERE id=' . $this->id) | |||
| ->quantity | |||
| ; | |||
| return $this; | |||
| } | |||
| /** | |||
| * Calculate the sum of a database column | |||
| * | |||
| * @param string $strField | |||
| * @param mixed $strColumn | |||
| * @param mixed $varValue | |||
| * | |||
| * @return int | |||
| */ | |||
| public static function sumBy($strField, $strColumn = null, $varValue = null) | |||
| { | |||
| $strQuery = "SELECT SUM($strField) AS sum FROM tl_iso_product_collection_item"; | |||
| if ($strColumn !== null) { | |||
| $strQuery .= ' WHERE ' . (\is_array($strColumn) ? implode(' AND ', $strColumn) : static::$strTable . '.' . $strColumn . "=?"); | |||
| } | |||
| return (int) Database::getInstance()->prepare($strQuery)->execute($varValue)->sum; | |||
| } | |||
| /** | |||
| * Add an error message | |||
| * | |||
| * @param string $strError | |||
| */ | |||
| public function addError($strError) | |||
| { | |||
| $this->arrErrors[] = $strError; | |||
| } | |||
| /** | |||
| * Return true if the collection item has errors | |||
| * | |||
| * @return bool | |||
| */ | |||
| public function hasErrors() | |||
| { | |||
| return 0 !== \count($this->arrErrors); | |||
| } | |||
| /** | |||
| * Return the errors array | |||
| * | |||
| * @return array | |||
| */ | |||
| public function getErrors() | |||
| { | |||
| return $this->arrErrors; | |||
| } | |||
| } | |||
| @@ -0,0 +1,36 @@ | |||
| <?php | |||
| namespace App\Command; | |||
| use App\ExactApi\IsotopeDatabaseHandler; | |||
| use App\ExactApi\ApiExact; | |||
| use Doctrine\DBAL\Connection; | |||
| use Symfony\Component\Console\Command\Command; | |||
| use Symfony\Component\Console\Input\InputInterface; | |||
| use Symfony\Component\Console\Output\OutputInterface; | |||
| class CmdUpdateProducts extends Command | |||
| { | |||
| protected static $defaultName = 'app:update-products'; | |||
| protected $connection; | |||
| private $statusCode = 0; | |||
| public function __construct(Connection $connection) | |||
| { | |||
| $this->connection = $connection; | |||
| parent::__construct(); | |||
| } | |||
| protected function configure(): void | |||
| { | |||
| $this->setDescription('Updates products from Exact-Online to Contao-Isotope tables.'); | |||
| } | |||
| protected function execute(InputInterface $input, OutputInterface $output): ?int | |||
| { | |||
| $apiConnector = new ApiExact(); | |||
| $products = $apiConnector->getProducts(); | |||
| $handler = new IsotopeDatabaseHandler($this->connection); | |||
| $handler->processProducts($products); | |||
| return $this->statusCode; | |||
| } | |||
| } | |||
| @@ -0,0 +1,122 @@ | |||
| <?php | |||
| namespace App\EventListener; | |||
| use Contao\CoreBundle\ServiceAnnotation\Hook; | |||
| use Contao\FrontendTemplate; | |||
| use Contao\Module; | |||
| use App\ExactApi\ApiExact; | |||
| use Isotope\Model\Address; | |||
| use Isotope\Model\Product; | |||
| use Isotope\Model\ProductCollection\Order; | |||
| use Isotope\Model\ProductCollectionItem; | |||
| class PostCheckoutListener | |||
| { | |||
| const WAREHOUSE_ID = 'd8a6a9b8-d0ac-4d36-8d00-bd420d0d81f5'; | |||
| /** | |||
| * @Hook("postCheckout") | |||
| */ | |||
| // public function __invoke(int $userId, array $userData, Module $module): void | |||
| public function __invoke(Order $order, $item): void | |||
| { | |||
| $apiExact = new ApiExact(); | |||
| //ob_start(); | |||
| $salesOrderItems = []; | |||
| /** @var ProductCollectionItem $orderItem */ | |||
| foreach($order->getItems() as $orderItem) { | |||
| $salesOrderItems[] = $this->createOrderItem($orderItem); | |||
| // /** @var Product $orderItem */ | |||
| // $product = $orderItem->getProduct(); | |||
| // var_dump($orderItem->id); | |||
| // var_dump($orderItem->sku); | |||
| // var_dump($orderItem->quantity); | |||
| // var_dump($orderItem->tax_free_price); | |||
| // | |||
| // var_dump($product->id); | |||
| // var_dump($product->name); | |||
| // var_dump($product->is_set); | |||
| } | |||
| //var_dump($apiExact->test()); | |||
| // file_put_contents('dan.txt', ob_get_contents()); | |||
| // ob_end_clean(); | |||
| $shippingCustomer = null; | |||
| // if ($this->compareAddresses() === false) { | |||
| // $shippingCustomer = $apiExact->createCustomer( | |||
| // $this->createCustomerApiData($order->getShippingAddress()) | |||
| // ); | |||
| // } | |||
| // | |||
| $billingCustomer = $apiExact->createCustomer( | |||
| $this->createCustomerApiData($order->getBillingAddress()) | |||
| ); | |||
| $apiExact->createSalesOrder($this->createSalesOrderData($order, $billingCustomer, $shippingCustomer)); | |||
| } | |||
| private function compareAddresses(Order $order) | |||
| { | |||
| return | |||
| $order->getBillingAddress()->firstname === $order->getShippingAddress()->firstname && | |||
| $order->getBillingAddress()->lastname === $order->getShippingAddress()->lastname && | |||
| $order->getBillingAddress()->street_1 === $order->getShippingAddress()->street_1 && | |||
| $order->getBillingAddress()->postal === $order->getShippingAddress()->postal && | |||
| $order->getBillingAddress()->city === $order->getShippingAddress()->city && | |||
| $order->getBillingAddress()->company === $order->getShippingAddress()->company && | |||
| $order->getBillingAddress()->phone === $order->getShippingAddress()->phone && | |||
| $order->getBillingAddress()->email === $order->getShippingAddress()->email; | |||
| } | |||
| private function createCustomerApiData(Address $address) | |||
| { | |||
| return [ | |||
| 'Name' => $address->firstname . ' ' . $address->lastname, | |||
| 'AddressLine1' => $address->street_1, | |||
| 'AddressLine2' => $address->company, | |||
| 'City' => $address->city, | |||
| 'Postcode' => $address->postal, | |||
| 'Email' => $address->email, | |||
| 'Country' => 'DE', | |||
| 'Phone' => $address->phone, | |||
| 'Status' => 'C', | |||
| ]; | |||
| } | |||
| private function createOrderItem(ProductCollectionItem $orderItem) | |||
| { | |||
| /** @var Product $orderItem */ | |||
| $product = $orderItem->getProduct(); | |||
| return [ | |||
| 'Description' => $product->name, | |||
| 'Item' => $product->exact_id, | |||
| 'UnitPrice' => (float) $orderItem->tax_free_price / (int) $orderItem->quantity, | |||
| 'Quantity' => $orderItem->quantity | |||
| ]; | |||
| } | |||
| private function createSalesOrderData(Order $order, $billingCustomer, $salesOrderItems, $shippingCustomer = null) | |||
| { | |||
| $date = new \DateTime('now'); | |||
| $res = [ | |||
| 'Description' => $billingCustomer['Name'], | |||
| 'OrderDate' => $date->format('m/d/Y H:i:s'), | |||
| 'OrderedBy' => $billingCustomer['ID'], | |||
| 'WarehouseID' => self::WAREHOUSE_ID, | |||
| 'SalesOrderLines' => [ | |||
| $salesOrderItems | |||
| ] | |||
| ]; | |||
| if ($shippingCustomer !== null) { | |||
| $res['DeliverTo'] = $shippingCustomer['ID']; | |||
| } | |||
| return $res; | |||
| } | |||
| } | |||
| @@ -0,0 +1,170 @@ | |||
| <?php | |||
| namespace App\ExactApi; | |||
| use App\ExactApi\Product; | |||
| class ApiExact | |||
| { | |||
| const DIVISION = '58687'; | |||
| const API_URL = "https://start.exactonline.de/api"; | |||
| const API_URL_WNR = self::API_URL . "/v1/" . self::DIVISION; | |||
| const CLIENT_ID = "5a04d118-349c-4750-aac3-fa3a386999c6"; | |||
| const CLIENT_SECRET = "E7Wuqcsp4Lih"; | |||
| const ACCESS_TOKEN_VALID_DURATION = 600 - 50; | |||
| const ITEM_GROUP_UUID = 'df17bdaf-2af7-4e9f-8d60-326e36b57764'; | |||
| const PRODUCT_CODE_PREFIX = "WR"; | |||
| public function getAccessToken() | |||
| { | |||
| $tokenData = json_decode(file_get_contents(__DIR__ . '/tokenData', true)); | |||
| ob_start(); | |||
| var_dump($tokenData); | |||
| ob_end_clean(); | |||
| if (!property_exists($tokenData, 'expiry_time') || (int)$tokenData->expiry_time < time()) { | |||
| $postData = array( | |||
| 'grant_type' => 'refresh_token', | |||
| 'refresh_token' => $tokenData->refresh_token, | |||
| 'client_id' => self::CLIENT_ID, | |||
| 'client_secret' => self::CLIENT_SECRET | |||
| ); | |||
| $ch = curl_init(); | |||
| curl_setopt_array($ch, array( | |||
| CURLOPT_URL => self::API_URL . '/oauth2/token', | |||
| CURLOPT_RETURNTRANSFER => TRUE, | |||
| CURLOPT_HTTPHEADER => array('Content-Type: application/x-www-form-urlencoded'), | |||
| CURLOPT_POSTFIELDS => http_build_query($postData) | |||
| )); | |||
| $response = curl_exec($ch); | |||
| curl_close($ch); | |||
| $jsonResult = json_decode($response); | |||
| if (property_exists($jsonResult, 'error')) { | |||
| $msg = $jsonResult->error; | |||
| throw new \Exception($msg); | |||
| } | |||
| $jsonResult->expiry_time = time() + self::ACCESS_TOKEN_VALID_DURATION; | |||
| file_put_contents(__DIR__ . '/tokenData', json_encode($jsonResult)); | |||
| $tokenData = json_decode(file_get_contents(__DIR__ . '/tokenData', true)); | |||
| } | |||
| return $tokenData->access_token; | |||
| } | |||
| public function getProducts() | |||
| { | |||
| $baseUrl = self::API_URL_WNR . "/bulk/Logistics/Items?"; | |||
| $fields = [ | |||
| 'ID', | |||
| 'StandardSalesPrice', | |||
| 'SalesVatCode', | |||
| 'SalesVatCodeDescription', | |||
| 'Description', | |||
| 'ExtraDescription', | |||
| 'IsWebshopItem', | |||
| 'ItemGroup', | |||
| 'ItemGroupCode', | |||
| 'Stock', | |||
| 'NetWeight', | |||
| 'GrossWeight', | |||
| 'Code' | |||
| ]; | |||
| $filter = "\$filter=ItemGroup eq guid'".self::ITEM_GROUP_UUID."'"; | |||
| $select = "&\$select=".$this->getFieldString($fields); | |||
| $requestUrl = $baseUrl . $filter . $select; | |||
| $response = $this->getApiData($requestUrl); | |||
| $responseArr = json_decode($response, 1)['d']['results']; | |||
| $res = []; | |||
| foreach ($responseArr as $productItem) { | |||
| if ( | |||
| array_key_exists('IsWebshopItem', $productItem) && | |||
| $productItem['IsWebshopItem'] === 1 && | |||
| array_key_exists('Code', $productItem) && | |||
| stripos(strtoupper($productItem['Code']), self::PRODUCT_CODE_PREFIX) === 0 | |||
| ) { | |||
| $res[] = Product::createProductFromApiItem($productItem); | |||
| } | |||
| } | |||
| return $res; | |||
| } | |||
| public function createCustomer($customerData) | |||
| { | |||
| $url = self::API_URL_WNR . "/crm/Accounts"; | |||
| return json_decode($this->postApiData($url, $customerData)); | |||
| } | |||
| public function createSalesOrder($salesOrderData) | |||
| { | |||
| $url = self::API_URL_WNR . "/salesorder/SalesOrders"; | |||
| // $parameters = [ | |||
| // 'Description' => 'Daniel Knudsen', | |||
| // 'OrderDate' => '07/13/2022 17:00:00', | |||
| // 'OrderedBy' => '9ba706a3-d6f5-40c8-8d4f-e55a37692cef', | |||
| // 'WarehouseID' => 'd8a6a9b8-d0ac-4d36-8d00-bd420d0d81f5', | |||
| // 'SalesOrderLines' => [ | |||
| // [ | |||
| // 'Description' => 'TEST: Reinigungstuch grün', | |||
| // 'Item' => '9dcd8b50-5de6-4f15-a51a-c51282292383', | |||
| // 'UnitPrice' => '2.45', | |||
| // 'Quantity' => '3' | |||
| // ], | |||
| // [ | |||
| // 'Description' => 'TEST: McQuade´s E-Bike Kettenrückführ - Tool', | |||
| // 'Item' => '0e04113f-1299-4d3c-b65e-01e83ca5b3b2', | |||
| // 'UnitPrice' => '476.00', | |||
| // 'Quantity' => '1' | |||
| // ], | |||
| // ] | |||
| // ]; | |||
| return json_decode($this->postApiData($url, $salesOrderData)); | |||
| } | |||
| private function getApiData($url) | |||
| { | |||
| $ch = curl_init(); | |||
| curl_setopt_array($ch, array( | |||
| CURLOPT_URL => $url, | |||
| CURLOPT_HTTPHEADER => array('Accept: application/json' , "Authorization: Bearer " . $this->getAccessToken() ), | |||
| CURLOPT_RETURNTRANSFER => TRUE, | |||
| )); | |||
| $res = curl_exec($ch); | |||
| curl_close($ch); | |||
| return $res; | |||
| } | |||
| private function postApiData($url, $data) | |||
| { | |||
| $ch = curl_init(); | |||
| curl_setopt_array($ch, array( | |||
| CURLOPT_URL => $url, | |||
| CURLOPT_HTTPHEADER => array( | |||
| 'Content-Type: application/json', | |||
| 'Accept: application/json', | |||
| "Authorization: Bearer " . $this->getAccessToken() ), | |||
| CURLOPT_RETURNTRANSFER => TRUE, | |||
| CURLOPT_POST => TRUE, | |||
| CURLOPT_POSTFIELDS => json_encode($data) | |||
| )); | |||
| $res = curl_exec($ch); | |||
| curl_close($ch); | |||
| return $res; | |||
| } | |||
| private function getFieldString($fields) | |||
| { | |||
| $res = ''; | |||
| $cnt = count($fields); | |||
| $i = 1; | |||
| foreach ($fields as $field) { | |||
| $res .= $cnt !== $i ? $field . ',' : $field; | |||
| $i++; | |||
| } | |||
| return $res; | |||
| } | |||
| } | |||
| @@ -0,0 +1,193 @@ | |||
| <?php | |||
| namespace App\ExactApi; | |||
| use App\ExactApi\Product; | |||
| use Doctrine\DBAL\Connection; | |||
| class IsotopeDatabaseHandler | |||
| { | |||
| private $connection; | |||
| public function __construct(Connection $connection) | |||
| { | |||
| $this->connection = $connection; | |||
| } | |||
| public function processProducts(array $products) | |||
| { | |||
| $this->connection->beginTransaction(); | |||
| $sql = "SELECT * FROM `tl_iso_product`"; | |||
| $stmt = $this->connection->query($sql); | |||
| $dbProducts = $stmt->fetchAll(\PDO::FETCH_ASSOC); | |||
| $sql = "SELECT * FROM `tl_iso_product_price`"; | |||
| $stmt = $this->connection->query($sql); | |||
| $dbProductPrices = $stmt->fetchAll(\PDO::FETCH_ASSOC); | |||
| $dbProductsBySku = []; | |||
| foreach ($dbProducts as $prod) { | |||
| $dbProductsBySku[$prod['sku']] = $prod; | |||
| } | |||
| $dbProductsPricesByPid = []; | |||
| foreach ($dbProductPrices as $price) { | |||
| $dbProductsPricesByPid[$price['pid']] = $price; | |||
| } | |||
| /** @var Product $product */ | |||
| foreach ($products as $product) { | |||
| if (array_key_exists($product->getCode(), $dbProductsBySku)) { | |||
| $dbProduct = $dbProductsBySku[$product->getCode()]; | |||
| $dbPrice = $dbProductsPricesByPid[$dbProduct['id']]; | |||
| $this->updateProduct($product, $dbProduct['id']); | |||
| $this->updatePriceTier($product, $dbPrice['id']); | |||
| } else { | |||
| $productId = $this->createProduct($product); | |||
| $productPriceId = $this->createPrice($product, $productId); | |||
| $this->createPriceTier($product, $productPriceId); | |||
| } | |||
| } | |||
| $this->connection->commit(); | |||
| } | |||
| private function updateProduct(Product $product, $productDbId) | |||
| { | |||
| $shippingWeight = $this->convertShippingWeight($product); | |||
| $sql = | |||
| "UPDATE `tl_iso_product` SET | |||
| tstamp = ".time().", | |||
| shipping_weight = '$shippingWeight' | |||
| WHERE `id` = ".$productDbId; | |||
| $this->connection->query($sql); | |||
| } | |||
| private function updatePriceTier(Product $product, $priceDbId) | |||
| { | |||
| $price = $product->getStandardSalesPrice() * (1 + Product::VAT); | |||
| $sql = | |||
| "UPDATE `tl_iso_product_pricetier` SET | |||
| tstamp = ".time().", | |||
| price = $price | |||
| WHERE `pid` = ".$priceDbId; | |||
| $this->connection->query($sql); | |||
| } | |||
| private function createProduct(Product $product) | |||
| { | |||
| $alias = 'exact-product-'.$product->getCode(); | |||
| $shippingWeight = $this->convertShippingWeight($product); | |||
| $sql = | |||
| "INSERT into `tl_iso_product` ( | |||
| pid, | |||
| gid, | |||
| tstamp, | |||
| language, | |||
| dateAdded, | |||
| type, | |||
| fallback, | |||
| alias, | |||
| gtin, | |||
| sku, | |||
| name, | |||
| description, | |||
| meta_title, | |||
| baseprice, | |||
| shipping_weight, | |||
| shipping_exempt, | |||
| shipping_pickup, | |||
| shipping_price, | |||
| protected, | |||
| guests, | |||
| cssID, | |||
| published, | |||
| start, | |||
| stop, | |||
| exact_id | |||
| ) VALUES ( | |||
| 0, | |||
| 0, | |||
| ".time().", | |||
| '', | |||
| ".time().", | |||
| 2, | |||
| '', | |||
| '$alias', | |||
| '', | |||
| '".$product->getCode()."', | |||
| '".$product->getDescription()."', | |||
| '".$product->getExtraDescription()."', | |||
| '', | |||
| '', | |||
| '$shippingWeight', | |||
| '', | |||
| '', | |||
| 0.00, | |||
| '', | |||
| '', | |||
| '', | |||
| '0', | |||
| '', | |||
| '', | |||
| '".$product->getId()."' | |||
| )"; | |||
| $this->connection->query($sql); | |||
| return $this->connection->lastInsertId(); | |||
| } | |||
| private function createPrice(Product $product, $productDbId) | |||
| { | |||
| $sql = | |||
| "INSERT INTO `tl_iso_product_price` ( | |||
| pid, | |||
| tstamp, | |||
| tax_class, | |||
| config_id, | |||
| member_group, | |||
| start, | |||
| stop | |||
| ) VALUES ( | |||
| $productDbId, | |||
| ".time().", | |||
| 1, | |||
| 0, | |||
| 0, | |||
| '', | |||
| '' | |||
| )"; | |||
| $this->connection->query($sql); | |||
| return $this->connection->lastInsertId(); | |||
| } | |||
| private function createPriceTier(Product $product, $productPriceDbId) | |||
| { | |||
| $price = $product->getStandardSalesPrice() * (1 + Product::VAT); | |||
| $sql = | |||
| "INSERT INTO `tl_iso_product_pricetier` ( | |||
| pid, | |||
| tstamp, | |||
| min, | |||
| price | |||
| ) VALUES ( | |||
| $productPriceDbId, | |||
| ".time().", | |||
| 1, | |||
| $price | |||
| )"; | |||
| $this->connection->query($sql); | |||
| return $this->connection->lastInsertId(); | |||
| } | |||
| private function convertShippingWeight(Product $product) | |||
| { | |||
| $shippingWeight = $product->getGrossWeight() !== '' ? | |||
| $product->getGrossWeight() : | |||
| $product->getNetWeight(); | |||
| return ((int) $shippingWeight === 0 || $shippingWeight === '') ? | |||
| '' : | |||
| 'a:2:{s:4:"unit";s:2:"kg";s:5:"value";s:3:"' . $shippingWeight . '";}'; | |||
| } | |||
| } | |||
| @@ -0,0 +1,267 @@ | |||
| <?php | |||
| namespace App\ExactApi; | |||
| class Product | |||
| { | |||
| const VAT = 0.19; | |||
| private $id; | |||
| private $standardSalesPrice; | |||
| private $salesVatCode; | |||
| private $salesVatCodeDescription; | |||
| private $description; | |||
| private $extraDescription; | |||
| private $isWebShopItem; | |||
| private $itemGroup; | |||
| private $itemGroupCode; | |||
| private $stock; | |||
| private $netWeight; | |||
| private $grossWeight; | |||
| private $code; | |||
| public static function getFields() | |||
| { | |||
| return [ | |||
| 'ID', | |||
| 'StandardSalesPrice', | |||
| 'SalesVatCode', | |||
| 'SalesVatCodeDescription', | |||
| 'Description', | |||
| 'ExtraDescription', | |||
| 'IsWebshopItem', | |||
| 'ItemGroup', | |||
| 'ItemGroupCode', | |||
| 'Stock', | |||
| 'NetWeight', | |||
| 'GrossWeight', | |||
| 'Code', | |||
| ]; | |||
| } | |||
| public static function createProductFromApiItem(array $item) | |||
| { | |||
| $res = new self(); | |||
| $res->setId($item['ID']); | |||
| $res->setStandardSalesPrice($item['StandardSalesPrice']); | |||
| $res->setSalesVatCode($item['SalesVatCode']); | |||
| $res->setSalesVatCodeDescription($item['SalesVatCodeDescription']); | |||
| $res->setDescription($item['Description']); | |||
| $res->setExtraDescription($item['ExtraDescription']); | |||
| $res->setIsWebShopItem($item['IsWebshopItem']); | |||
| $res->setItemGroup($item['ItemGroup']); | |||
| $res->setItemGroupCode($item['ItemGroupCode']); | |||
| $res->setStock($item['Stock']); | |||
| $res->setNetWeight($item['NetWeight']); | |||
| $res->setGrossWeight($item['GrossWeight']); | |||
| $res->setCode($item['Code']); | |||
| return $res; | |||
| } | |||
| /** | |||
| * @return mixed | |||
| */ | |||
| public function getId() | |||
| { | |||
| return $this->id; | |||
| } | |||
| /** | |||
| * @param mixed $id | |||
| */ | |||
| public function setId($id) | |||
| { | |||
| $this->id = $id; | |||
| } | |||
| /** | |||
| * @return mixed | |||
| */ | |||
| public function getStandardSalesPrice() | |||
| { | |||
| return $this->standardSalesPrice; | |||
| } | |||
| /** | |||
| * @param mixed $standardSalesPrice | |||
| */ | |||
| public function setStandardSalesPrice($standardSalesPrice) | |||
| { | |||
| $this->standardSalesPrice = $standardSalesPrice; | |||
| } | |||
| /** | |||
| * @return mixed | |||
| */ | |||
| public function getSalesVatCode() | |||
| { | |||
| return $this->salesVatCode; | |||
| } | |||
| /** | |||
| * @param mixed $salesVatCode | |||
| */ | |||
| public function setSalesVatCode($salesVatCode) | |||
| { | |||
| $this->salesVatCode = $salesVatCode; | |||
| } | |||
| /** | |||
| * @return mixed | |||
| */ | |||
| public function getSalesVatCodeDescription() | |||
| { | |||
| return $this->salesVatCodeDescription; | |||
| } | |||
| /** | |||
| * @param mixed $salesVatCodeDescription | |||
| */ | |||
| public function setSalesVatCodeDescription($salesVatCodeDescription) | |||
| { | |||
| $this->salesVatCodeDescription = $salesVatCodeDescription; | |||
| } | |||
| /** | |||
| * @return mixed | |||
| */ | |||
| public function getDescription() | |||
| { | |||
| return $this->description; | |||
| } | |||
| /** | |||
| * @param mixed $description | |||
| */ | |||
| public function setDescription($description) | |||
| { | |||
| $this->description = $description; | |||
| } | |||
| /** | |||
| * @return mixed | |||
| */ | |||
| public function getExtraDescription() | |||
| { | |||
| return $this->extraDescription; | |||
| } | |||
| /** | |||
| * @param mixed $extraDescription | |||
| */ | |||
| public function setExtraDescription($extraDescription) | |||
| { | |||
| $this->extraDescription = $extraDescription; | |||
| } | |||
| /** | |||
| * @return mixed | |||
| */ | |||
| public function getIsWebShopItem() | |||
| { | |||
| return $this->isWebShopItem; | |||
| } | |||
| /** | |||
| * @param mixed $isWebShopItem | |||
| */ | |||
| public function setIsWebShopItem($isWebShopItem) | |||
| { | |||
| $this->isWebShopItem = $isWebShopItem; | |||
| } | |||
| /** | |||
| * @return mixed | |||
| */ | |||
| public function getItemGroup() | |||
| { | |||
| return $this->itemGroup; | |||
| } | |||
| /** | |||
| * @param mixed $itemGroup | |||
| */ | |||
| public function setItemGroup($itemGroup) | |||
| { | |||
| $this->itemGroup = $itemGroup; | |||
| } | |||
| /** | |||
| * @return mixed | |||
| */ | |||
| public function getItemGroupCode() | |||
| { | |||
| return $this->itemGroupCode; | |||
| } | |||
| /** | |||
| * @param mixed $itemGroupCode | |||
| */ | |||
| public function setItemGroupCode($itemGroupCode) | |||
| { | |||
| $this->itemGroupCode = $itemGroupCode; | |||
| } | |||
| /** | |||
| * @return mixed | |||
| */ | |||
| public function getStock() | |||
| { | |||
| return $this->stock; | |||
| } | |||
| /** | |||
| * @param mixed $stock | |||
| */ | |||
| public function setStock($stock) | |||
| { | |||
| $this->stock = $stock; | |||
| } | |||
| /** | |||
| * @return mixed | |||
| */ | |||
| public function getNetWeight() | |||
| { | |||
| return $this->netWeight; | |||
| } | |||
| /** | |||
| * @param mixed $netWeight | |||
| */ | |||
| public function setNetWeight($netWeight) | |||
| { | |||
| $this->netWeight = $netWeight; | |||
| } | |||
| /** | |||
| * @return mixed | |||
| */ | |||
| public function getGrossWeight() | |||
| { | |||
| return $this->grossWeight; | |||
| } | |||
| /** | |||
| * @param mixed $grossWeight | |||
| */ | |||
| public function setGrossWeight($grossWeight) | |||
| { | |||
| $this->grossWeight = $grossWeight; | |||
| } | |||
| /** | |||
| * @return mixed | |||
| */ | |||
| public function getCode() | |||
| { | |||
| return $this->code; | |||
| } | |||
| /** | |||
| * @param mixed $code | |||
| */ | |||
| public function setCode($code) | |||
| { | |||
| $this->code = $code; | |||
| } | |||
| } | |||
| @@ -0,0 +1,23 @@ | |||
| <?php | |||
| //require __DIR__ . '/ApiExact.php'; | |||
| //require __DIR__ . '/IsotopeDatabaseHandler.php'; | |||
| namespace App\ExactApi; | |||
| //use App\ExactApi\ApiExact; | |||
| //use App\ExactApi\IsotopeDatabaseHandler; | |||
| //use App\ExactApi\ApiExact as ApiExact; | |||
| //use App\ExactApi\IsotopeDatabaseHandler as IsotopeDatabaseHandler; | |||
| //include_once('App\ExactApi\ApiExact.php'); | |||
| //include_once('App\ExactApi\IsotopeDatabaseHandler.php'); | |||
| require_once './ApiExact.php'; | |||
| require_once './IsotopeDatabaseHandler.php'; | |||
| $apiConnector = new ApiExact(); | |||
| $databaseHandler = new IsotopeDatabaseHandler(); | |||
| //echo $apiConnector->getAccessToken() . "\n"; | |||
| $products = $apiConnector->getProducts(); | |||
| $databaseHandler->processProducts($products); | |||
| @@ -0,0 +1 @@ | |||
| {"access_token":"stampDE001.gAAAAKLeomGUJnO_59CX7KmwE4tkt1bPxLR7EfU5E2eHjj6LeU2dAPfkRstKdlJJoudf3y6ehyKwF5wQQ1SljbA5sRrGwQsw4M876o0RRtxVHwyR6_QSoM7kP3qj7HfJLEDWjY-KrlJf_rlKtZZ2sRGJkX9jbd-4XDA_uqQECkf2NnIfVAIAAIAAAAC2ADvWhSjIjtyvs4_rhH9HzqaVKUbQthUoKf6StMSX4O5GzilHhM2cQCQJly_7hbpKHJMe4o167cPYIQLTeOilQdlQh21nhGykWfOKtXG31h210WtT2bg8Kc0MtrHQGi_f161wE_xINPXr-H7GTcXLe4klIGUkv0kmmmUacNoDUDfU1fPRTCCFvmkRnE7iBzd116aY9XMVw6mDucFExI9U-dLOD8IFWCPa9U6DSd50T7WoglDHC8Enh-J92C1c8kAhCCiGyA5GHyb9S1eJAnHFDYzOl9y0pwtIHekBJq-LxpzAbVieBUZrXEsTJnD7eiHvKoy9g_Bl-jOVgPTJT29n0dFDUClE_iQ6MGydAiEVKoyhQO20W7EJg64G8l6ZHZrd8r1J36TZ4jmdZ8E8_wqw3MzYGZm4vEXK4G0Lf2-j5uD0eh3aKPQCCdn_jEiCaEABQ-bsvd9sQazNNHbDwzl82GZdlhY8sqln14NpNloahOxhY9-jKu5nT9IBN2EftT-VoLNWp_E4ydPltM_7SS0Xro9j8nf7xNjpBDa__GeR2QSg_He5VR6u2n4lUbU1Qzks2W38RiCA3jXrQ-T6FCtiiaf5rhMzudqeq2c94Ggbn1g7BD_3S9xUFeA5kmSCkn1NZ3RzSqmkMq4IVPcbsJFYeoCiuZ1gYcvSOoBMHN8mjZ8j3xpPEvzJSX39arD5IeNqrLZDXuEU5aORiRnqjN6kPC4w1GQbbMsEL94r8tYGy8VkW7bstxkar7vb2J8vOPhLamN0mcxJdTZCCJ8tj18e","token_type":"bearer","expires_in":"600","refresh_token":"stampDE001.vhK0!IAAAANKu8TC2-MBN5wFyro7uwanpxXBsRFy7YkYSPaM9jMCm8QEAAAEVEJUYfzoc3Q1hatgTMAOm__pmwjTgdkOyzb7qUj7oRvir1XftD_cb5eE7J-06Q0SxWegFPfULR65PDc6Id0pDXZbugqaKhK24Gg9Gr0x74g4EW8HMYJ0PsW1uqvpOb_ph9mQxYllDQf598_uPZvuvxAMN4NOipKiHEXn9tExbn-rmJdrV9bwa3fAm5b2ALbzfBEKCuzuwLkrjH3cheaMC2TaksJ6M2we9IeAezapsyOVEl6QtnhY67L3Irp_bUV_SCZ6H-gbPDX3mFujxyw-lvRoC-ZcgIpM8EuWC3pN24abFp7Qaj8wE2X9_oAgvoJgn42lHl-x6M-QChdf78f6wiCPquk3OwwuBYsjmiJrHFQt4a6nRS-Y_A-J8IOdwq-_KZkvNTY5UI1ZmiGH5pvIDfaXTcvPn6r3C-sQecdImJM43Y_tgZ9a6p2gwPi8zNZSkXfwQH5bfal6tBcH43_8eGSp_M-VbLb7TotZfJgEIePSlZ--VV1aTaRMJ78_JBloFWvQabvhYEvvuvRaePbdGRgXzj95KgQeW8DzBXE6_zHSmhwkUEv11xgXimH3BFToSV_F25ZND7H3OkVM1-M8Jhbvrzh4SR5wRV5WfzC83Mk58HmDtI7xR0owYAu-9mtidFsK8mRC3nJzZSOWdGenZ","expiry_time":1757726518} | |||