Hi Everyone in this tutorial, we’re going to learn how to create Sortable, drag and drop multi-level list with jquery, like wordpress menu page and save data to database.
This example will help you to create sortable nested list, you can manage the list of items (add, edit, delete) and save it to database. Every item has two input: navigation label and navigation url, of course you can add any input you want (checkbox, radio, etc.).
This this how it gonna look like 😍.
Now, let’s dive into coding! 😀
We are going to use this package below for nestable list.
Step1: Create a database and add this menu table below.
1 2 3 4 5 6 7 8 | CREATE TABLE IF NOT EXISTS `menu` ( `id_menu` int(11) NOT NULL AUTO_INCREMENT primary key, `label_menu` varchar(100) NOT NULL, `url_menu` varchar(300) NOT NULL, `parent_id` int(11) NOT NULL ) |
Step2: Create project folder and add db.php file and insert code below
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | <?php // Set your database information define('DB_SERVER', 'localhost'); define('DB_USERNAME', 'root'); define('DB_PASSWORD', ''); define('DB_DATABASE', 'nadjmandev_tuto'); $db = new db(DB_SERVER, DB_USERNAME, DB_PASSWORD,DB_DATABASE); class db { protected $connection; protected $query; public $query_count = 0; public function __construct($dbhost = 'localhost', $dbuser = 'root', $dbpass = '', $dbname = '', $charset = 'utf8') { $this->connection = new mysqli($dbhost, $dbuser, $dbpass, $dbname); if ($this->connection->connect_error) { die('Failed to connect to MySQL - ' . $this->connection->connect_error); } $this->connection->set_charset($charset); } public function query($query) { if ($this->query = $this->connection->prepare($query)) { if (func_num_args() > 1) { $x = func_get_args(); $args = array_slice($x, 1); $types = ''; $args_ref = array(); foreach ($args as $k => &$arg) { if (is_array($args[$k])) { foreach ($args[$k] as $j => &$a) { $types .= $this->_gettype($args[$k][$j]); $args_ref[] = &$a; } } else { $types .= $this->_gettype($args[$k]); $args_ref[] = &$arg; } } array_unshift($args_ref, $types); call_user_func_array(array($this->query, 'bind_param'), $args_ref); } $this->query->execute(); if ($this->query->errno) { die('Unable to process MySQL query (check your params) - ' . $this->query->error); } $this->query_count++; } else { die('Unable to prepare statement (check your syntax) - ' . $this->connection->error); } return $this; } public function fetchAll() { $params = array(); $meta = $this->query->result_metadata(); while ($field = $meta->fetch_field()) { $params[] = &$row[$field->name]; } call_user_func_array(array($this->query, 'bind_result'), $params); $result = array(); while ($this->query->fetch()) { $r = array(); foreach ($row as $key => $val) { $r[$key] = $val; } $result[] = $r; } $this->query->close(); return $result; } public function fetchArray() { $params = array(); $meta = $this->query->result_metadata(); while ($field = $meta->fetch_field()) { $params[] = &$row[$field->name]; } call_user_func_array(array($this->query, 'bind_result'), $params); $result = array(); while ($this->query->fetch()) { foreach ($row as $key => $val) { $result[$key] = $val; } } $this->query->close(); return $result; } public function numRows() { $this->query->store_result(); return $this->query->num_rows; } public function insertedId() { return $this->query->insert_id; } public function close() { return $this->connection->close(); } public function affectedRows() { return $this->query->affected_rows; } private function _gettype($var) { if (is_string($var)) { return 's'; } if (is_float($var)) { return 'd'; } if (is_int($var)) { return 'i'; } return 'b'; } } ?> |
Step3: Create index.php file and insert code below
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | <?php include 'db.php'; function renderMenuItem($id, $label, $url) { return '<li class="dd-item dd3-item" data-id="' . $id . '" data-label="' . $label . '" data-url="' . $url . '">' . '<div class="dd-handle dd3-handle" > Drag</div>' . '<div class="dd3-content"><span>' . $label . '</span>' . '<div class="item-edit">Edit</div>' . '</div>' . '<div class="item-settings d-none">' . '<p><label for="">Navigation Label<br><input type="text" name="navigation_label" value="' . $label . '"></label></p>' . '<p><label for="">Navigation Url<br><input type="text" name="navigation_url" value="' . $url . '"></label></p>' . '<p><a class="item-delete" href="javascript:;">Remove</a> |' . '<a class="item-close" href="javascript:;">Close</a></p>' . '</div>'; } function menuTree($parent_id = 0) { global $db; $items = ''; $query = $db->query("SELECT * FROM menu WHERE parent_id = ? ORDER BY id_menu ASC", $parent_id); if ($query->numRows() > 0) { $items .= '<ol class="dd-list">'; $result = $query->fetchAll(); foreach ($result as $row) { $items .= renderMenuItem($row['id_menu'], $row['label_menu'], $row['url_menu']); $items .= menuTree($row['id_menu']); $items .= '</li>'; } $items .= '</ol>'; } return $items; } ?> <!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>Nestable</title> <meta name="description" content="Nestable"> <meta name="author" content="NadjmanDev"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/nestable2/1.6.0/jquery.nestable.min.css"> <link rel="stylesheet" href="css/style.css"> </head> <body> <form id="add-item"> <input type="text" name="name" placeholder="Name"> <input type="text" name="url" placeholder="Url"> <button type="submit">Add Item</button> </form> <hr /> <div class="dd" id="nestable"> <?php $html_menu = menuTree(); echo (empty($html_menu)) ? '<ol class="dd-lisat"></ol>' : $html_menu; ?> </div> <hr /> <form action="menu.php" method="post"> <input type="hidden" id="nestable-output" name="menu"> <button type="submit">Save Menu</button> </form> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/nestable2/1.6.0/jquery.nestable.min.js"></script> <script src="script.js"></script> </body> </html> |
Step4: Create script.js file and insert this code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | $(document).ready(function () { var updateOutput = function () { $('#nestable-output').val(JSON.stringify($('#nestable').nestable('serialize'))); }; $('#nestable').nestable().on('change', updateOutput); updateOutput(); $("#add-item").submit(function (e) { e.preventDefault(); id = Date.now(); var label = $("#add-item > [name='name']").val(); var url = $("#add-item > [name='url']").val(); if ((url == "") || (label == "")) return; var item = '<li class="dd-item dd3-item" data-id="' + id + '" data-label="' + label + '" data-url="' + url + '">' + '<div class="dd-handle dd3-handle" > Drag</div>' + '<div class="dd3-content"><span>' + label + '</span>' + '<div class="item-edit">Edit</div>' + '</div>' + '<div class="item-settings d-none">' + '<p><label for="">Navigation Label<br><input type="text" name="navigation_label" value="' + label + '"></label></p>' + '<p><label for="">Navigation Url<br><input type="text" name="navigation_url" value="' + url + '"></label></p>' + '<p><a class="item-delete" href="javascript:;">Remove</a> |' + '<a class="item-close" href="javascript:;">Close</a></p>' + '</div>' + '</li>'; $("#nestable > .dd-list").append(item); $("#nestable").find('.dd-empty').remove(); $("#add-item > [name='name']").val(''); $("#add-item > [name='url']").val(''); updateOutput(); }); $("body").delegate(".item-delete", "click", function (e) { $(this).closest(".dd-item").remove(); updateOutput(); }); $("body").delegate(".item-edit, .item-close", "click", function (e) { var item_setting = $(this).closest(".dd-item").find(".item-settings"); if (item_setting.hasClass("d-none")) { item_setting.removeClass("d-none"); } else { item_setting.addClass("d-none"); } }); $("body").delegate("input[name='navigation_label']", "change paste keyup", function (e) { $(this).closest(".dd-item").data("label", $(this).val()); $(this).closest(".dd-item").find(".dd3-content span").text($(this).val()); }); $("body").delegate("input[name='navigation_url']", "change paste keyup", function (e) { $(this).closest(".dd-item").data("url", $(this).val()); }); }); |
Step5: Create and insert this code below into menu.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | <?php include 'db.php'; $menu = $_POST['menu']; $array_menu = json_decode($menu, true); $db->query('TRUNCATE TABLE menu'); function updateMenu($menu,$parent = 0) { global $db; if (!empty($menu)) { foreach ($menu as $value) { $label = $value['label']; $url = (empty($value['url'])) ? '#' : $value['url']; $sql = "INSERT INTO menu (label_menu, url_menu, parent_id) VALUES ('$label', '$url', $parent)"; $db->query($sql); $id = $db->insertedId(); if (array_key_exists('children', $value)) updateMenu($value['children'],$id); } } } updateMenu($array_menu); header("Location: index.php") ?> |
Great, if you followed this tutorial you gonna have same result as this demo below.
By the way, i also offering you source code of this tutorial.
I hope you like it, see you next time 😀.
Leave a Reply