I searched on many posts, but I can't find one to fix my problem.
In my database I have some data : a name and a "treepath" like "1.1.2". I don't have a parent ID or anything like that. So my question is : is there a way to createa nested multi level menu with only the "treepath" ?
In my table I got (ID | NAME | TREEPATH):
0 | Phone | 1
1 | Samsung | 1.1
2 | Galaxy S | 1.1.1
3 | Galaxy Note | 1.1.2
...
Output I need :
<ul>
<li>1. Phones
<ul>
<li>1.1. Samsung
<ul>
<li>1.1.1. Galaxy S</li>
<li>1.1.2. Galaxy Note</li>
<li>1.1.3. Galaxy Ace</li>
</ul></li>
<li>1.2. Apple</li>
<li>1.3. Google</li>
</ul></li>
</ul>
I certainly need a recursive function with php, but i can't got it right, so if anyone could help me on this one, that'd be great !
Thanks !
Well, you could use a recursive function that employs regular expressions, but that might be sub-optimal. Pseudo-code follows:
function drawTree($root) {
while($results = yourQuery("SELECT * FROM table WHERE treepath REGEXP '^{$root}\.'")) {
echo "stuff";
drawTree($results['treepath']);
}
}
Your situation is odd.
I don't see a technical reason not to just have a parent id in your table. Or to use first normal form. Feels a bit contrived, but you could do something like:
<?php
$db = new mysqli(...);
$stmnt = $db->prepare("select name, treepath, LENGTH(treepath) - LENGTH(REPLACE(treepath, '.', '')) AS depth from tree order by treepath");
$stmnt->bind_result($name,$treepath,depth);
$tree = array();
$current_depth = 0;
while($stmnt->fetch())
{
if($current_depth < $depth)
{
echo '<ol><li>' . $name . '</li>';
$current_depth=$depth;
}
else if($current_depth == $depth)
{
echo '<li>' . $name . '</li>';
}
else //current_depth greater than depth
{
echo '</ol><li>' . $name .'</li>';
}
}
$stmnt->close();
$db->close();
echo '</ol>';
?>
Not tested, will need tweeks.
If your application needs to provide nested menus, you are better off implementing nesting ( In database language, it would be grouping, foreign keys and constraints ) in your database. It's not recommended that you maintain different designs in your presentation layer and database layer. After all, a strong database design is pivotal for any application, isn't it?
There are many other advantages as well - you streamline your application design, data remains organized and consistent, lesser effort in transforming database content into presentation content, and it becomes easier to add new features as well. Why don't you start with some categorization and a good schema design?
If you ask me, I would make something like a "phones" table that has phone_name, year, manufacturer, model, type etc.
Again, there would be a different table called manufacturers which have manufacturer id's, names, years, and other information. You can also have another table for "type". There will be foreign key relationships between manufacturer id's and manufacturer column in phone table and similar stuff with other columns/tables as well. If you are confused, a good place to start off would be drawing an E-R diagram which will eventually be put down into tables and views.
Related
So I'm working on this/these problem(s), and there's just a lot going on for recently using codeigniter..
So, I have these specific tables:
sect_tbl ----------- sub_tbl
sect_id ----------- sub_code
2 --------------------ST20
3 --------------------AS13
3 --------------------DA11
1 --------------------PE40
2 --------------------SE10
3 --------------------PE20
4 --------------------RT40
I made sect_id the foreign key in sub_tbl.
the display - a side menu:
link of sect_ids:
1
2
3
4
I want to make it so, that if I click the link of sect_id 3, I'll be redirected to a new page with its corresponding set of sub_code -- "AS13, DA11, PE20" as seen in the table above.
Expected outcome is something like this:
sub_code
(in sect_id-3)
| AS13 |
| DA11 |
| PE20 |
Model: (most definitely not right )
function fetch_view($data){
$query = $this->db->query("SELECT a.sub_code, a.sub_name, a.sect_id,
b.block_name, b.year_level FROM subject as a JOIN section as b
WHERE a.sect_id = b.sect_id ");
return $query->result();
}
Controller:
$data["subjects"] = $this->view_model->fetch_view($this->session-
userdata('user_id'));
view: (this lacking since I cant get around much further with foreach loop yet )
<?php
foreach ($subjects as $row) {
?>
<?php echo $row->year_level.$row->block_name; ?>
<?php
}
?>
-> I tried working my way with href to redirect me and then be able to view it dynamically but it backfired. Thanks a lot to those who could spare their time to share solutions.
If you say you want all sub codes where sect ID = 3
$sect_id = 3;
I simplified your query, but the main problem was that you had no use of "on x = y" for your join
$query = $this->db->query("
SELECT
sec.year_level,
sec.block_name,
sub.sub_code,
FROM section as sec
JOIN subject as sub
ON sec.sect_id = sub.sect_id
WHERE sec.sect_id = ?
", [ $sect_id ] );
if( $query->num_rows() > 0 )
return $query->result();
return [];
Then, because I don't have your actual table schemas, I couldn't suggest if your view is correct, but in general, something like this should work:
$this->load->helper('url');
foreach( $subjects as $row )
{
echo anchor( 'subs/show/' . $row->sub_code, $row->year_level . $row->block_name );
}
Also, as a side note, try to always follow a code style with proper indentation. It makes things that are wrong stand out and easier to fix.
i have a database which contains 11 tables 9 of which have a one to many relation with a User Information table.
User Table:
ID , Name , Age , phone , . .......
on basis of ID the relation is with tables for e.g
User_cars
ID,User_ID(fk),Make , Model , .....
What i need to be able to do is present this data in DB in a CSV file. Since the There is a one to many relation a simple join doesnt work as rows are duplicated.(Not presentable Format for the client :S)
Im using Yii as web app and need some extension which can present it in some readable way.
or maybe a php script can do the task as well.
You can handle it in one of two ways:
1. Use GROUP_CONCAT (as #Dave mentioned in the comment - this is the link: MYSQL PHP API - Displaying & Fetching multiple rows within rows from another table).
There is a downside however, GROUP_CONCAT has a limit (by default 1024 characters), which you can change with the server variable group_concat_max_len, but you may not have access to do so.
This will look pretty much like this:
ID | Name | Car Model | Year
1 John BMW, Audi, Renault 1999, 2000, 2003
2 David Mercedes, Ford 2000, 2005
This can get very complicated if there are 30-40 entries per user, and non-readable.
2. The second option is to export it in the following format (not hard to do, you just iterate through all the cars a user has, but write the user only in the first iteration).
ID | Name | Car Model | Year
1 John BMW 1999
Audi 2000
Renault 2003
2 David .....
Here's some sample code (I use plenty of made up methods, that have suggestive names).
$csvArray = array();
foreach ($users as $user) {
$cars = $user->getCars(); //random method name. Grabs an array with the cars this user has :)
$firstIteration = 1;
foreach ($cars as $car) {
if ($firstIteration == 1) { // we only set the CSV row the first time we iterate through cars
$csvSingle['ID'] = $user->getId(); // more random method names
$csvSingle['Name'] = $user->getName();
$firstIteration = 0;
}
$csvSingle['car_model'] = $car->getCarModel();
$csvSingle['car_year'] = $car->getCarYear();
$csvArray[] = $csvSingle; // now we add the single row to the big CSV array.
}
}
fputcsv($handle, $csvArray); // $handle is the file you export to
i like first variant by #"Vlad Preda" and in your controller or component for export you can use something like this:
get users with cars (and cars must fill in user model relations)
$users = User::model()->with('cars')->findAll();
and then
foreach ($users as $user) {
$csvOneUser = array('id'=>$user->id, "name"=>$user->name);
if (!$user->cars) {
$csvAll[] = $csvOneUser;
continue;
}
foreach ($user->cars as $car) {
$car_attributes = $car->getAttributes();
foreach ($car_attributes as $k=>$v) {
$csvOneUser[$k][] = $v;
}
}
}
I am creating a questionnaire for a client that requires the questions to be organized by 3 layers of levels, with a unknown amount of questions and categories. I've successfully created the U.I. and the code to pull the information from the database however I've been trying to find out how to get everything loading in the right place. The database is organized like so by the client so I have no control over it:
id description parentId
1 Level 1 0
2 Level 2 0
3 Level 1a 1
4 Level 1b 1
5 Level 1a1 3
and the code I'm using right now is this:
function printChildQuestions($parentid) {
$sql="SELECT * FROM pB_test WHERE parentID=$parentid";
$result=mysql_query($sql);
$i=0;
while (true) {
$row=mysql_fetch_array($result);
if (!$row) break;
if ($row['parentId'] == 0) {
echo '<div class="tabs">';
echo '<span class="level1">'.$row['description'].'</span>';
echo '<ul id="main-nav"><h2 align="center">Categories</h2></ul>';
echo '<div>'.$row['id'].' '.$row['description'].' '.$row['parentId'].'</div>';
if ($row['parentId'] == 1) {
}
echo '</div>';
} else {
echo '<div>'.$row['id'].' '.$row['description'].' '.$row['parentId'].'</div>';
}
printChildQuestions($row['id']);
}
}
printChildQuestions(0);
and the html that needs to be generated for is here: http://jsfiddle.net/Cyb2N/
The issue is every idea I've come up with needs me to hardcode the levels in and breaks when level 2 is introduced. Could someone shove me in the right direction? Thanks!
If I understand your question correctly, you're looking for a way to resolve the levels implied by the parentId references in the client's data? Like so:
Level 1
Level 1a
Level 1a1
Level 1b
Level 2
How about creating a Level class with a parent property which references the parent Level? Then you can iterate through the client's data, resolving the Level instances into a nice tree, and do anything you want with it from there.
Alternatively, you could give the Level class a children property, which would be a collection of the levels that reference that instance as a parent... anyway the point is that you can transform your client's flat data into a useful tree model with a fairly simple class. By counting parents (or children), you can tell how far up/down the tree a given Level is.
printChildQuestions is a recursive function. It sounds like you don't know how to determine which level of recursion you're at. Here's how:
printChildQuestions($parent_id);
function printChildQuestions($parent_id, $level = 1){
if( $need_to_recurse ){
printChildQuestions($new_parent, $level + 1);
}
}
I'm looking for the best way to create a complex navigation element on the fly. I have all of the elements in a database (title, id, parentId) and I want to efficiently take them out of the DB and display them correctly. I also want to collapse all of the navigation elements that aren't active. So if I was browsing through "Sofas" I wouldn't see "Chandeliers" or any of the categories under lighting but I would see "Lighting".
This is what I want the final product to look like:
Furniture
Living Room
Sofas
Chairs
Ottomans
Bedroom
Beds
Nightstands
Lighting
Chandeliers
Floor Lamps
Sconces
Rugs & Textiles
Contemporary
Vintage
My current method is
write one SQL query that pulls down all of the category names, ids, and parent ids
Iterate through the categories and put into a sorted multi-dimensional array with child categories stored under their parents.
Iterate through the new array and add another entry to mark the appropriate categories as open (all categories are closed by default)
iterate through the array and write HTML
I'm trying to to this with as few interations as possible and I'm sure the code I have right now is inefficient. Especially step 2 I iterate through the array several times. There has to be a general solution to this (common?) problem.
Consider adding a new field to your database table: level.
Main-categories will have level 0.
Sub-categories will have level 1.
Sub-sub-categories will have level 2.
etc.
This trick will help you to know which sub-categories to disable without 2nd iteration of the array.
I believe this is the perfect place to generate your html code using recursion.
I used this function a while ago. It is working with a multi-dimensional array (tree)
function buildMenu($menu_array, $is_sub=FALSE) {
$attr = (!$is_sub) ? 'id="menu"' : 'class="submenu"';
$menu = "<ul $attr>\n";
foreach($menu_array as $id => $elements) {
foreach($elements as $key => $val) {
if(is_array($val)) {
$sub = buildMenu($val, TRUE);
}
else {
$sub = NULL;
$$key = $val;
}
}
if(!isset($url)) {
$url = $id;
}
$menu .= "<li>$display$sub</li>\n";
unset($url, $display, $sub);
}
return $menu . "</ul>\n";
}
echo buildMenu($menu_array);
This adds css properties too. If you wish to mark the currently active page you can use the strpos() function to find your current url. If you need some more functionality you can easily add them to buildMenu()
Using level as mentioned in the answer above will help too. If you were using the nested set model in your database I could also help you with my query which is a single select returning the whole menu data.
I have a set of Organizations and their Board Members.
All organizations have board members and many board members are on the board of more than one organization.
I am using JIT Hypertree to illustrate their relationships. The JIT Hypertree schema requires that one item be the parent of all and is drawn based on a single JSON array.
I would love to have the re-centering event query and re-populate the graph based on the change. Then 2 levels would be fine but I have not been able to work out how to do that.
The code I have at present recurses manually for three levels from the starting organization but what I want is to re-curse through all related records.
So it would start with an Org and add Org's array of children (board members). Then fetch all of the boards (other than current Org) for each board member and add those as children of the board member.
This would continue until each trail dead ends - presumably at a board member who only belongs to one board.
Anyone have advice on how to create this array and avoid duplicates?
$board = $center->board();
$top['id'] = $center->ID;
$top['name'] = $center->Org;
$top['children'] = array();
if ($board) {
foreach ($board as $b) {
$child['id'] = $b->ID;
$child['name'] = (strlen(trim($b->Last)) > 0) ? $b->First . ' ' . $b->Last : 'Unknown';
$child['data']['orgname'] = $center->Org;
$child['data']['relation'] = $b->Role;
$child['data']['occupation'] = $b->Occupation;
$child['children'] = array();
$childboards = $b->boards();
if ($childboards) { foreach ($childboards as $cb) {
$gchild['id'] = $cb->ID;
$gchild['name'] = $cb->Org;
$gchild['data']['orgname'] = (strlen(trim($b->Last)) > 0) ? $b->First . ' ' . $b->Last : 'Unknown';
$gchild['children'] = array();
$childboardmembers = $cb->board();
if ($childboardmembers) { foreach ($childboardmembers as $cbm) {
$ggchild['id'] = $cbm->ID;
$ggchild['name'] = (strlen(trim($cbm->Last)) > 0) ? $cbm->First . ' ' . $cbm->Last : 'Unknown';
$ggchild['data']['orgname'] = $cb->Org;
$ggchild['data']['relation'] = $cbm->Role;
$ggchild['data']['occupation'] = $cbm->Occupation;
$ggchild['children'] = array();
$gchild['children'][]= $ggchild;
}}
$child['children'][]= $gchild;
}}
$top['children'][] = $child;
}
}
$top['data'] = array();
$top['data']['description'] = $center->Desc;
echo json_encode($top);
// Edit 2011.10.24 In Re hakre response
My data structure is a table of Organizations with unique IDs, a table of People with Unique IDs, and then a bridging table for the two specifying Organization (Entity) and Person and the Role the Person is playing in the Entity. A typical many-to-many. No sub-boards at all. I made an image of it which now seems kind of pointless but I'll add it at the bottom.
The JIT library data structure is a little nuts (to me) in that it goes like this in their band example:
Top: Nine Inch Nails
Child: Jerome Dillon
Child: Howlin Maggie (another band)
{all the bands' members and then all of their bands...}
So the organization (band) is treated as though it is a Person even though it is comprised of a number of Persons. And when I recurse using the code above I get (I think) terrible bloat but the JSON it makes works correctly despite bloat.
Example JSON and Example Visualization
// End Edit
Your question is hard to answer in the sense that your data-structure is mainly unknown.
For the graphical represenation you only need to provide simple relationships if I understand that correctly:
*- Parent
+- Child
+- Child
...
`- Child
Your data structure has a different format, I don't know specifically but it's something like:
Org <-- 1:n --> Board
Board <-- n:n --> Board # If you have sub-boards
Board <-- n:n --> Member
Whichever your data is represented, to map or transpose your data onto the required structure for the graphical representation, you need some functions that take care of that.
To do that you need to share classification/type between both and specific keys, so that you can look-up the needed data from the event to return the data. For example:
if (request_parent_is_org())
{
$id = request_parent_id();
$parent = data_get_board($id);
$parent->addChildrent(data_get_board_children($id));
}
else
{
... # All the other decisions you need to take based on request type
}
view_response_to_json($parent);
What you have with your many-to-many data model is a graph. JIT is designed for trees.
To put it another way, JIT will not correctly show the crossing lines that are represented in the data whenever a single person is connected to multiple organizations.
I'd recommend a proper network graph visualization - D3.js has a great implementation for modern browsers.
The JSON data format it uses is actually easier to implement given your table structure - for all the organizations and people, you define objects:
{
"name": "Mme.Hucheloup",
"group": 1
},
{
"name": "Afton School Board",
"group": 2
}
And for each association in your association table you define connection objects that wire them together:
{
"source": 1,
"target": 2
},
The fancy coding in D3 takes care of the rest. Good luck!
You can use function below:
function get_orgs_and_childs ($child_id, $found = array())
{
array_push ($found, $child['name']);
if($child['children'])){
$found[] = get_parents($child['id'], $found);
}
return $found;
}
Call it using:
$orgs = get_orgs_and_childs($child['id']);