* @copyright 2019 Dalibor Stojcevski * @license Dalibor Stojcevski */ /** No max line limit since the lines can be more than 4096. Performance impact is not significant. */ function filter_category_array ($element) { return (!is_array($element) && $element !== ''); } define('MAX_LINE_SIZE', 0); class Import { private $json_array = array(); private $product_links = array(); private $settings; public function __construct($settings) { $this->settings = $settings; } public function getAllData($path, $test = false) { $fields = $this->settings['import_api_field']; $nested_data = array(); $id_parts = explode('->', $path); array_shift($id_parts); foreach ($fields as $field => $value) { if(!$value) continue; $field_paths = $value ? html_entity_decode($value, ENT_QUOTES, 'UTF-8') : ''; $field_parts = explode('->', $field_paths); $nested_data[$field] = $this->getFieldData($this->json_array, $field_parts, $id_parts); } if ($test == 'view-raw') { return $nested_data; } $nested_data = $this->checkCombinations($nested_data); if ($test == 'view-split') { return $nested_data; } $original_product = $this->resolveAllNestedData($nested_data); if ($test == 'view-grouping') { return $original_product; } $modified = $this->changeOriginal($original_product); return $modified; } public function resolveAllNestedData($nested_data) { if (!empty($nested_data['feature_value'])) { if (empty($nested_data['feature'])) { if (!empty($this->settings['import_api_modification']['feature'])) { $nested_data['feature'] = $this->settings['import_api_modification']['feature']; } else { $nested_data['feature'] = 'Feature'; } } $feature_map = $this->resolveNested($nested_data['feature_value'], $nested_data['feature']); $nested_data['features'] = array(); foreach ($feature_map as $key => $map) { $nested_data['features'][$key] = array( 'feature' => $map['parent'], 'feature_value' => $map['value'] ); } } $nested_data['attributes'] = array(); for ($i = 1;$i <= 3; $i++) { if (!empty($nested_data['attribute_value' . $i])) { if (empty($nested_data['attribute' . $i])) { if (!empty($this->settings['import_api_modification']['attribute' . $i])) { $nested_data['attribute' . $i] = $this->settings['import_api_modification']['attribute' . $i]; } else { $nested_data['attribute' . $i] = 'Select ' . $i; } } $attribute_map = $this->resolveNested($nested_data['attribute_value' . $i], $nested_data['attribute' . $i]); $attribute_details_par = array('price', 'ean', 'quantity', 'weight'); foreach ($attribute_map as $key => $map) { foreach ($attribute_details_par as $parameter) { if (isset($nested_data['attribute_' . $parameter])) { if (is_array($nested_data['attribute_' . $parameter])) { if (isset($nested_data['attribute_' . $parameter][$key])) { $nested_data['attribute_details'][$parameter][$map['value']] = $nested_data['attribute_' . $parameter][$key]; } } else { $nested_data['attribute_details'][$parameter][$map['value']] = $nested_data['attribute_' . $parameter]; } } } $nested_data['attributes'][$i][$key] = array( 'attribute' => $map['parent'], 'attribute_value' => $map['value'] ); } } } if (!empty($nested_data['category'])) { if (!isset($nested_data['category_parent'])) { $nested_data['category_parent'] = 0; // it is not good idea to put default top category here } if ($this->settings['import_api_settings']['category_path'] && is_array($nested_data['category'])) { $nested_data['category'] = implode('->', array_filter($nested_data['category'], "filter_category_array")); } $nested_data['category_path'] = $this->resolveNested($nested_data['category'], $nested_data['category_parent']); } $nested_data['options'] = array(); if (!empty($nested_data['option_value'])) { if (!isset($nested_data['option'])) { $nested_data['option'] = $this->settings['import_api_default_option']; } if (!isset($nested_data['option_price'])) { $nested_data['option_price'] = 0; } if (!isset($nested_data['option_weight'])) { $nested_data['option_weight'] = 0; } $option_map= $this->resolveNested($nested_data['option_value'], $nested_data['option']); $option_price_map = $this->resolveNested($nested_data['option_price'], $nested_data['option_value']); $option_weight_map = $this->resolveNested($nested_data['option_weight'], $nested_data['option_value']); foreach ($option_map as $key => $map) { if (is_array($nested_data['option_price'])) { foreach ($option_price_map as $price_map) { if ($map['value'] == $price_map['parent']) { $nested_data['options'][$key] = array( 'option' => $map['parent'], 'option_value' => $map['value'], 'price' => $price_map['value'] ); break; } } } else { $nested_data['options'][$key] = array( 'option' => $map['parent'], 'option_value' => $map['value'], 'price' => $nested_data['option_price'] ); } if (is_array($nested_data['option_weight'])) { foreach ($option_weight_map as $weight_map) { if($map['value'] == $weight_map['parent']){ $nested_data['options'][$key]['weight'] = $weight_map['value']; break; } } } else { $nested_data['options'][$key]['weight'] = $nested_data['option_weight']; } } } return $nested_data; } public function getFieldData($tree, $field_parts, $id_parts) { while($field_parts) { if ($this->isSeq($tree)) { $seq_id = array_shift($id_parts); $tree = $tree[$seq_id]; } else { $part = array_shift($field_parts); $uni_c = array_shift($id_parts); if($part == $uni_c){ $tree = $tree[$part]; } else { $field_parts = array_merge([$part], $field_parts); // vrati mu ga return $this->followDifferentPath($field_parts, $tree); } } } } public function followDifferentPath($own_parts, $tree){ $prev= ''; // for test foreach($own_parts as $part){ //if ($part == 'image') {var_dump($tree);var_dump($own_parts);exit;} if ($this->isSeq($tree)) { $ret = array(); foreach($tree as $t){ $ret[] = $this->followDifferentPath($own_parts, $t); } return $ret; } else { array_shift($own_parts); $tree = isset($tree[$part]) ? $tree[$part] : ''; } $prev = $part; //for test } if(!is_array($tree)){ return trim($tree); } return $tree; } public function findUniqueProductsIdentifier($tree, $parts, $identifier, $path = ''){ $tmp_parts = $parts; foreach ($parts as $part) { if ($part == $identifier && !$this->isSeq($tree) && isset($tree[$part])) { if (!empty($tree[$part]) && !is_array($tree[$part])) { $path .= '->'. $tree[$part]; $this->product_links[] = $path; } //exit; } elseif ($this->isSeq($tree)) { foreach($tree as $key => $t){ $local_path = $path. '->' . $key; $this->findUniqueProductsIdentifier($t, $tmp_parts, $identifier, $local_path); } return; } else { if (isset($tree[$part])) { $tree = $tree[$part]; $path .= '->'. $part; } array_shift($tmp_parts); } } } public function resolveNested($child_data, $parent_data, $default_parent = 'Value'){ $child_indexes = array(); if(!is_array($child_data)){ $child_data = array($child_data); } $this->setIndexPath($child_data, $child_indexes); $parent_indexes = array(); if(is_array($parent_data)){ $this->setIndexPath($parent_data, $parent_indexes); } else { $map = array(); foreach($child_indexes as $child){ $map[] = array( 'value' => $child['value'], 'parent' => $parent_data ); } return $map; } return $this->addParent($child_indexes, $parent_indexes); } public function addParent($children, $parents){ $map = array(); foreach($parents as $parent){ foreach($children as $key => $child){ if(substr($child['key_path'], 0, strlen($parent['key_path'])) === $parent['key_path']){ $map[] = array( 'value' => $child['value'], 'parent' => $parent['value'] ); unset($children[$key]); } } } return $map; } /** * Takes nested array with unknown nesting levels and return one level array with indexes to values. * * @param array $array array with unknown nesting levels * @param string $key_path string made from array keys * @return array with values and string from value indexes */ public function setIndexPath($array, &$return_values = array(), $key_path = ''){ foreach($array as $key => $array_part){ if(is_array($array_part)){ $this->setIndexPath($array_part, $return_values, $key_path . $key . '.'); } else { $return_values[] = array( 'key_path' => $key_path . $key . '.', 'value' => $array_part ); } } } public function changeOriginal($product) { $modifications = $this->settings['import_api_modification']; foreach ($modifications as $field => $modification) { //if (!$modification) { if ($modification === '') { continue; } if (in_array($field, ['price']) && !$product[$field]) { continue; } if (!isset($product[$field])) { $product[$field] = ''; } if (in_array($field, ['image', 'images', 'cover']) && !$product[$field]) { continue; } $modification = html_entity_decode($modification, ENT_QUOTES, 'UTF-8'); $modified = $this->check_string($modification, $product); $product[$field] = $modified; if (in_array($field, ['price', 'quantity', 'option_price','option_quantity', 'special', 'additional_shipping_cost', 'width', 'height', 'depth', 'weight', 'attribute_price', 'attribute_quantity', 'attribute_weight', 'minimal_quantity'])) { $modified = $this->calculateMath($modified); $product[$field] = $modified; } } return $product; } public function checkCombinations($product) { $combinations = $this->settings['import_api_combination']; foreach ($combinations as $field => $combination) { if (!$combination || !isset($product[$field])) { continue; } else { $position = 'all'; $parts = explode('##', $combination); if(isset($parts[1])){ $position = $parts[1]; } } $exploded = $this->explodeCombinations($product[$field], $parts[0], $position); $product[$field] = $exploded; } return $product; } public function explodeCombinations($value, $separator, $position = 'all') { if (!is_array($value)) { $parts = explode($separator, $value); if ($position == 'all') { return $parts; } if ($position == 'last') { return end($parts); } if (isset($parts[$position-1])) { return $parts[$position-1]; } return $value; } else { $ret = array(); foreach ($value as $key => $v) { $ret[] = $this->explodeCombinations($v, $separator, $position); } return $ret; } } public function getProductLinks() { return $this->product_links; } private function check_string($string, $prod) { if ($string) { preg_match_all('/\[\[([a-z_]*\#?[^\]]*)\]\]/', $string , $match); if ($match) { $string = $this->replace_string($string, $match[1], $prod); } } return $string; } private function replace_string($string, $keys, $d_array) { foreach ($keys as $key) { $exploded_key = explode('#', $key); $separator = false; if (isset($exploded_key[1])) { $string = str_replace($key, $exploded_key[0], $string); $key = $exploded_key[0]; $separator = $exploded_key[1]; } if (isset($d_array[$key])) { if (is_array($d_array[$key])) { $string = $this->replace_all_array_strings($d_array[$key], $key, $string, $separator); } else { $string = str_replace('[['. $key .']]', $d_array[$key], $string); } } } return $string; } private function replace_all_array_strings($array, $key, $string, $separator = false) { $s = array(); foreach ($array as $d_part) { $s[] = str_replace('[['. $key .']]', $d_part, $string); } if ($separator !== false) { $s = implode($separator, $s); } return $s; } private function calculateMath($expression) { $expression = str_replace(',', '.', $expression); $expression = str_replace(' ', '', $expression); $expression = preg_replace('/[^0-9\.\+\-\*\/\(\)]/', '', $expression); $expression = $this->replaceMathExpession($expression, '*'); $expression = $this->replaceMathExpession($expression, '/'); $expression = $this->replaceMathExpession($expression, '+'); $expression = $this->replaceMathExpession($expression, '-'); return $expression; } private function replaceMathExpession($expression, $sign) { $patern = '\\' . $sign; $expression = preg_replace_callback( '/[0-9]*\.?[0-9]+' . $patern . '[0-9]*\.?[0-9]+(' . $patern . '[0-9]*\.?[0-9]+)*/', function ($matches) use ($sign) { if (!empty($matches[0])) { return $this->calculate_string($matches[0], $sign); } }, $expression); return $expression; } private function calculate_string($string, $sign) { $parts = explode($sign, $string); $final = $parts[0]; for ($i = 1; $i < count($parts); $i++) { if ($sign == '*') { $final = $final * $parts[$i]; } elseif ($sign == '/') { $final = $final / $parts[$i]; } elseif ($sign == '+') { $final = $final + $parts[$i]; } elseif ($sign == '-') { $final = $final - $parts[$i]; } } return $final; } public function jsonToArray($json_url) { $json_string = @file_get_contents($json_url); $json_array = json_decode($json_string, true); return $json_array; } public function setJsonArray($json_array) { $this->json_array = $json_array; } public function isSeq($array) { if (is_array($array) ) { return is_int(array_key_first($array)); } //return is_array($array) && isset($array[0]); return false; } }