VirtueMart – liczba produktów w kategorii

Pracowicie dłubię w szablonach sklepu, który od jakiegoś czasu dla jednego z moich klientów. Ostatnio na jego życzenie pozmieniałam nieco konstrukcje drzewa kategorii. Dziś klient zadzwonił do mnie i z rozpaczą w głosie oznajmił mi, że jego sklep jest pusty. Po chwili okazało się, że pusty nie jest, ale w opisie kategorii pojawia się nieprawdziwa liczba produktów do niej przypisanych. A liczba zero nie nastraja klienta do tego by do takiej kategorii zajrzał. Zaczęłam więc dociekać w czym problem.

Nie musiałam długo szukać. Twórcy VM dopuszczają oczywiście dość rozbudowane drzewka kategorii, ale nie zadbali o to, by skrypt prawidłowo zliczał ilość produktów przy zagnieżdżeniu głębszym niż dwa poziomy. Trochę to niepoważne. Na szczęście dodanie zaledwie trzech linijek w odpowiednim miejscu załatwia sprawę raz na zawsze.

Przygotowanie informacji o kategoriach

Aby w szablonie wyświetlającego listę kategorii (components/com_virtuemart/themes/default/common/categoryChildlist.tpl.php) można było wyświetlić liczbę produktów znajdujących się w każdej kategorii, w skrypcie przygotującym dane sklepu (administrator/components/com_virtuemart/html/shop.browse.php) wywołana jest (w okolicach l:112) metoda ‚get_child_list’ klasy ‚ps_product_category’ (zdefiniowanej w pliku administrator/components/com_virtuemart/classes/ps_product_category.php).

Metoda ta pobiera wszystkie kategorie z danego poziomu (kategorie główne lub podkategorie kategorii bieżącej) wraz z ich parametrami. a następnie wykorzystuje metodę ps_product_category::products_in_category aby dla każdej z nich obliczyć liczbę produktów w niej zawartych.

Algorytm obliczania liczby produktów w kategorii

Dla ułatwienia przytoczę ciało metody ps_product_category::products_in_category:

	function products_in_category( $category_id ) {
		if( PSHOP_SHOW_PRODUCTS_IN_CATEGORY == '1' || vmIsAdminMode() ) {
			$num = ps_product_category::product_count($category_id);
			if( empty($num) && ps_product_category::has_childs( $category_id )) {
				$db = new ps_DB;
				$q = "SELECT category_child_id FROM #__{vm}_category_xref ";
				$q .= "WHERE category_parent_id='$category_id' ";
				$db->query($q);
				while( $db->next_record() ) {
					$num += ps_product_category::product_count($db->f("category_child_id"));
				}
			}
			return " ($num) ";
		}else return ( "" );
	}

Jak widać metoda ta działa w bardzo prosty sposób. Po sprawdzeniu, czy w ogóle w konfiguracji sklepu dopuszczono możliwość wyświetlania ilości produktów, wywołuje metodę ps_product_category::product_count, która pobiera z bazy informację o ilości produktów przypisanych bezpośrednio do wybranej kategorii. Następnie algorytm sprawdza, czy wybrana kategoria nie posiada przypadkiem podkategorii. Jeśli tak, a w dodatku poprzednia operacja nic nie zwróciła zwróciła, wczytywana jest lista kategorii podrzędnych i dla każdej z nich wywoływana jest metoda ps_product_category::product_count.

Widać więc, że jeśli w żadnej z kategorii podrzędnych nie będzie produktów tylko kolejne kategorie podrzędne to opisana metoda zwróci nam informację o braku jakichkolwiek produktów w tej gałęzi drzewa kategorii. Jeśli zaś w kategorii będą jakieś produkty przypisane bezpośrednio do niej, to nie zostanie podana informacja o liczbie produktów w podkategoriach. I tak źle i tak niedobrze.

Poprawka

