0

I have in database people with relation to parents and I need to create recursive function which return array with genealogical tree. Database example:

NUMBER;NAME;FATHER;MOTHER
001;Name1;002;005
002;Name2;007;018
003;Name3;018;025
005;Name5;006;019
023;Name23;019;045
018;Name18;062;097
007;Name7;;-
...

I prepared two functions - first for man detail and second recursive for search ancestors.

I need this result:

$out[0][0] // first person
$out[1][0] // mother of first person
$out[1][1] // father of first person
$out[2][0] // grandmother of first person (mother side)
$out[2][1] // grandfather of first person (mother side)
$out[2][2] // grandmother of first person (father side)
$out[2][3] // grandmother of first person (father side)
...

next generation have 8 items, next 16 items, ... Maximum is 6 generations back.

I prepared two functions - first for man detail and second recursive for tree building. In second function

define("MAX_GEN",5);

function detail($number) {
    $d = mysql_query("select * from table where number = '$number'");
    if(mysql_num_rows($d) == 0) {
        $p[name] = "N/A";
        $p[number] = "N/A";
        $p[m_number] = "N/A";
        $p[f_number] = "N/A";
    }
    else $p = mysql_fetch_assoc($d);
    return $p;
}

function gen($number, $generation = 0, $out) {

    if ($generation >= MAX_GEN) {
        return false;
    }

    $record = detail($number);

    if ($generation == 0) $out[0][] = $record; // first man

    $generation++; // next generation

    if (!$out[$generation] && ($generation != MAX_GEN)) $out[$generation] = array();

    $x_mother = gen($record[m_number], $generation ); // continue with mother
    $x_father = gen($record[f_number], $generation ); // continue with father

    if ($out[$generation]) {
        $out[$generation][] = $x_mother;
        $out[$generation][] = $x_father;
    }
    return $out;
}

But in second function is problem - I don't know how pass array with results to next and next generation. It's still return one element or (after few attempts) it returns array which I want but with inserted arrays between generations and result is unusable. Can anyone help me please?

Example of result which I wand is here: http://www.foxterrier.cz/_TM_importy/example.php

Tomas Macek
  • 189
  • 1
  • 5
  • 3
    Why are you using the long-deprecated `mysql_` code library? It was discontinued many years ago and removed entirely in PHP7. No new code should be written using this library. It leaves you vulnerable to SQL injection attacks (due to the lack of parameterised query support) and potentially other unpatched vulnerabilities. Switch to using `mysqli` or `PDO` as soon as possible, and then learn how to write parameterised queries to protect your data from malicious input. See http://bobby-tables.com for a simple explanation of the risks and some sample PHP code to write queries safely. – ADyson Oct 30 '18 at 10:54
  • Anyway, your example code is a bit incomplete - show us how you are initially calling the "gen" method for the first time please. And also show us what result it currently produces. Thanks. – ADyson Oct 30 '18 at 10:55
  • Using old deprecated function - I inherited old web pages (with old hosting and php 5.6) and now preparing new version. Now I'm only testing possibility generating genealogy tree, so I use their old functions. New web will be with mysqli – Tomas Macek Oct 30 '18 at 12:21
  • ok well that's a reasonable explanation. Now more importantly can you address my second comment please, so we can try and fix your algorithm? Thanks. – ADyson Oct 30 '18 at 12:24
  • Thank for you reply. I add few lines to database example and result which I try make [link](http://www.foxterrier.cz/_TM_importy/example.php). – Tomas Macek Oct 30 '18 at 12:39
  • Ok thanks but that's not actually what I asked you for. Read my comment again, thanks. – ADyson Oct 30 '18 at 12:40
  • initially call of gen is: $result = gen('001',0); – Tomas Macek Oct 30 '18 at 12:45
  • Ok. And the current output? – ADyson Oct 30 '18 at 12:49
  • now I get only array result where first element have data (name,number,..) and all another array elements are empty – Tomas Macek Oct 30 '18 at 13:06

3 Answers3

2

This is mostly a comment - but its a bit long.

You are very constrained by both your database design and your desire output format. Even without going to the cost/complexity of a graph database there are lots of ways of representing hierarchical data in a relational database.

Your output format list each generation but does not maintain the mother-father association, and does not lend itself to mapping out sideways or forwards - only backwards.

Your implementation requires that you run 2^N (where N is the number of generations) queries, each time making a round triup from PHP code back to the DBMS. Performance will suck.

As a minimum you should consider resolving a generation at a time:

  $ref=mysql_real_escape_string(session_id());
  mysql_query(
    "INSERT INTO results (generation, number, ref) VALUES (0, $start_number, '$ref')"
  );

  for ($x=1; $x<=max_gens; $x++) {
    mysql_query(
  "INSERT INTO results (generation, number, ref)
  SELECT $x, father, '$ref'
  FROM yourtable yt JOIN results rs ON yt.number=results.number 
  WHERE rs.generation=$x-1
  UNION 
  SELECT $x, mother, '$ref'
  FROM yourtable yt JOIN results rs ON yt.number=results.number 
  WHERE rs.generation=$x-1"
     );
  }

(but with more error checking - and remember to clear out the data later)

symcbean
  • 47,736
  • 6
  • 59
  • 94
1

Updated essam eg's solution - now fully working:
- removed char $ before MAX_GET in first for cycle
- removed $generation in cycle ($i enough)
- add call detail in ($out[$i][] = $record[m_number]; -> $out[$i][] = detail($record[m_number]);)
- modify adding father to array (mother was twiced and father was missing)

define("MAX_GEN",5);

function detail($number) {
    $d = mysql_query("select * from table where number = '$number'");
    if(mysql_num_rows($d) == 0) {
        $p[name] = "N/A";
        $p[number] = "N/A";
        $p[m_number] = "N/A";
        $p[f_number] = "N/A";
    }
    else $p = mysql_fetch_assoc($d);
    return $p;
}

$main_person_number=001;//number in database
$out[0][] =detail($number);// main person


for($i=1;$i<MAX_GEN;$i++){
  $parent=$i-1;
  for($j=0;$j<pow(2,$parent);$j++){
      $record = detail($out[$parent][$j]['number']);
  $out[$i][] = detail($record[m_number]);
  $out[$i][] = detail($record[f_number]);
    }
}
Tomas Macek
  • 189
  • 1
  • 5
0

I prefer to use iteration to create $out array

instead of gen function

the iteration count increases 1 -> 2 -> 4 ->8

= 2 ^($generation-1)

$parent=$generation-1;
      for($j=0;$j<pow(2,$parent);$j++){

the full code

define("MAX_GEN",5);

function detail($number) {
    $d = mysql_query("select * from table where number = '$number'");
    if(mysql_num_rows($d) == 0) {
        $p[name] = "N/A";
        $p[number] = "N/A";
        $p[m_number] = "N/A";
        $p[f_number] = "N/A";
    }
    else $p = mysql_fetch_assoc($d);
    return $p;
}

$main_person_number=001;//number in database
$generation = 0;
$out[0][] =detail($number);// main person
$generation++; // next generation

for($i=1;$i<$MAX_GEN;$i++){
  $parent=$generation-1;
  for($j=0;$j<pow(2,$parent);$j++){
      $record = detail($out[$parent][$j]['number']);
  $out[$generation][] =$record[m_number];
  $out[$generation][] =$record[m_number];
    }
   $generation++;   
}
essam eg
  • 109
  • 5