Rekordlista-szerű tömbök fa struktúrába rendezése - Fa menüstruktúra

Gyakori feladat olyan menüket készíteni, amik fa struktúrában épülnek fel és akár egy javascript program készít belőle lenyíló menüt. Vagy akár csak egy oldaltérképre is gondolhatunk. A következő megoldásom főként azon alapszik, amikor egy adatbázisban tárolunk valamilyen adatokat. Legyen az többszintű kategória rendszer vagy menüszerkezet. Ilyenkor többnyire van egy egyedi azonosító. Amire az alárendelt rekordok hivatkoznak egy másik mezőben. Emellett persze egyéb adatokat tartalmaz a rekord. Legalább egy megjelenítendő mező is van, ami például a menü szövege. De lehet hozzá leírás is rendelve. Különböző listák lehetnek, de ami közös bennük, az a felépítés módja. Azon túl más lehet a megjelenítés. Az egyiket például egyszerű rendezetlen listában kell megjeleníteni, a másikat talán már táblázatban. A harmadikat pedig csak tabulátorokat használva. Ráadásul ha már html megjelenítésről beszélünk, ott képbe jön a css is. HTML attribútumok, osztályok. Célszerűen, lehetőleg elérhetővé téve minden elemhez egy hivatkozást, amivel egyedien lehet szükség esetén formázni akár a második menüelem 3. gyerekének 1 gyerekét. Stb... Erre mutatok egy lehetséges, ám nem mindenre kiterjedő megoldást.

Példa

Felhasznált tömb

  1. $array = array(
  2.         array(
  3.                 'id' => 1,
  4.                 'name' => 'Első menü',
  5.                 'description' => 'Ez az első menü',
  6.                 'url' => 'elso',
  7.                 'parent' => 0
  8.         ),
  9.         array(
  10.                 'id' => 2,
  11.                 'name' => 'Első menü - Almenü 1',
  12.                 'description' => 'Ez a főmenübe tartozó első almenü',
  13.                 'url' => 'elso/almenu1',
  14.                 'parent' => 1
  15.         ),
  16.         array(
  17.                 'id' => 3,
  18.                 'name' => 'Első menü - Almenü 2',
  19.                 'description' => 'Második almenü a főmenüben',
  20.                 'url' => 'elso/almenu2',
  21.                 'parent' => 1
  22.         ),
  23.         array(
  24.                 'id' => 4,
  25.                 'name' => 'Második menü',
  26.                 'description' => 'Ez egy új menü kategória',
  27.                 'url' => 'masodik',
  28.                 'parent' => 0
  29.         ),
  30.         array(
  31.                 'id' => 5,
  32.                 'name' => 'Második menü - Almenü 1',
  33.                 'description' => 'Második menübe való első almenü',
  34.                 'url' => 'masodik/almenu1',
  35.                 'parent' => 4
  36.         ),
  37.         array(
  38.                 'id' => 6,
  39.                 'name' => 'Második almenü - Almenü 1 - Al almenü 1',
  40.                 'description' => 'Ez már harmadik szint',
  41.                 'url' => 'masodik/almenu1/alalmenu1',
  42.                 'parent' => 5
  43.         ),
  44.         array(
  45.                 'id' => 7,
  46.                 'name' => 'Második almenü - Almenü 1 - Al almenü 2',
  47.                 'description' => '',
  48.                 'url' => 'masodik/almenu1/alalmenu2',
  49.                 'parent' => 5
  50.         ),
  51.         array(
  52.                 'id' => 9,
  53.                 'name' => 'Ismeretlen menü',
  54.                 'description' => 'Menü, aminek nincs meg a szülője',
  55.                 'url' => 'ismeretlen',
  56.                 'parent' => 8
  57.         ),
  58.         array(
  59.                 'id' => 14,
  60.                 'name' => 'Ismeretlen menü - Almenü 1',
  61.                 'description' => 'Itt nincs',
  62.                 'url' => 'ismeretlen/almenu1',
  63.                 'parent' => 9
  64.         )
  65. );

