Hoe kan ik alle producten tellen binnen categorieen die ook subcategorieen hebben
Dit telt alleen de producten binnen de toplevel categorie. Terwijl er ook producten binnen subcategorieen kunnen staan, deze worden niet meegeteld.
Mijn structuur is nu als volgt:
categorie:
id - categorie id
parent_id - id van parent categorie (topcategory is altijd 1), dit is hetzelfde als het id van zijn parent categorie.
artikel(product)
id - id van het product
catid - id van de categorie waar hij onder hangt (zelfde als id van de categorie)
Dus als mijn data er zo uitziet:
categorie - wastafels - 2 producten
subcategorie - wastafelssub - 5 producten
subsubcategorie - wastafelssubsub - 2 producten
Met mijn huidige code krijg ik naast Wastafels een 2, maar ik zou willen dat hij alles optelt, dus dat er 9 komt te staan.
Dit is nu mijn code:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//Haal alle toplevel categorieen op
$pcat = "SELECT * FROM `snm_categories` WHERE published = 1 and level = 1 and id NOT IN (1) order by rgt ";
$pcatcon = $conn->query($pcat);
$catids = '';
while ($pcat = $pcatcon->fetch_assoc()){
$catids[] = $pcat['id'];
// Alle producten binnen bovenstaande categorieen
$aantal = "SELECT DISTINCT id FROM `snm_content` WHERE catid = ".$pcat['id']." and state = 1";
$aantalcon = $conn->query($aantal);
$aantal = $aantalcon->fetch_assoc();
if(is_null($aantal)){
$totaal = '';
}else{
$totaal = $aantalcon->num_rows;
}
if($totaal != 0){
$amount = '('.$totaal.')';
}else{
$amount = $totaal;
}
$productcatoverzicht .= '
<li class="cat-item"><a href="'.$pcat['alias'].'">'.$pcat['title'].'</a><span class="count">'.$amount.'</span></li>';
}
echo productcatoverzicht;
$pcat = "SELECT * FROM `snm_categories` WHERE published = 1 and level = 1 and id NOT IN (1) order by rgt ";
$pcatcon = $conn->query($pcat);
$catids = '';
while ($pcat = $pcatcon->fetch_assoc()){
$catids[] = $pcat['id'];
// Alle producten binnen bovenstaande categorieen
$aantal = "SELECT DISTINCT id FROM `snm_content` WHERE catid = ".$pcat['id']." and state = 1";
$aantalcon = $conn->query($aantal);
$aantal = $aantalcon->fetch_assoc();
if(is_null($aantal)){
$totaal = '';
}else{
$totaal = $aantalcon->num_rows;
}
if($totaal != 0){
$amount = '('.$totaal.')';
}else{
$amount = $totaal;
}
$productcatoverzicht .= '
<li class="cat-item"><a href="'.$pcat['alias'].'">'.$pcat['title'].'</a><span class="count">'.$amount.'</span></li>';
}
echo productcatoverzicht;
Moet ik dit net als mijn menu code recursief oplossen? Mijn menu code ziet er zo uit:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<?PHP
$alias = $_GET['alias'];
//Haal alle categorieen en check gelijk of de desbetreffende categorie artikelen onder zich heeft hangen.
$menu = "
SELECT cat.id as cat_id, cat.level, cat.parent_id, cat.title as cat_title, cat.alias as cat_alias, cat.published, cat.rgt, cnt.state, cnt.id as content_id, cnt.catid, cnt.title as content_title, cnt.alias as content_alias
FROM snm_categories cat
LEFT JOIN snm_content cnt
ON cnt.catid = cat.id
WHERE cat.id NOT IN (1, 2, 3, 4, 5, 7)
AND cat.published = 1
GROUP BY cat.id
ORDER BY cat.rgt ASC";
$menuconn = $conn->query($menu);
// Maak een nieuwe array om te vullen met onderstaand resultaat
$menuData = array(
'items' => array(),
'parents' => array()
);
// Maak een nieuwe array met simpelweg items en parents, welke gekoppeld zitten aan cat_id/parent_id
while($menu = $menuconn->fetch_assoc())
{
$menuData['items'][$menu['cat_id']] = $menu;
$menuData['parents'][$menu['parent_id']][] = $menu['cat_id'];
}
// Functie om menu te maken, $parentId is 1 (de categorieen die geen parent hebben)
function buildMenu($parentId, $menuData)
{
$html = '';
if (isset($menuData['parents'][$parentId]))
{
//Als parent_id gelijk is aan 1 gooi een li eromheen (want het is geen subcat) anders een ul
if($parentId == '1'){
$html = '<li>';
}else{
$html = '<ul class="sub-menu">';
}
foreach ($menuData['parents'][$parentId] as $itemId)
{
$html .= '<li class="menu-item"><a href="'.$menuData['items'][$itemId]['cat_alias'].'">'.$menuData['items'][$itemId]['cat_title'].'</a>';
// Voer deze functie uit binnen de functie loop (recursief)
$html .= buildMenu($itemId, $menuData);
$html .= '</li>';
}
//Als parent_id gelijk is aan 1 gooi een li eromheen (want het is geen subcat)
if($parentId == '1'){
$html .= '</li>';
}else{
$html .= '</ul>';
}
}
return $html;
}
// Echo het resultaat van de functie en geef 1 mee als parent_id
echo buildMenu(1, $menuData);
?>
$alias = $_GET['alias'];
//Haal alle categorieen en check gelijk of de desbetreffende categorie artikelen onder zich heeft hangen.
$menu = "
SELECT cat.id as cat_id, cat.level, cat.parent_id, cat.title as cat_title, cat.alias as cat_alias, cat.published, cat.rgt, cnt.state, cnt.id as content_id, cnt.catid, cnt.title as content_title, cnt.alias as content_alias
FROM snm_categories cat
LEFT JOIN snm_content cnt
ON cnt.catid = cat.id
WHERE cat.id NOT IN (1, 2, 3, 4, 5, 7)
AND cat.published = 1
GROUP BY cat.id
ORDER BY cat.rgt ASC";
$menuconn = $conn->query($menu);
// Maak een nieuwe array om te vullen met onderstaand resultaat
$menuData = array(
'items' => array(),
'parents' => array()
);
// Maak een nieuwe array met simpelweg items en parents, welke gekoppeld zitten aan cat_id/parent_id
while($menu = $menuconn->fetch_assoc())
{
$menuData['items'][$menu['cat_id']] = $menu;
$menuData['parents'][$menu['parent_id']][] = $menu['cat_id'];
}
// Functie om menu te maken, $parentId is 1 (de categorieen die geen parent hebben)
function buildMenu($parentId, $menuData)
{
$html = '';
if (isset($menuData['parents'][$parentId]))
{
//Als parent_id gelijk is aan 1 gooi een li eromheen (want het is geen subcat) anders een ul
if($parentId == '1'){
$html = '<li>';
}else{
$html = '<ul class="sub-menu">';
}
foreach ($menuData['parents'][$parentId] as $itemId)
{
$html .= '<li class="menu-item"><a href="'.$menuData['items'][$itemId]['cat_alias'].'">'.$menuData['items'][$itemId]['cat_title'].'</a>';
// Voer deze functie uit binnen de functie loop (recursief)
$html .= buildMenu($itemId, $menuData);
$html .= '</li>';
}
//Als parent_id gelijk is aan 1 gooi een li eromheen (want het is geen subcat)
if($parentId == '1'){
$html .= '</li>';
}else{
$html .= '</ul>';
}
}
return $html;
}
// Echo het resultaat van de functie en geef 1 mee als parent_id
echo buildMenu(1, $menuData);
?>
Hoe kan ik dit het beste oplossen?
Gewijzigd op 09/04/2018 13:41:53 door Ben van Velzen
Code (php)
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
SELECT cat.id as cat_id, cat.level, cat.parent_id, cat.title as cat_title, cat.alias as cat_alias, cat.published, cat.rgt, cnt.state, cnt.id as content_id, cnt.catid, cnt.title as content_title, cnt.alias as content_alias
FROM snm_categories cat
LEFT JOIN snm_content cnt
ON cnt.catid = cat.id
WHERE cat.id NOT IN (1, 2, 3, 4, 5, 7)
AND cat.published = 1
GROUP BY cat.id
ORDER BY cat.rgt ASC";
FROM snm_categories cat
LEFT JOIN snm_content cnt
ON cnt.catid = cat.id
WHERE cat.id NOT IN (1, 2, 3, 4, 5, 7)
AND cat.published = 1
GROUP BY cat.id
ORDER BY cat.rgt ASC";
wat doet die group by daar?
Je gebruikt nergens een aggregatie functie (zoals sum(), count()) dus waarom dan group by?
En als je dat al toepast: dan staan achter group-by alle kolommen die niet een aggregatie functie. Dus gewoon alle kolommen in jouw geval, dus netto zou dat niets doen.
In feite is het zaak dat je een optelsom van de "children" maakt.
Nu heb je e.e.a. als volgt gestructureerd:
Code (php)
1
2
3
4
5
6
7
2
3
4
5
6
7
<?php
while($menu = $menuconn->fetch_assoc())
{
$menuData['items'][$menu['cat_id']] = $menu;
$menuData['parents'][$menu['parent_id']][] = $menu['cat_id'];
}
?>
while($menu = $menuconn->fetch_assoc())
{
$menuData['items'][$menu['cat_id']] = $menu;
$menuData['parents'][$menu['parent_id']][] = $menu['cat_id'];
}
?>
(waarbij "parents" eigenlijk "children" zou moeten zijn?)
Maar je zou het ook als volgt kunnen doen:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
// @todo maak hier een onzichtbaar root-element, in wezen initialiseer je $menuItems hier
$menuItems[0] = array(
'data' => array(/* zet hier bijvoorbeeld een label in */),
'parent' => false,
'children' => array(),
);
// haal hier *alle* items in de goede volgorde op, inclusief root-elementen
while($menu = $menuconn->fetch_assoc())
{
$parentId = $menu['parent_id'] === NULL ? 0 : $menu['parent_id'];
$menuItems[$menu['cat_id']] = array(
'data' => $menu,
'parent' => $parentId,
'children' => array(),
);
// voeg item toe als child van parent
$menuItems[$parentId]['children'][] = $menu['cat_id'];
}
?>
// @todo maak hier een onzichtbaar root-element, in wezen initialiseer je $menuItems hier
$menuItems[0] = array(
'data' => array(/* zet hier bijvoorbeeld een label in */),
'parent' => false,
'children' => array(),
);
// haal hier *alle* items in de goede volgorde op, inclusief root-elementen
while($menu = $menuconn->fetch_assoc())
{
$parentId = $menu['parent_id'] === NULL ? 0 : $menu['parent_id'];
$menuItems[$menu['cat_id']] = array(
'data' => $menu,
'parent' => $parentId,
'children' => array(),
);
// voeg item toe als child van parent
$menuItems[$parentId]['children'][] = $menu['cat_id'];
}
?>
Hierbij helpt het als je dus in de query de root-elementen toevoegt, en hierboven nog een soort van "onzichtbare" root zet waaraan alle root-elementen verbonden zijn. Op die manier krijg je een echte boom (met één hoofd-root) waar je makkelijk doorheen kunt lopen.
Gewijzigd op 09/04/2018 16:49:39 door Thomas van den Heuvel
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function getProdsInCat($cat, $conn){
global $prods;
// Tell alle producten binnen de doorgegeven categorie
$countproducts = "SELECT COUNT(`id`) FROM `snm_content` WHERE `catid` = '".$cat."'";
$countproductscon = $conn->query($countproducts);
$countproducts = $countproductscon->fetch_assoc();
// Haal alle categorieen op waar parent_id gelijk is aan het meegestuurde id
$countcats = "SELECT * FROM snm_categories WHERE parent_id = '".$cat."'";
$countcatscon = $conn->query($countcats);
while($countcats = $countcatscon->fetch_assoc()){
getProdsInCat($countcats['id'],$conn);
}
}
// Loop alle categorieen en stop de functie in de loop zodat deze alle ids doorgeeft aan de functie
$allcats = "SELECT * FROM snm_categories WHERE published = 1";
$allcatsconn = $conn->query($allcats);
while($allcats = $allcatsconn->fetch_assoc()){
$allprods = getProdsInCat($allcats['id'], $conn);
}
global $prods;
// Tell alle producten binnen de doorgegeven categorie
$countproducts = "SELECT COUNT(`id`) FROM `snm_content` WHERE `catid` = '".$cat."'";
$countproductscon = $conn->query($countproducts);
$countproducts = $countproductscon->fetch_assoc();
// Haal alle categorieen op waar parent_id gelijk is aan het meegestuurde id
$countcats = "SELECT * FROM snm_categories WHERE parent_id = '".$cat."'";
$countcatscon = $conn->query($countcats);
while($countcats = $countcatscon->fetch_assoc()){
getProdsInCat($countcats['id'],$conn);
}
}
// Loop alle categorieen en stop de functie in de loop zodat deze alle ids doorgeeft aan de functie
$allcats = "SELECT * FROM snm_categories WHERE published = 1";
$allcatsconn = $conn->query($allcats);
while($allcats = $allcatsconn->fetch_assoc()){
$allprods = getProdsInCat($allcats['id'], $conn);
}
Eerst bouw je een datastructuur in de vorm van een boom, en vervolgens laat je op deze boom recursieve functies los. Op het moment dat je voldoende informatie in deze datastructuur hebt zitten kun je daarmee de dataverrijking uitvoeren (bijvoorbeeld het tellen van het totaal aantal children van een categorie) zonder verdere tussenkomt van een database.
Thomas van den Heuvel op 09/04/2018 17:45:36:
Het is hier weer een kwestie van problemen opsplitsen denk ik.
Eens. En het eerste probleem is dan: wáárom moet er überhaupt een telling in een menu worden getoond? ;-)
Ward van der Put op 09/04/2018 17:53:29:
Eens. En het eerste probleem is dan: wáárom moet er überhaupt een telling in een menu worden getoond? ;-)
Thomas van den Heuvel op 09/04/2018 17:45:36:
Het is hier weer een kwestie van problemen opsplitsen denk ik.
Eens. En het eerste probleem is dan: wáárom moet er überhaupt een telling in een menu worden getoond? ;-)
Waarom niet?
Snelle Jaap op 11/04/2018 15:23:03:
Waarom niet?
Ah, nu komen we ergens. Door dit soort inhoudelijke vragen is het probleem snel opgelost :-)
- vanuit je "root" genereer je de sub-menus
- vanuit de sub-menus geef je naast de resulterende HTML ook het aantal items terug (en tel je die op bij de items in het menu zelf - en die geef je weer terug, enz).