| 
<?php
/*
 Name: SB Menu - A PHP Class for Building Menus
 File: class.sb_menu.php
 License: BSD 3-Clause License <https://opensource.org/licenses/BSD-3-Clause>
 Description: A flatfile (look ma! no DB!) based php class to create/modify/save/load/display a menu tree.
 Author: https://www.phpclasses.org/browse/author/144301.html
 Homepage URL: https://www.phpclasses.org/package/12978-PHP-SB-Menu-A-PHP-Class-for-Building-Menus.html
 
 PHP Version: 8+
 
 Version: 1.0
 
 Long Description:
 The SB Menu class is a PHP class that allows for the creation, modification, saving, loading, and display of a menu tree without the need for a database. This class is designed to be simple, efficient, and easily customizable. It uses a flatfile approach, meaning that the menu structure is stored in a text file rather than a database.
 'label' (user) and 'id' (generated) are default fields in the base class.
 
 Methods:
 save($filename) - saves the menu tree to a file
 load($filename) - loads the menu tree from a file
 
 getNodeByField($field, $value) - returns a reference to the node with a field that matches a value
 applyCallbackToNode($id, $callback) - applies a callback to a node with a specific id
 applyCallbackToNodeByField($field, $value, $callback) - applies a callback to a node with a specific field that matches a value
 
 
 Categories/Keywords:
 Menu Creation
 Tree Structure
 PHP Class
 Extended Class
 Flatfile Database
 Database-less
 No DB
 Menu Modification
 Menu Manipulation
 Callback Function
 Node Manipulation
 File Handling
 Dynamic Data
 Tree Root Node
 Child Nodes
 
 
 // include this file
 include_once("class.sb_menu_1.0.php");
 
 // example creation... (look at class.sb_menu.test.simple.php for extended class needed to render)
 class SimpleTree_Extended extends Tree { ... extended code ( render(), renderNode(), etc ) ... }
 
 // create extended class of Tree and throw in a render and renderNode to customize display based on dynamic data fields
 // we pass the name we want for the main root node. In the example, we skip rendering this root node.
 $menu = new SimpleTree_Extended(new TreeNode('Main Menu')); // .. or the extended class to seperate the render() method
 
 
 // now add some menus...
 // when menu is created, a root node is created and assigned to $menu->root. This is our main level.
 $last_menu = $menu->root->addChild(new TreeNode('Submenu 1'));
 $last_menu = $menu->root->addChild(new TreeNode('Submenu 2'));
 // another level
 $next_level = $last_menu->addChild(new TreeNode('Item 2.1'));
 
 // example adding more data for our extended class to use with a custom render() method
 $next_level->data['url'] = "https://www.google.com";    // handled in extended class render
 $next_level->data['target'] = "_blank";                 // handled in extended class render
 
 // # use a callback to run some code on a specific node via its id (id and label are default fields in base class)
 $menu->applyCallbackToNode($s2_id, function($node) {
 $node->data['label'] = 'New Label';
 $new_submenu = new TreeNode('New Submenu');
 $node->addChild($new_submenu);
 });
 
 
 // example usage of applyCallbackToNodeByField($field, $value, $callback)
 // (this example changes the label of the node with label 'Submenu 1' to 'New Label')
 $menu->applyCallbackToNodeByField('label', 'Submenu 1', function($node) {
 $node->data['label'] = 'New Label';
 });
 
 
 // get the reference to the node by a field for modification. (only returns first match)
 // example usage of getNodeByField($key, $value)
 $s2 = $menu->getNodeByField('label', 'Submenu 2');
 $s2_id = $s2->data['id'];
 
 
 # save (ez) (make sure directory or file is writeable)
 $menu->save('menu-simple.txt');
 
 # load (pz)
 $menu2 = $menu->load('menu-simple.txt');
 
 
 // render (display the menu :: see example Tree extended class in class.sb_menu.test.simple.php )
 echo $menu->render('sbmenu'); // render defined in extended class
 
 */
 
 class TreeNode {
 public $data;
 public $children = [];
 
 public function __construct($data) {
 #$this->data = $data;
 $this->data['label'] = $data;
 $this->data['id'] = uniqid();
 }
 
 public function addChild(TreeNode $child) {
 $this->children[] = $child;
 // return a reference to the created object
 return $child;
 }
 
 }
 
 class Tree {
 public $root;
 
 public function __construct(TreeNode $root) {
 $this->root = $root;
 }
 
 public function render() {
 // extended class for customizing rendering output
 return '';
 }
 
 private function renderNode(TreeNode $node, $depth)
 {
 // extended class for customizing (see example in class.sb_menu.test.simple.php)
 }
 
 # a function to get a node by a field that matches a value
 public function getNodeByField($field, $value) {
 return $this->getNodeByFieldHelper($this->root, $field, $value);
 }
 
 private function getNodeByFieldHelper($node, $field, $value) {
 if (!empty($node->data[$field]) && $node->data[$field] == $value) {
 return $node;
 }
 foreach ($node->children as $child) {
 $result = $this->getNodeByFieldHelper($child, $field, $value);
 if ($result !== null)
 return $result;
 }
 return null;
 }
 
 /*
 // example usage of getNodeByField()
 $s2 = $menu->getNodeByField('label', 'Submenu 2');
 $s2_id = $s2->data['id'];
 */
 
 # a function to apply a callback to node with a specific id in the tree
 public function applyCallbackToNode($id, $callback) {
 $this->applyCallbackToNodeHelper($this->root, $id, $callback);
 }
 
 private function applyCallbackToNodeHelper($node, $id, $callback) {
 if ($node->data['id'] == $id) {
 $callback($node);
 }
 foreach ($node->children as $child) {
 $this->applyCallbackToNodeHelper($child, $id, $callback);
 }
 }
 /*
 // example usage of applyCallbackToNode()
 $menu->applyCallbackToNode('id', function($node) {
 $node->data['label'] = 'New Label';
 });
 */
 
 # callback based off some other field...
 public function applyCallbackToNodeByField($field, $value, $callback) {
 $this->applyCallbackToNodeByFieldHelper($this->root, $field, $value, $callback);
 }
 
 private function applyCallbackToNodeByFieldHelper($node, $field, $value, $callback) {
 if ($node->data[$field] == $value) {
 $callback($node);
 }
 foreach ($node->children as $child) {
 $this->applyCallbackToNodeByFieldHelper($child, $field, $value, $callback);
 }
 }
 
 /*
 // example usage of applyCallbackToNodeByField()
 $menu->applyCallbackToNodeByField('label', 'Submenu 1', function($node) {
 $node->data['label'] = 'New Label';
 });
 */
 
 # save
 public function save($filename) {
 $serializedData = serialize($this);
 file_put_contents($filename, $serializedData);
 }
 
 # load
 public static function load($filename) {
 $serializedData = file_get_contents($filename);
 if ($serializedData !== false) {
 $tree = unserialize($serializedData);
 if ($tree instanceof Tree) {
 return $tree;
 }
 }
 return null;
 }
 
 
 }
 
 
 
 // # use a callback to run some code on a specific node via its id
 // $menu->applyCallbackToNode($s2_id, function($node) {
 //     $node->data['label'] = 'New Label';
 //     $new_submenu = new TreeNode('New Submenu');
 //     $node->addChild($new_submenu);
 // });
 
 
 
 ?>
 |