PHP és HTML vegyesen, sorban

  1. <?php
  2. $tree = new MenuTree($array, 'mainmenu');
  3. ?>
  1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
  2. <html xmlns="http://www.w3.org/1999/xhtml">
  3.         <head>
  4.                 <title></title>
  5.                 <meta http-equiv="content-type" content="text/html; charset=utf-8" />
  6.                 <link rel="stylesheet" type="text/css" href="style.css" />
  7.         </head>
  8.         <body>
  9.                 <div id="container">

                <?php echo $tree; ?>

  1.                 </div>
  2.         </body>
  3. </html>

style.css

  1. #mainmenu .menuitem.level-1.title {
  2.         font-weight: bold;
  3. }
  4. #mainmenu .menuitem.level-2.menuitem-1.title {
  5.         color: red;
  6. }
  7. .menu a.title {
  8.         text-decoration: none;
  9. }

Az eredmény

Forrás

  1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
  2. <html xmlns="http://www.w3.org/1999/xhtml">
  3.         <head>
  4.                 <title></title>
  5.                 <meta http-equiv="content-type" content="text/html; charset=utf-8" />
  6.                 <link rel="stylesheet" type="text/css" href="style.css" />
  7.         </head>
  8.         <body>
  9.                 <div id="container">
  10.                 <ul class="menu level-0 menu-0" id="mainmenu"><li class="mainmenu menuitem level-1 menuitem-1 menuitem-path-1" id="mainmenu-item-1" ><a title="Ez az első menü" href="elso" class="mainmenu menuitem level-1 menuitem-1 title">Első menü</a><ul class="menu level-1 menu-1" id="mainmenu-1"><li class="mainmenu menuitem level-2 menuitem-1 menuitem-path-1-1" id="mainmenu-item-2" ><a title="Ez a főmenübe tartozó első almenü" href="elso/almenu1" class="mainmenu menuitem level-2 menuitem-1 title">Első menü - Almenü 1</a></li><li class="mainmenu menuitem level-2 menuitem-2 menuitem-path-1-2" id="mainmenu-item-3" ><a title="Második almenü a főmenüben" href="elso/almenu2" class="mainmenu menuitem level-2 menuitem-2 title">Első menü - Almenü 2</a></li></ul></li><li class="mainmenu menuitem level-1 menuitem-2 menuitem-path-2" id="mainmenu-item-4" ><a title="Ez egy új menü kategória" href="masodik" class="mainmenu menuitem level-1 menuitem-2 title">Második menü</a><ul class="menu level-1 menu-2" id="mainmenu-4"><li class="mainmenu menuitem level-2 menuitem-1 menuitem-path-2-1" id="mainmenu-item-5" ><a title="Második menübe való első almenü" href="masodik/almenu1" class="mainmenu menuitem level-2 menuitem-1 title">Második menü - Almenü 1</a><ul class="menu level-2 menu-1" id="mainmenu-5"><li class="mainmenu menuitem level-3 menuitem-1 menuitem-path-2-1-1" id="mainmenu-item-6" ><a title="Ez már harmadik szint" href="masodik/almenu1/alalmenu1" class="mainmenu menuitem level-3 menuitem-1 title">Második almenü - Almenü 1 - Al almenü 1</a></li><li class="mainmenu menuitem level-3 menuitem-2 menuitem-path-2-1-2" id="mainmenu-item-7" ><a title="" href="masodik/almenu1/alalmenu2" class="mainmenu menuitem level-3 menuitem-2 title">Második almenü - Almenü 1 - Al almenü 2</a></li></ul></li></ul></li></ul>          </div>
  11.         </body>
  12. </html>

Megjelenés

Megoldás

Opciók

Konstruktorban átadhatók opciók tömbként, amiket a lista generáláskor tovább ad a generáló metódus szükség szerint módosítva a gyermek listáknak.

Név Leírás Alapértelmezett
i Egy ciklusváltozó annak jelölésére, hogy adott listában adott elem hányadik. 1
pathi Egy tömb, ami tartalmazza sorra azon „i” indexeket, amin keresztül az elemhez eljuthatunk. array()
level Annak száma, hogy adott menü hányadik szintű listában szerepel. 0
parentIndex Az egyedi azonosító neve. id
refToParent A szülő egyedi azonosítójára hivatkozó mező neve. parent
parentValue A szülő egyedi azonosítójának értéke 0
titleIndex A megjelenítendő mező neve. Egyedi megjelenítésnél figyelmen kívül is hagyható. title

