CodingCircular references

 

Press Ctrl+Enter to quickly submit your post
Quick Reply  
 
 
  
 From:  Mikee  
 To:  ALL
34921.1 
Am I allowed to do them? Is it really naughty?


I have something similar to this:


php code:
 
 GedcomElement extends DOMElement {
   private $obj;
   public function __construct($name){
     switch($name){
       case "INDI":
        $this->obj = new Individual($this);
        break;
       case "FAM":
        $this->obj = new Family($this);
        break;
     }
   }
   public function __destruct(){
     $this->obj = null;
   }
 }
 

Basically I'm overriding DOMElement with a new version of the element which stores an object inside it with a reference to itself.

Is this really bad practice?

I'm trying to clean up at the end with the destruct method.

Basically, I have a list of 'individuals' (with lots of attributes) and a list of 'families' (with lots of attributes). individuals also store id's of two different families and familes also store id's of multiple individuals.

Example XML file:

xml code:
 
<?xml version="1.0"?>
<GEDCOM>
  <INDIVIDUALS>
    <INDI id="@I1@">
      <NAME text="Bob /Cox/"/>
      <SEX text="M"/>
      <FAMS text="@F1@"/>
      <CHAN>
        <DATE text="11 FEB 2006"/>
      </CHAN>
    </INDI>
    <INDI id="@I2@">
      <NAME text="Joann /Para/"/>
      <SEX text="F"/>
      <FAMS text="@F1@"/>
      <CHAN>
        <DATE text="11 FEB 2006"/>
      </CHAN>
    </INDI>
    <INDI id="@I3@">
      <NAME text="Bobby Jo /Cox/"/>
      <SEX text="M"/>
      <FAMC text="@F1@"/>
      <CHAN>
        <DATE text="11 FEB 2006"/>
      </CHAN>
    </INDI>
  </INDIVIDUALS>
  <FAMILIES>
    <FAM id="@F1@">
      <HUSB text="@I1@"/>
      <WIFE text="@I2@"/>
      <MARR/>
      <CHIL text="@I3@"/>
    </FAM>
  </FAMILIES>
</GEDCOM>
 


I can then do something like this:

php code:
 
 $dom->getIndividualById("@F1@")->GetWife();
 


Where getIndividualById will return the $obj in the relevant node, and GetWife() will use XPath on the owner document to find the correct element then return the $obj within that element.

So, is it really really bad for objects to store references to themselves in objects contained within themselves?
0/0
 Reply   Quote More 

 From:  AND HIS PROPHET IS (MOHAMED42)  
 To:  Mikee     
34921.2 In reply to 34921.1 

No, it'll work fine, although someone else seeing the code may scratch their head for a bit. I suspect there may be a better way to do it but I don't understand the situation enough to make a recommendation.

 

But yes, you're fine, genealogy software is always going to be a self-referential nightmare. I've worked on some before and I remember it being weird and loopy and stuff.


--
XML is like violence. If it hasn't solved your problems yet, you're just not using enough.
0/0
 Reply   Quote More 

 From:  Mikee  
 To:  ALL
34921.3 
Ignoring the absolute madness of my classes and names (this is a desperate mess from me trying 10000 different ways to structure this, so it needs refactoring/cleaning/rewriting!)

php code:
 
