I get username (child) and referral (parent) records from database and put them into arrays by code below :
$sql = "SELECT username, referral FROM acc_status";
$q = $conn->prepare($sql);
$q->execute(array());
$data = array();
while($row = $q->fetch(PDO::FETCH_ASSOC)){
$data[] = $row;
}
$map = array();
foreach ($data as $node) {
// init parent
if (!array_key_exists($node['referral'], $map)) {
$map[$node['referral']] = array();
}
// add to parent
$map[$node['referral']][$node['username']] = & $map[$node['username']];
}
echo '<pre>';
print_r($map["123"]); //get parents and child of username "123" only.
echo '</pre>';
And here is the output :
Now I want to covert the array into ul and li list, below is the code :
function printTree($tree) {
if(!is_null($tree) && count($tree) > 0) {
echo '<ul>';
foreach($tree as $node) {
echo '<li>'.$node['referral'];//error here
printTree($node['username']);//error here
echo '</li>';
}
echo '</ul>';
}
}
printTree($map["123"]);
I am getting 2 errors :
1) Notice: Undefined index: referral
2) Notice: Undefined index: username
How to fix the errors?
I'll be brief here, put simply the name of your referral and username is actually your index. So for $node['username'] it would be qqq I do not have my computer with me to show the working code however that's what it will be.
Related
I have a table in my database that host many genealogy trees.
-----------------------------
- id name parent_id
-----------------------------
- 1 grandfather NULL
- 2 father 1
- 3 uncle 1
- 4 son 2
- 5 brother 2
- 6 cousin's dauther 7
- 7 cousin 8
- 8 auntie 1
The problem is I can't get to show all the names because of an edge case:
-When I have a person with a parent_id that is bigger than it's parent's parent_id
(see cousin's daugter)
I use this queries to get the table:
$sql = "SELECT p1.id, p1.name, p1.parent_id FROM pariente p1
ORDER BY p1.parent_id";
$result = $conn->query($sql);
The problem is that if I use "ORDER BY parent_id" "cousin's dauther" won't show and if I use "ORDER BY id" "cousin" won't show.
I use this functions to make a the tree into an array and draw it:
function make_tree($data, $root) {
$tree = [];
foreach ($data as $node) {
insert($tree, $node);
}
return $tree;
}
function insert(&$root, &$node) {
if (!$root) {
$root = $node;
}
else if ($root["id"] === $node["parent_id"]) {
$root["children"][] = $node;
}
else if (array_key_exists("children", $root)) {
foreach ($root["children"] as &$c) {
if (insert($c, $node)) {
break;
}
}
}
}
function preorder2(&$root) {
if ($root) {
echo "<li>";
echo $root["name"];
if (array_key_exists("children", $root)) {
echo "<ul>";
foreach ($root["children"] as $c) {
preorder2($c);
}
echo "</ul>";
}
echo "</li>";
}
}
?>
And after I use this to call the functions:
<div>
<?php
while( $row = mysqli_fetch_assoc( $result)){
$resguard[] = $row;
}
$tree = make_tree($resguard);
preorder2($tree);
?>
</div>
I had a similar problem once, and here's how I fixed it.
Iterate over the dataset, putting each node in your array, and keep track of what you want to be your root node.
Iterate over the array. For each node where the parent_id is not null, lookup the parent node by id, and add the current node as a child. There's no need to use recursion when building the tree.
At the end I believe I didn't get toastifier answer, but it get me thinking and I ended up solving the problem (still using the way ggorlen shows the tree).
First this are the queries:
$sql = "SELECT p1.id, p2.name, p2.id as minor, p2.name FROM pariente p1 INNER JOIN pariente p2 ON p1.id = p2.parent_id ORDER BY p1.id";
$result = $conn->query($sql);
$sql2 = "SELECT p1.id, p1.nombre, p1.padre_id FROM pariente p1 WHERE p1.padre_id IS NULL ORDER BY p1.id";
$raices = $conn->query($sql2);
The functions:
function make_tree($resguardo, $root){
$tree = [];
$tree = $root;
foreach ($resguardo[$root["id"]] as $node) {
add($tree, $node, $resguardo);
}
return $tree;
}
function add(&$root, &$children, $resguardo){
$root["children"][$children["minor"]] = $children;
$flag= false;
if (isset($resguardo[$children["minor"]])) {
$flag = true;
}
if ($flag == false){
return;
} else {
foreach ($resguardo[$children["minor"]] as $child) {
agregar($root["children"][$children["minor"]], $child, $resguardo);
}
}
}
function preorder2(&$root) {
if ($root) {
echo "<li>";
echo '<a href="">';
echo $root["name"];
echo "</a>";
if (array_key_exists("children", $root)) {
echo "<ul>";
foreach ($root["children"] as $c) {
preorder2($c);
}
echo "</ul>";
}
echo "</li>";
}
}
And I call them here:
while( $row = mysqli_fetch_assoc($result)){
$resguardo[$row["id"]][] = $row;
}
while( $root = mysqli_fetch_assoc( $roots)){
echo '<ul>';
$tree = make_tree($resguardo, $root);
preorder2($tree);
echo "</ul>";
}
Thanks alot to both of you, I would never solve it without your help.
I have this code but when I run it it give me error ( Notice: Undefined offset: 1 in C:\wamp\www\test3.php on line $str[$row['term_no']] += ",".$row['code']; )
How can I solve this problem?
<?php
$con = mysqli_connect('localhost', 'root', '');
mysqli_select_db($con, "uoh");
$q = " SELECT * FROM `degree_plan` LEFT JOIN courses ON
degree_plan.course_number=courses.course_number
where major='COE'";
$result = mysqli_query($con, $q);
if ($result) {
$str = [];
while ($row = mysqli_fetch_array($result)) {
{
$str[$row['term_no']] += "," . $row['code'];
}
foreach ($str as $key => $value) {
echo $value;
echo $key;
}
}
}
?>
In your code, you are puting all codes in one element which is never defined.
change this:
$str[$row['term_no']] += ",".$row['code'];
to this:
$str[$row['term_no']] = $row['code'];
The error happens because the array element doesn't exist, so, first check the element : if it exist, add, if it doesn't, create:
while($row = mysqli_fetch_array($result))
{
if ( isset( $str[$row['term_no']] ) ) // IF ELEMENT EXISTS...
$str[$row['term_no']] .= ",".$row['code']; // ADD TO ELEMENT.
else $str[$row['term_no']] = $row['code']; // CREATE ELEMENT.
}
I' am trying my best to create dynamic menu and submenu. While trying I got plenty errors, Was able to solve some errors.
This is the current errror message am getting
Warning: Illegal offset type in D:\xampp\htdocs\projects\xxxx.php on line 40
array(0) { }
This function loops an array and created UL LI Dynamically & Levels
function loop_section($array = array(), $section_parent_id = 0)
{
var_dump($array );
if (!empty($array[$section_parent_id])) {
echo '<ul>';
foreach ($array as $values) {
echo '<li>';
echo $values->section_name;
echo $values->section_id;
loop_section($array, $values->section_id);
echo '</li>';
}
echo '</ul>';
}
}
This function Query the database and gets the results
function fetch_sections()
{
global $db;
$array = array();
$query = $db->SELECT("SELECT * FROM sections");
$rows = $db->ROWS(); // Returns an error
$array[$rows];
loop_section($array);
}
Database Strucutre
This is the database structure
$array[$rows];
Should be:
$array[] = $rows;
Since $rows is also an array, it cannot be the key in an array. Also the line $array[$rows]; would do nothing, even if $rows was a valid variable to be an array key (int, string)
I have a simple news system that i need to change so i can limit the items per page but i don't have anything that would do this, so i need to do this myself.
Basically the function just displays the whole array of the flat files and i would like to limit it to 3 items per page(configurable).
My starting logic was:
$itemspage=pages->current_page;
$itemsperpage=3;
$limitn=$itemperpage*$itemspage;
$itemindex=$limitn-2;
if page was 1 it would display the news from array index 1,2,3
if page was 2 it would display the news from array index 4,5,6
and so on...
i think this logically should work but what if the news in the array can't be split into 3? with this logic this should (at least on the starting index) cause a bug.
The whole code is here:
$list = $this->getNewsList();
$pages = new Paginator;
echo "<table class='newsList'>";
foreach ($list as $value) {
$newsData = file($this->newsDir.DIRECTORY_SEPARATOR.$value);
$newsTitle = $newsData[0];
$submitDate = $newsData[1];
unset ($newsData['0']);
unset ($newsData['1']);
$newsContent = "";
$itemspage=pages->current_page;
$itemperpage=3;
$limitn=$itemperpage*$itemspage;
$itemindex=$limitn - 2;
foreach ($newsData as $value) {
$newsContent .= $value;
}
echo "<tr><th align='left'>$newsTitle</th>
<th class='right'>$submitDate</th></tr>";
echo "<tr><td colspan='2'>".$newsContent."<br/></td></tr>";
}
echo "</table>";
The for hasn't been done yet only the logic behind the split. Could you help me?
I'm not sure how you're building your $newsContent, but maybe your answer is in here:
$list = $this->getNewsList();
$pages = new Paginator;
$itemspage = $pages->current_page;
$itemperpage = 3; // you might want te define this property in the paginator also ($itemperpage = $pages->items_per_page);
$currentPageList = array_slice($list, ($itemspage - 1) * $itemperpage, $itemspage);
echo "<table class='newsList'>";
foreach ($currentPageList as $value) {
$newsData = file($this->newsDir.DIRECTORY_SEPARATOR.$value);
$newsTitle = $newsData[0];
$submitDate = $newsData[1];
unset ($newsData['0']);
unset ($newsData['1']);
$newsContent = "";
$limitn=$itemperpage*$itemspage;
$itemindex=$limitn - 2;
foreach ($newsData as $value) {
$newsContent .= $value;
}
echo "<tr><th align='left'>$newsTitle</th>
<th class='right'>$submitDate</th></tr>";
echo "<tr><td colspan='2'>".$newsContent."<br/></td></tr>";
}
echo "</table>";
Notice the array_slice:
array_slice() returns the sequence of elements from the array array as specified by the offset and length parameters.
I'm creating a tree-structure of categories with parentid's which can be called from children in such a way:
ID | Name | ParentID
1 1 0
2 2 1
3 3 2
4 4 1
Resulting in this:
1 = 1
2 = 1 -> 2
3 = 1 -> 2 -> 3
4 = 1 -> 4
which means 3 is a child of 2, which is a child of 1.
when trying to get this idea (with the -> to show what relations are set) I only get to the second grade (1 -> 2) but not to the third (1->2->3) because of the looping function I use for it.
//put all ID's in an array
while ($row2 = $connector->fetchArray($result2)){
$id = $row2['ID'];
$parents[$id] = $row2['name'];
}
// show the tree-structure
while ($row = $connector->fetchArray($result)){
if($row['parentid']!=0)echo $parents[$row['parentid']].' -> ';
echo $row['name'].' - ';
echo '<br>';
}
I'd like two things to change:
have the code automatically generate a tree sized as necessary.
in the while-loops i have to select the $result twice (once as $result, once as $result2) to make it work. these $result's have exactly the same database-query:SELECT ID,name,parentid FROM categories
to fetch results from. I'd like to only declare this once.
Thanks for all the good answers. I've gone with the easiest, less-code-to-implement approach:
$result = $connector->query('SELECT ID,name,parentid FROM categories');
// Get an array containing the results.
$parents = array();
while ($row = $connector->fetchArray($result)){
$id = $row['ID'];
$parents[$id] = array('ID' => $row['ID'],'name' => $row['name'],'parentid' => $row['parentid']);
}
foreach ($parents as $id => $row){
$pid=$id;
$arrTmp= array();
do { // iterate through all parents until top is reached
$arrTmp[]=$pid;
$pid = $parents[$pid]['parentid'];
}while ($pid != 0);
$arrTmp = array_reverse($arrTmp);
foreach($arrTmp as $id){
echo $parents[$id]['name'].' -> ';
}
echo '<br>';
}
Rather than have PHP organize the items into a tree, why not ask the database to do it for you? I found this article on hierarchical data to be very good and the examples are almost identical to yours.
EDIT
The SQL for getting the full tree using the Adjacency Model is not ideal. As the article explains it requires rather a lot of joins for even a small hierarchy. Is it not possible for you to use the Nested Set approach? The SQL stays the same regardless of the size of the hierarchy and INSERT and DELETE shouldn't be very difficult either.
If you really want to do hierachies with parent ids(suitable only for small number of items/hierachies)
I modified your code a little bit(I did not test it so there may be some syntax errors):
//put all recordsets in an array to save second query
while ($row2 = $connector->fetchArray($result2)){
$id = $row2['ID'];
$parents[$id] = array('name' => $row2['name'],'parent' => $row2['parentid']);
}
// show the tree-structure
foreach ($parents as $id => $row){
$pid = $row['parentid'];
while ($pid != 0){ // iterate through all parents until top is reached
echo $parents[$pid]['name'].' -> ';
$pid = $parents[$pid]['parentid'];
}
echo $parents[$id]['name'].' - ';
echo '<br>';
}
To answer your comment:
$parents = array();
$parents[2] = array('ID'=>2,'name'=>'General','parentid'=>0);
$parents[3] = array('ID'=>3,'name'=>'Gadgets','parentid'=>2);
$parents[4] = array('ID'=>4,'name'=>'iPhone','parentid'=>3);
foreach ($parents as $id => $row){
$pid=$id;
$arrTmp= array();
do { // iterate through all parents until top is reached
$arrTmp[]=$pid;
$pid = $parents[$pid]['parentid'];
}while ($pid != 0);
$arrTmp = array_reverse($arrTmp);
foreach($arrTmp as $id){
echo $parents[$id]['name'].' -> ';
}
echo '<br>';
}
Prints out:
General ->
General -> Gadgets ->
General -> Gadgets -> iPhone ->
Maybe easier with OOP. Just sort the query by parentId
Note: The listChildren method and the printout at the bottom is just there to show it is listed correctly. I did not interpret the question that the display was important.
class Element {
public $id;
public $name;
public $parent = null;
public $children = array();
public function __construct($id, $name)
{
$this->id = $id;
$this->name = $name;
}
public function addChild($element)
{
$this->children[$element->id] = $element;
$element->setParent($this);
}
public function setParent($element)
{
$this->parent = $element;
}
public function hasChildren()
{
return !empty($this->children);
}
public function listChildren()
{
if (empty($this->children)) {
return null;
}
$out = array();
foreach ($this->children as $child) {
$data = $child->id . ':' . $child->name;
$subChildren = $child->listChildren();
if ($subChildren !== null) {
$data .= '[' . $subChildren . ']';
}
$out[] = $data;
}
return implode(',', $out);
}
}
$elements = array();
$noParents = array();
while ($row = $connector->fetchArray($result)) {
$elements[$row['id']] = $element = new Element($row['id'], $row['name']);
if (isset($elements[$row['parent']])) {
$elements[$row['parent']]->addChild($element);
} else {
$noParents[] = $element;
}
}
foreach ($noParents as $element) {
if ($element->hasChildren()) {
echo "Element {$element->id} has children {$element->listChildren()}.\n";
} else {
echo "Element {$element->id} has no children.\n";
}
}
If you are using PostgreSQL as the database, you can use the connectby() function to create the record set:
SELECT *
FROM connectby('tableName', 'id', 'parent_id')
AS t(keyid text, parent_keyid text, level int);
I love this function, and use all the time in my code. It can do some very powerful things, very quickly, and you don't have maintain the left/right values like the (adjacency model).