Recursing Properly Through Related Entities - php

I have a set of Organizations and their Board Members.
All organizations have board members and many board members are on the board of more than one organization.
I am using JIT Hypertree to illustrate their relationships. The JIT Hypertree schema requires that one item be the parent of all and is drawn based on a single JSON array.
I would love to have the re-centering event query and re-populate the graph based on the change. Then 2 levels would be fine but I have not been able to work out how to do that.
The code I have at present recurses manually for three levels from the starting organization but what I want is to re-curse through all related records.
So it would start with an Org and add Org's array of children (board members). Then fetch all of the boards (other than current Org) for each board member and add those as children of the board member.
This would continue until each trail dead ends - presumably at a board member who only belongs to one board.
Anyone have advice on how to create this array and avoid duplicates?
$board = $center->board();
$top['id'] = $center->ID;
$top['name'] = $center->Org;
$top['children'] = array();
if ($board) {
foreach ($board as $b) {
$child['id'] = $b->ID;
$child['name'] = (strlen(trim($b->Last)) > 0) ? $b->First . ' ' . $b->Last : 'Unknown';
$child['data']['orgname'] = $center->Org;
$child['data']['relation'] = $b->Role;
$child['data']['occupation'] = $b->Occupation;
$child['children'] = array();
$childboards = $b->boards();
if ($childboards) { foreach ($childboards as $cb) {
$gchild['id'] = $cb->ID;
$gchild['name'] = $cb->Org;
$gchild['data']['orgname'] = (strlen(trim($b->Last)) > 0) ? $b->First . ' ' . $b->Last : 'Unknown';
$gchild['children'] = array();
$childboardmembers = $cb->board();
if ($childboardmembers) { foreach ($childboardmembers as $cbm) {
$ggchild['id'] = $cbm->ID;
$ggchild['name'] = (strlen(trim($cbm->Last)) > 0) ? $cbm->First . ' ' . $cbm->Last : 'Unknown';
$ggchild['data']['orgname'] = $cb->Org;
$ggchild['data']['relation'] = $cbm->Role;
$ggchild['data']['occupation'] = $cbm->Occupation;
$ggchild['children'] = array();
$gchild['children'][]= $ggchild;
}}
$child['children'][]= $gchild;
}}
$top['children'][] = $child;
}
}
$top['data'] = array();
$top['data']['description'] = $center->Desc;
echo json_encode($top);
// Edit 2011.10.24 In Re hakre response
My data structure is a table of Organizations with unique IDs, a table of People with Unique IDs, and then a bridging table for the two specifying Organization (Entity) and Person and the Role the Person is playing in the Entity. A typical many-to-many. No sub-boards at all. I made an image of it which now seems kind of pointless but I'll add it at the bottom.
The JIT library data structure is a little nuts (to me) in that it goes like this in their band example:
Top: Nine Inch Nails
Child: Jerome Dillon
Child: Howlin Maggie (another band)
{all the bands' members and then all of their bands...}
So the organization (band) is treated as though it is a Person even though it is comprised of a number of Persons. And when I recurse using the code above I get (I think) terrible bloat but the JSON it makes works correctly despite bloat.
Example JSON and Example Visualization
// End Edit

Your question is hard to answer in the sense that your data-structure is mainly unknown.
For the graphical represenation you only need to provide simple relationships if I understand that correctly:
*- Parent
+- Child
+- Child
...
`- Child
Your data structure has a different format, I don't know specifically but it's something like:
Org <-- 1:n --> Board
Board <-- n:n --> Board # If you have sub-boards
Board <-- n:n --> Member
Whichever your data is represented, to map or transpose your data onto the required structure for the graphical representation, you need some functions that take care of that.
To do that you need to share classification/type between both and specific keys, so that you can look-up the needed data from the event to return the data. For example:
if (request_parent_is_org())
{
$id = request_parent_id();
$parent = data_get_board($id);
$parent->addChildrent(data_get_board_children($id));
}
else
{
... # All the other decisions you need to take based on request type
}
view_response_to_json($parent);

What you have with your many-to-many data model is a graph. JIT is designed for trees.
To put it another way, JIT will not correctly show the crossing lines that are represented in the data whenever a single person is connected to multiple organizations.
I'd recommend a proper network graph visualization - D3.js has a great implementation for modern browsers.
The JSON data format it uses is actually easier to implement given your table structure - for all the organizations and people, you define objects:
{
"name": "Mme.Hucheloup",
"group": 1
},
{
"name": "Afton School Board",
"group": 2
}
And for each association in your association table you define connection objects that wire them together:
{
"source": 1,
"target": 2
},
The fancy coding in D3 takes care of the rest. Good luck!

You can use function below:
function get_orgs_and_childs ($child_id, $found = array())
{
array_push ($found, $child['name']);
if($child['children'])){
$found[] = get_parents($child['id'], $found);
}
return $found;
}
Call it using:
$orgs = get_orgs_and_childs($child['id']);

Related

How to create a nested multi level menu without parent id

I searched on many posts, but I can't find one to fix my problem.
In my database I have some data : a name and a "treepath" like "1.1.2". I don't have a parent ID or anything like that. So my question is : is there a way to createa nested multi level menu with only the "treepath" ?
In my table I got (ID | NAME | TREEPATH):
0 | Phone | 1
1 | Samsung | 1.1
2 | Galaxy S | 1.1.1
3 | Galaxy Note | 1.1.2
...
Output I need :
<ul>
<li>1. Phones
<ul>
<li>1.1. Samsung
<ul>
<li>1.1.1. Galaxy S</li>
<li>1.1.2. Galaxy Note</li>
<li>1.1.3. Galaxy Ace</li>
</ul></li>
<li>1.2. Apple</li>
<li>1.3. Google</li>
</ul></li>
</ul>
I certainly need a recursive function with php, but i can't got it right, so if anyone could help me on this one, that'd be great !
Thanks !
Well, you could use a recursive function that employs regular expressions, but that might be sub-optimal. Pseudo-code follows:
function drawTree($root) {
while($results = yourQuery("SELECT * FROM table WHERE treepath REGEXP '^{$root}\.'")) {
echo "stuff";
drawTree($results['treepath']);
}
}
Your situation is odd.
I don't see a technical reason not to just have a parent id in your table. Or to use first normal form. Feels a bit contrived, but you could do something like:
<?php
$db = new mysqli(...);
$stmnt = $db->prepare("select name, treepath, LENGTH(treepath) - LENGTH(REPLACE(treepath, '.', '')) AS depth from tree order by treepath");
$stmnt->bind_result($name,$treepath,depth);
$tree = array();
$current_depth = 0;
while($stmnt->fetch())
{
if($current_depth < $depth)
{
echo '<ol><li>' . $name . '</li>';
$current_depth=$depth;
}
else if($current_depth == $depth)
{
echo '<li>' . $name . '</li>';
}
else //current_depth greater than depth
{
echo '</ol><li>' . $name .'</li>';
}
}
$stmnt->close();
$db->close();
echo '</ol>';
?>
Not tested, will need tweeks.
If your application needs to provide nested menus, you are better off implementing nesting ( In database language, it would be grouping, foreign keys and constraints ) in your database. It's not recommended that you maintain different designs in your presentation layer and database layer. After all, a strong database design is pivotal for any application, isn't it?
There are many other advantages as well - you streamline your application design, data remains organized and consistent, lesser effort in transforming database content into presentation content, and it becomes easier to add new features as well. Why don't you start with some categorization and a good schema design?
If you ask me, I would make something like a "phones" table that has phone_name, year, manufacturer, model, type etc.
Again, there would be a different table called manufacturers which have manufacturer id's, names, years, and other information. You can also have another table for "type". There will be foreign key relationships between manufacturer id's and manufacturer column in phone table and similar stuff with other columns/tables as well. If you are confused, a good place to start off would be drawing an E-R diagram which will eventually be put down into tables and views.

How to search neo4j for a node by a property name?

I am trying to set up some data and am fairly new to cypher queries. I m doing a project whereby nodes and relationships are created as part of a soccer team.
for example I am creating nodes on games and referees only at the moment. My logic is that I read in data for a game and if the referee does not exist as a node, to create him/her. If on a future game i see a referee that already exists that I just relate them to the game rather then creating a duplicate record. I am doing the following and wanted to see if my logic made sense before I even attempted to populate my data.
//JUST CREATED A GAME NODE called $GameNode
//let's search if the referee exists
$queryString = "START n=node({nodeId}) ".
"MATCH (n)<-[:REFEREED]-(x)".
"WHERE x.name = {name}".
"RETURN x";
$query = new Everyman\Neo4j\Cypher\Query($client, $queryString, array('nodeId' => 0, 'name' => $referee['referee_name']));
$result = $query->getResultSet();
$referee = 0;
if (count($result)==0)
{
//create referee as he/she does not exist
$referee= $client->makeNode();
$referee->setProperty('name', $referee['referee_name'])
->save();
}
else
{
//let's point the referee to this law case also
$referee = $client->getNode($result['x']->getProperty('id'));
}
$referee->relateTo($GameNode, 'REFEREED')
->save();
many thanks in advance
This should work but does a lot of roundtrips.
You can use Cypher only for this task:
Neo4j 1.9 use CREATE UNIQUE
START root=node({nodeId})
CREATE UNIQUE (root)<-[:REFEREED]-(referee {name: {name}})
RETURN referee
In Neo4j 2.0 you can use MERGE
MERGE (referee:Referee {name: {name}})
RETURN referee
Please also use better names for your identifiers.

CakePHP & MVC – Potentially superfluous SQL queries when looking up 'names' associated with ids

I've probably murdered the whole concept of MVC somewhere along the line, but my current situation is thus:
I have participants in events and a HABTM relationship between them (with an associated field money_raised). I have a controller that successfully creates new HABTM relationships between pre-existing events and participants which works exactly as I want it to.
When a new relationship is created I wish to set the flash to include the name of the participant that has just been added. The actually addition is done using ids, so I've used the following code:
public function addParticipantToEvent($id = null) {
$this->set('eventId', $id);
if ($this->request->is('post')) {
if ($this->EventsParticipant->save($this->request->data)) {
$participant_id = $this->request->data['EventsParticipant']['participant_id'];
$money_raised = $this->request->data['EventsParticipant']['money_raised'];
$participant_array = $this->EventsParticipant->Participant->findById($participant_id);
$participant_name = $participant_array['Participant']['name'];
$this->Session->setFlash('New participant successfully added: ' . $participant_name . ' (' . $participant_id . ') ' . '— £' . $money_raised);
} else {
$this->Session->setFlash('Unable to create your event-participant link.');
}
}
}
This works, but generates the following SQL queries:
INSERT INTO `cakephptest`.`cakephptest_events_participants` (`event_id`, `participant_id`, `money_raised`) VALUES (78, 'crsid01', 1024) 1 1 0
SELECT `Participant`.`id`, `Participant`.`name`, `Participant`.`college` FROM `cakephptest`.`cakephptest_participants` AS `Participant` WHERE `Participant`.`id` = 'crsid01' LIMIT 1 1 1 0
SELECT `Event`.`id`, `Event`.`title`, `Event`.`date`, `EventsParticipant`.`id`, `EventsParticipant`.`event_id`, `EventsParticipant`.`participant_id`, `EventsParticipant`.`money_raised` FROM `cakephptest`.`cakephptest_events` AS `Event` JOIN `cakephptest`.`cakephptest_events_participants` AS `EventsParticipant` ON (`EventsParticipant`.`participant_id` = 'crsid01' AND `EventsParticipant`.`event_id` = `Event`.`id`)
This final one seems superfluous (and rather costly) as the second should give me all that I need, but removing $this->EventsParticipant->Participant->findById($participant_id) takes out both the second and third queries (which sort of makes sense to me, but not fully).
What can I do to remedy this redundancy (if indeed I'm not wrong that it is a redundancy)? Please tell me if I've made a complete hash of how these sorts of things should work – I'm very new to this.
This is probably due to the default recursive setting pulling the relationship. You can remedy this by setting public $recursive = -1; on your model (beware this will affect all find calls). Or, disable it temporarily for this find:
$this->EventsParticipant->Participant->recursive = -1;
$this->EventsParticipant->Participant->findById($participant_id);
I always suggest setting public $recursive = -1; on your AppModel and using Containable to bring in the related data where you need it.

PHP/mySQL Products System (Very General)

I'm designing a PHP Products system which is required to be very general, in that each product may have a number of options ("height", "width", "colour", "gender", etc.) but that some products may not have those options, and some may have none.
For example, a chest of drawers may have "size" (list of appropriate sizes set somewhere by the website user), "polished" (yes/no) and "painted" (yes/no). However, a new TV may have no options at all.
Can anyone recommend a way for the user to set the options they require for their products (for example, the "polished" would not be relevant at all for an electrical retailed) and then to store prices for various combinations of options as they specify for each product?
`options` text null,
//DONT run this directly, it needs escaping...but you get the idea.
insert into tablename values({['polished':(int)1, 'size':'oh_its_big']}), ({['color':'hotpink']});
//run your query
$query = $db->select("* from tablename");
//loop query, json_decode the options string and assign to array
foreach($query as $q)
{
$tmp[] = (array)json_decode($q->options);
foreach($tmp as $options)
{
//run some conditional statements
if(array_key_exists('size', $options))
{
//do something
}
}
}
IMO, This is a Very general question and you need to provide more information on specifics.
One solution that pops up in my mind is as follows
public class myAwesomeClass {
$typeOfMyClass; //got from a DB when a user creates a new type.
$keyAndValuePair; //holds the key=>value pair of the type's data
}
and to access it in the outside world.
$obj = new myAwesomeClass();
//based on $obj->typeOfMyClass value process
foreach ($obj->keyValuePair as $key=>$value) {
//perform your operations
}

"Family Tree" Data Structure

I'm looking for a way to represent a family tree in PHP. This means that children will need to inherit from two (or more) parents.
Here are the requirements:
1, 2, or more parents
Bonus points if I can attach metadata like a last name or relationship status
Here is my non-working attempt (no arrays as keys, sadly):
$tree = array(
'uncle' => false, // no children
array('mom', 'dad') => array(
'me' => false,
array('brother', 'sister-in-law') => array(
'niece' => false
)
)
);
The question is, how can I represent a family tree with these requirements?
You won't be able to do it all in a single array() like that. You can set up trees like that, but setting up more complicated graphs with multiple parents and other relations requires multiple lines of code.
It'll help a lot if you throw some OO at this. Let's create a Person class to help manage the relationships. Fundamentally, we've got people and their relationships with other people, so we'll start there.
Person class
What I imagine is each person having an array of relationships. This array will be indexed first by the type of relationship, for example "parents" or "children". Each entry will then be an array of Persons.
class Person {
var $name, $relations;
function __construct($name) {
$this->name = $name;
$this->relations = array();
}
function addRelation($type, $person) {
if (!isset($this->relations[$type])) {
$this->relations[$type] = array();
}
$this->relations[$type][] = $person;
}
// Looks up multiple relations, for example "parents".
function getRelations($type) {
if (!isset($this->relations[$type])) {
return array();
}
return $this->relations[$type];
}
// Looks up a single relation, for example "spouse".
function getRelation($type) {
$relations = $this->getRelations($type);
return empty($relations) ? null : $relations[0];
}
function __toString() {
return $this->name;
}
Friendly adders and getters
With the above as a foundation we can then add some friendlier-named methods. For illustration we'll handle the parent/child relation and spouses.
function addParents($mom, $dad) {
$mom->addChild($this);
$dad->addChild($this);
}
function addChild($child) {
$this ->addRelation('children', $child);
$child->addRelation('parents', $this);
}
function addSpouse($spouse) {
$this ->addRelation('spouse', $spouse);
$spouse->addRelation('spouse', $this);
}
function getParents () { return $this->getRelations('parents'); }
function getChildren() { return $this->getRelations('children'); }
function getSpouse () { return $this->getRelation ('spouse'); }
}
Creating people
Now we can create a couple of people and setup their relationships. Let's try Billy and his parents John and Jane.
$john = new Person('John');
$jane = new Person('Jane');
$billy = new Person('Billy');
$john ->addSpouse ($jane);
$billy->addParents($jane, $john);
And we can check out their relationships like so:
echo "John is married to " . $john->getSpouse() . ".\n";
echo "Billy's parents are " . implode(" and ", $billy->getParents()) . ".\n";
Output:
John is married to Jane.
Billy's parents are Jane and John.
Display family tree
We can traverse the graph recursively if it grows bigger. Here's an example tree-walking function which displays a rudimentary family tree. I've added Sara, her husband Mike, and their son Bobby to the mix.
$john = new Person('John');
$jane = new Person('Jane');
$sara = new Person('Sara');
$mike = new Person('Mike');
$bobby = new Person('Bobby');
$billy = new Person('Billy');
$john ->addSpouse ($jane);
$sara ->addParents($jane, $john);
$sara ->addSpouse ($mike);
$bobby->addParents($sara, $mike);
$billy->addParents($jane, $john);
function displayFamilyTree($root, $prefix = "") {
$parents = array($root);
if ($root->getSpouse() != null) {
$parents[] = $root->getSpouse();
}
echo $prefix . implode(" & ", $parents) . "\n";
foreach ($root->getChildren() as $child) {
displayFamilyTree($child, "....$prefix");
}
}
displayFamilyTree($john);
Output:
John & Jane
....Sara & Mike
........Bobby
....Billy
Edit: Here's #Wrikken's comment below, reproduced for readability:
About it indeed. IMHO add a from-until date to every relationship though (possibly NULL for no end). Divorces happen, as do adoptions, etc. Also: I'd add a reverse types & 'ping-back' to the addRelation() function:
function addRelation($type, $person, $reverseType, $pingback = false) {
if (!isset($this->relations[$type])) {
$this->relations[$type] = array();
}
if (!in_array($person, $this->relations[$type], true)) {
$this->relations[$type][] = $person;
}
if (!$pingback) {
$person->addRelation($reverseType, $this, $type, true);
}
}
GEDCOM is an open specification for exchanging genealogical data between different genealogy software. A GEDCOM file is plain text (usually either ANSEL or ASCII) containing genealogical information about individuals, and meta data linking these records together. Most genealogy software supports importing from and/or exporting to GEDCOM format.
A major advantage of using GEDCOM, would be that you could use desktop programs like Aldfaer (Dutch only), Gramps or Legacy Family Tree as well as online tools like Geneanet to build or modify your family tree and compare it with the family trees of others.
Another major advantage of using the GEDCOM format, is that there are libraries in multiple programming language at your disposal to save and load your data. Examples of PHP libraries would be GEDCOM Import/Export-Filter, GenealogyGedcom or PHP GEDCOM.
Using PHP GEDCOM, reading and parsing a GEDCOM file would be as simple as this:
$parser = new \PhpGedcom\Parser();
$gedcom = $parser->parse('gedcom.ged');
A major disadvantage of using GEDCOM, is that GEDCOM's data format is built around the nuclear family, which means that the standard is limited in its support of non-traditional family structures, like same-sex partnerships, blended families or cohabitation. While it is possible to extend GEDCOM to support this type of relationships, inter-operability between different software is limited for such extensions.
Another major disadvantage of using GEDCOM, is that GEDCOM files are monolithic. If you have a dataset of thousands of people or your want to frequently change your data structure, you're likely to experience performance problems. Especially in those cases, it's best to store your data in a database instead. Still, that doesn't mean GEDCOM is useless. In those cases, you might want to consider using a database schema based on the GEDCOM format that allows you to import/export between your database and the GEDCOM format. For this, libraries exist as well. Oxy-Gen would be an example.
Alternatives to GEDCOM would be GenTech's data model or Gramps data model. While they aren't as commonly used as the GEDCOM standard, they might better suit your needs.
If you want to use the Gramps data model, you can use eg. the Gramps PHP exporter to export your data into an SQLite database. See also this source on how to design your database to be suitable for the Gramps data model.

Categories