I have table menu with column: id, value, id_parent.
And have multilevel menu:
Item0
Item1
Item2
Item3
I want to delete Item1 and I have his id.
How I can delete Item2 and Item3 when I delete Item1?
I select from table:
public function searchChildren($id)
{
$sql = "SELECT id FROM menu WHERE id_parent = '{$id}'";
return $this->db->query($sql);
}
I can find id the first child:
$idChild = $this->model->search_children_id($id);
How can I find the rest children using a cycle or recursion?
I would create a variable function that i pass to itself by reference, probably better ways to do this, but i created a tree navigator using this method and it works perfectly
I would simply check if there is child under that id, if there is loop though them til there is no more childs, Once there then delete the id
this is non-tested code and sort of pseudo-code, but you will get the idea
$DeleteIdAndChilds= function($Id) use( &$DeleteIdAndChilds){
while($ChildId = $this->model->search_children_id($Id) != null){
$DeleteIdAndChilds($ChildId);
}
//once we are here there is no more child lets delete the $Id
}
cuz you have an id, just make function delete. examp:
function delete_menu($id){
$this->db->where('id_parent', $id);
$this->db->delete('menu');
}
I recommend a recursive function to find all of his children. What do you want to happen if there are multiple children at the same level?
UPDATE: My bad, I didn't mean to leave you hanging... Here is some code for a recursive function. It is essentially the same I use to display my menu except stripped down and refactored for your purpose.
I use pk_ID, Menu_Item, and Menu_Parent for the data columns.
I really hope this helps you.
Call the recursive function which will return a string of itself and all child Items.
Convert to an array.
Loop through the array to delete what you want.
$menu = rtrim(($this->getMenu(0)), ",");
$menu = explode(",", $menu);
public function getMenu($p_parentID=0) {
$output = "";
$data = $this->db->order_by("Menu_Parent, Menu_Item", "ASC")->get_where("my_menu", array(
"Menu_Parent" => $p_parentID,
));
foreach ($data->result() as $row) {
$output .= "{$row->Menu_Item}<br>";
if ($this->hasChildren($row->pk_ID)) {
$output .= $this->getMenu($row->pk_ID);
}
}
return $output;
} # END FUNCTION getMenu
public function hasChildren($p_value=0) {
$data = $this->db->get_where("my_menu", array(
"Menu_Parent" => $p_value
));
$result = $data->num_rows();
return $result;
} # END FUNCTION hasChildren
Related
I am using Academy LMS Source code is in Github
it's a Learning Management system and it is designed to have one category per course
I want to add multiple categories to one course
In /controllers/API.php I have
// Fetch all the categories
public function categories_get($category_id = "") {
$categories = array();
$categories = $this->api_model->categories_get($category_id);
$this->set_response($categories, REST_Controller::HTTP_OK);
}
// Fetch all the courses belong to a certain category
public function category_wise_course_get() {
$category_id = $_GET['category_id'];
$courses = $this->api_model->category_wise_course_get($category_id);
$this->set_response($courses, REST_Controller::HTTP_OK);
}
then in /models/Api_model.php, I have
// Get categories
public function categories_get($category_id)
{
if ($category_id != "") {
$this->db->where('id', $category_id);
}
$this->db->where('parent', 0);
$categories = $this->db->get('category')->result_array();
foreach ($categories as $key => $category) {
$categories[$key]['thumbnail'] = $this->get_image('category_thumbnail', $category['thumbnail']);
$categories[$key]['number_of_courses'] = $this->crud_model->get_category_wise_courses($category['id'])->num_rows();
}
return $categories;
}
// Get category wise courses
public function category_wise_course_get($category_id)
{
$category_details = $this->crud_model->get_category_details_by_id($category_id)->row_array();
if ($category_details['parent'] > 0) {
$this->db->where('sub_category_id', $category_id);
} else {
$this->db->where('category_id', $category_id);
}
$this->db->where('status', 'active');
$courses = $this->db->get('course')->result_array();
// This block of codes return the required data of courses
$result = array();
$result = $this->course_data($courses);
return $result;
}
then in /model/Crud_model.php, I got
public function get_category_details_by_id($id)
{
return $this->db->get_where('category', array('id' => $id));
}
function get_category_wise_courses($category_id = "")
{
$category_details = $this->get_category_details_by_id($category_id)->row_array();
if ($category_details['parent'] > 0) {
$this->db->where('sub_category_id', $category_id);
} else {
$this->db->where('category_id', $category_id);
}
$this->db->where('status', 'active');
return $this->db->get('course');
}
in SQL course table has a column named category_id int(11) which stores one category per course I've changed it to TEXT format and put comma-separated values like 1,2,3 and used the ways like
$this->db->where_in('category_id',$category_id)
and
$this->db->like('category_id',$category_id)
and
$this->db->where("FIND_IN_SET(".$category_id.",category_id) >", 0);
and got no result I just need courses to have comma-separated values in the category_id column
Simply, I want courses to have multiple categories
DB tables
https://pasteboard.co/JNSxO2kz.png
course table
https://pasteboard.co/JNSy7g0.png
category table
https://pasteboard.co/JNSyhlk.png
category_id table (Mapping Table suggested by #micheal-la-ferla )
https://pasteboard.co/JNSyGGn.png
anybody could help?
The way Academy-LMS is designed, it lets you only add 1 category per course. It limits the user and it can be frustrating.
One workaround which could work is to create a new mapping table with 3 columns as follows:
Field Name | Data Type
------------+------------------
ID | int(11)
Course ID | int(11)
Category ID | int(11)
Obviously you need to have the permission to create new tables to use this workaround. Not sure if this is possible for your implementation of Academy-LMS.
The step above will essentially create a one to many relationship between the course and the category, so that 1 course can then be part of multiple categories. (In reality, this would be a many to many relationship since I am assuming that a category may obviously belong to multiple courses).
Therefore the database design will be similar to the one I created here.
If you implement this change, you would then need to make the following changes to the code you have:
// Get categories
public function categories_get($category_id)
{
if ($category_id != "") {
$this->db->where('category_id', $category_id);
}
$this->db->where('parent', 0);
$categories = $this->db->get('course_id')->result_array();
foreach ($categories as $key => $category) {
$categories[$key]['thumbnail'] = $this->get_image('category_thumbnail', $category['thumbnail']);
$categories[$key]['number_of_courses'] = $this->crud_model->get_category_wise_courses($category['id'])->num_rows();
}
return $categories;
}
Essentially, what I did here was replace the parent in the categories with category_id and category with course_id. Not sure if these are correct. You will need to review these when reading from the database as I never used CodeIgniter.
Based on a previous question on how to create a tree from a nested array with parent ID's, I now have an almost completed script. The only thing is that in the output json the ordering is messed up. This is due that the array is now build on parent_id instead of the ordering by database field 'sorting.'
Can any one help me figure out where and how to make a change for it to work, that in the json the item with the lowest number in 'sorting' is listed first?
public function sorting() {
$pages = Page::orderBy('sorting')->get()->toArray();
$new = array();
foreach ($pages as $page){
$new[$page['parent_id']][] = $page;
}
$tree = $this->createTree($new, $new[0]);
return view('base\backend\pages\sorting', [
'pages' => json_encode($tree)
]);
}
private function createTree(&$list, $parent){
$tree = array();
foreach ($parent as $key=>$item){
if(isset($list[$item['id']])){
$item['children'] = $this->createTree($list, $list[$item['id']]);
}
$tree[] = $item;
}
return $tree;
}
Found/created the solution: This is working very well. My mistake was that the parent id's were never reset to zero, thus messing up the sort order.
I'm building a codeigniter site and I have a database table of books - one record for each book with title, author, etc etc fields. I know how to get the db table contents and pass an object to the view and then do a foreach loop to get the values. I could print a table of all the data. However I then get a bit muddled. The page has areas for each book and the data for a book will be one row of the data array. So what I want to do is, if the author is 'this' value, find the correct row in which the author appears and then get the other fields. How can I do a foreach loop that finds the one row with the author's name?
I can't think how to do it - I seem to go round and round in circles.
Help!
Edit: Code:
OK so here's the controller method:
public function index()
{
$books = $this->Books_model->getbooks();
$data = array(
'body_id'=>'home',
'main'=>'home_view',
'books'=>$books
);
$this->load->view('templates/template_main_view', $data);
}
and here's the model method:
function getbooks(){
$query = $this->db->get('books');
return $query->result();
}
so I end up with the variable $books (which is of course an object, not an array) in the view and a var_dump() shows that it has all the data. So far so good. I can then use a foreach loop to get values.
Now I want to extract a single row/record conditional on the fact that it has a given value for 'author' and assign each field of that row to a variable that I can use them in the view. And then I want to repeat that for the other rows. I can't seem to work out how to do that.
Afternote:
I found a way of doing this but not sure if it's the best or neatest:
I do this:
if(isset($books)){
foreach($books as $row){
if($row->author == 'authorname'){
$title = $row->title;
}
}
}
it works but seems a bit clumsy/overkill??
How about creating a specific function for getting the book/s that matches the author name?
For example:
In your controller you can do something like this:
public function index()
{
$data['books'] = $this->Books_model->get_each_book();
$data = array(
'body_id'=>'home',
'main'=>'home_view',
'books'=>$books
);
$this->load->view('templates/template_main_view', $data);
}
The in your model, do something like this:
function get_each_book(){
$this->db->where('author','authorname');
$query = $this->db->get('books');
return $query->result_array();
}
This way, you are gonna filter the results in the model already.
Then in your view, access the variable 'books' through a foreach loop:
Example:
foreach($books as $row)
{
$title = $row['title'];
echo $title;
}
So I have a table of menu, id, title, parent_id
I have attached some content fields to the most bottom level of menus(lets say id = 50), then what if I click on the top parent of that tree menu(id = 1), i want it to display the content that is attached to the child menu(doesn't matter how deep), so I guess that I need to loop through the parents, can you help me with the logic of this?
The Controller:
$data['query'] = $this->item_model->getByType($menu_id);
The Model:
function getByType($menu_id) {
if($menu_id) {
$q = $this->db->get_where('menus', array('id' => $menu_id));
$parent_id = $q->row()->parent_id;
}else{
return;
}
$this->db->select('*');
$this->db->from('items');
$this->db->where('menu_id', $menu_id);
$this->db->or_where('menu_id', $parent_id);
$query = $this->db->get();
return $query;
}
so by now I only get the items if some menu if I clicked on his parent(2 level), but how to make it infinite level of deep?, I guess that here I need to put in a loop the or_where, can you help me with that?
Hmz... Recursion!
function getChildrenAttachments($menu_id) {
// invalid menu id
if(!$menu_id) return;
// get all rows with this parent id
$query = $this->db->get_where('menus', array('parent_id' => $menu_id));
// result will hold all the attachments
$result = array();
// loop through results
foreach($query->result() as $row) {
// does this row have an attachment?
if($row->attachment!='') {
$result[] = $row->attachment;
}
// add all children attachments
$result = array_merge($result, getChildrenAttachments($row->id));
}
// return result set
return $result
}
This implementation does not take into account the current menu_id actually having an attachment. You can create a new function that takes into account that getAttachmentsForTree() which checks the current id, then then passes the id to getChildrenAttachments()...
P.S. I have not run the code, so I'm sorry for any possible issues with the code. It's just an example on how you can use recursion to your advantage in this scenario.
I am trying to display categories stored in my database:
http://dl.dropbox.com/u/16459516/Screen%20shot%202012-05-05%20at%2015.59.40.png
My idea was to get all data out of the database within my model and the reconstruct the array within my function:
function get_all_categories() {
$query = $this->db->query('SELECT * FROM categories');
//code to reconstruct my array like:
//foreach($query->result_array() as $row) {
//$categories[$row['niveau_1']] = array[]
//}
return $categories;
}
and output some array like:
[0] => Array
(
[niveau_1] => main category name
[niveau_2] => Array(
...children here and children of these childs
)
)
Does anyone know how to do this? Im stuck here and dont know how to create the array... i know how to display it in my views but getting them in the array seems hard for me.
Thanks in advance!
This is an idea:
foreach category (except level 4) check if it is present into the result array($c)
if it is not then create an array using a label the category's name
once controlle categories 2 and 3 you can insert the last one inside the generated array
$query = $this->db->query('SELECT * FROM categories');
$raw_categories = $query->result_array();
$c = array();
foreach($raw_categories AS $row)
{
if(!array_key_exists($row['niveau_2'],$c) && !empty($row['niveau_2']))
{
$c[$row['niveau_2']] = array();
}
if(!array_key_exists($row['niveau_3'],$c[$row['niveau_2']]) &&
!empty($row['niveau_3']))
{
$c[$row['niveau_2']]['niveau_3'] = array();
}
$c[$row['niveau_2']]['niveau_3'][] = $row['niveau_4'];
}
this is not tested but you can use it as an idea
I think your database is wrong. As I understand it's a "table of content" or similar (category, sub-category, sub-sub-category, etc.). "1. Visie, Beleid..." appears many-many times.
If I'd do I change the database:
table niveau_1
id_1 - primary key
title - string
table niveau_2
id_2 - primary key
id_1 - connect to niveau_1 (which niveau_1 is its 'parent')
title - string
table niveau_3
id_3 - primary key
id_2 - connect to niveau_2 (which niveau_2 is its parent)
title - string
In this case in your database will not duplicated elements and I think your desired query will be simplier:
SELECT
niveau_1.title AS title_1,
GROUP_CONCAT(niveau_2.title,"#") AS title_2,
GROUP_CONCAT(niveau_3.title,"#") AS title_3
FROM niveau_1
LEFT JOIN niveau_2 ON niveau_2.id_1=niveau_1.id_1
LEFT JOIN niveau_3 ON niveau_3.id_2=niveau_2.id_2
GROUP BY niveau_2.id_2
This query is only a half-solution, it isn't perfect for you but you can start from here.
// your model using activerecord
public function getCategories(){
$q = $this->db->get('categories');
if($q->num_rows > 0){
return $q->result();
}else{
return false;
}
}
//your controller
$this->load->model('category_model');
$data['results'] = $this->category_model->getCategories();
foreach($data['results'] as $c){
//iterate through categories, match them to points.
//match category_id in results with category_id in the other table results
//setup a multi dimensional array to build up what you need.
}