Na szczęście poprawienie tej ewidentnej niedoróbki jest bardzo proste. Po pierwsze jeśli w opisie kategorii chcemy mieć podaną sumaryczną liczbę produktów z kategorii oraz ze wszystkich jej podkategorii to należy zmienić warunek na następujący:

	if( ps_product_category::has_childs( $category_id )) {

Jeśli zaś chcemy mieć sumaryczną liczbę produktów ze wszystkich poziomów pokategorii jakie tylko są zdefiniowane w tej gałęzi to należy dołożyć rekurencję:

	while( $db->next_record() ) {
		$strCatID = $db->f("category_child_id");
		$intChild = ps_product_category::product_count($strCatID);
		if( ps_product_category::has_childs($strCatID) ) 
			$intChild += ps_product_category::products_in_category($strCatID);
		$num += $intChild;
	}

I już. No w każdym razie ja jestem zadowolona. Przecież obiecywałam, że pójdzie łatwo,prawda?

12 komentarzy do wpisu „VirtueMart – liczba produktów w kategorii”

    • A to dlaczego? Prawie w każdej innej sytuacji unikałabym tego jak ognia. Jednak to jest sklep internetowy. Nikt przy zdrowych zmysłach (czytaj dbający o wygodę nawigacji po sklepie) nie zrobi tam nie wiadomo jakich zagłębień.

  1. Tu się mylisz ;) wszystko zależy od struktury kategorii. Ja np. musiałem wycinać rekurencję z PrestaShop… bo kategorii mam kilka tysięcy. Lepiej chyba zliczać i odświeżać liczniki przy parent do root’a

    • Po pierwsze rekurencja zabija przy głębokim zagnieżdżeniu. Ilość kategorii jest to drugorzędnym czynnikiem. Po drugie nigdy nie zrobiłabym sklepu z taką ilością kategorii na skrypcie Joomla!.

  2. znalazłem BUGa, mianowicie w połączeniu z modułem VirtueMart Full Category List ładnie wyświetla liczbę produktów (jak napisano powyżej)- jednak przy wyświetleniu koszyka nie pobiera klasy ‚ps_product_category’ i wyświetla błąd. Joanno, masz na to może jakąś radę? Pozdrawiam

  3. i znalazłem odpowiedź :)
    wystarczy w module, w pliku default.php nad funkcją z powyższego tutorialu wstawić:

    require_once(CLASSPATH.’ps_product_category.php’);

    koszyk już się wyświetla wraz z listą produktów :) pozdrawiam

  4. No tak. Zapomniałam, że to nie jest oczywiste. To modyfikacja funkcji products_in_category() znajdującej się w lokalizacji:
    administrator/components/com_virtuemart/classes/ps_product_category.php

    Przytoczyłam ciało tej metody a następnie zaproponowałam jak należy ją przekształcić, w szczególności pętle while() :)

  5. Witam Joanno.
    Zrobiłem tak jak pisałaś i dalej działa to tak jak wcześniej :(
    Nie pokazuje ilości produktów w głębszych zagnieżdżeniach.
    Albo ja coś źle robię, albo…

    Wklej całą zawartość tej funkcji po poprawkach.

  6. Znalazłem rozwiązanie na innej stronie, które u mnie działa:

    function prod_in_cat_recursive( $category_id ) {
    $num=0;
    if (ps_product_category::has_childs( $category_id )) {
    $db = new ps_DB;
    $q = „SELECT category_child_id FROM #__{vm}_category_xref „;
    $q .= „WHERE category_parent_id=’$category_id’ „;
    $db->query($q);
    while( $db->next_record() ) {
    $num += ps_product_category::prod_in_cat_recursive($db->f(„category_child_id”));
    }
    }
    $num += ps_product_category::product_count($category_id);
    return $num;
    }

    function products_in_category( $category_id ) {
    if( PSHOP_SHOW_PRODUCTS_IN_CATEGORY == ‚1’ ) {
    $num = ps_product_category::prod_in_cat_recursive( $category_id );
    return ” ($num) „;
    }
    else
    return „”;
    }

  7. Pomijając sam przedstawiony problem, skoro i tak modyfikowany jest kod samego sklepu,
    efektywniej byłoby (jeżeli istnieje taka możliwość) dodać do tabeli kategorii kolumnę przechowującą liczbę znajdujących się w niej produktów, a przytoczoną funkcję uruchamiać np. raz dziennie (w godzinach kiedy liczba odwiedzin na stronie jest względnie niewielka) dla wszystkich kategorii?
    Wtedy pobranie liczby produktów w danej kategorii sprowadziłoby się do jednego zapytania, zamiast kilkunastu/kilkudziesięciu, lub więcej (zależnie od liczby kategorii)

    • No wiesz.Zawsze można tak zmienić organizację danych w tabeli, żeby bez dodatkowego pola odbierać dane jednym zapytaniem :) Wtedy i dodatkowy skrypt obliczający ilość produktów i wstawiający dane do tabeli nie będzie potrzebny.

Dodaj komentarz

%d bloggers like this: