Warning: chmod(): Operation not permitted in /var/www/hopeinstoughton_www/wp-content/plugins/simple-universal-google-analytics/main.php on line 8 [0] in function chmod in /var/www/hopeinstoughton_www/wp-content/plugins/simple-universal-google-analytics/main.php on line 8 [1] in function include_once in /var/www/hopeinstoughton_www/wp-settings.php on line 560 [2] in function require_once in /var/www/hopeinstoughton_www/wp-config.php on line 85 [3] in function require_once in /var/www/hopeinstoughton_www/wp-load.php on line 50 [4] in function require_once in /var/www/hopeinstoughton_www/wp-blog-header.php on line 13 [5] in function require in /var/www/hopeinstoughton_www/index.php on line 17
| Server IP : 94.177.8.99 / Your IP : 216.73.217.165 Web Server : Apache/2.4.58 (Ubuntu) System : Linux aries 6.8.0-134-generic #134-Ubuntu SMP PREEMPT_DYNAMIC Fri Jun 26 18:43:11 UTC 2026 x86_64 User : www-data ( 33) PHP Version : 8.1.34 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /var/www/theyoungdesigners_com/modules/storeVariation/model/ |
Upload File : |
<?php
// https://www.bestbuy.com/site/100-apple-gift-card-app-store-apple-music-itunes-iphone-ipad-airpods-accessories-and-more-digital/6421951.p?skuId=6421951
class ProductVariation_Model extends Database_Model {
private $group_id;
private $product_id;
private $variationgroup_id;
private $DATA;
private $max;
private $model_variation = NULL;
private $VARIATIONS;
private $VARIATION_SOURCE;
private $DEFINITION;
private $CURRENT;
private $ACTIVE;
private $VARIANTS;
private $x_editable;
private $x_price;
public function GroupId() {
if (func_num_args()) {
$this->group_id = func_get_arg(0);
$this->Load();
return $this;
}
return $this->group_id;
}
// this is a list of variations specific to a product. This establishes the group of products, related by the combinations of products
// this is not a record id, but a tag. All products with the same number are in the same group
public function VariationGroup() {
if (func_num_args()) {
$this->variationgroup_id = func_get_arg(0);
$this->LoadVariationGroup();
return $this;
}
return $this->variationgroup_id;
}
public function ModelVariation($model) {
$this->model_variation = $model;
return $this;
}
// get current variations for a productgroup
protected function Load() {
$sql = <<<BLOCK
select *
from `pg_x_variation`
left join `store_variation` on pxv_variation=sv_id
BLOCK;
$rows = $this->Query($sql)
->Where('pxv_productgroup=%d', $this->group_id)
->OrderBy('pxv_order')
->Get();
$this->DATA = [];
$this->max = 0;
foreach($rows as $row) {
$this->DATA[] = [
'id' => $row->sv_id,
'caption' => $row->sv_caption,
'order' => $row->pxv_order,
];
if ($row->pxv_order > $this->max) $this->max = $row->pxv_order;
}
}
private function LoadVariationGroup() {
$this->VARIANTS = [];
$this->ACTIVE = [];
if ($this->variationgroup_id) {
$sql = <<<BLOCK
select pxv_product, pxv_variation, pxv_caption, sp_active, so_order
from `prod_x_variation` as pxv
left join `store_product` on pxv_product=sp_id
left join `store_varoption` on so_variation=pxv_variation and so_caption=pxv_caption
BLOCK;
// get only options present for this product
$rows = $this->Query($sql)
->Where('sp_variation = %d', $this->variationgroup_id)
->OrderBy('pxv_product')
->OrderBy('pxv_variation')
->OrderBy('so_order')
->Get();
$this->DbErr();
foreach($rows as $row) {
$product_id = (int) $row->pxv_product;
$variation_id = (int) $row->pxv_variation;
$caption = $row->pxv_caption;
if (!isset($this->VARIANTS[$product_id])) {
$this->VARIANTS[$product_id] = [
'active' => (int) $row->sp_active,
'variation' => [],
];
}
$this->VARIANTS[$product_id]['variation'][$variation_id] = [ 'cap' => $caption, 'idx' => (int) $row->so_order ];
}
foreach($this->VARIANTS as $item) {
if ($item['active']) {
foreach($item['variation'] as $variation_id => $itemrec) {
$caption = $itemrec['cap'];
$this->ACTIVE[$variation_id][$caption] = $itemrec['idx'];
}
}
}
foreach($this->ACTIVE as $variation_id => &$captions) {
asort($captions);
$captions = array_keys($captions);
}
unset($item);
foreach($this->VARIANTS as &$item) {
foreach($item['variation'] as $variation_id => &$itemrec) {
$itemrec = $itemrec['cap'];
}
}
}
}
// * the available variations for this product, based on product and product group
// * the current variation values for this product
protected function AsHandlebars() {
$result = [];
return $result;
}
private function listVariations() {
$ids = array_column($this->DATA, 'id');
// get all items
$rows = $this->Query('select * from store_variation')
->OrderBy('sv_caption')
->Get();
$output = [];
foreach($rows as $row) {
$id = $row->sv_id;
if (!in_array($id, $ids)) {
$output[] = [
'key' => $id,
'value' => $row->sv_caption,
];
}
}
return $output;
}
public function AsPopupAdd() {
return [
'title' => 'Add Variation(s)',
'template' => 'store_productgroup_popup',
'width' => 500,
'modal' => true,
'sc_id' => $this->group_id,
'list_variation' => $this->listVariations(), // list unallocated variations
];
}
public function saveOptions($post) {
$pg = $post['pg'] ?? [];
$ids = array_column($this->DATA, 'id');
foreach($pg as $index => $variation_id) {
if (in_array($variation_id, $ids)) unset($elements[$index]);
}
// add remaining
if (count($pg)) {
$this->TableName('pg_x_variation');
$max = $this->max;
foreach($pg as $variation_id) {
$max++;
$this->InsertOrUpdate([
'pxv_productgroup' => $this->group_id,
'pxv_variation' => $variation_id,
],[
'pxv_order' => $max,
]);
}
}
}
public function ProductGroupSave(&$result, $id, $post) {
}
public function addHandlebars(&$res) {
if (count($this->DATA)) {
$items = [];
foreach($this->DATA as $item) {
$items[] = [
'key' => $item['id'],
'value' => $item['caption'],
];
}
$res['sc_variation'] = $items;
}
}
function escape($value) {
return '"' . str_replace(array("\\", '"', "\n", "\r", "\t", "\0"),
array("\\\\", '\\"', "\\n", "\\r", "\\t", "\\0"), $value) . '"';
}
protected function _reorder($data, $tablename, $idfield, $record_id, $keyfield, $order) {
$case = '';
foreach($data as $key => $item) {
$safevalue = $this->escape($key);
$case .= " when $keyfield = {$safevalue} then {$item['new']} ";
}
$sql = "update $tablename set $order = case $case else $order end";
$this->Query($sql)
->Where("{$idfield}=%d", $record_id)
->Get();
$this->DbErr();
}
public function Reorder($order) {
$reorder = [];
$rows = $this->Query('select * from `pg_x_variation`')
->Where('pxv_productgroup=%d', $this->group_id)
->Get();
foreach($rows as $row) {
$reorder[$row->pxv_variation] = [ 'original' => (int) $row->pxv_order ];
}
foreach($order as $index => $value) {
$reorder[$value]['new'] = $index + 1;
}
$this->_reorder($reorder, 'pg_x_variation', 'pxv_productgroup', $this->group_id, 'pxv_variation', 'pxv_order');
return 1;
}
// -------------------------------------------------------------------------------
// Product Group Variation Tree - updated whenever a category changes
// -------------------------------------------------------------------------------
// this is a tree of product groups, and the variations supported for each left in the tree
// the text fields are just comments for readability, and are not used internally
public function generateVariationTree() {
$filename = HOME . '/storage/productcategory.json';
if (file_exists($filename)) {
$content = json_decode(file_get_contents($filename), TRUE);
$CATAGORY = $content['DATA'];
$expiry = $content['expiry'];
} else {
$expiry = 0;
}
if ($expiry < time()) {
// load in all categories
$CATEGORY = [];
$rows = $this->Query('select sc_id,sc_parent,sc_caption from `store_category`')
->Get();
foreach($rows as $row) {
$parent = (int)$row->sc_parent;
$id = (int)$row->sc_id;
$CATEGORY[$id] = [
'id' => $id,
'parent' => $parent,
'caption' => $row->sc_caption,
'variation' => [],
'vcaption' => [],
];
}
// load in all variations ( pg_x_variation )
$sql = <<<'BLOCK'
select pxv_productgroup,pxv_variation,sv_caption
from `pg_x_variation` as v
left join `store_variation` as sv on sv.sv_id=v.pxv_variation
BLOCK;
$rows = $this->Query($sql)
->Get();
foreach($rows as $row) {
$parent = (int)$row->pxv_productgroup;
$id = (int)$row->pxv_variation;
$CATEGORY[$parent]['variation'][] = $id;
$CATEGORY[$parent]['vcaption'][] = $row->sv_caption;
}
$content = json_encode(['expiry' => time() + 86400, 'DATA' => $CATEGORY], JSON_UNESCAPED_SLASHES);
file_put_contents($filename, $content);
}
return $CATAGORY;
}
// -------------------------------------------------------------------------------
// Product
// -------------------------------------------------------------------------------
public function ProductId() {
if (func_num_args()) {
$this->product_id = func_get_arg(0);
$this->LoadProduct();
return $this;
}
return $this->product_id;
}
public function LoadProduct() {
$TREE = $this->generateVariationTree(); // this is a cached file. Delete whenever a product group is added
// get category
$row = $this->Query('select sp_category,sp_variation,sp_price from `store_product`')
->Where('sp_id=%d', $this->product_id)
->First();
$group_id = (int) $row->sp_category;
$variationgroup_id = (int) $row->sp_variation;
$this->x_price = (float) $row->sp_price;
// get all variation options for product
$this->VARIATION_SOURCE = [];
$this->VARIATIONS = $this->_VariationFromGroup($TREE, $group_id);
if (is_object($this->model_variation)) {
// get definitions
$this->DEFINITION = $this->model_variation
->VariationId($this->VARIATIONS)
->AsHandlebars($this->VARIATION_SOURCE); // which product group did this variation come from
} else {
$this->DEFINITION = [];
}
// get variation values for current product
$this->CURRENT = [];
$rows = $this->Query(
'select sv_caption, pxv_variation, pxv_caption from `prod_x_variation` '.
'left join `store_variation` on sv_id=pxv_variation'
)
->Where('pxv_product=%d', $this->product_id)
->Get();
foreach($rows as $row) {
$this->CURRENT[$row->pxv_variation] = [
'key' => $row->pxv_caption,
'title' => $row->sv_caption
];
}
foreach($this->DEFINITION as &$def) {
$def['option'] = array_column($def['option'], 'value'); // drop key
if (isset($this->CURRENT[$def['id']])) {
$def['current'] = $this->CURRENT[$def['id']]['key'];
} else {
$def['current'] = $def['option'][0];
}
}
$this->VariationGroup($variationgroup_id);
// get canChange flag
}
private function AsHtml() {
$output = '';
foreach($this->CURRENT as $item) {
if ($output != '') $output .= ' ';
$output .= '<strong>' . htmlspecialchars($item['title']) . ':</strong> ' . $item['key'];
}
return $output;
}
private function _VariationFromGroup($TREE, $group_id) {
$output = [];
$depth = 0;
while($group_id && $depth < 10) {
if (isset($TREE[$group_id])) {
$variation_list = $TREE[$group_id]['variation'];
if ($depth) {
foreach($variation_list as $var_id) {
$this->VARIATION_SOURCE[$var_id] = $TREE[$group_id]['caption'];
}
}
$output = array_merge($output, $variation_list);
$group_id = $TREE[$group_id]['parent'];
} else {
break;
}
$depth++;
}
return $output;
}
private function _asTable_assembleVariation($variations, $index) {
$result = [];
for($i = count($variations) - 1; $i >= 0; $i--) {
$column = $variations[$i];
$c = count($column);
$idx = $index % $c;
$result[] = $column[$idx];
$index = intval($index / $c);
}
return array_reverse($result);
}
private function _isVariation($node, $source) {
foreach($this->DEFINITION as $index => $def) {
$variation_id = $def['id'];
if ($node[$index] != $source[$variation_id]) return FALSE;
}
return TRUE;
}
private function _isVariation_current($node) {
foreach($this->DEFINITION as $index => $def) {
$variation_id = $def['id'];
$source = $this->CURRENT[$variation_id]['key'];
if ($node[$index] != $source) return FALSE;
}
return TRUE;
}
// type = 0 (not present - can be cloned), 1 = present, 2 = present (disabled), 3 = current
private function _asTable() {
$header = [];
$detail = [];
$variations = [];
$total = 1;
foreach($this->DEFINITION as $def) {
$header[] = $def['caption'];
$variations[] = $def['option'];
$total *= count($def['option']);
}
// get all variations
for($index = 0; $index < $total; $index++) {
$item = $this->_asTable_assembleVariation($variations, $index);
if ($this->_isVariation_current($item)) {
$type = 3;
} else {
$type = $this->_isUsedVariation($item, $product_id);
}
$detail[] = [
'depth' => $item,
'link' => $this->_asTable_link($item, $type, $product_id),
'type' => $type,
];
}
return [$header, $detail];
}
private function _asTable_link($node, $type, $product_id) {
$output = '';
if ($type == 1 || $type == 2) {
return "/admin/product/{$product_id}/edit#variation";
}
foreach($this->DEFINITION as $index => $def) {
$variation_id = $def['id'];
$text = $node[$index];
if ($output != '') $output .= '&';
$output .= "v[{$variation_id}]=" . rawurlencode($text);
}
return "/admin/product-variations/{$this->product_id}/clone?$output";
}
private function _isUsedVariation($item, &$product_id) {
// var_dump($this->VARIANTS);
// exit;
foreach($this->VARIANTS as $product => $info) {
if ($this->_isVariation($item, $info['variation'])) {
$product_id = $product;
return $info['active'] ? 2 : 1;
}
}
return 0;
}
// Product Admin: from the Variation tab
public function AsHandlebars_Product() {
$data = [];
$data['variation'] = $this->DEFINITION;
$data['sp_id'] = $this->product_id;
[ $data['header'], $data['detail'] ] = $this->_asTable();
$data['hasvariation'] = count($this->DEFINITION) ? 1 : 0;
return $data;
}
public function Save($postdata) {
$this->TableName('prod_x_variation');
foreach($this->DEFINITION as &$def) {
$variation_id = $def['id'];
if (isset($postdata[$variation_id])) {
$this->InsertOrUpdate([
'pxv_product' => $this->product_id,
'pxv_variation' => $variation_id,
], [
'pxv_caption' => $postdata[$variation_id],
]);
}
}
}
/*
private function _activeVariationOptions() {
// get count of all products in the variation group in the specified variation
$sql = <<<'BLOCK'
SELECT pv.*, p.sp_id
FROM `store_product` as p
right join prod_x_variation as pv on pxv_product=sp_id
BLOCK;
$rows = $this->Query($sql)
->Where('sp_active = 1')
->Where('sp_variation=%d', $variationgroup_id)
->OrderBy('pxv_variation')
->OrderBy('pxv_caption')
->Get();
$result = [];
foreach($rows as $row) {
$result[$row['pxv_variation']][$row['pxv_caption']] = (int) $row['sp_id'];
}
return $result;
}
*/
// if more variation options appear than are expected - make sure sp_variation as a value
// dont show parent
public function addHandlebarsProduct(&$res) {
$defs = $this->DEFINITION;
foreach($defs as &$def) {
$variation_id = $def['id'];
unset($def['parent']);
if (isset($this->ACTIVE[$variation_id])) {
$def['option'] = $this->ACTIVE[$variation_id];
}
}
$res['variation'] = $defs;
}
// clone the variations - use the params from the query string
public function CloneProduct($param) {
$this->TableName('prod_x_variation');
foreach($this->DEFINITION as $def) {
$variation_id = $def['id'];
if (isset($param['variation'][$variation_id])) {
$this->InsertOrUpdate([
'pxv_product' => $param['target'],
'pxv_variation' => $variation_id,
], [
'pxv_caption' => $param['variation'][$variation_id],
]);
$this->DbErr();
}
}
}
private function isMatch($set1, $set2) {
$res = array_intersect($set1, $set2);
return count($res) == count($set1);
}
private function getBestVariation($current) {
$best = FALSE;
$variation = [];
header('Content-type: text/plain');
foreach($current as $group_id => $current_variation) {
$variation[] = $current_variation;
$current_id = FALSE;
foreach($this->VARIANTS as $product_id => $item) {
if ($item['active']) {
$res = array_intersect($variation, $item['variation']);
if (count($res) == count($variation) && $current_id === FALSE) {
$current_id = $product_id;
break;
}
}
}
if ($current_id !== FALSE) {
$best = $current_id;
}
}
return $best;
}
public function locateVariation($post) {
$current = $post['variation'];
// get all variants that match the first variation - product id = first entry
// then get all the variants that match the second variation = product id = first entry
$product_id = $this->getBestVariation($current);
if ($product_id) return $product_id;
foreach($this->VARIANTS as $product_id => $item) {
if ($item['active'] && $this->isMatch($item['variation'], $current)) return $product_id;
}
return FALSE;
}
private function _getVariationsByPrice($group_id) {
$sql = <<<BLOCK
select x.*, v.sv_id, v.sv_caption, p.sp_price, count(distinct pxv_caption) as c
from prod_x_variation as x
left join `store_product` as p on pxv_product=sp_id
left join store_variation as v on pxv_variation=v.sv_id
BLOCK;
$row = $this->Query($sql)
->Where('sp_variation=%d', $group_id)
->Where('sp_price=%f', $this->x_price)
->groupBy('sv_caption')
->orderBy('c desc')
->Get()
->First();
if (!is_null($row) && !$row->empty()) {
$c = (int) $row->c;
if ($c > 1) { // this variation can be changed without changing the price
return [
'sv_id' => (int) $row->sv_id,
'sv_caption' => $row->sv_caption
];
}
}
return NULL;
}
public function AddCartContent(&$data, $tag = '') {
$this->x_editable = [];
foreach($data['cart'] as &$item) {
$text = $this->ProductId($item['product'])
->AsHtml();
if ($text != '') {
$item['variation'] = $text;
if ($tag == 'edit' && $this->variationgroup_id) {
if (!isset($this->x_editable[$this->variationgroup_id])) {
// does this product have an editable variation - one where every variation of the product is the same price as this?
$info = $this->_getVariationsByPrice($this->variationgroup_id);
$this->x_editable[$this->variationgroup_id] = $info;
}
if (is_array($info)) {
$item['button_variation'] = $info['sv_caption'];
}
}
}
}
}
public function categoryChange() {
// clear out cache file
$filename = HOME . '/storage/productcategory.json';
@unlink($filename);
}
private function _isCompatibleVariant($variation, $info, $current) {
// $info = this is the variation that can change
$var_id = $info['sv_id'];
// make sure all variations other than var_id are the same
foreach($this->CURRENT as $variation_id => $stuff) {
if ($variation_id != $var_id) {
if ($current[$variation_id] != $variation[$variation_id]) return FALSE;
}
}
return $variation[$var_id];
}
private function getAssociatedVariants($info, $current) {
$output = [];
foreach($this->VARIANTS as $product_id => $prod_info) {
if ($prod_info['active']) {
$key = $this->_isCompatibleVariant($prod_info['variation'], $info, $current);
if ($key) {
$output[] = [
'product' => $product_id,
'caption' => $key,
// this needs to have uri too?
];
}
}
}
$res = ['cart' => $output];
// now we have the products - we need the primary thumbnail for each
$res = hook_execute('cart.display', $res, 'fulfill'); // add images
return $res['cart'];
}
public function getProductVariations() {
// which variation has all products of the same price
$info = $this->_getVariationsByPrice($this->variationgroup_id);
$current = $this->VARIANTS[$this->product_id]['variation'] ?? NULL;
// get all suitable products
$products = $this->getAssociatedVariants($info, $current);
return $products;
}
}