4.3 Кастомный код в итерации цикла товара

4.3 Кастомный код в итерации цикла товара. В этом поле есть возможность работать с данными в формате $product где содержится массив данных конкретного товара. Синтаксис PHP без открывающих и закрывающих скобок. Просто код.

Скриншот модуля UniXML

Что бы увидеть что находится в массиве товара в этом поле достаточно прописать код:

echo "<pre>";
print_r($product);
exit();

Что бы увидеть конфигурацию выгрузки достаточно прописать такой код:

echo "<pre>";
print_r($data);
exit();

В массиве $data вся конфигурация фида. Его можно изменять, как и любые другие данные через этот пункт настроек

Примеры применения кастомного кода на практике

Если надо не выгружать товары у которых цена = 0. Для этого достаточно написать код (* но гораздо правильней прописать в пункт 4.1 AND p.price > 0)

if((int)$product['price'] == 0){
continue;
}

Когда включено умножение на опции и в опциях нет актуальных данных по количеству можно забирать количество из поля quantity а не с опции

$product['quantity'] = $product['quantity_original'];

Сделать наценку в зависимости от цены товара можно так

if($product['price'] > 2000 && $product['price'] < 10000){ //Если цена товара от 2 до 10 тыс, наценка 100
$product['price'] += 100;
}
if($product['price'] > 200 && $product['price'] < 1000){ //Если цена товара от 200 до 1 тыс, наценка 60
$product['price'] += 60;
}

Чтобы удалить видео с описаний достаточно вставить код

$product['description'] = preg_replace('~<iframe\b[^>]*+>|</iframe\b[^>]*+>~', '', $product['description']);

Что бы вывести опции как атрибуты достаточно прописать такой код

$product['attributes'] = array(); //Для игнорирования атрибутов, если атриубуты нужны - удаляем строку
$sql = "SELECT od.name as option_name, ovd.name as option_value
             FROM " . DB_PREFIX . "product_option po
        LEFT JOIN " . DB_PREFIX . "product_option_value pov ON(po.product_option_id = pov.product_option_id)
        LEFT JOIN " . DB_PREFIX . "option_description od ON(po.option_id = od.option_id)
        LEFT JOIN " . DB_PREFIX . "option_value_description ovd ON(pov.option_value_id = ovd.option_value_id)
        WHERE po.product_id = '" . $product['product_id'] . "'
        AND od.language_id = '" . $data['language'] . "'
        AND ovd.language_id = '" . $data['language'] . "'
        ";
$options_query = $this->db->query($sql);
if($options_query->num_rows){
  foreach($options_query->rows as $row){
    $product['attributes'][] = array(
      'name' => $row['option_name'],
      'text' => $row['option_value']
    );
  }
}

Для того что бы предотвратить выгрузку акции которая больше или равна цене нужно променить код

if($product['special'] >= $product['price']){$product['special'] = false;}

В ситуации когда акция менее 5% нужно записывать акцию в цену (например для Яндекс Маркета) можно вставить такой код

if($product['special']){
  $perc = (($product['price'] - $product['special'])/$product['price'])*100;
  if($perc < 5){
    $product['price'] = $product['special'];
    $product['special'] = false;
  }
}

Что бы заменить ссылки на товары при использовании модуля AJAX замена товара по моделям - HYPER PRODUCT MODELS достаточно поставить код

if(!isset($hpm_links)){
  $hpm_links = array();
}
$hpm_query = $this->db->query("SELECT product_id, parent_id FROM " . DB_PREFIX ."hpmodel_links WHERE parent_id != product_id");
foreach($hpm_query->rows as $link_data){
  $hpm_links[$link_data['product_id']] = $this->url->link('product/product', 'product_id=' . (int)$link_data['parent_id']) . '#' . $link_data['product_id'];
}
if(isset($hpm_links[$product['product_original_id']])){
  $product['url'] = $hpm_links[$product['product_original_id']];
}

На многих магазинах есть фильтр ocfilter где опции фильтра идут отдельно от системных, и часто надо передать их в выгрузку. Для реализации такого надо добавить код

