MaterializedPath

Renny

Новичок
MaterializedPath

Пишу простенький класс для работы с деревом методом MaterializedPath. Хочу потом выложить у себя на сайте, для свободного доступа и здесь, в Wiki.

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

Необходимо создать методы:
1 - Выдача данных о ноде, по ее номеру
2 - Добавление ноды
3 - Удаление ноды и всех ее потомков
4 - Редактирование данных ноды
5 - Выдать список(массив) всех непосредственных потомков заданной ноды
6 - Выдать массив всех потомков заданной ноды (если задать 0, то выведется все дерево целиком)
7 - Перемищение ноды в пределах одного родителя
8 - ...

И так начало:
SQL_PREFIX - это префикс для таблиц в базе данных, он берется из файла конфигурации.
parents - это строка вида ' 1/5/12/78/234 ', где числа это номера родительских элементов, последнее число - это ID прямого предка, то есть непосредственного родителя.

LAST MODIFIED 03.02.2006 13:40
PHP:
class MaterializedPath{

var $tbl_prefix;
var $col_id;
var $col_top;
var $col_parents;
var $col_order;

  function MaterializedPath($tbl_prefix='',$col_arr='')
  {
    if($tbl_prefix=='')
    {
      $this->tbl_prefix = "_nodes";
    }
    else
    {
      $this->tbl_prefix = "_".$tbl_prefix;
    }
    if($col_arr=='')
    {
      $this->col_id='n_id';
      $this->col_top='n_top';
      $this->col_parents='n_par';
      $this->col_order='n_ord';
    }
    else
    {

      $this->col_id=$col_arr['id'];
      $this->col_top=$col_arr['top'];
      $this->col_parents=$col_arr['par'];
      $this->col_order=$col_arr['ord'];
    }
  }


  function GetNodeInfo($id)
  {
    $result=mysql_query("select ".$this->col_top.",".$this->col_parents.",".$this->col_order." from ".SQL_PREFIX.$this->tbl_prefix." where ".$this->col_id."=$id");
    if(mysql_num_rows($result)>0)
    {
      $res=mysql_fetch_array($result);
      $node_info=array(
      "id"=>$res[$this->col_id],
      "top"=>$res[$this->col_top],
      "par"=>$res[$this->col_parents],
      "ord"=>$res[$this->col_order]);
    }
    else
    {
      $node_info=false;
    }
    return $node_info;
  }

 function AddNode($parent_id=0)
  {
    //error_a - viborka max znachenija ne udalas
    //error_b - ne naidena noda s id==$parent_id
    //error_c - ne udalos dobavit nodu
    //error_d - ne ustanovlen tip AUTO_INCREMENT u polja id
    //error_z - nepravilnii vhodjashii parametr!

    if(!is_numeric($parent_id)) $new_node='error_z';
    else
    {
      if($parent_id==0) //esli $parent_id==0 to znachit nado dobavit nodu v koren
      {
        $max_result=mysql_query("select MAX(".$this->col_order.") from ".SQL_PREFIX.$this->tbl_prefix." where ".$this->col_top."=$parent_id");
        if(!$max_result) $new_node='error_a';
        else
        {
          if(mysql_num_rows($max_result)>0)
          {
            $max=mysql_fetch_array($max_result);
            $order=$max['MAX('.$this->col_order.')']+1;
          }
          else $order=1;
          $new_parents='0';
        }
      }
      else
      {
        $parent_result=mysql_query("select ".$this->col_parents." from ".SQL_PREFIX.$this->tbl_prefix." where ".$this->col_id."=$parent_id");
        if(mysql_num_rows($parent_result)>0)
        {
          $parents=mysql_fetch_array($parent_result);
          $max_result=mysql_query("select MAX(".$this->col_order.") from ".SQL_PREFIX.$this->tbl_prefix." where ".$this->col_top."=$parent_id");
          if(!$max_result) $new_node='error_a';
          else
          {
            if(mysql_num_rows($max_result)>0)
            {
              $max=mysql_fetch_array($max_result);
              $order=$max['MAX('.$this->col_order.')']+1;
            }
            else $order=1;
            $new_parents=$parents[$this->col_parents]."/".$parent_id;
          }
        }
        else $new_node='error_b';
      }
      if(!isset($new_node)) //esli ne proizoshla oshibka
      {
        $add_result=mysql_query("insert into ".SQL_PREFIX.$this->tbl_prefix." (".$this->col_top.",".$this->col_parents.",".$this->col_order.") values ($parent_id,'$new_parents',$order)");
        if(!$add_result) $new_node='error_c';
        else $new_node=mysql_insert_id();
        if($new_node==0)$new_node='error_d';
      }
    }
    return $new_node;
  }
  //vozvrashaem massiv neposredstvennih potomkov ili  nomer oshibki
  function GetKids($parent_id)
  {
    // 0 - bad parameter
    // 1 - mysql_error
    // 2 - no kids
    if(!ctype_digit($parent_id)) $tree=0; //bad parametr
    $result=mysql_query("select ".$this->col_id.",".$this->col_top.",".$this->col_parents.",".$this->col_order." from ".SQL_PREFIX.$this->tbl_prefix." where ".$this->col_top."=$parent_id order by ".$this->col_order);
    if(!$result) $tree=1;
    else
    {
      $num=mysql_num_rows($result);
      if($num!=0)
      {
        $tree=array();
        while($res=mysql_fetch_array($result))
        {
          $tree[]=array("id"=>$res[$this->col_id],"top"=>$res[$this->col_top],"par"=>$res[$this->col_parents],"ord"=>$res[$this->col_order]);
        }
      }
      else $tree=2;
    }
    return $tree;
  }
}
 

