Как вывести все дочерние товары в Virtuemart в виде списка с картинками, SKU, ценой и другими опциями без использования настраиваемых полей.

Версия Vrtuemart 2.6.x

Преамбула

Довольно часто у интернет-магазинов возникает потребность использовать дочерние товары. Например, мебельная стенка является товаром с ценой, но при этом состоит из модулей (дочерних товаров), которые можно продавать отдельно. Компонент Virtuemart позволяет реализовать такую возможность через настраиваемые поля. Однако это не всегда удобно. Есть более простой способ вывести дочерние товары со всей атрибутикой и калькуляцией. Для этого потребуется немного подправить ядро Virtuemart. Однако, нужно иметь в виду, что после очередного обновления Virtuemart все изменения пропадут.

Дочерние и родительские товары хранятся в общей таблице virtuemart_products. Для их разделения используется поле product_parent_id, в этом поле для родительских товаров записывается значение 0, для дочерних товаров записывается значение айдишника родительского товара из поля virtuemart_product_id. Это дает возможность получить список дочерних товаров по значению product_parent_id родителя. Ничего сложного.

Постановка задачи

Рассмотрим на примере магазина керамической плитки. Товаром магазина являются разнообразные коллекции-интерьеры, которые как LEGO состоят из различных плиток-элементов. Их мы и будем заводить в таблицу Товары, а в качестве дочерних товаров к каждой коллекции заведем плитку. Такой подход позволит нам с одной стороны объединить товары-коллекции в категории (для ванной, для кухни, для пола, мозаичные панно, под дерево и так далее), а с другой стороны мы можем привязать каждую коллекцию к Производителю (таблица manufacturers). При таком подходе прекрасно работает поиск: первыми выводятся коллекции.

На скриншоте ниже показана страница Категории, на которой выведены все родительские товары.

Списое товаров категории

Но здесь есть один очень важный момент. В объектную переменную этой  страницы попадают и родительские, и дочерние товары. К чему это приводит? При большом количестве дочерних товаров происходит сильная загрузка базы данных запросами для наполнения объекта всеми атрибутами каждого товара. Но эти данные мы совершенно не собираемся использовать на этой странице, здесь нам нужны данные только по родительским товарам. Отключая дочерние товары мы получаем фантастическую оптимизацию по скорости загрузки страницы. Чтобы отключить дочерние товары на этой странице, нужно открыть модель

/administrator/components/com_virtuemart/models/product.php

и в строке 336 заменить код

if ($this->product_parent_id) {
   $where[] = ' p.`product_parent_id` = ' . $this->product_parent_id;
}

 на

if ($isSite) {
	$where[] = ' p.`product_parent_id` = 0';
	} else {
if ($this->product_parent_id) {
	$where[] = ' p.`product_parent_id` = ' . $this->product_parent_id;
	}
}

 


 

На скриншоте показана страница товара. Ниже выведен список дочерних товаров.

Списое дочерних товаров

Итак. Для получения списка дочерних товаров будем использовать функцию (метод) getProductChilds ($product_id), которая находится в классе VirtueMartModelProduct. Стандартно эта функция создает объект, состоящий только из названия дочернего товара и его ID. Мы немного изменим ее, чтобы получить все атрибуты товара, а так же подключим таблицы с картинками: virtuemart_product_medias и virtuemart_medias
Открываем файл /administrator/components/com_virtuemart/models/product.php и находим строку
function getProductChilds ($product_id) Код этой функции:

function getProductChilds ($product_id) { if (empty($product_id)) { return array(); } $db = JFactory::getDBO (); $db->setQuery (' SELECT virtuemart_product_id, product_name FROM `#__virtuemart_products_' . VMLANG . '` JOIN `#__virtuemart_products` as C using (`virtuemart_product_id`) WHERE `product_parent_id` =' . (int)$product_id); return $db->loadObjectList (); }

Заменим этот код на следующий

