3-dimensionales Array aus Datenbank-Ergebnis erstellen

Masaku

Mitglied
Hi, folgende Situation: In meiner Tabelle befinden sich Projekte unterschiedlicher Kategorien und Subkategorien. Aus dieser lese ich außerdem noch den Namen und die ID aus.
Aus den Ergebnisdaten möchte ich ´ne Navigation bauen. Diese Plane ich in der Form $menu[<kategorie>][<subkategorie>][<id> => <name>].
Gleich die erste Frage: Ist das so möglich, oder hab ich da was falsch verstanden? :X

Ich bin grad dabei, das zu schreiben und arbeitete bisher mit mehreren foreach-Schleifen. Das allerdings schien mir zu aufwändig und ineffizient, deshalb frage ich euch, ob ihr ´ne gute Möglichkeit kennt, sowas zu erstellen.

Bisher sehen meine Variablen so aus:
PHP:
//SQL-Ergebnis - per PDO::fetchAll()
$data = array(
[0] => array(
    'id' => <id>,
    'name' => <name>,
    'kategorie' => <kategorie>,
    'subkategorie' =><subkategorie>
)

//Stand bisher
//(erste Variante)
foreach($data as $el){
	if(!in_array($el['kategorie'], $menu)) {
		$menu[] = $el['kategorie'];
		$menu[$el['kategorie']] = array();
	}
}
foreach($data as $el){
// checke ob  $el['subkatgorie'] in $menu vorhanden ist
// wenn nicht: $menu['katgorie'][] = el['subkatgorie']
}

Ziel ist dieses oben beschrieben 3-dimenionale Array, bei dem ich nur noch per foreach-Schleifen <ul>-<li>-Strukturen bauen kann.
 
Zuletzt bearbeitet:
Ich blick es nicht.

Vielleicht kannst du auch direkt die Navigation aufbauen, statt das ganze noch in ein großes Array zu packen.
Kannst du deine Tabellenstruktur mit ein paar Beispielzeilen auch mal liefern?
Insgesamt scheint deine Idee schon in die richtige Richtung zu gehen.
 
Hi,

lies die Daten nach Kategorie und Subkategorie sortiert aus der Datenbank und programmier in einer einfachen while-Schleife einen 2-stufigen Gruppenwechsel.

LG
 
Vielleicht kannst du auch direkt die Navigation aufbauen, statt das ganze noch in ein großes Array zu packen.
Nein. So habe ich das vorher gemacht (is grad ´n Re-Roll) und da hatte ich 3 DB-Queries. Aber ich will nich unnötig viele Datenbank-Abfragen abschicken, weil mir das nacher zu viel Performance kostet.
Zudem sind die Projekte nich sortiert. Deshalb ist ein On-The-Fly-Bau des Menüs nich möglich.
Hier mal ein paar Bsp. der Projekte-Tabelle (id und name lass ich außen vor, die sind dafür nich interessant:
Code:
|---------------------------------------|
| id | name | kategorie | subkategorie  |
|---------------------------------------|
|    |      | grafik    | avatare       |
|    |      | artwork   | coloration    |
|    |      | grafik    | logos         |
|    |      | grafik    | signaturen    |
|    |      | artwork   | speedpainting |
|    |      | web       | forum         |
|    |      | grafik    | wallpaper     |
|    |      | grafik    | logos         |
|    |      | web       | cms           |
|    |      | artwork   | zeichnung     |
|---------------------------------------|

Nacher soll daraus ´ne Nested List entstehen in der Form:
  • kategorie
    • subkategorie
      • projekt
      • projekt
    • subkategorie
      • projekt
    • subkategorie
      • projekt
  • kategorie
    • subkategorie
      • projekt
      • projekt
      • projekt



Oh, neue Antwort während des Schreibens.
Was meinst du mit 2-stufigen Gruppenwechsel?
 
Klassischer Gruppenwechsel:
PHP:
$kategorie = "";
foreach ($data as $a)
{
  if ($data["kategorie"] != $kategorie)
  {
    $kategorie = $data["kategorie"];
    echo "Kategorie:" . $kategorie;
  }
  echo "Inhalt";
}

So wird der Inhalt ständig ausgegeben und zwischendurch eine "Kategorien-Überschrift".

Das kann man in beliebige Tiefe machen:
http://imeneo.unibw-hamburg.de/lehre/ab-wi96/gr_w2.html

Für dein Fall würd vielleicht sogar eine weiterer if-Block reichen denke ich:
PHP:
$sub = $kategorie = "";
foreach ($data as $a)
{
  if ($data["kategorie"] != $kategorie)
  {
    $kategorie = $data["kategorie"];
    echo "Kategorie:" . $kategorie;
  }
  if ($data["subkategorie"] != $sub)
  {
    $sub = $data["subkategorie"];
    echo "Subkategorie:" . $sub;
  }
  echo "Inhalt";
}
 
Zuletzt bearbeitet:
Das funktioniert in meinem Fall nich. Ich will bei der Ausgabe die Subkategorien in Beziehung mit den Kategorien setzen. Deshalb muss ich die auch im Code miteinander verbinden. Nach deinem Beispiel findet eine solche Verbindung nicht statt.
Ich habe aber versucht, dein Skript etwas umzuschreiben. Statt != hab ich !in_array() geschrieben. Aber auch so funktionierte das nicht, weil ich als Haystack das (innere) Array der Kategorie übergeben will (Funktionsaufruf in der Form !in_array($el['subkategorie'], $cats[$el['kategorie']]). Aber so ein Parameter ist unzulässig. Deshalb würde ich wieder per foreach durchgehen müssen.

Dieser Weg ist jetz komplett und liefert genau das von mir gewünschte Ergebnis:
PHP:
$menu = array();
//speichern von $menu[<kategorien>]
foreach($data as $el){
	if(!in_array($el['kategorie'], $menu)) {
		$menu[$el['kategorie']] = array();
	}
}
//pro $menu[<kategorie>] wird $menu[<kategorie>][<subkategorie>] gespeichert
foreach($data as $el){
	foreach($menu as $categ => $toFill)
		if($el['kategorie'] == $categ && !in_array($el['subkategorie'], $toFill))
			$menu[$categ][$el['subkategorie']] = array();
}
//pro $menu[<kategorie>][<subkategorie>] wird $menu[<kategorie>][<subkategorie>][<id> => <name>] geschrieben
foreach($data as $el){
	foreach($menu as $catIndex => $catArray)
		foreach($catArray as $subcatIndex => $subcatArray)
			if($el['kategorie'] == $catIndex 
				&& $el['subkategorie'] == $subcatIndex
				&& !array_key_exists($el['id'], $subcatArray)
			)
			{
				$menu[$catIndex][$subcatIndex][$el['id']] = $el['name'];
			}
}
Ich kann nun per Aufruf von $menu['grafik']['logo'][xx] ein Projekt aufrufen - genauso, wie ich es wollte (ob nun die ID oder der Name der Index is, is egal).
Sorgen machen mir aber die vielen (verschachtelten) foreach-Schleifen. Ich bin mir ziemlich sicher, dass die an der Performance zerren. Und als ich mir die Struktur angeguck hab, is mir ´ne ganz simple Sache eingefallen.
PHP:
$menu = array();
foreach($data as $el){
	$menu[$el['kategorie']][$el['subkategorie']][$el['id']] = $el['name'];
}
Vorteile: Es entfallen alle foreach-Schleifen, es entfallen die if-Abfragen und der Code is schlanger und leichter durchschaubar.
Aber ich bin mir noch unsicher. Das sieht zu einfach aus. :S
Wie sieht´s performance-technisch aus? Immerhin werden die einzelnen Spalten mehrmals überschrieben. Sind Überschreibungen belastender als Schleifendurchläufe und If-Abfragen? Wenn ja, bleib ich bei der komplizierten Variante.
 
Zuletzt bearbeitet:
Sind Überschreibungen belastender als Schleifendurchläufe und If-Abfragen?
Man kann Äpfel noch immer nicht mit Birnen vergleichen. ;)

Gegenüber sechs foreach-Schleifen, wovon drei ineinander verschachtelt sind ist das jedoch nahezu ein Quantensprung.
Da ist es vollkommen irrelevant ob diese Art, ein Array zu füllen, langsamer als eine andere ist.
 
Das könnte man versuchen zu beweisen und ich habs einfach mal probiert:
PHP:
/**
 * Angenommen es seien die Schlüssel von $data numerisch und $data nie leer:
 * c beschreibt die jeweilige Laufzeit der Zeile
 */

//speichern von $menu[<kategorien>]
$n = count($data);										#c1			1
for ($i = 0; $i < $n; $i++)
{
    if(!in_array($data[$i]['kategorie'], $menu)) {		#c2			n
        $menu[$el['kategorie']] = array();				#c3			n
    }
}

//pro $menu[<kategorie>] wird $menu[<kategorie>][<subkategorie>] gespeichert
$m = count($menu);										#c1			1
for ($i = 0; $i < $n; $i++)
{
	$key = array_keys($menu);							#c4			n
	for ($j = 0; $j < $m; $j++)
	{
        if(
        	$data[$i]['kategorie'] == $key[$j] &&			#c5		n*m
        	!in_array($data[$i]['subkategorie'], $menu[$j])	#c2		n*m
        )
        {
			$menu[$key[$j]][$data[$i]['subkategorie']] = array();	#c6	n*m
        }
	}
}


/**
 * Bis hier reicht es für einen Worst-Case Vergleich:
 * 
 * Laufzeit: T(n,m) := 2*c1 + n*(c2+c3+c4+m(c5+c2+c6))
 * 
 * Da $menu aus $data generiert wird ist m kleiner gleich n (n >= m)
 * =>  T(n,m) <= 2*c1 + n*(c2+c3+c4) + n^2(c5+c2+c6)
 * 
 * Nun die zweite Variante (nicht auf die Form achten):
 */

$n = count($data);						#c1			1
for ($i = 0; $i < $n; $i++)
{
    $menu[
    	$el['kategorie']
    ][	$el['subkategorie']
    ][	$el['id']]
    	= $el['name']
    ;									#ungefähr gleich c6		n
}

/**
 * Hier ist für Worst-Case T(n) := c1 + n*c6
 * 
 * Nun ist gefordert T(n) < T(n,m), was durch transitivität äquivalent ist zu
 * T(n) < 2*c1 + n*(c2+c3+c4) + n^2(c5+c2)
 * Annahme: $data nie leer => n > 0
 * 
 * <=>	c1 + n*c6	<	2*c1 + n*(c2+c3+c4) + n^2(c5+c2)		| -c1
 * <=>		 n*c6	<	c1 + n*(c2+c3+c4) + n^2(c5+c2+c6)		| :n
 * <=>		c6		<	c1/n + c2+c3+c4 + n*(c5+c2+c6)			| - c6
 * <=>		0		<	c1/n + c2+c3+c4 + n*(c5+c2) + (n-1)*c6
 * q.e.d.
 */

Hab sowas noch nie gemacht also wenn ein Fehler ist bescheid sagen, lern ich auch noch was ;)
Wenn du es noch etwas schneller haben willst ersetzt das foreach durch eine for (http://phpperformance.de/zaehlschleife-so-oft-durchfuehren-wie-ein-array-eintraege-hat/), wobei das nicht eindeutig genug ist aber man sieht in jedem Test immer wieder, dass for schneller als foreach.
 
Zuletzt bearbeitet:
Hi,

Das funktioniert in meinem Fall nich. Ich will bei der Ausgabe die Subkategorien in Beziehung mit den Kategorien setzen. Deshalb muss ich die auch im Code miteinander verbinden. Nach deinem Beispiel findet eine solche Verbindung nicht statt.

Das war ja auch nur ein simples Beispiel, wie so eine Steuerung aussieht, kein Code, den Du so übernehmen kannst. Die Beziehung von Kategorien und Subkategorien ist in Deiner Tabelle ja gegeben, Du musst nur die Abfragen richtig setzen. Du musst das auch überhaupt nicht erst in ein Array packen, Du kannst die verschachtelte Liste mit der genannten Technik beim Durchlaufen des Datenbankergebnisses aufbauen.

LG
 
Hilfe - DANKE

Die Datenbank ist wohl so aufgebaut,

Kategorie - Wert1,Subkategorie - Wert1,Inhalt - Wert1
Kategorie - Wert1,Subkategorie - Wert1,Inhalt - Wert2
Kategorie - Wert1,Subkategorie - Wert2,Inhalt - Wert1
Kategorie - Wert1,Subkategorie - Wert2,Inhalt - Wert2
Kategorie - Wert2,Subkategorie - Wert1,Inhalt - Wert1
Kategorie - Wert2,Subkategorie - Wert1,Inhalt - Wert2
Kategorie - Wert2,Subkategorie - Wert2,Inhalt - Wert1
Kategorie - Wert2,Subkategorie - Wert2,Inhalt - Wert2
.
.
.
alle Datenbankinhalte in einem array, aber das Ergebnis mit dem foreach leider so:

$sub = $kategorie = "";
foreach ($data as $a)
{
if ($data["kategorie"] != $kategorie)
{
$kategorie = $data["kategorie"];
echo "Kategorie:" . $kategorie;
}
if ($data["subkategorie"] != $sub)
{
$sub = $data["subkategorie"];
echo "Subkategorie:" . $sub;
}
echo "Inhalt";
}

Kategorie - Wert1
Subkategorie - Wert1
Inhalt - Wert1
Kategorie Werte1
Subkategorie - Wert1
Inhalt - Wert2

Sollte aber so lauten:

Kategorie - Wert1
Subkategorie - Wert1
Inhalt - Wert1
Inhalt - Wert2
.
.
Inhalt - Wert3
Kategorie - Wert1
Subkategorie - Wert2
Inhalt - Wert1
Inhalt - Wert2
.
.
Inhalt - Wert3

usw. Wer kann mir weiterhelfen. Würd' mich freune.

Danke
 
Zurück