moxnatiy

Новичок
Вопрос по коду.
Что это и для чего это?


Включи для начала нотайсы и посмотри что тебе говорит интерпретатор.
И вообще непонятно что этот класс с одним методом делает. Зачем и почему ООП ради ООП?
 

Renny

Новичок
Автор оригинала: moxnatiy
Вопрос по коду.
Что это и для чего это?
Включи для начала нотайсы и посмотри что тебе говорит интерпретатор.
И вообще непонятно что этот класс с одним методом делает. Зачем и почему ООП ради ООП?
Это библиотека для работы с деревями. Надо ее когда будет готова добавить в wiki.
Ошибки правлю, если заметил, скажи где. Методы надо добавлять, этим я и занимаюсь.
 

Renny

Новичок
Этот класс необходим для работы с несколькими деревьями, в рамках одной базы данных, для тех кто предпочитает использовать метод Materialized Path. Например удобно хранить тектстовые разделы на простеньком сайте.

Сейчас делаю метод удаления ноды и всех ее потомков.

-~{}~ 03.02.06 11:48:

Хоть кому то это интересно?
 

Popoff

popoff.donetsk.ua
Renny
Интересно.

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

Проблема - не в простеньком сайте, а в больших деревьях. Если ты рассчитываешь эту библиотеку для маленьких деревьев, то она не нужна. Она может понадобиться только для очень больших деревьев. В случае, если она окажется лучше уже существующих.

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

Renny

Новичок
2 Popoff
Привет, спасибо за интерес.

Я начал писать эту библиотеку, тк выбрал именно этот метод хранения по ряду причин ( раньше везде пользовал Nested Sets, но он не везде применим ), например меня жутко напрягает перебор значительной части дерева при перемещении узла с большим количеством потомков, в пределах одного родителя, и тд

В дальнейшем хочу написать новую библиотеку, но уже модификацию Tropashko.

Сам знаешь, что в инете найти что-то дельное немного проблематично, вот и предлагаю, совместными усилиями написать дельную библиотеку.

А большие деревья не такая уж редкость.
 

Popoff

popoff.donetsk.ua
Renny
раньше везде пользовал Nested Sets, но он не везде применим
Материализованные пути тоже имеют свои недостатки :) Может, тебе стоит для начала написать сравнительную таблицу - что чем лучше, а что чем хуже? :)

вот и предлагаю, совместными усилиями написать дельную библиотеку.
Если ты хочешь получить эту библиотеку, то тебе придется написать ее самостоятельно. Тебе не помогут.
 

Renny

Новичок
Автор оригинала: Popoff
Renny
Материализованные пути тоже имеют свои недостатки :) Может, тебе стоит для начала написать сравнительную таблицу - что чем лучше, а что чем хуже? :)