$product['attributes'] = [];
$sql = "SELECT od.name, ovd.name as text FROM " . DB_PREFIX . "ocfilter_filter_value_to_product v2p
        LEFT JOIN " . DB_PREFIX . "ocfilter_filter_description od ON (v2p.filter_id = od.filter_id)
        LEFT JOIN " . DB_PREFIX . "ocfilter_filter_value_description ovd ON (v2p.value_id = ovd.value_id)
        WHERE v2p.product_id = '" . $product['product_original_id'] ."'
        AND od.language_id = '" . $data['language'] . "'
        AND ovd.language_id = '" . $data['language'] . "'
        ";
$attrs_query = $this->db->query($sql);
foreach($attrs_query->rows as $att_item){
  $product['attributes'][] = array(
    'name' => $att_item['name'],
    'text' => $att_item['text'],
    'finish' => $att_item['name']
  );
}

Также что бы вывести из ocfilter опции в описание достаточно использовать такой код

$sql = "SELECT od.name, ovd.name as text FROM " . DB_PREFIX . "ocfilter_filter_value_to_product v2p
        LEFT JOIN " . DB_PREFIX . "ocfilter_filter_description od ON (v2p.filter_id = od.filter_id)
        LEFT JOIN " . DB_PREFIX . "ocfilter_filter_value_description ovd ON (v2p.value_id = ovd.value_id)
        WHERE v2p.product_id = '" . $product['product_original_id'] ."'
        AND od.language_id = '" . $data['language'] . "'
        AND ovd.language_id = '" . $data['language'] . "'
        ";
$attrs_query = $this->db->query($sql);
$desc_plus = array();
foreach($attrs_query->rows as $att_item){
  if(isset($desc_plus[$att_item['name']])){
    $desc_plus[$att_item['name']] = $desc_plus[$att_item['name']] . ', ' . $att_item['text'];
  }else{
    $desc_plus[$att_item['name']] = $att_item['text'];
  }
}
$desc_final = array();
foreach($desc_plus as $dk => $dv){
  $desc_final[] = $dk . ': ' . $dv;
}
$product['description'] .= implode('; ', $desc_final);

Некоторые магазины используют стандартный фильтр. Что бы вывести фильтра как атрибуты можно использовать такой код

$sql = "SELECT fgd.name as name, fd.name as text FROM " . DB_PREFIX . "filter_description fd
        LEFT JOIN " . DB_PREFIX . "filter_group_description fgd ON (fgd.filter_group_id = fd.filter_group_id)
        LEFT JOIN " . DB_PREFIX . "product_filter pf ON (pf.filter_id = fd.filter_id)
        WHERE pf.product_id = '" . $product['product_original_id'] . "'";
$filter_query = $this->db->query($sql);
$product['attributes'] = $filter_query->rows;

Что бы корректно собирались разновидности на prom.ua надо поставить код

if(!isset($group)){
  $group = 0;
}
if($group != $product['product_original_id']){
  $group = $product['product_original_id'];
}else{
  foreach($product['attributes'] as $ak => $av){
     if($av['name'] != 'Международный размер'){ //Международный размер - название опции для умножения
       unset($product['attributes'][$ak]);
     }
  }
}

Для добавления фото опции к разновидности (13 - id опции цвет)

foreach($product['option_data'] as $od){
  if($od['option_id'] == 13){
    $q = $this->db->query("SELECT product_option_id, product_option_value_id FROM " . DB_PREFIX . "product_option_value WHERE option_value_id = '" . $od['option_value_id'] . "' AND option_id = '13' AND product_id = '" . (int)$product['product_original_id'] . "'");
    if($q->num_rows){
      $qd = $this->db->query("SELECT image FROM " . DB_PREFIX . "poip_option_image WHERE product_option_id = '" . $q->row['product_option_id'] . "' AND product_option_value_id = '" . $q->row['product_option_value_id'] . "' AND product_id = '" . (int)$product['product_original_id'] . "'");
      if($qd->num_rows){
        $product['image'] = HTTPS_SERVER . 'image/' . $qd->row['image'];
      }
    }
  }
}

Для выгрузки цены с другой группы покупателей или с другой таблицы достаточно в пункте 4.2 прописать код

$data['prices_custom'] = array();
$prices_query = $this->db->query("SELECT product_id, price FROM " . DB_PREFIX . "product_discount WHERE customer_group_id = 7");
foreach($prices_query->rows as $price_item){
  $data['prices_custom'][$price_item['product_id']] = $price_item['price'] * $data['currency'];
}

