Problem mit Ausgabe in rekursiver Methode

Alles, was PHP betrifft, kann hier besprochen werden.

Problem mit Ausgabe in rekursiver Methode

Postby Knight1 » 21. November 2009 23:32

Hi AF-Com,
ich habe eine rekursive Methode zur Darstellung von Kategorien in einer Liste mit unbekannter Tiefe.
Die Auflistung habe ich schon hinbekommen.
Ich hänge im moment bei der Erstellung der Ausgabe.
Hier mal mein Code den ich bis jetzt habe:
Code: Select all
PUBLIC function references_categories ($values)
 {
  if ($values[0] === TRUE)
  {
  $query = $this->mysqli->query("SELECT `id` FROM `".MYSQL_TABLE_REFERENCES_CATEGORIES."`");
  RETURN $query->num_rows;
  }
 
  if ((($values[1] === TRUE) || ($values[2] === TRUE)) && (is_numeric($values[3])))
  {
  $run = 1;
  #$query = $this->mysqli->query("SELECT * FROM `".MYSQL_TABLE_REFERENCES_CATEGORIES."` WHERE `parent_id` = '{$values[3]}' ORDER BY `category_name_{$_SESSION['lang']}` ASC LIMIT ".intval($_GET['start']).",{$this->config['references_per_page']}");
  $query = $this->mysqli->query("SELECT * FROM `".MYSQL_TABLE_REFERENCES_CATEGORIES."` WHERE `parent_id` = '{$values[3]}' ORDER BY `parent_id` ASC, `category_name_{$_SESSION['lang']}` ASC");
  $rows = $query->num_rows;
   while ($result = $query->fetch_array(MYSQL_ASSOC))
   {
    if ($values[1] === TRUE)
    {
?>
 getElementById("overview_categories_cell_<? echo $result['id']; ?>").style.width = main_document_width-(side_spacer_cells+border_cells);
 getElementById("overview_categories_table_<? echo $result['id']; ?>").style.width = (main_document_width-(side_spacer_cells+border_cells))-2;
<?
    }
    elseif ($values[2] === TRUE)
    {
     if ($result['parent_id'] == 0)
     {
?>
<li><? echo $result['category_name_de']; ?></li>
<?
     }
     elseif ($result['parent_id'] != 0)
     {
      if (($values[4] != 0))
      {
?>
<ul>
<?
      }
?>
<li>--<? echo $result['category_name_de']; ?></li>
<?
      if (($values[4] != 0) && ($run == $rows))
      {
?>
</ul>
<?
      }
     }
    }
   $run++;
   $this->references_categories(array($values[0],$values[1],$values[2],$result['id'],$values[4]++));
   }
  }
 }


Der Aufruf erfolgt folgendermaßen:
Code: Select all
$overviews->references_categories(array(FALSE,FALSE,TRUE,0,0));


Jetzt hänge ich daran dass ul's der Kindkategorien nicht an der richtigen Stelle im HTML-Code eingebaut bekomme.
Hat vielleicht jemand einen Tipp für mich wie ich die if-Bedingungen aufbauen muss um das zu realisieren?

Danke schon im vorraus.


Kai aka Knight1
Image
User avatar
Knight1
 
Posts: 310
Joined: 18. October 2003 10:03
Location: Trier
Operating System: Windows 7 Ultimate x64

Re: Problem mit Ausgabe in rekursiver Methode

Postby Nobbie » 22. November 2009 12:54

Grausamer Spaghetti-Code.

Vielleicht deklarierst Du erst einmal die Parameter für diese Funktion "vernünftig", d.h.:

a) nicht als Array, sondern wirklich fünf Parameter angeben
b) nicht als nichtssagende Namen $values[0], sondern mit einem sprechenden Namen, der den jeweiligen Sinn des einzelnen Parameters wiederspiegelt