Если ты хочешь получить эту библиотеку, то тебе придется написать ее самостоятельно. Тебе не помогут.
Сравнительная табличка лежит в Wiki, ( твоя? :) ) Если не дожду сь помощи, то помещу, такую какая есть.
 

Renny

Новичок
нет, плохо делать не буду, она для рабочего проекта, просто наименьшими усилиями хочу сделать наиболее качественно.
Сейчас создаю метод вывода ветви дерева.

PHP:
Вот следующий метод - выдает массив всех потомков.

  //выдает массив всех потомков
  function GetSubBranch($parent_id)
  {
    if(!isset($this->tree)) $this->tree=array();
    $result=mysql_query("select ".$this->col_id.",".$this->col_top.",".$this->col_parents.",".$this->col_order." from ".SQL_PREFIX.$this->tbl_prefix." where ".$this->col_top."=$parent_id order by ".$this->col_order);

    while($res=mysql_fetch_array($result))
    {
      $this->tree[]=array("id"=>$res[$this->col_id],"top"=>$res[$this->col_top],"par"=>$res[$this->col_parents],"ord"=>$res[$this->col_order]);
      $this->GetSubBranch($res[$this->col_id]);
    }
    return $this->tree;
  }
-~{}~ 04.02.06 15:16:

// выдает true или false
function DelNodes($n_id)
{
$node_kids=$this->GetSubBranch($n_id);
$num_kids=count($node_kids);
$i=0;
if($num_kids!=0)
{
foreach($node_kids as $k=>$v)
{
$result=mysql_query("delete from ".SQL_PREFIX.$this->tbl_prefix." where ".$this->col_id."=".$v['id']);
if($result) $i++;
}
}
$result2=mysql_query("delete from ".SQL_PREFIX.$this->tbl_prefix." where ".$this->col_id."=".$n_id);
if($num_kids==$i && $result2) $res=true;
else $res=false;
return $res;
}
 

zarus

Хитрожопый макак
2Renny
А чем эта библиотека отличается от библиотеки для Adjacency Lists, написанной Popoff?
Плюс полного пути в том, что можно обратиться к предку узла на несколько уровней выше непосредственного родителя, не используя рекурсивную выборку из базы, а просто отсекая ненужные части полного пути. Аналогично касается и выборки потомков узла - опять же идет поиск по полному пути.
В остальном преимуществ не вижу. Да и в Вашем коде я использования этих преимуществ пока не заметил - Вы используете стандартный подход для списков смежности.
 

Renny

Новичок
2zarus
Вы правы - это Adjacency Lists, немного модифицированный.
Его плюсы:
1 - быстрая выборка всех родителей (очень частая операция, например для пути на сайте)
2 - Можно работать с разными деревьями, можно называть таблицы, как тербуется, и полям присваяивая любые имена.
 

su1d

Старожил PHPClubа
Автор оригинала: Renny
2 Popoff
В дальнейшем хочу написать новую библиотеку, но уже модификацию Tropashko.
попробуй, конечно. может хоть у тебя получится. у меня не вышло. =)

метод Тропашко -- больше теоретический, чем практический.
коэффициенты у него слишком быстро растут и выходят за пределы 4-х байт одного INT'а.
в итоге получается, что максимальная глубина дерева -- 6-7 уровней.
к тому же, там ещё некоторые проблемы с индексацией будут.
 

Renny

Новичок
на практике лично мне ни разу не приходилось встречаться с глубиной дерева больше 6.
 

zk

Новичок
ммм... А что это она такая вся под mysql заточена? Может есть смыл делать её более асбтрактно?
 

zarus

Хитрожопый макак
Автор оригинала: zk
ммм... А что это она такая вся под mysql заточена? Может есть смыл делать её более асбтрактно?
Абстракция мешает конкретной реализации, лучше сначала сделать рабочий код и отточить его.
 

zk

Новичок
Тоже вариант!
Но иногда лучче подумать сразу об этом =)) А то потом рефакторить запаришься! =)
 

Renny

Новичок
Так зачем дело то встало? Пиши :) Коллективное творчество привествуется :)
 
Сверху