И потом в пункте 4.3 кодом просто менять цену с этого массива цен

if(isset($data['prices_custom'][$product['product_id']])){
  $product['price'] = $data['prices_custom'][$product['product_id']];
}

Для вывода атрибутов в конце описания можно использовать код

if($product['attributes']){
  $product['description'] .= '<p><b>Характеристики</b></p><ul>';
  foreach($product['attributes'] as $attribute){
    $product['description'] .= '<li>' . $attribute['name'] . ':' . $attribute['text'] . '</li>';
  }
  $product['description'] .= '</ul>';
}

Для вывода атрибутов для второго языка код

$attrs1 = array();
if($product['attributes']){
  $at_ids = implode(',', array_keys($product['attributes']));
  if($at_ids){
    $pa_sql = "SELECT pa.text, ad.name 
               FROM " . DB_PREFIX . "product_attribute pa 
               LEFT JOIN " . DB_PREFIX . "attribute_description ad ON(pa.attribute_id = ad.attribute_id) 
               WHERE pa.product_id = '" . $product['product_original_id'] . "' 
               AND pa.attribute_id IN(" . $at_ids . ") 
               AND pa.language_id = 1
               AND ad.language_id = 1";
    $attrs1_query = $this->db->query($pa_sql);
  }
  if($attrs1_query->num_rows){
    foreach($attrs1_query->rows as $attribute1){
      $product['attributes_full'][] = array(
        'name' => 'param name="' . $attribute1['name'] . '" lang="ru"',
        'text' => $attribute1['text'],
        'end' => 'param',
      );
    }
  }
}

Для вывода атрибутов на двух языках для розетки

if($product['attributes']){
  $at_ids = implode(',', array_keys($product['attributes']));
  if($at_ids){
    $pa_sql = "SELECT attribute_id, text 
               FROM " . DB_PREFIX . "product_attribute 
               WHERE product_id = '" . $product['product_original_id'] . "' 
               AND attribute_id IN(" . $at_ids . ") 
               AND language_id = '" . (int)$data['lang_data']['lang_id'] . "'";
    $attrs1_query = $this->db->query($pa_sql);
  }
  if($attrs1_query->num_rows){
    foreach($attrs1_query->rows as $attribute1){
      $product['attributes_full'][] = array(
        'name' => 'param name="' . $product['attributes'][$attribute1['attribute_id']]['name'] . '"',
        'text' => '<value lang="uk">' . $attribute1['text'] . '</value><value lang="ru">' . $product['attributes'][$attribute1['attribute_id']]['text'] . '</value>',
        'end' => 'param',
        'decode' => true
      );
    }
    $product['attributes'] = array();
  }
}

Для того чтобы обрезать фото например до 5 можно поставить

$product['images'] = array_slice($product['images'], 0, 4); //первое фото главное + 4 дополнительных

Если нужно добавить в описание видео из кастомный вкладок шаблона от oct можно поставить код

$tab = 1;
$video = '';
$video_title = '';
$video_query = $this->db->query("SELECT text FROM " . DB_PREFIX . "oct_product_extra_tabs WHERE extra_tab_id = '" . $tab . "' AND language_id = '" . $data['language'] . "' AND product_id = '" . $product['product_original_id'] . "'");
if($video_query->num_rows){
  $video = $video_query->row['text'];
}
$title_query = $this->db->query("SELECT title FROM " . DB_PREFIX . "oct_extra_tabs_description WHERE extra_tab_id = '" . $tab . "' AND language_id = '" . $data['language'] . "'");
if($title_query->num_rows){
  $video_title = $title_query->row['title'];
}
if($video_title && $video){
  $product['description'] .= '<br><h3>' . $video_title . '</h3>' . $video;
}

Для того чтобы обрезать количество атрибутов до 10 можно поставить

$product['attributes'] = array_slice($product['attributes'], 0, 10);

Для вывода всех категорий с вложенностью в $product['category'] нужно в пункте 4.2 добавить

$data['category_path_final'] = unserialize($data['category_path']);

И в пункте 4.3