<?php
class GedComDom extends DOMDocument {
	private $gedcom;
	private $linesraw;
	private $root;
	private $path;
	public $xpath;
	public function __construct ($path){
 
		parent::__construct();
		$this->path = $path;
		$this->xpath = new DOMXPath($this);
 
		$this->registerNodeClass('DOMDocument', 'GedComDom');
		$this->registerNodeClass('DOMElement', 'GedComDomElement');
 
		$this->root = $this->setRoot('GEDCOM');
		$this->individuals = $this->addCollection("INDIVIDUALS");
		$this->families = $this->addCollection("FAMILIES");
 
   		$this->gedcom = file_get_contents($this->path);
		$this->linesraw = explode("\r\n", $this->gedcom);
 
    	$currentobj = null;
		for ($i=0; $i<sizeof($this->linesraw); $i++){
			$linedata = self::SplitGedLine($this->linesraw[$i]);
			if ($linedata["level"] == 0){
				$currentobj = $this->create($linedata);
			}else if ($linedata["level"] > $currentobj->level){
				$currentobj = $currentobj->addData($linedata);
			}else if ($linedata["level"] < $currentobj->level){
				$difference = abs($linedata["level"]-$currentobj->level)+1;
				for ($a=0; $a<$difference; $a++){
					$currentobj = $currentobj->parentNode;
				}
				$currentobj = $currentobj->addData($linedata);
			}else {
				$currentobj = $currentobj->parentNode;
				$currentobj = $currentobj->addData($linedata);
			}
		}
	}
	function addCollection ($name){
		return $this->root->appendChild(new DOMElement($name));
	}
   	function create($linedata, $class = null){
		$node = new GedComDomElement($linedata["tag"]);
		if ($node->obj != null){
			if ($node->obj instanceof Individual){
				$this->individuals->appendChild($node);
			}else if ($node->obj instanceof Family){
				$this->families->appendChild($node);
			}
		}else {
			$this->root->appendChild($node);
		}
		$node->level = $linedata["level"];
		$node->addUpdateAttributes($linedata);
		return $node;
   	}
   	function setRoot($name) {
      	return $this->appendChild(new GedComDomElement($name));
   	}
   	static function SplitGedLine ($linetext){
		preg_match_all("|^(\d+)\s*(@\S+@)?\s*(\S+)\s*(.*)|", mb_convert_encoding($linetext, "UTF-8"), $out, PREG_PATTERN_ORDER);
		$level = trim($out[1][0]);
		$id = trim($out[2][0]);
		$tag = trim(strtoupper($out[3][0]));
		$text = trim($out[4][0]);
		return array("level"=>$level, "id"=>$id, "tag"=>$tag, "text"=>$text);
   	}
   	public function GetFamilyById($id){
   		return $this->getElementById($id)->obj;
   	}
   	public function GetIndividualById($id){
   		return $this->getElementById($id)->obj;
   	}
}
class GedComDomElement extends DOMElement {
	public $obj;
	public function __construct($name){
		switch($name){
			case "INDI":
				$this->obj = new Individual($this);
				break;
			case "FAM":
				$this->obj = new Family($this);
 
				break;
		}
		parent::__construct($name);
	}
	function addData ($linedata){
      	$node = new GedComDomElement($linedata["tag"]);
      	$node->level = $linedata["level"];
      	$this->appendChild($node);
      	$node->addUpdateAttributes($linedata);
      	return $node;
	}
	function addUpdateAttributes($linedata){
		if (!empty($linedata["id"])){
			$this->setAttribute("id", $linedata["id"]);
			$this->setIdAttribute("id", true);
		}
		if (!empty($linedata["text"])){
			$this->setAttribute("text", $linedata["text"]);
		}
	}
	function __destruct (){
		$this->obj = null;
	}
}
 
