Advertisement
azverin

AL+MP one-to-many

Feb 27th, 2012
1,152
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 7.12 KB | None | 0 0
  1. <?php
  2.  
  3. class Tree {
  4.  
  5.     /** Колонка по которой строится путь */
  6.     static $col_pid = 'parent_id';
  7.  
  8.     /** Суффикс таблицы где хранятся пути */
  9.     static $table_suffix = '_paths';
  10.  
  11.     /** @var PDO */
  12.     static $pdo;
  13.  
  14.     /** Хендлер подключения PDO */
  15.     public static function SetConnection ( PDO $pdo ) {
  16.         self::$pdo = $pdo;
  17.     }
  18.  
  19.     /** Запрос в БД и фетч массива */
  20.     public static function Query ( $sql ) {
  21.         $stmt = self::$pdo->query($sql);
  22.         if ( $stmt )
  23.             return $stmt->fetchAll(PDO::FETCH_ASSOC);
  24.         $i = self::$pdo->errorInfo();
  25.         return $i[2];
  26.     }
  27.  
  28.     /** Путь к элементу */
  29.     public static function GetPath ( $table, $id ) {
  30.         $sql = 'SELECT t.*, p.level as p_level, p.order as p_order '
  31.               .'FROM `'.$table.self::$table_suffix.'` p '
  32.               .'JOIN `'.$table.'` t ON `id`=p.`parent_id` '
  33.               .'WHERE item_id='.$id.' ORDER BY `order`';
  34.         return self::Query($sql);
  35.     }
  36.  
  37.     /** Все дети */
  38.     public static function GetChildren ( $table, $id ) {
  39.         $sql = 'SELECT t.*, p.level as p_level, p.order as p_order '
  40.               .'FROM `'.$table.self::$table_suffix.'` p '
  41.               .'JOIN `'.$table.'` t ON `id`=`item_id` '
  42.               .'WHERE p.`parent_id`='.$id.' ORDER BY p.`level`, p.`order`';
  43.         return self::Query($sql);
  44.     }
  45.  
  46.     /** Количество детей */
  47.     public static function GetChildrenCount ( $table, $id ) {
  48.         $sql = 'SELECT count(*) FROM `'.$table.self::$table_suffix.'` WHERE parent_id='.$id.'';
  49.         $stmt = self::$pdo->query($sql);
  50.         if ( $stmt )
  51.             return $stmt->fetchColumn(0);
  52.         else
  53.             return false;
  54.     }
  55.  
  56.     /** Построение дерева из родительской таблицы */
  57.     public static function GetTree ( $table ) {
  58.         $sql = 'SELECT `id`, `'.self::$col_pid.'` FROM `'.$table.'`';
  59.         $stmt = self::$pdo->query($sql);
  60.         if ( !$stmt ) return false;
  61.         $res = $stmt->fetchAll(PDO::FETCH_ASSOC);
  62.         return self::BuildTree( $res );
  63.     }
  64.  
  65.     /**
  66.      * Создание путей в доп.таблице
  67.      * @param $table имя основной таблицы
  68.      * @param bool $ids если нужно достроить путь - указываются ID нужных веток
  69.      * @return bool
  70.      */
  71.     public static function GeneratePaths ( $table, $ids = true ) {
  72.  
  73.         if ( !$tree = self::GetTree( $table ) ) return false;
  74.  
  75.         self::$pdo->beginTransaction();
  76.         if ( $ids === true ) self::$pdo->query( 'TRUNCATE '.$table.self::$table_suffix );
  77.  
  78.         $sql = 'INSERT INTO '.$table.self::$table_suffix.' (`item_id`, `parent_id`, `level`, `order`) '
  79.               .'VALUES (:item_id, :parent_id, :level, :order)';
  80.         $stmt = self::$pdo->prepare($sql);
  81.  
  82.         try {
  83.             self::WalkthruTree( $tree, $stmt, $ids );
  84.             self::$pdo->commit();
  85.         } catch ( Exception $e ) {
  86.             echo $e->getMessage();
  87.             self::$pdo->rollBack();
  88.         }
  89.     }
  90.  
  91.     /** Создание в таблице $table количества $total записей на $lvls уровней вложенности */
  92.     public static function Fixture ( $table, $total, $lvls = 3 ) {
  93.  
  94.         //TRUNCATE не работает из-за внешнего ключа
  95.         self::$pdo->query( 'DELETE FROM '.$table );
  96.         self::$pdo->query( 'ALTER TABLE '.$table.' AUTO_INCREMENT=1' );
  97.  
  98.         //Распределение элементов по уровням вложенности
  99.         $num = $total;
  100.         $lvls_arr = array();
  101.         for ( $i=$lvls; $i>0; $i-- ) {
  102.             if ( $i == 1 ) $limit = $num;
  103.             else {
  104.                 $limit = ceil($num*0.6);
  105.                 $num -= $limit;
  106.             }
  107.             $lvls_arr[$i] = $limit;
  108.         }
  109.  
  110.         //Пихаем всё в БД
  111.         $stmt = self::$pdo->prepare('INSERT INTO `'.$table.'` (parent_id, title) VALUES (:parent_id, :title)');
  112.         $p_arr = array();
  113.         for ( $i = 1; $i<=$lvls; $i++ ) {
  114.             $limit = $lvls_arr[$i];
  115.             echo $i.' уровень: '.$limit.' штук.<br>';
  116.             while ( $limit-- ) {
  117.                 $prev = NULL;
  118.                 if ( $i>1 ) {
  119.                     $c = count ( $p_arr[($i-1)] )-1;
  120.                     $prev = $p_arr[($i-1)][mt_rand(0,$c)];
  121.                 }
  122.                 $stmt->bindValue(':parent_id', $prev);
  123.                 $stmt->bindValue(':title', md5(uniqid(NULL, true)));
  124.                 if ( $stmt->execute() )
  125.                     $p_arr[$i][] = self::$pdo->lastInsertId();
  126.                 else throw new Exception ( 'Ошибка при добавлении страницы: '.$stmt->errorInfo() );
  127.             }
  128.         }
  129.     }
  130.  
  131.     /** Проход по дереву и создание соотв. путей в БД */
  132.     private static function WalkthruTree ( $arr, &$pdo_stmt, $ids = true, $prev = NULL, $lvl = 0 ) {
  133.  
  134.         if ( is_numeric( $ids ) ) $ids = array( $ids );
  135.  
  136.         if ( is_array( $arr ) ) foreach ( $arr as $id=>$a ) {
  137.             if ( is_array( $prev ) ) foreach ( $prev as $pid=>$order ) {
  138.                 try {
  139.                     if ( $ids === true )
  140.                         self::PutPathItem( $pdo_stmt, $id, $pid, $order, $lvl );
  141.                     elseif ( is_array( $ids ) && in_array( $id, $ids ) ) {
  142.                         self::PutPathItem( $pdo_stmt, $id, $pid, $order, $lvl );
  143.                         if ( is_array( $a ) ) $ids += array_merge($ids, array_keys($a) );
  144.                     }
  145.                 } catch ( Exception $e ) {
  146.                     echo '<p>'.$e->getMessage().'</p>';
  147.                 }
  148.             }
  149.             if ( is_array( $a ) ) {
  150.                 $prev_new = $prev;
  151.                 $prev_new[$id] = $lvl;
  152.                 self::WalkthruTree( $a, $pdo_stmt, $ids, $prev_new, ($lvl+1) );
  153.             }
  154.         }
  155.     }
  156.  
  157.     /** Пихалово пути в БД */
  158.     private static function PutPathItem ( &$pdo_stmt, $id, $pid, $order, $level ) {
  159.         $str = 'item_id: '.$id.', parent_id: '.$pid.', order: '.$order.', level: '.$level;
  160.         if ( $pdo_stmt instanceof PDOStatement ) {
  161.             $pdo_stmt->bindValue(':item_id',     $id);
  162.             $pdo_stmt->bindValue(':parent_id',   $pid);
  163.             $pdo_stmt->bindValue(':order',       $order);
  164.             $pdo_stmt->bindValue(':level',       $level);
  165.             if ( !$pdo_stmt->execute() )
  166.                 throw new Exception ( 'Ошибка при добавлении пути: '.$str );
  167.         } else //{}
  168.             echo $str.'<br>';
  169.     }
  170.  
  171.     /** Построение массива по таблице id => parent_id */
  172.     private static function BuildTree ( $arr, $pid = NULL ) {
  173.         $tmp = array();
  174.         foreach ( $arr as $row ) if ( $row['parent_id'] == $pid )
  175.             $tmp[$row['id']] = self::BuildTree( $arr, $row['id'] );
  176.         return count($tmp) ? $tmp : true;
  177.     }
  178. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement