[PHP MySQL] Multi-level menu i.c.m. foreign keys

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • Matis
  • Registratie: Januari 2007
  • Laatst online: 14:14

Matis

Rubber Rocket

Topicstarter
Beste PRGers,

Momenteel ben ik een inhouse-tool aan het maken, welke als GUI een webpaginaatje heeft. Ik gebruik CodeIgniter als MVC-platform en had voorheen een model gemaakt welkte het menu genereerde uit statische PHP-code; naast het feit dat het ontzettend veel werk is om het menu te onderhouden, werkt het ook nog eens lastig om het netjes te verwerken.
Nu kwam ik laatst weer de blog van Crisp tegen: Crisp's blog: Formatting a multi-level menu using only one query. Zijn aanpak is retesimpel qua code en ook nog eens overzichtelijke datalaag (MySQL).

Ik heb de volgende MySQL table aangemaakt:
SQL:
1
2
3
4
5
6
7
8
9
10
11
delimiter $$

CREATE TABLE `menus` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `parent_id` int(10) unsigned DEFAULT NULL,
  `name` varchar(255) NOT NULL,
  `url` varchar(255) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `fk_menus_parent_id` (`parent_id`),
  CONSTRAINT `fk_menus_parent_id` FOREIGN KEY (`parent_id`) REFERENCES `menus` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8$$


Echter kan ik nu niet een parent_id van 0 aangeven zoals de tutorial aangeeft. Op dat moment krijg ik de volgende MySQL-foutmelding:
ERROR 1452: Cannot add or update a child row: a foreign key constraint fails (`ci_showcase`.`menus`, CONSTRAINT `fk_menus_parent_id` FOREIGN KEY (`parent_id`) REFERENCES `menus` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION)
SQL Statement:
UPDATE `ci_showcase`.`menus` SET `parent_id`=0 WHERE `id`='5'

Ik begrijp wat er staat, maar mij lijkt het het netste om wel een foreign key te gebruiken om te verwijzen naar het parent_id.

Omdat ik nu het root-menu-item een parent_id van NULL geef, dan valt de opbouw van het array in het water; Immers schrijft
PHP:
1
$this->_menuData['parents'][$menuItem->parent_id][] = $menuItem->id;

het $menuItem->id weg op index $this->_menuData['parents'][NULL][]
Daarbij komt nog dat
PHP:
1
if (isset($this->_menuData['parents'][$parentId]))

onherroepelijk faalt voor een root-object, omdat $parentId 0 niet bestaat (waar de code wel vanuit gaat).

Naar mijn menig heb ik 3 mogelijke oplossingen;
  1. Ik pas de FK aan in MySQL zodat hij parent_id 0 slikt
  2. Ik wijzig de voorbeeldcode zodat NULL toch bruikbaar wordt
  3. Ik maak een root-root-object (met parent_id = NULL) aan en gebruik zijn id als parent_id voor de opbouw van het menu
Wat is hierin jullie visie of best practice?

Alvast bedankt!

Matis

If money talks then I'm a mime
If time is money then I'm out of time


Acties:
  • 0 Henk 'm!

  • P_de_B
  • Registratie: Juli 2003
  • Niet online
Vanuit de db geredeneerd zou ik zeggen gebruik NULL waar hij voor bedoeld is. Als een waarde niet bekend is, is deze NULL en niet 0. Daar is deze bijzondere waarde juist voor. Dat je dan wat aanpassingen moet doen in je PHP-code is dan niet anders in mijn beleving.

Ik stem dus op 2 :)

Oops! Google Chrome could not find www.rijks%20museum.nl


Acties:
  • 0 Henk 'm!

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
^ Met hummes. Als je een FK gebruikt gebruik 'm (en NULL) dan ook zoals 'ie bedoeld is :)

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


Acties:
  • 0 Henk 'm!

  • Matis
  • Registratie: Januari 2007
  • Laatst online: 14:14

Matis

Rubber Rocket

Topicstarter
Bedankt voor jullie feedback; Ik heb de voorbeeldcode aangepast. De load-functie ziet er nu zo uit:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
    private function loadMenu()
    {
        $this->db->order_by('parent_id');
        $this->db->order_by('name');
        $query = $this->db->get('menus');
        foreach($query->result() as $menuItem)
        {
            $parent_id = (int)($menuItem->parent_id === null ? 0 : $menuItem->parent_id);
            $this->_menuData['items'][$menuItem->id] = $menuItem;
            $this->_menuData['parents'][$parent_id][] = $menuItem->id;
        }
    }

If money talks then I'm a mime
If time is money then I'm out of time