$category_query = $this->db->query("SELECT category_id FROM " . DB_PREFIX . "product_to_category WHERE product_id = '" . $product['product_original_id'] . "'");
$product['category_full'] = array();
foreach($category_query->rows as $cat){
  $path = explode('_', $data['category_path_final'][$cat['category_id']]);
  foreach($path as $item){
    $product['category_full'][$cat['category_id']][] = $data['categories_xml'][$item]['original'];
  }
}
foreach($product['category_full'] as $cat_id => $cat_item){
  $product['category_full'][$cat_id] = implode('>', $cat_item);
}
$product['category'] = implode('|',  $product['category_full']);

Если вам надо делать замены в описаниях на втором языке надо в пункте 4.2 добавить

$data['replace_from'] = array();
$data['replace_to'] = array();
$q = $this->db->query("SELECT name_from, name_to, replace_where FROM " . DB_PREFIX . "unixml_replace_name WHERE feed = '" . $this->db->escape($data['feed']) . "' AND replace_where LIKE '4'");
$data['replace_from'] =array_column($q->rows, 'name_from');
$data['replace_to'] =array_column($q->rows, 'name_to');

и в пункте 4.3 код

if(isset($product['langdata'][$data['lang_data']['lang_id']]['description'])){
$product['langdata'][$data['lang_data']['lang_id']]['description'] = str_replace($data['replace_from'], $data['replace_to'], $product['langdata'][$data['lang_data']['lang_id']]['description']);
}

Если у вас в опциях свои фото и шаблон от octemplates достаточно указать код для вывода фото опции

if(isset($product['option_data']) && $product['option_data']){
  foreach($product['option_data'] as $od){
    $qd = $this->db->query("SELECT o_v_image FROM " . DB_PREFIX . "product_option_value WHERE option_value_id = '" . $od['option_value_id'] . "' AND option_id = '" . $od['option_id'] . "' AND product_id = '" . $product['product_original_id'] . "'");
    if($qd->num_rows){
      $product['image'] = HTTPS_SERVER . 'image/' . $qd->row['o_v_image'];
      $product['images'] = false;
    }
  }
}

Если вам надо выгружать фото только к опциям которые заданы через шаблон от octemplates тогда достаточно указать код

if(isset($product['option_data']) && $product['option_data']){
  $ids = [];
  foreach($product['option_data'] as $option){
    $ids[] = $option['option_value_id'];
  }

  if($ids){
    $q = $this->db->query("SELECT product_image_id FROM " . DB_PREFIX . "oct_product_image_by_option WHERE product_id = '" . $product['product_original_id'] . "' AND option_value_id IN(" . implode(',', $ids) . ")");
    if($q->num_rows){
      $i = 0;
      foreach($q->rows as $r){
        $iq = $this->db->query("SELECT * FROM " . DB_PREFIX . "product_image WHERE product_image_id = '" . $r['product_image_id'] . "'");
        if($iq->num_rows){
          $product['images'] = [];
          if(!$i){
            $product['image'] = HTTP_SERVER . 'image/' . $iq->row['image'];
          }else{
            $product['images'][] = HTTP_SERVER . 'image/' . $iq->row['image'];
          }
          $i++;
        }
      }
    }
  }
}

Если надо добавить в фид (например пром) group_id когда у вас товары объединены модулей kjseries достаточно добавить код

$q = $this->db->query("SELECT * FROM " . DB_PREFIX . "kjseries_links WHERE parent_id = '" . $product['product_original_id'] . "'");
if($q->num_rows){
  $product['group_id'] = $product['product_original_id'];
}else{
  $qq = $this->db->query("SELECT parent_id FROM " . DB_PREFIX . "kjseries_links WHERE product_id = '" . $product['product_original_id'] . "'");
  if($qq->num_rows){
    $product['group_id'] = $qq->row['parent_id'];
  }
}

Как видим с помощью этого пункта настроек можно добавить максимальной гибкости прямо из админки UniXML

Где находится и исполняется кастомный код

Код находится в файле system/unixml/rozetka/ExportСustomXml

Где rozetka - это фид. Для каждого фида будет своя папка

В параметрах системы UniXML настройка находится в $data['custom_xml']
×
Информация только для UniXML 7.x
Модуль не работает в рф и работать не будет

Разработчик модуля - Прут Николай.

Работаю с opencart c 2010 года.

Создал успешные модули UniXML Pro, MicrodataPro, Easyphoto, Редиректор 301 и другие.

Авторское право на модуль UniXML и информацию на этом сайте принадлежит Прут Николаю.

Копирование материала или использования нелицензионного модуля запрещено.