Tree osztály

Ez az osztály tartalmazza a lista felépítéséhez szükséges metódusokat. Valamint olyan, a megjelenítést szabályzó metódusokat, amiket egy gyermekosztályban felül lehet írni. Így egyedi, saját megjelenítést adni a listának.

Metódus Leírás Argumentumok
__construct Konstruktor
  • $array: Kétdimenziós tömb. Mint egy adatbázisból lekérd rekordlista.
  • $options: Opciók
asArray Visszaadja a fa struktúrát tömbként.
asString() Visszaadja a fát stringként
getChildsAsString Visszaadja a gyerekek listáját stringként.
  • $node: A node tömbje.
getChildsStartString Visszatérés: Egy node gyerekeinek string alakban listázásánál mi kerüljön a lista elé
  • $node: A node tömbje.
getChildsEndString Visszatérés: Egy node gyerekeinek string alakban listázásánál mi kerüljön a lista után
  • $node: A node tömbje.
getItemAsString Visszaad egy lista elemet stringként
  • $node: A node tömbje.
getItemStartString Visszaadja, mi kerüljön egy listaelem elé.
  • $node: A node tömbje.
getItemEndString Visszaadja, mi kerüljön egy listaelem után.
  • $node: A node tömbje.

A node alatt a következő struktúrájú tömb értendő:

  1.    'item' => //Aktuális lista elem tömbje,
  2.    'childs' => //Újabb node,
  3.    'level' => //szint száma,
  4.    'i' => //elem sorszáma,
  5.    'pathi' => //útvonal tömbje
  6. )

MenuTree osztály

Ez az osztály felülírja a Tree osztály alapbeállításait. Csak azokat, amikben különbözik, vagy speciálisabb működésre van szüksége. Így például az átadott $node argumentumokat felhasználva egyedi html attribútumokat lehet rendelni a listák címkéihez. Ezt meg is teszi a MenuTree osztály.

Metódus Leírás Argumentumok
__construct Konstruktor
  • $array: Kétdimenziós tömb. Mint egy adatbázisból lekérd rekordlista.
  • $id: ID, amit felhasznál a html címkék osztályaiban és ID-kben azonosításra.
getChildsStartString Visszatérés: Egy node gyerekeinek string alakban listázásánál mi kerüljön a lista elé
  • $node: A node tömbje.
getItemAsString Visszaad egy lista elemet stringként
  • $node: A node tömbje.
getItemStartString Visszaadja, mi kerüljön egy listaelem elé.
  • $node: A node tömbje.

A többi metódus nem változik a Tree osztályhoz képest.

Forráskódok

