aaroncatolico1 Posted February 29 Share Posted February 29 I'm trying not to break the main MVC architecture of my custom PHP MVC framework when generating multi-level navigation menus and a commenting system. In which part of the MVC files should I create recursive functions to generate multilevel navigation menus or if I wanted to generate a commenting system that requires recursion? Here's part of the main MVC folder/path structure that I'm currently using: This is the Core Model "Core/Model.php" file: <?php class Model { private $host = DBHOST; private $user = UN; private $pw = PW; private $dbname = DBNAME; private $dbh; private $stmt; private $error; public function __construct() { $dsn = "mysql:host=$this->host; dbname=$this->dbname"; $options = [ PDO::ATTR_PERSISTENT => true, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION ]; try { $this->dbh = new PDO($dsn, $this->user, $this->pw, $options); #echo 'Successfully connected to the database using PDO!'; } catch(PDOException $e) { $this->error = $e->getMessage(); echo $this->error; } } public function query($sql){ $this->stmt = $this->dbh->prepare($sql); } #Bind the values: *NOTE: Bind only when selecting by passing a property argument through a param. public function bind($param, $value, $type = null){ if(is_null($type)){ switch(true){ case is_int($value): $type = PDO::PARAM_INT; break; case is_bool($value): $type = PDO::PARAM_BOOL; break; case is_null($value): $type = PDO::PARAM_NULL; break; default: $type = PDO::PARAM_STR; } } $this->stmt->bindValue($param, $value, $type); } public function execute(){ return $this->stmt->execute(); } public function resultSet(){ $this->execute(); return $this->stmt->fetchAll(PDO::FETCH_OBJ); } public function single(){ $this->execute(); return $this->stmt->fetch(PDO::FETCH_OBJ); } } Here's the Models directory "models/PagesModel.php" file: <?php class PagesModel { private $db; public function __construct(){ $this->db = new Model(); } #Top Navigation: public function top_nav(){ $this->db->query('SELECT * FROM navs WHERE level = 1'); return $this->db->resultSet(); } } This is the controllers "controllers/Pages.php" file: <?php class Pages extends Controller { #Load the homepage model data and view: public function homepage(){ $topnav = $this->model('PagesModel')->top_nav(); // Call a view helper function to render multi-level menu $data = [ 'topnav' => $topnav, ]; $this->view('pages/index', $data); } } This is the Core "core/Controller.php" file: <?php class Controller { public function model($model){ if(file_exists('../app/models/'. $model . '.php')){ require_once '../app/models/'. $model . '.php'; return new $model(); } } public function view($view, $data = []){ if(file_exists('../app/views/' . $view . '.php')){ require_once '../app/views/' . $view . '.php'; } else { die('View file does NOT exist.'); } } } I can share more of the framework files if needed, but what I'm asking is how would I implement the multilevel navigation menus that require recursive functions without breaking the main architecture of the overall framework? Is this supposed to be done in the helper files or which would you recommend I try? Any support is appreciated. Quote Link to comment Share on other sites More sharing options...
requinix Posted February 29 Share Posted February 29 If you want to model the concept of navigation menus then you should probably use a Model. If you want to write code to determine how navigation menus are viewed then you should probably put the code in a view. Consider that you can create an anonymous, recursive function in a view file, then call it. If you're not sure then your first step is to make the functionality happen at all. You can figure things out along the way - it's not like you have to get everything right on your first try. And when you have it working, then you can think about how to improve it. Quote Link to comment Share on other sites More sharing options...
aaroncatolico1 Posted February 29 Author Share Posted February 29 1 hour ago, requinix said: If you want to model the concept of navigation menus then you should probably use a Model. If you want to write code to determine how navigation menus are viewed then you should probably put the code in a view. Consider that you can create an anonymous, recursive function in a view file, then call it. If you're not sure then your first step is to make the functionality happen at all. You can figure things out along the way - it's not like you have to get everything right on your first try. And when you have it working, then you can think about how to improve it. Great response! I've definitely thought of just creating my own way & then improving on it later. Also, here's what the multilevel menu recursive function would look similar to: <?php $conn = new PDO('mysql:host=localhost; dbname=Mydb', 'MyUsername', 'MyPassword'); function left_nav($parent_id, $conn){ $sql = 'SELECT * FROM navs WHERE parent_id = ?'; $stmt = $conn->prepare($sql); $stmt->bindParam(1, $parent_id, PDO::PARAM_INT); $stmt->execute(); $data = $stmt->fetchAll(PDO::FETCH_ASSOC); if($data){ echo "<ul>\n"; foreach ($data as $row) { echo "<li><a href='#'>{$row['nav_item_name']}</a>"; # Recursive (function calls itself in loop) call for sub-items left_nav($row['id'], $conn); echo "</li>\n"; } echo "</ul>"; } } # Start 'left_nav()' function to build navigation from root level (parent_id = 0) left_nav(0, $conn); Quote Link to comment Share on other sites More sharing options...
Strider64 Posted March 1 Share Posted March 1 (edited) I didn't create a Model, but a trait though maybe it will give you some ideas? <?php // NavigationMenuTrait.php namespace clearwebconcepts; use function htmlspecialchars; use function hash_equals; trait NavigationMenuTrait { public function regular_navigation(): void { $navItems = [ 'Home' => 'index.php', 'About' => 'about.php', 'Puzzle' => 'puzzle.php', 'Portfolio' => 'portfolio.php', 'Contact' => 'contact.php' ]; // Check if the user is logged in $isLoggedIn = isset($_COOKIE['login_token']) && isset($_SESSION['login_token']) && hash_equals($_SESSION['login_token'], $_COOKIE['login_token']); if ($isLoggedIn) { unset($navItems['Home']); // Remove 'Home' from the navigation menu // Add 'Dashboard' to the start of the navigation menu $navItems = array('Dashboard' => 'dashboard.php') + $navItems; } else { $navItems['Login'] = 'login.php'; // Add 'Login' to the navigation menu } $navLinks = []; foreach ($navItems as $title => $path) { $href = $this->generateHref($path); $safeTitle = htmlspecialchars($title, ENT_QUOTES, 'UTF-8'); $navLinks[] = "<a href=\"{$href}\">{$safeTitle}</a>"; } // Check if the user is logged in if ($isLoggedIn) { $navLinks[] = '<a href="logout.php">Logout</a>'; // Add 'Logout' to the end of the navigation menu } echo implode('', $navLinks); } public function showAdminNavigation(): void { $protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' ? 'https://' : 'http://'; $host = $_SERVER['HTTP_HOST']; // Define your base path here $base_path = ($host === 'localhost:8888') ? '/clearwebconcepts' : ''; $base_url = $protocol . $host . $base_path; $adminItems = [ 'Create Entry' => $base_url . '/create_cms.php', 'Edit Entry' => $base_url . '/edit_cms.php', 'Add to Portfolio' => $base_url . '/new_portfolio.php', 'Edit Portfolio Page' => $base_url . '/edit_portfolio.php', 'Add Jigsaw' => $base_url . '/add_to_puzzle.php', 'Edit Jigsaw' => $base_url . '/edit_puzzle.php', 'Service Form' => $base_url . '/service_form.php' ]; echo '<div class="admin-navigation">'; foreach ($adminItems as $adminTitle => $adminPath) { $adminSafeTitle = htmlspecialchars($adminTitle, ENT_QUOTES, 'UTF-8'); echo "<a href=\"{$adminPath}\">{$adminSafeTitle}</a>"; } echo '</div>'; } private function generateHref(string $path): string { $protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' ? 'https://' : 'http://'; $host = $_SERVER['HTTP_HOST']; // Define your base path here $base_path = ($host === 'localhost:8888') ? '/clearwebconcepts' : ''; $base_url = $protocol . $host . $base_path; // Build the URL first, then validate it $url = $base_url . '/' . $path; $sanitized_url = filter_var($url, FILTER_SANITIZE_URL); $valid_url = filter_var($sanitized_url, FILTER_VALIDATE_URL); if ($valid_url === false) { die('Invalid URL'); } return $valid_url; } } Edited March 1 by Strider64 Quote Link to comment Share on other sites More sharing options...
gizmola Posted March 4 Share Posted March 4 On 2/29/2024 at 1:41 PM, aaroncatolico1 said: Great response! I've definitely thought of just creating my own way & then improving on it later. Also, here's what the multilevel menu recursive function would look similar to: <?php $conn = new PDO('mysql:host=localhost; dbname=Mydb', 'MyUsername', 'MyPassword'); function left_nav($parent_id, $conn){ $sql = 'SELECT * FROM navs WHERE parent_id = ?'; $stmt = $conn->prepare($sql); $stmt->bindParam(1, $parent_id, PDO::PARAM_INT); $stmt->execute(); $data = $stmt->fetchAll(PDO::FETCH_ASSOC); if($data){ echo "<ul>\n"; foreach ($data as $row) { echo "<li><a href='#'>{$row['nav_item_name']}</a>"; # Recursive (function calls itself in loop) call for sub-items left_nav($row['id'], $conn); echo "</li>\n"; } echo "</ul>"; } } # Start 'left_nav()' function to build navigation from root level (parent_id = 0) left_nav(0, $conn); Yet it seems you missed several important suggestions. What you presented is neither a model, nor a view. The approach of the function above should be avoided for a few reasons. The actual query code ought to be part of your navs model. You presented your custom model code, so why did you not use it and make a navs model? A view should have nothing other than markup and whatever minimal logic you need to process the data and integrate it. Since you are making your own mvc, have you created a view base class? Typically people will put view in a particular subdirectory, and name the view files using some convention Most view subsystems actually involve a parsing/combination step, since the views are often not .php files This facilitates partials and all sorts of valuable structure support, but you could get away with using require_once and having snippets of code With that said, just keeping it simple your views can be plain old .php files, but perhaps named as home.view.php. You will probably also want files like header.view.php or perhaps header.part.view.php and footer.view.php. You could also do a view base class to help with template code that should be shared. Cakephp has something like this and their templates are for the most part straight php code. Views should assume that the required data (typically data from model calls in the controller) is passed in via a standard parameter Take a look at symfony & twig, laravel & blade or Cakephp 4's view system to get some ideas of how popular frameworks have handled Views. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.