Простое меню PHP с иерархией MPTTA - PullRequest
0 голосов
/ 03 июня 2011

Я пытаюсь создать простой неупорядоченный список меню с использованием PHP. Список заполняется из MySQL. У меня много проблем с поиском инструкций, соответствующих моим конкретным потребностям.

Требования

  1. Я стараюсь избегать функций, ссылающихся на себя.
  2. Я пытаюсь сделать это одним запросом для повышения производительности.
  3. Я использую модифицированный алгоритм обхода дерева по предварительному заказу для отображения меню.
  4. Каждая строка должна быть окружена тегом элемента списка.
  5. Списки должны открываться и закрываться там, где узлы начинаются и заканчиваются

Код:

CREATE TABLE IF NOT EXISTS `maj_topmenu` (
`menu_id` int(11) NOT NULL auto_increment,
`menu_title` varchar(100) NOT NULL default '',
`menu_url` varchar(200) NOT NULL default '',
`menu_level` int(10) NOT NULL default '0',
`parent_id` int(11) NOT NULL default '0',
`lft` int(10) NOT NULL,
`rgt` int(10) NOT NULL,
`displayorder` int(11) default NULL,
PRIMARY KEY  (`menu_id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=8 ;

INSERT INTO `maj_topmenu` (`menu_id`, `menu_title`, `menu_url`, `menu_level`, `parent_id`, `lft`, `rgt`, `displayorder`) VALUES
(1, 'Home', '/home', 0, 0, 1, 2, 0),
(2, 'About Us', '/about', 0, 0, 5, 10, 10),
(3, 'News', '/news', 0, 0, 3, 4, 5),
(4, 'Blogs', '/viewblog', 0, 0, 11, 12, 15),
(5, 'Contact Us', '/contact', 0, 0, 13, 14, 20),
(6, 'Sub Menu 1', '/link1', 1, 2, 6, 9, 0),
(7, 'Sub Menu 2', '/link2', 2, 6, 7, 8, 0);

PHP Part

$topmenu = '';
$nodes=array();
$nlbr = "\n";

// Now, retrieve all descendants of the $root node
$sql="SELECT n.menu_title, n.menu_title, n.menu_url, n.parent_id, n.lft, n.rgt FROM maj_topmenu AS n, maj_topmenu AS p WHERE n.lft BETWEEN p.lft AND p.rgt AND p.parent_id = '0' ORDER BY n.lft";
$result = mysql_query($sql) or die(mysql_error());

$topmenu .= '<ul>' . $nlbr;
while ($row = mysql_fetch_array($result))
{
    if ( ($row['rgt'] - $row['lft']) == 1 )
    {
        // No child elements
        if (sizeof($nodes) == 0)
        {
            // We're at the top with no children
            $topmenu .= '<li>'.$row['lft'].' <a href="'.$row['menu_url'].'">'.$row['menu_title'].'</a> '.$row['rgt'].'</li>' . $nlbr;
            continue;
        }
        else
        {
            // We're in the middle with no children; We'll need to end at least one branch
            $topmenu .= '<li>'.$row['lft'].' <a href="'.$row['menu_url'].'">'.$row['menu_title'].'</a> '.$row['rgt'].'</li>' . $nlbr;
            while (  ($row['rgt'] + 1) - end($nodes) == 0  )
            {
                $topmenu .= '</ul>' . $nlbr;
                array_pop($nodes);
            }

            continue;
        }
    }
    else
    {
        // Start a new branch
        $topmenu .= '<li>'.$row['lft'].' <a href="'.$row['menu_url'].'">'.$row['menu_title'].'</a> '.$row['rgt'].'</li>' . $nlbr;
        $topmenu .= '<ul>' . $nlbr;

        // End leaf at this point later
        $nodes[] = $row['rgt'];
    }
}
$topmenu .= '</ul>';

Проблема, с которой я столкнулся, заключается в том, что цикл не завершает узлы в нужных местах. Если я нахожусь на ветке 3-го уровня, это может закончить либо слишком много, либо слишком мало списков.

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

1 Ответ

0 голосов
/ 04 июня 2011

Я отредактировал этот ответ, чтобы улучшить его.

  1. Используется один SQL-запрос вместо трех

  2. Используется «порядок по», поэтому вы уверены, что перемещение по предварительному заказу соблюдается, это означает, что вы можете определить путь прохождения только путем редактирования уровня и порядка, рассматривая уровень как перемещение сверху вниз, а порядок - как перемещение влево до правый.

  3. .. тег элемента списка .. Вы имеете в виду что-то вроде 1.1, 1.2 и т. Д ... если это так, то все готово.

  4. .. где узлы начинаются и заканчиваются ... Вы имеете в виду, что уровень на ссылке полностью показан, и другой уровень не (они близки), если это так, то все в порядке.

  5. .. self-reference ... Если вы имеете в виду функцию, вызывающую ее самостоятельно, я действительно не вижу где это происходит (включая код перед редактированием) ??! Ссылки здесь могут быть отформатированы и использованы по вашему желанию!

Каждый раз, когда вы загружаете страницу, выполняется функция уровня, если вы не хотите, чтобы это происходило вообще или так часто, вы можете перейти с http-запросом - ajax!

Или вы можете использовать пользовательский кеш, например, хранить содержимое меню в сеансе, или переносить со страницы на страницу (стоимость связи, которую вы платите в любом случае, загружаете немного больше текста, не имеет значения) содержимое меню, тогда, если текущая ссылка находится на том же уровне, что и вы, используя кешированное меню, если нет, вы запускаете новый запрос Таким образом, вы можете уменьшить связь дБ.

  <?php


// Make a MySQL Connection
mysql_connect("localhost", "root", "") or die(mysql_error());

//select database
mysql_query("drop database if exists `my_test`;")or die(mysql_error());  
mysql_query("create database `my_test`;")or die(mysql_error());  
mysql_select_db("my_test") or die(mysql_error());

//create table 
mysql_query(" drop table if exists `menu_item`;")or die(mysql_error());  
mysql_query("   
CREATE TABLE `menu_item` (
    `item_id` MEDIUMINT(8) UNSIGNED NOT NULL  ,
    `item_title` VARCHAR(100) NOT NULL ,
    `item_url` VARCHAR(200)   NULL  ,
    `item_level` TINYINT UNSIGNED NOT NULL   ,
    `order` TINYINT UNSIGNED NOT NULL   , 

    PRIMARY KEY (`item_id`) 
)
COLLATE='latin1_swedish_ci'
ENGINE=InnoDB;") or die(mysql_error());  





 mysql_query("INSERT INTO `menu_item`  
VALUES (0, 'titile_000', 'http://www', 0, 0  ) ") or die(mysql_error());   
 mysql_query("INSERT INTO `menu_item`  
VALUES (1, 'titile_111', 'http://www', 0, 1  ) ") or die(mysql_error());   
 mysql_query("INSERT INTO `menu_item`  
VALUES (2, 'titile_222', 'http://www', 0, 2  ) ") or die(mysql_error());  
 mysql_query("INSERT INTO `menu_item`  
VALUES (3, 'titile_333', 'http://www', 0, 3  ) ") or die(mysql_error());  

 mysql_query("INSERT INTO `menu_item`  
VALUES (4, 'titile_444', 'http://www', 1, 0  ) ") or die(mysql_error());  
 mysql_query("INSERT INTO `menu_item`  
VALUES (5, 'titile_555', 'http://www', 1, 1 ) ") or die(mysql_error());  
 mysql_query("INSERT INTO `menu_item`  
VALUES (6, 'titile_666', 'http://www', 1, 2 ) ") or die(mysql_error());  
 mysql_query("INSERT INTO `menu_item`  
VALUES (7, 'titile_777', 'http://www', 1, 3  ) ") or die(mysql_error());  

 mysql_query("INSERT INTO `menu_item`  
VALUES (8, 'titile_888', 'http://www', 2, 0  ) ")  or die(mysql_error());  
 mysql_query("INSERT INTO `menu_item`  
VALUES (9, 'titile_999', 'http://www', 2, 1  ) ") or die(mysql_error());    
 mysql_query("INSERT INTO `menu_item`  
VALUES (10, 'titile_010', 'http://www', 2, 2  ) ") or die(mysql_error());    





function  get_level($level )
{ 

    //get level     
    $result = mysql_query("SELECT `item_title`, `item_url`, `item_level`, `order` FROM `menu_item`  
       where `order` = '0' or `item_level` ='{$level}'  order by  `item_level` , `order` ;")
       or die(mysql_error());   

    $output='<b>_____MENU_____</b><br>';

    while($row = mysql_fetch_row($result)) 
    {
        $prefix=($row[3]==0)?$row[2].'.&nbsp;':'&nbsp;&nbsp;&nbsp;'.$row[2].'.'.$row[3].'&nbsp;'; 
        $output.=  $prefix.$row[0].' | '.$row[1].'<br>'; 
    }
    echo $output;   
}


get_level(0);



?>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...