Sodann erklärst Du ein wenig den Code, insbesondere die Bedeutung der Parameter (wobei $values[0] und $values[1] irgendwie nie benötigt werden, sie werden ungeprüft durchgereicht.

Und dann überarbeitest Du den Code dahingehend, dass sinnlose doppel-if-Abfragen herausfliegen, Beispiel:

Code: Select all
     if ($result['parent_id'] == 0)
     {
?>
<li><? echo $result['category_name_de']; ?></li>
<?
     }
     elseif ($result['parent_id'] != 0)
     {


Dieser elseif ist sinnfrei, da reicht ein simpler else-Fall. Außerdem ist das ausgelagerte HTML-Coding potthäßlich, vor allem wenn dann auch noch dort wieder PHP eingebettet wird. Viel viel viel besser sieht das gleiche Coding so aus:

Code: Select all
     if ($result['parent_id'] == 0)
     {
          echo '<li>'.$result['category_name_de'].'</li>';
     }
     else
     {


Und last not least kommentierst Du das ganze Geschehen noch ein wenig, denn so blickt da keiner durch und kann auch keiner helfen.
Nobbie
 
Posts: 13183
Joined: 09. March 2008 13:04

Re: Problem mit Ausgabe in rekursiver Methode

Postby Nobbie » 22. November 2009 13:23

Ich habe den Code mal überarbeitet (aber die Variablen musst Du noch selbst umbenennen) und da die <ul> hingesetzt, wo sie (soweit ich das Coding verstehe) hinkommen sollten:

Code: Select all
PUBLIC function references_categories ($values)
{
   if ($values[0] === TRUE)
   {
      $query = $this->mysqli->query("SELECT `id` FROM `".MYSQL_TABLE_REFERENCES_CATEGORIES."`");
      RETURN $query->num_rows;
   }

   if ((($values[1] === TRUE) || ($values[2] === TRUE)) && (is_numeric($values[3])))
   {
      $query = $this->mysqli->query("SELECT * FROM `".MYSQL_TABLE_REFERENCES_CATEGORIES."` WHERE `parent_id` = '{$values[3]}' ORDER BY `parent_id` ASC, `category_name_{$_SESSION['lang']}` ASC");

      if (($values[3] != 0) && ($values[4] != 0))
         echo '<ul>';

      while ($result = $query->fetch_array(MYSQL_ASSOC))
      {
         if ($values[1] === TRUE)
         {
            echo 'getElementById("overview_categories_cell_'.$result['id'].'").style.width = main_document_width-(side_spacer_cells+border_cells)';
            echo 'getElementById("overview_categories_table_'.$result['id'].'").style.width = (main_document_width-(side_spacer_cells+border_cells))-2';
         }
         elseif ($values[2] === TRUE)
         {
            if ($values[3] == 0)
            {
               echo '<li>'.$result['category_name_de']'.'</li>';
            }
            else
            {
               echo '<li>--'.$result['category_name_de'].'</li>';
            }
         }
         $this->references_categories(array($values[0],$values[1],$values[2],$result['id'],$values[4]++));
      }

      if (($values[3] != 0) && ($values[4] != 0))
         echo '</ul>';
   }
}


Ich habe das ganze nicht durch PHP geschickt, evtl. Parse-Errors mußt Du selbst korrigieren.
Nobbie
 
Posts: 13183
Joined: 09. March 2008 13:04

Re: Problem mit Ausgabe in rekursiver Methode

Postby Knight1 » 24. November 2009 20:07

Hi,
sorry dass ich mich jetzt erst melde. Hatte meinen Server mit akutellem openSUSE neu aufgesetzt. Deswegen konnte ich mich nicht um die Bearbeitung meines Problems und Deiner Antworten, Nobbie, kümmern.
So nun zu dem Problem.

Nobbie wrote:Grausamer Spaghetti-Code. ...

Hast Recht, das was ich gepostet hatte war ja nur ein sehr früher Alpha-Code.
Oft Code ich einfach nach gefühl drauf los, und wenn die Funktionalität dann gegeben ist bereinige ich den Code.

Nobbie wrote:...Vielleicht deklarierst Du erst einmal die Parameter für diese Funktion "vernünftig", d.h.:

a) nicht als Array, sondern wirklich fünf Parameter angeben
b) nicht als nichtssagende Namen $values[0], sondern mit einem sprechenden Namen, der den jeweiligen Sinn des einzelnen Parameters wiederspiegelt
...

Habe ich auch getan. Reduziert habe ich auf 3 Parameter.
Code: Select all
$return_rows
$build_list
$parent_id


Der Sinn der Methode ist es, erstens nur die Anzahl an Kategorien zu liefern und zweitens eine Liste mit den Katagorien zu bauen.
Die Anzahl der Kategorien benötige ich um zu bestimmen ob zum Beispiel eine Infotabelle angezeigt wird dass keine Kategorien vorhanden sind oder halt die Übersichtstabelle anzuzeigen.
Hier der HTML-Code:
Code: Select all
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="<? echo $language['common']['language']['short']; ?>" dir="<? echo $language['common']['language']['dir']; ?>">
<head>
<meta http-equiv="content-type" content="text/html; charset=<? echo $language['common']['language']['charset']; ?>">
<meta http-equiv="Content-Style-Type" content="text/css">
<meta http-equiv="expires" content="0">
<meta http-equiv="cache-control" content="no-cache">
<link rel="stylesheet" type="text/css" href="themes/<? echo $_SESSION['theme']; ?>/css/content.css">
<link rel="stylesheet" type="text/css" href="themes/<? echo $_SESSION['theme']; ?>/css/content_box.css">
<link rel="stylesheet" type="text/css" href="themes/<? echo $_SESSION['theme']; ?>/css/title.css">
<title><? echo $language['admin']['overviews']['references']['categories']['title']; ?></title>
<script type="text/javascript">
with (parent)
{
header1.location.href = "header1.php?category=website";
header2.location.href = "header2.php?category=website&sub_category=references_categories";
<?
if ($overviews->references_categories(TRUE,FALSE,NULL) == 0)
{
?>
pathbar.location.href = "pathbar.php?main=website&sub=references_categories_no_entries";
<?
}
else
{
?>
pathbar.location.href = "pathbar.php?main=website&sub=references_categories_overview";
<?
}
?>
}
function width_values()
{
var main_document_width = document.body.clientWidth;
var side_spacer_cells = (2*10);
var border_cells = (2*6);
var button_cells = (1*16);
 with (document)
 {
 getElementById("main_table").style.width = main_document_width;
 getElementById("top_separator").style.width = main_document_width;
 getElementById("bottom_separator").style.width = main_document_width;
<?
if ($overviews->references_categories(TRUE,FALSE,NULL) == 0)
{
?>
 getElementById("no_categories_title").style.width = main_document_width-(side_spacer_cells+border_cells);
 getElementById("no_categories_content").style.width = main_document_width-(side_spacer_cells+border_cells);
 getElementById("no_categories_footer").style.width = main_document_width-side_spacer_cells;
<?
}
else
{
?>
 getElementById("overview_category_title").style.width = main_document_width-(side_spacer_cells+border_cells+button_cells);
 getElementById("overview_category_footer").style.width = main_document_width-(side_spacer_cells+border_cells);
<?
}
?>
 }
}
</script>
</head>
<body onload="width_values();">
<script type="text/javascript" src="../js/wz_tooltip.js"></script>
<table border="1" cellpadding="0" cellspacing="0" id="main_table">
<?
if ($overviews->references_categories(TRUE,FALSE,NULL) == 0)
{
?>
<tr>
 <td colspan="5" id="top_separator">&nbsp;</td>
</tr>
<tr>
 <td rowspan="3" style="width:10px;"><img src="../images/spacer.gif" style="width:10px;"></td>
 <td class="title_left" style="width:6px;"><img src="../images/spacer.gif" style="width:6px;"></td>
 <td class="title_middle" id="no_categories_title" style="text-align:right;"><a href="new.php?what=references_category&start=<? echo $_GET['start']; ?>"><img src="themes/<? echo $_SESSION['theme']; ?>/icons/new.gif" alt="" onmouseover="Tip('<? echo $language['tooltips']['admin']['overviews']['references']['categories']['new']['text']; ?>',TITLE,'<? echo $language['tooltips']['admin']['overviews']['references']['categories']['new']['title']; ?>',SHADOW,'TRUE',WIDTH,350,JUMPHORZ,'TRUE')" onmouseout="UnTip()"></a></td>
 <td class="title_right" style="width:6px;"><img src="../images/spacer.gif" style="width:6px;"></td>
 <td rowspan="3" style="width:10px;"><img src="../images/spacer.gif" style="width:10px;"></td>
</tr>
<tr>
 <td class="content_box_left" style="width:6px;"><img src="../images/spacer.gif" style="width:6px;"></td>
 <td class="content_box_middle" id="no_categories_content" style="text-align:center;"><h1><? echo $language['admin']['overviews']['references']['categories']['no_references_categories']; ?></h1></td>
 <td class="content_box_right" style="width:6px;"><img src="../images/spacer.gif" style="width:6px;"></td>
</tr>
<tr>
 <td class="content_foot" id="no_categories_footer" colspan="3">&nbsp;</td>
</tr>
<tr>
 <td id="bottom_separator" colspan="5">&nbsp;</td>
</tr>
<?
}
else
{
?>
<tr>
 <td id="top_separator" colspan="6">&nbsp;</td>
</tr>
<tr>
 <td style="width:10px;"><img src="../images/spacer.gif" style="width:10px;"></td>
 <td class="title_left" style="width:6px;"><img src="../images/spacer.gif" style="width:6px;"></td>
 <td class="title_middle" id="overview_category_title" style="text-align:center;">&nbsp;</td>
 <td class="title_middle" style="width:16px;"><a href="new.php?what=references_category&start=<? echo $_GET['start']; ?>"><img src="themes/<? echo $_SESSION['theme']; ?>/icons/new.gif" alt="" onmouseover="Tip('<? echo $language['tooltips']['admin']['overviews']['references']['categories']['new']['text']; ?>',TITLE,'<? echo $language['tooltips']['admin']['overviews']['references']['categories']['new']['title']; ?>',SHADOW,'TRUE',WIDTH,350,JUMPHORZ,'TRUE')" onmouseout="UnTip()"></a></td>
 <td class="title_right" style="width:6px;"><img src="../images/spacer.gif" style="width:6px;"></td>
 <td style="width:10px;"><img src="../images/spacer.gif" style="width:10px;"></td>
</tr>
<tr>
 <td style="width:10px;"><img src="../images/spacer.gif" style="width:10px;"></td>
 <td class="content_box_left" style="width:6px;"><img src="../images/spacer.gif" style="width:6px;"></td>
 <td><ul><? $overviews->references_categories(FALSE,TRUE,0); ?></ul></td>
 <td class="content_box_right" style="width:6px;"><img src="../images/spacer.gif" style="width:6px;"></td>
 <td style="width:10px;"><img src="../images/spacer.gif" style="width:10px;"></td>
</tr>
<tr>
 <td style="width:10px;"><img src="../images/spacer.gif" style="width:10px;"></td>
 <td class="content_foot" id="overview_category_footer" style="text-align:center;" colspan="4">&nbsp;<? #$common->page_navigation($parent_categories_total_rows,$config['references_per_page']); ?></td>
 <td style="width:10px;"><img src="../images/spacer.gif" style="width:10px;"></td>
</tr>
<tr>
 <td id="bottom_separator" colspan="6">&nbsp;</td>
</tr>
<?
}
?>
</table>
</body>
</html>


Und hier der überarbeitete Code der Methode:
Code: Select all
PUBLIC function references_categories ($return_rows,$build_list,$parent_id)
 {
  if ($return_rows === TRUE)
  {
  $query = $this->mysqli->query("SELECT `id` FROM `".MYSQL_TABLE_REFERENCES_CATEGORIES."`");
  RETURN $query->num_rows;
  }
 
  if (($build_list === TRUE) && is_numeric($parent_id))
  {
  $run = 1;
  $query = $this->mysqli->query("SELECT * FROM `".MYSQL_TABLE_REFERENCES_CATEGORIES."` WHERE `parent_id` = '{$parent_id}' ORDER BY `parent_id` ASC, `category_name_{$_SESSION['lang']}` ASC");
  $rows = $query->num_rows;
   while ($result = $query->fetch_array(MYSQL_ASSOC))
   {
    if (($parent_id != 0) && ($run == 1)) { echo "<ul>"; }
   echo "<li>";
    if ($_SESSION['lang'] != "de")
    {
     if (empty($result['category_name_'.$_SESSION['lang']]))   { echo $result['category_name_de']; }
     else                                          { echo $result['category_name_'.$_SESSION['lang']]; }
    }
    else { echo $result['category_name_de']; }
   echo "</li>";
   $this->references_categories($return_rows,$build_list,$result['id']);
    if (($result['parent_id'] != 0) && ($run == $rows))
    {
    echo "</ul>";
    }
   $run++;
   }
  }
 }


Die Tabellenstruktur sieht folgendermaßen aus:
Code: Select all
CREATE TABLE IF NOT EXISTS `categories` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `category_name_de` varchar(25) COLLATE latin1_general_cs NOT NULL,
  `category_name_fr` varchar(25) COLLATE latin1_general_cs NOT NULL,
  `category_name_lb` varchar(25) COLLATE latin1_general_cs NOT NULL,
  `description_de` varchar(100) COLLATE latin1_general_cs NOT NULL,
  `description_fr` varchar(100) COLLATE latin1_general_cs NOT NULL,
  `description_lb` varchar(100) COLLATE latin1_general_cs NOT NULL,
  `parent_id` bigint(20) unsigned NOT NULL DEFAULT '0',
  `active` enum('0','1') COLLATE latin1_general_cs NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 COLLATE=latin1_general_cs PACK_KEYS=0;


Das hier ist der (Test-)Inhalt:
Code: Select all
INSERT INTO `categories` (`id`, `category_name_de`, `category_name_fr`, `category_name_lb`, `description_de`, `description_fr`, `description_lb`, `parent_id`, `active`) VALUES
(1, 'Neubau', '', '', '', '', '', 0, '1'),
(2, 'Altbau', '', '', '', '', '', 0, '1'),
(3, 'Bäder', '', '', '', '', '', 2, '1'),
(4, 'Bäder', '', '', '', '', '', 1, '1'),
(5, 'Küchen', '', '', '', '', '', 2, '1'),
(6, 'Test', '', '', '', '', '', 5, '1'),
(7, 'Test 2', '', '', '', '', '', 3, '1'),
(8, 'Test 3', '', '', '', '', '', 6, '1');


Es soll die Möglichkeit gegeben sein mittel dem Feld parent_id (theoretisch) beliebig viele Kategorien und beliebig viele Ebenen anzulegen.
Aussehen sollte es mit den momentanen Testeinträgen folgendermaßen:
    Altbau
      Bäder
        Test 2
      Küchen
        Test
          Test3
    Neubau
      Bäder

Das habe ich jetzt hinbekommen.

Ein paar Kleinigkeiten sind noch zu machen.

Danke Nobbie für Deine Hilfe.


Kai aka Knight1
Image
User avatar
Knight1
 
Posts: 310
Joined: 18. October 2003 10:03
Location: Trier
Operating System: Windows 7 Ultimate x64

Re: Problem mit Ausgabe in rekursiver Methode

Postby Altrea » 03. December 2009 13:07

Vielleicht auch mal für später zum im Hinterkopf behalten:

Für Baumstrukturen beliebiger Tiefe, die zwar oft ausgelesen aber seltener geändert werden bieten sich "Nested Sets" an, da diese ohne rekursion auskommen können.
Die Implementierung ist komplizierter, das möchte ich nicht unter den Teppich kehren. Aber für bestimmte Projekte lohnt sich der Aufwand.

Einfach mal nach googlen, falls das Thema noch neu ist.
We don't provide any support via personal channels like PM, email, Skype, TeamViewer!

It's like porn for programmers 8)
User avatar
Altrea
AF Moderator
 
Posts: 11935
Joined: 17. August 2009 13:05
XAMPP version: several
Operating System: Windows 11 Pro x64


Return to PHP

Who is online

Users browsing this forum: No registered users and 21 guests