abstract class GedComDomQueryObject {
	protected $node;
	public function __construct($node){
		$this->node = $node;
	}
	public function GetNodeAttribute($nodename, $attrname){
		return $this->query($nodename."/@".$attrname, $this->node);
	}
	private function query($query, $context = null){
		return $this->node->ownerDocument->xpath->query($query, $context);
	}
	public function __destruct(){
		$this->node = null;
	}
}
class Individual extends GedComDomQueryObject{
	private $name = false;
	private $sex = false;
	private $famcid = false;
	private $famsid = false;
	public function GetName(){
		if ($this->name === false){
			$namenodes = $this->GetNodeAttribute("NAME", "text");
			$this->name = $namenodes->length > 0 ? $namenodes->item(0)->value : null;
		}
		return $this->name;
	}
	public function GetSex(){
		if ($this->sex === false){
			$sexnodes = $this->GetNodeAttribute("SEX", "text");
			$this->sex = $sexnodes->length > 0 ? $sexnodes->item(0)->value : null;
		}
		return $this->sex;
	}
	private function getFamilyS (){
		if ($this->famsid === false){
			$this->famsid = array();
			$famsid = $this->GetNodeAttribute("FAMS", "text");
			for ($i=0; $i<$famsid->length; $i++){
				  $this->famsid[] = $famsid->item($i)->value;
			}
		}
		return $this->famsid;
	}
	private function getFamilyC (){
		if ($this->famcid === false){
			$famcid = $this->GetNodeAttribute("FAMC", "text");
			$this->famcid = $famcid->length > 0 ? $famcid->item(0)->value : null;
		}
		return $this->famcid;
	}
	public function GetWives(){
		$fams = $this->getFamilyS();
		$wives = array();
		for ($i=0; $i<sizeof($fams); $i++){
			$wives[] = $this->node->ownerDocument->GetFamilyById($fams[$i])->GetWife();
		}
		return $wives;
	}
	public function GetMother(){
		if ($this->getFamilyC() !== null){
			return $this->node->ownerDocument->GetFamilyById($this->getFamilyC())->GetWife();
		}
		return null;
	}
 
}
class Family extends GedComDomQueryObject{
	private $wife = false;
	public function GetWife(){
		if ($this->wife === false){
			$wives = $this->GetNodeAttribute("WIFE", "text");
			$this->wife = $wives->length > 0 ? $this->node->ownerDocument->GetIndividualById($wives->item(0)->value) : null;
		}
		return $this->wife;
	}
}
 


Run that against a gedcom file like this:

code:
 
0 HEAD 
1 SOUR Reunion
2 VERS V8.0
2 CORP Leister Productions
1 DEST Reunion
1 DATE 11 FEB 2006
1 FILE test
1 GEDC 
2 VERS 5.5
1 CHAR MACINTOSH
0 @I1@ INDI
1 NAME Bob /Cox/
1 SEX M
1 FAMS @F1@
1 CHAN 
2 DATE 11 FEB 2006
0 @I2@ INDI
1 NAME Joann /Para/
1 SEX F
1 FAMS @F1@
1 CHAN 
2 DATE 11 FEB 2006
0 @I3@ INDI
1 NAME Bobby Jo /Cox/
1 SEX M
1 FAMC @F1@
1 CHAN 
2 DATE 11 FEB 2006
0 @F1@ FAM
1 HUSB @I1@
1 WIFE @I2@
1 MARR 
1 CHIL @I3@
0 TRLR
 


Then you can do stuff like this:

php code:
 
$ged = new GedComDom("test_family_tree.ged");
echo $ged->GetIndividualById("@P3206597029@")->GetMother()->GetMother()->GetFather()->GetName();
 


or just spit out the XML using the normal DOM functions:

php code:
 
$ged = new GedComDom("test_family_tree.ged");
echo $ged->saveXML();
 



Bit of a nightmare but with the relationships the way they are, it was the only niceish way of converting the gedcom into a nice format then doing fun stuff with it.

Next I want to be able to automatically say which generation each person is in, relative to the oldest generation in the tree. This will be a nightmare because of how the trees spread out and each person has two parents.

Then, I want to write a method within Individual where you can do something like this:

php code:
 
Individual {
 public function GetRelationToOther ($individual){
   find relation between $this and $individual;
   // return "great great great great great 6nd cousin 4 times removed"
 }
}
 


which'll be uh.. fun!
0/0
 Reply   Quote More 

 From:  AND HIS PROPHET IS (MOHAMED42)  
 To:  Mikee     
34921.4 In reply to 34921.3 
I'm sorry but there is simply no way that I am going to be able to wrap my head around that until at least next Fluesday.

--
XML is like violence. If it hasn't solved your problems yet, you're just not using enough.
0/0
 Reply   Quote More 

Reply to All    
 

1–4

Rate my interest:

Adjust text size : Smaller 10 Larger

Beehive Forum 1.5.2 |  FAQ |  Docs |  Support |  Donate! ©2002 - 2024 Project Beehive Forum

Forum Stats