Rolf B: Vorfahren aus Datenbank rekursiv ermitteln?

Beitrag lesen

Hallo Linuchs,

Du weißt sicher schon, was jetzt kommt. Ein Hinweis-Marathon...

Den csv_string mit den gefundenen Ahnen

Nimm JSON. Das Volumen ist nicht so groß, dass der gesparte Datenoverhead sich lohnt, und dafür musst Du keinen selbstgemachten CSV-Codierer und -Parser verwenden. Schreib nicht selbst, was fertig vorhanden ist. CSV ist auch nicht einfach Stringverkettung; du musst eventuell vorhandene CSV-Steuerzeichen in Strings maskieren.

"'" . addslashes($id) . "'"

Zweimal "wieso?". Deine ids sind in der DB int-Werte. D.h. die Query sollte auch Integers vergleichen, nicht Strings. Prüfe die ID in $_GET[] auf numerisch und weise sie bei Fehler ab. Danach konvertiere sie mit intval() in einen Integer und damit ist es gut. Ich würde Dir ja Type Hints für die Lesefunktion empfehlen wollen, aber das geht erst ab PHP 7. Du hast VIEL zum Updaten! Ohne Type Hints solltest Du an dieser Stelle einfach nochmal intval() setzen, das ist schnell fertig wenn es einen int als Argument bekommt.

SELECT per1.*

Nein. SELECT * verwendet man maximal in ad hoc Queries in phpMyAdmin, nicht in Programmen. Man führt die Spalten einzeln auf.

-- alter = geschuetztes Wort

Dafür gibt's die Backticks. Je nach gesetztem SQL_MODE (ANSI_QUOTES oder MSSQL) kannst Du auch die "Anführungszeichen" oder [Brackets] verwenden - aber idiomatisch für die MySQL-Welt sind Backticks.

SELECT   `select`, `as`, `order` 
FROM     `from` JOIN `left` ON `from`.`on`=`left`.`by`
WHERE    `and` = 42
ORDER BY `order`

Reservierte Wörter? Null Problemo.

@mysql_query

Whoaaa - mysql? Nicht nur PHP 5.6, sondern auch eine "verbotene" MySQL Schnittstelle? Verwende unbedingt mysqli oder PDO für neue Anwendungen.

Und das @ Präfix brauchst Du eigentlich nicht. Statt dessen solltest Du nach dem query-Aufruf $res_person === FALSE abfragen, in dem Fall ging die Query schief. Desgleichen für den fetch-Aufruf. Der kann ein Array sein, null bei Ende der Abfrage oder FALSE bei einem Error. Errors sollte man zumindest loggen.

liesMutterVaterId(...)

  • Die Funktion liest keine IDs, sondern Eltern-Rows. Ich würde deshalb das Id aus dem Namen entfernen.
  • Die Funktion sollte einen Parameter $stufe oder $generation bekommen, damit... ah moment. $lfd. Siehe unten.
  • Rechne mit Ahnenschwund. Ehen unter Cousins können durchaus vorkommen, in dem Fall haben zwei Eltern das gleiche Großelternteil A. In dem Fall bekommst Du die Person zweimal in die Liste. Clientseitig wirst Du sie vermutlich in einem Array mit der ID als Index sammeln, d.h. Duplikate eliminieren sich dort dann sowieso. Ein Grund gegen die CSV-Formatierung.

liesMutterVaterId( $lfd . "-", ...)

Ist das dein Generationenzähler? Die Anzahl von Minuszeichen beschreibt die Generation? Tu das nicht, verwende eine Zahl. Die Frage, wie man diese Zahl dann visualisiert, ist Sache des UI. Z.B. mit "".padEnd(lfd, "-") (oder "-----".substr(0,lfd) für IE-Kompatibilität und max. 5 Generationen)

Aus Architektursicht würde ich die gefundenen Personen entweder in einem Array sammeln, einfach Generation, Kind-ID und Geschlecht von Hand in $row eintragen und dann ablegen in $persons[] = $row. Und am Ende schickst Du dieses Array mit json_encode($persons) an den Client.

Am Client verwendest Du JSON.parse(array, reviver) - reviver ist eine Funktion, die pro Wert im JSON-Objekt aufgerufen wird. Damit kannst Du die Datümer von YYYY-MM-DD gleich beim Deserialisieren in JavaScript-Date Objekte umwandeln. Oder in TT.MM.YYYY, wenn Du unbedingt willst 😉. Wie man den Reviver verwendet, hab ich vor 3 Jahren mal hier aufgeschrieben.

Ob Du die Kind-Id überhaupt brauchst? Das ist eine doppelte Verpointerung. Am Kind hast Du die ID von Vater und Mutter stehen. Bei Cousinenehen würdest Du den gemeinsamen Großelternteil dann zweimal in der Tabelle haben, mit unterschiedlichen Kind-IDs. Lohnt diese Redundanz?

An Stelle eines globalen Sammelarrays könntest Du auch einen funktionaleren Ansatz wählen und einfach ein Array der gefundenen Elternteile zurückgeben.

liesStammbaum($stufe, $id, $geschlecht, $db) {
   $row_person = ... // query;
   if (!$row_person) return FALSE;

   $vaterseite = liesStammbaum($stufe+1, $row['vater_id'], 'm', $db);
   if ($vaterseite === FALSE) {
      // Konstruiere Vater aus Namensangabe, als Array aus einer Row
   }
   $mutterseite = liesStammbaum($stufe+1, $row['mutter_id'], 'w', $db);
   if ($mutterseite === FALSE) {
      // Konstruiere Mutter aus Namensangabe, als Array aus einer Row
   }
   return array_merge( [ $row_person ], $vaterseite, $mutterseite);
}

Look Ma, no globals!

Rolf

--
sumpsi - posui - obstruxi