function getProductChilds ($product_id) { 
if (empty($product_id)) {
return array();
}
$db = JFactory::getDBO ();
$db->setQuery ('SELECT * FROM `#__virtuemart_products` p
LEFT JOIN `#__virtuemart_products_'.VMLANG.'` pl USING (`virtuemart_product_id`)
LEFT JOIN `#__virtuemart_product_medias` pm USING (`virtuemart_product_id`)
LEFT JOIN `#__virtuemart_medias` med USING (`virtuemart_media_id`)
LEFT JOIN `#__virtuemart_product_prices` pp USING (`virtuemart_product_id`)
WHERE `p`.`product_parent_id` = '. (int)$product_id . '
GROUP BY `pm`.`virtuemart_product_id`');
return $db->loadObjectList ();
}

Теперь у нас имеется объект дочерних товаров. Если у вас только одна валюта в магазине и не используются налоги или скидки, то просто передадим полученные данные в файл представления. Открываем файл /components/com_virtuemart/views/productdetails/view.html.php и находим строку parent::display($tpl) Выше этой строки вставляем код:

// Список дочерних товаров
$childs = array(); // Массив дочерних товаров
$childproductprices = array() ; // массив цен для дочернего товара
$childs = $product_model->getProductChilds($virtuemart_product_id); // Получаем список дочерних товаров
$parentproduct = $product_model->getProduct($product_parent_id,TRUE,TRUE,TRUE)->product_name; // Достаём имя родительского товара
foreach($childs as $key=>$value) { // Заполняем цены для каждого товара
$childproductprices[$value->virtuemart_product_id] = $product_model->getProduct($value->virtuemart_product_id,TRUE,TRUE,TRUE,$quantity)->prices;
}
$this->assignRef('childprices',$childproductprices); // Передаем массив цен на каждый дочерний товар
$this->assignRef('childslist',$childs); // Передаем спиок в файл шаблона
$this->assignRef('parentproduct',$parentproduct); // Передаем имя родительского товара
// Конец списка дочерних товаров

И наведем красоту. Товары будем выводить списком в тэгах ul-li . Но можно вывести дивами. Итак, открываем файл шаблона
/components/com_virtuemart/views/productdetails/tmpl/default.php и выбираем место, где нужно вывести список дочерних товаров. Мы выбрали место сразу после вывода описания родительского товара

 <?php
// Product Description
if (!empty($this->product->product_desc)) {
?>
<div class="product-description">
<?php /** @todo Test if content plugins modify the product description */ ?>
<span class="title"><?php echo JText::_('COM_VIRTUEMART_PRODUCT_DESC_TITLE') ?></span>
<?php echo $this->product->product_desc; ?>
</div>
<?php
} // Product Description END

Ниже этого кода вставим цикл вывода на экран списка дочерних товаров:

// Список дочерних товаров
if ( !empty($this->childslist)) {
?><hr /><ul class="subproduct"><?php
foreach($this->childslist as $key=>$value) {
$link = JRoute::_ ('index.php?option=com_virtuemart&view=productdetails&virtuemart_product_id=' . $value->virtuemart_product_id , FALSE);?>
<li style="display:block;clear:both;">
<div style="float:left;width:50%;"><a href="/<?php echo $link; ?>"><img src = "/<?php echo $value->file_url; ?>" /></a></div>
<div style="float:right;width:50%;">
<a href="/<?php echo $link; ?>"><? echo $value->product_name; ?></a>
<p>Артикул: <?php echo $value->product_sku; ?></p>
<p><strong>Цена</strong>: <?php echo $value->product_price; ?>р</p>
<p><a href="/<?php echo $link; ?>">Заказать >></a></p>
</div>
</li>
<?php } ?></ul><?php
}
// Конец списка дочерних товаров

Данный код выводит картинку, ссылку на дочерний товар и его название, цену и артикул. С помощью класса CSS subproduct мы легко создадим дизайн полученного списка:

.subproduct img { padding: 10px; width: 180px; } 
.subproduct li { border-bottom: 6px solid #ccc; margin: 10px !important; }
.subproduct { list-style: none outside none !important; }

Если в магазине используется несколько валют, или используются правила расчета цены с налогами или скидкой, то для вывода нескольких цен будем использовать встроенную систему калькуляции.