Tree osztály

  1. /**
  2.  * Fa struktúra
  3.  */
  4. class Tree {
  5.  
  6.         /**
  7.          * Eredeti tömb
  8.          *
  9.          * @var array
  10.          */
  11.         private $_items = array();
  12.  
  13.         /**
  14.          * A létrehozott fa tömbje
  15.          *
  16.          * @var array
  17.          */
  18.         private $_tree = null;
  19.  
  20.         /**
  21.          * A fa string reprezentációja
  22.          *
  23.          * @var string
  24.          */
  25.         private $_treeAsString = null;
  26.  
  27.         /**
  28.          * Opciók a fa és annak string alakjának létrehozásához.
  29.          *
  30.          * @var array
  31.          */
  32.         private $_options = array(
  33.                 'parentIndex' => 'id',
  34.                 'refToParent' => 'parent',
  35.                 'parentValue' => 0,
  36.                 'titleIndex' => 'title',
  37.                 'level' => 0,
  38.                 'i' => 1,
  39.                 'pathi' => array()
  40.         );
  41.  
  42.         /**
  43.          *
  44.          * @param array $array Kétdimenziós tömb. Mint egy adatbázisból lekért rekordlista.
  45.          * @param array $options Opciók
  46.          */
  47.         public function __construct(array $array, array $options = array()) {
  48.                 $this->_items = $array;
  49.                 $this->_options = $options + $this->_options;
  50.         }
  51.  
  52.         /**
  53.          * Lista string kezdése
  54.          *
  55.          * Egy node gyerekeinek string alakban listázásánál mi kerüljön a lista elé.
  56.          *
  57.          * @param array $node Node
  58.          * @return string
  59.          */
  60.         public function getChildsStartString(array $node) {
  61.                 return '<ul>';
  62.         }
  63.  
  64.         /**
  65.          * Lista  befejezése
  66.          *
  67.          * Egy node gyerekeinek string alakban listázásánál mi kerüljön a lista után.
  68.          *
  69.          * @param array $node Node
  70.          * @return string
  71.          */
  72.         public function getChildsEndString(array $node) {
  73.                 return '</ul>';
  74.         }
  75.  
  76.         /**
  77.          * Egy lista elem megjelenítése
  78.          *
  79.          * @param array $node
  80.          * @return string
  81.          */
  82.         public function getItemAsString(array $node) {
  83.                 $item = &$node['item'];
  84.                 return isset($item[$this->_options['titleIndex']]) ? $item[$this->_options['titleIndex']] : $item[$this->_options['parentIndex']];
  85.         }
  86.  
  87.         /**
  88.          * Mi kerüljön egy listaelem elé.
  89.          *
  90.          * @param array $node Node
  91.          * @return string
  92.          */
  93.         public function getItemStartString(array $node) {
  94.                 return '<li>';
  95.         }
  96.  
  97.         /**
  98.          * Mi kerüljön egy listaelem végére
  99.          *
  100.          * @param array $node Node
  101.          * @return string
  102.          */
  103.         public function getItemEndString(array $node) {
  104.                 return '</li>';
  105.         }
  106.  
  107.         /**
  108.          * Egy lista megjelenítése
  109.          *
  110.          * @param array $node Node
  111.          * @return string
  112.          */
  113.         public function getChildsAsString(array $node) {
  114.                 return sprintf('%s%s%s%s', $this->getItemStartString($node),
  115.                                 $this->getItemAsString($node), $node['childs']
  116.                                         ? $this->_asString($node)
  117.                                         : '', $this->getItemEndString($node));
  118.         }
  119.  
  120.         /**
  121.          * A teljes fa lekérése tömbként
  122.          *
  123.          * @return array
  124.          */
  125.         public function asArray() {
  126.                 if (is_null($this->_tree)) {
  127.                         $this->_tree = $this->_build($this->_items, $this->_options);
  128.                 }
  129.                 return $this->_tree;
  130.         }
  131.  
  132.         /**
  133.          *
  134.          * @param array $node
  135.          * @return string
  136.          */
  137.         private function _asString(array $node) {
  138.                 $ret = $this->getChildsStartString($node);
  139.  
  140.                 foreach ($node['childs'] as &$item) {
  141.                         $ret .= $this->getChildsAsString($item);
  142.                 }
  143.  
  144.                 $ret .= $this->getChildsEndString($node);
  145.  
  146.                 return $ret;
  147.         }
  148.  
  149.         /**
  150.          * Teljes fa lekérése stringként.
  151.          *
  152.          * @return string
  153.          */
  154.         public function asString() {
  155.                 if (is_null($this->_tree)) {
  156.                         $this->_tree = $this->_build($this->_items, $this->_options);
  157.                 }
  158.  
  159.                 if (is_null($this->_treeAsString)) {
  160.                         $this->_treeAsString = $this->_asString($this->_tree);
  161.                 }
  162.  
  163.                 return $this->_treeAsString;
  164.         }
  165.  
  166.         /**
  167.          * Teljes fa generálása
  168.          *
  169.          * @param array $items Eredeti tömb, amiből a fa generálandó
  170.          * @param array $options Opciók
  171.          * @return array
  172.          */
  173.         private function _build(array $items, array $options = array()) {
  174.                 return array(
  175.                         'item' => null,
  176.                         'childs' => $this->_buildChilds($items, array('level' => $options['level'] + 1) + $options),
  177.                         'level' => $options['level'],
  178.                         'i' => 0,
  179.                         'pathi' => array()
  180.                 );
  181.         }
  182.  
  183.         /**
  184.          * A gyerekek generálása
  185.          *
  186.          * @param array $items Eredeti tömb
  187.          * @param array $options Opciók
  188.          * @return array
  189.          */
  190.         private function _buildChilds(array $items, array $options = array()) {
  191.                 $options += $this->_options;
  192.  
  193.                 $tree = array();
  194.  
  195.                 foreach ($items as $key => $item) {
  196.                         if (isset($item[$options['refToParent']]) and isset($item[$options['parentIndex']]) and
  197.                                         $item[$options['refToParent']] == $options['parentValue']) {
  198.                                 $tree[] = array(
  199.                                         'item' => $item,
  200.                                         'childs' => $this->_buildChilds(&$items, array(
  201.                                                 'parentValue' => $item[$options['parentIndex']],
  202.                                                 'level' => $options['level'] + 1,
  203.                                                 'pathi' => array_merge($options['pathi'], array($options['i'])),
  204.                                                 'i' => 1
  205.                                                         ) + $options),
  206.                                         'level' => $options['level'],
  207.                                         'pathi' => array_merge($options['pathi'], array($options['i'])),
  208.                                         'i' => $options['i']++
  209.                                 );
  210.  
  211.                                 unset($items[$key]);
  212.                         }
  213.                 }
  214.  
  215.                 return $tree;
  216.         }
  217.  
  218.         /**
  219.          * Magic method: Stringgé konvertálás
  220.          *
  221.          * @return string
  222.          */
  223.         public function __toString() {
  224.                 return $this->asString();
  225.         }
  226.  
  227. }

