Структура данных "Семейное древо"

Я ищу способ представить генеалогическое древо в PHP. Это означает, что дети должны будут унаследовать от двух (или более) родителей.

Вот требования:

  • 1, 2 или более родителей
  • Бонусные баллы, если я могу прикрепить метаданные, такие как фамилия или статус отношений

Вот моя неработающая попытка (к сожалению, без массивов в качестве ключей):

$tree = array(
    'uncle' => false, // no children
    array('mom', 'dad') => array(
        'me' => false,
        array('brother', 'sister-in-law') => array(
            'niece' => false
        )
    )
);

Вопрос в том, как я могу представить генеалогическое дерево с этими требованиями?

2 ответа

Вы не сможете сделать все это за один раз array() как это. Вы можете настроить деревья таким образом, но для создания более сложных графиков с несколькими родителями и другими отношениями требуется несколько строк кода.

Это очень поможет, если вы добавите OO в это. Давайте создадим Person класс, чтобы помочь управлять отношениями. По сути, у нас есть люди и их отношения с другими людьми, поэтому мы начнем там.

Персональный класс

Я представляю себе, что у каждого человека есть множество отношений. Этот массив будет сначала проиндексирован по типу отношений, например, "родители" или "дети". Каждая запись будет массивом 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;
    }

Дружественные сумматоры и добытчики

Используя вышеперечисленное в качестве основы, мы можем добавить некоторые более дружественные методы. Для иллюстрации рассмотрим отношение родитель / ребенок и супруги.

    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');   }
}

Создание людей

Теперь мы можем создать пару человек и установить их отношения. Давайте попробуем Билли и его родителей Джона и Джейн.

$john  = new Person('John');
$jane  = new Person('Jane');
$billy = new Person('Billy');

$john ->addSpouse ($jane);
$billy->addParents($jane, $john);

И мы можем проверить их отношения так:

echo "John is married to " . $john->getSpouse() . ".\n";
echo "Billy's parents are " . implode(" and ", $billy->getParents()) . ".\n";

Выход:

Джон женат на Джейн.
Родители Билли - Джейн и Джон.

Показать семейное древо

Мы можем рекурсивно пересечь график, если он станет больше. Вот пример функции обхода дерева, которая отображает элементарное генеалогическое дерево. Я добавил Сара, ее мужа Майка и их сына Бобби.

$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);

Выход:

Джон и Джейн
.... Сара и Майк
........Бобби
....Билли


Изменить: Вот комментарий @Wrikken ниже, воспроизведенный для удобства чтения:

Об этом действительно. ИМХО, хотя добавьте дату "до" для каждого отношения (возможно, NULL без конца). Происходят разводы, как и усыновление, и т. Д. Кроме того: я бы добавил обратные типы & 'ping-back' к addRelation() функция:

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 - это открытая спецификация для обмена генеалогическими данными между различными программами генеалогии. Файл GEDCOM представляет собой простой текст (обычно ANSEL или ASCII), содержащий генеалогическую информацию об отдельных лицах и метаданные, связывающие эти записи вместе. Большинство генеалогических программ поддерживают импорт и / или экспорт в формат GEDCOM.

Основным преимуществом использования GEDCOM будет то, что вы можете использовать настольные программы, такие как Aldfaer (только на голландском языке), Gramps или Legacy Family Tree, а также онлайн-инструменты, такие как Geneanet, для создания или изменения вашего семейного дерева и сравнения его с семейными деревьями других.,

Другим важным преимуществом использования формата GEDCOM является то, что в вашем распоряжении есть библиотеки на нескольких языках для сохранения и загрузки ваших данных. Примерами библиотек PHP могут быть GEDCOM Import / Export-Filter, GenealogyGedcom или PHP GEDCOM.

Используя PHP GEDCOM, чтение и разбор файла GEDCOM будет таким простым:

$parser = new \PhpGedcom\Parser();
$gedcom = $parser->parse('gedcom.ged');

Основным недостатком использования GEDCOM является то, что формат данных GEDCOM строится вокруг ядерной семьи, что означает, что стандарт ограничен в поддержке нетрадиционных семейных структур, таких как однополые партнерства, смешанные семьи или совместное проживание. Несмотря на то, что возможно расширить GEDCOM для поддержки этого типа отношений, совместимость между различными программами ограничена для таких расширений.

Еще одним существенным недостатком использования GEDCOM является то, что файлы GEDCOM являются монолитными. Если у вас есть набор данных из тысяч человек или вы хотите часто менять структуру данных, у вас могут возникнуть проблемы с производительностью. Особенно в этих случаях лучше хранить ваши данные в базе данных. Тем не менее, это не значит, что GEDCOM бесполезен. В этих случаях вы можете рассмотреть возможность использования схемы базы данных на основе формата GEDCOM, которая позволяет импортировать / экспортировать между вашей базой данных и форматом GEDCOM. Для этого также существуют библиотеки. Окси-Ген был бы примером.


Альтернативами GEDCOM могут быть модель данных GenTech или модель данных Gramps. Хотя они не так часто используются, как стандарт GEDCOM, они могут лучше соответствовать вашим потребностям.

Если вы хотите использовать модель данных Gramps, вы можете использовать, например,. PHP-экспортер Gramps для экспорта ваших данных в базу данных SQLite. См. Также этот источник о том, как спроектировать базу данных так, чтобы она подходила для модели данных Gramps.

Другие вопросы по тегам