MenuTree osztály

  1. /**
  2.  * Menü fa struktúrában
  3.  */
  4. class MenuTree extends Tree {
  5.  
  6.         /**
  7.          * Menü id-je
  8.          *
  9.          * @var string
  10.          */
  11.         private $id;
  12.  
  13.         /**
  14.          *
  15.          * @param array $items Menü rekordlista szerint
  16.          * @param string $id Menü id-je (Olyan, ami bekerülhet html id -nek is)
  17.          */
  18.         public function __construct(array $items, $id) {
  19.                 parent::__construct($items, array('titleIndex' => 'name'));
  20.                 $this->id = $id;
  21.         }
  22.  
  23.         /**
  24.          * A menü elem elé kerülő string
  25.          *
  26.          * UL lista item ( LI ) nyitása osztályokkal, id-vel
  27.          *
  28.          * @param array $node
  29.          * @return string
  30.          */
  31.         public function getItemStartString(array $node) {
  32.                 return '<li class="' . $this->id . ' menuitem level-' . $node['level']
  33.                                 . ' menuitem-' . $node['i'] . ' menuitem-path-'
  34.                                 . implode('-', $node['pathi']) . '" id="' . $this->id
  35.                                 . '-item-' . $node['item']['id'] . '" >';
  36.         }
  37.  
  38.         /**
  39.          * Menü elem lista kezdése
  40.          *
  41.          * UL lista kezdés osztályokkal, id-vel.
  42.          *
  43.          * @param array $node
  44.          * @return string
  45.          */
  46.         public function getChildsStartString(array $node) {
  47.                 return '<ul class="menu level-' . $node['level'] . ' menu-' . $node['i'] . '" id="' . $this->id . ($node['level'] ? '-' . $node['item']['id'] : '') . '">';
  48.         }
  49.  
  50.         /**
  51.          * Menü elem megjelenítése
  52.          *
  53.          * A menü elemre link kerül osztályokkal, id-vel.
  54.          *
  55.          * @param array $node
  56.          * @return string
  57.          */
  58.         public function getItemAsString(array $node) {
  59.                 $item = &$node['item'];
  60.                 return '<a title="'.htmlspecialchars($item['description']).'" href="' . $item['url'] . '" class="' . $this->id . ' menuitem level-' . $node['level'] . ' menuitem-' . $node['i'] . ' title">' . htmlspecialchars($item['name']) . '</a>';
  61.         }
  62.  
  63. }
Kategóriák: 
Megosztás/Mentés

Új hozzászólás