How to show sql select parent and child in php - php

Maybe a simple question, but after hours of reading i still didn't find an answer.
I'm trying to get the values of subrow next to those of parent row, without constantly repeating parent row:
For example:
Table 1: ParentId = 1, ParentName = ParentName
Table 2:
ChildId = 1, ParentId = 1, ChildName = ChildName1
ChildId = 2, ParentId = 1, ChildName = ChildName2
The result with JOIN currently is:
ParentId, ParentName, ChildId, ChildName1
ParentId, ParentName, ChildId, ChildName2
But i would like to display the data like:
<div>Div with parent name</div>
<div>Div with ChildName1</div>
<div>Div with ChildName2</div>
<div>Div with parent name2</div>
<div>Div with ChildName1</div>
<div>Div with ChildName2</div>
etc...
Can anyone tell me how to get to this?
Something like
echo$Parent1 foreach child as $child echo$ChildName?

You can sort by parent ID, then check if it has changed. If it has changed output the name of the parent. Something like this:
$query = "query selecting data with join... ORDER BY ParentId";
$result = $mysqli->query($query);
$lastParent = 0;
while ( $row = $result->fetch_assoc($result) )
{
if ( $row['ParentId'] != $lastParent )
{
echo '<div>' . $row['ParentName'] . '</div>';
}
$lastParent = $row['ParentId'];
echo '<div>' . $row['ChildName'] . '</div>';
}
Using that the parent name will only output when the ID changes, and as we're ordering by ID all the children will be grouped by their Parent and output together.

Issue fixed with following code
$result = mysqli_query($link, "SELECT * FROM TestParent a
LEFT JOIN TestChild b ON a.ParentId = b.ParentId
ORDER BY a.ParentId ASC, b.ChildName ASC");
$lastParent = 0;
while($row = mysqli_fetch_assoc($result))
{
if ( $row['ParentId'] != $lastParent ) {
echo '<div>' . $row['ParentName'] . '</div>';
}
$lastParent = $row['ParentId'];
echo '<div>' . $row['ChildName'] . '</div>';
}

Related

How can I fetch results from a junction table and insert them as an array into a JSON object using PHP?

I have Articles with Categories in a mysql Database. I would like to print out all Articles with their corresponding Categories as JSON in PHP to fetch with my Vue-App.
I'm working with the following tables: Articles, Categories and Article_has_Category (junction table, many to many):
Articles
"ID" | "Title"
------------
1 | First
2 | Second
Categories
"ID" | "Category"
------------
1 | Lifestyle
2 | Webtech
Article_has_Categories
"ID" | "Article_ID" | "Category_ID"
--------------------------------------
1 | 1 | 1
2 | 1 | 2
The following PHP-Code selects and prints all Articles for my Frontend to fetch:
$stmt = $pdo->prepare("SELECT * FROM Articles;");
$stmt->bindParam(':param');
if ($stmt->execute()) {
$array = $stmt->fetchAll();
$jsonArray = json_encode($array);
print_r($jsonArray);
}
Printed JSON-Output:
[
{"ID":"1","Title":"First"},
{"ID":"2","Title":"Second"}
]
Is it somehow possible to insert all Categories as an array into that JSON-Output?
Desired JSON-Output:
[
{"ID":"1","Title":"First", "Categories": "[Lifestyle, Webtech]" },
{"ID":"2","Title":"Second", "Categories": "[]"}
]
Currently I'm building the desired object in my frontend first using "SELECT * FROM Articles;" to fetch all articles and then in a seperate call, fetching the corresponding categories by Article ID using the statement below:
SELECT c.Category
FROM article_has_category ac
INNER JOIN Categories c ON c.ID = ac.Category_ID
WHERE ac.Article_ID = :id;
Is there any solution combining the two statements and building the desired object directly in my PHP File?
Okay I solved this by assembling my own JSON in PHP, instead of using json_encode().
My code is not very pretty but I commented it a bit for you to understand:
<?php
$stmt = $pdo->prepare("
SELECT * FROM Articles;
");
$stmt_categories = $pdo->prepare("
SELECT c.Category
FROM article_has_category ac
INNER JOIN Categories c ON c.ID = ac.Category_ID
WHERE ac.Article_ID = :id;
");
if ($stmt->execute()) {
$result = $stmt -> fetchAll();
// count items in result, in order to determine later which is the last one
$numItems = count($result);
$i = 0;
// prepare the json-array to print out in the php file
$printArray = '[';
// for each article, run the second sql-statement using the article-ID
foreach( $result as $row ) {
$stmt_categories->bindParam(':id', $row['ID']);
// executing the second statement
if ($stmt_categories->execute()) {
$result_category = $stmt_categories -> fetchAll();
// save the fetched categories into a new array, using the function makeArray()
$categories = makeArray($result_category);
// build the json object by hand, no more need for json_encode()
$element = '{';
$element .= ' "ID": ';
$element .= '"' . $row['ID'] . '",';
$element .= $categories;
$element .= ' "Title": ';
$element .= '"' . $row['Title'] . '"';
$element .= '}';
if(++$i === $numItems) {
// if it's the last item, do nothing
} else {
// if not, add a comma
$element .= ',';
}
// add the built element to the printArray
$printArray .= $element;
}
}
$printArray .= ']';
// finally print the array
print_r($printArray);
}
function makeArray($categoryArray){
$category_element .= ' "Category": ';
$category_element .= "[";
$numCategories = count($categoryArray);
$n = 0;
foreach( $categoryArray as $row ) {
$category_element .= '"' . $row['Category'] . '"';
if(++$n === $numCategories) {
// if it's the last item, do nothing
} else {
// if not, add a comma
$category_element .= ',';
}
}
$category_element .= "],";
return $category_element;
}

undefined index by selecting data from two tables with a common column

I have two tables - home and posts;
home.artid is equal to posts.id.
Want select id, artid, inde from home PLUS title from posts
where home.pos is slider:
$items = "";
$st = $db->query("select home.id, home.artid, home.inde
from home
join posts on home.artid = posts.id
where home.pos = 'slider'
order by home.inde asc");
while ($row = $st->fetch()){
$items .= "<div class='slidertitle'>" . $row['posts.title'] . "</div>\n";
}
echo $items;
Error:
Undefined index posts.title...
Any help?
If you want to select posts.title then select it
$st = $db->query("select home.id, home.artid, home.inde,
posts.title
from home
join posts on home.artid = posts.id
where home.pos = 'slider'
order by home.inde asc");
// and it would be called just `title`
while ($row = $st->fetch()){
$items .= "<div class='slidertitle'>" . $row['title'] . "</div>\n";
}
echo $items;

handle parent/child categories in mysql/php

I have this mysql table "categories":
id category parent
1 category1 NULL
2 subcategory1 1
3 category2 NULL
4 subcategory2 2
and I want to get this result:
category1
subcategory1
subcategory2
category2
to get this result I use this code, but is very slow:
foreach($db->query("SELECT * FROM categories WHERE parent IS NULL") as $parent)
{
$t=0;
categories($db,$t,$parent['id'],$parent['category']);
}
function categories($db,$t,$id,$category)
{
echo"<option value=".$id.">";//
for($i=0;$i<$t;$i++) {echo" "; $GLOBALS['cat'].=" ";}
echo $category."</option>";//" ".$id.
$GLOBALS['cat'].=$category."<br>";
$t++;
if($db->query("SELECT * FROM categories WHERE parent=".$id)->rowCount()>0)
{
foreach($db->query("SELECT * FROM categories WHERE parent=".$id) as $child)
categories($db,$t,$child['id'],$child['category']);
}
}
Do you have a faster solution?
Thanks
If you know the depth of your tree (maximum or desired), you can have it all in SQL, flattened to a row representing path:
SELECT c.category, c1.category AS sub1, c2.category AS sub2, c3.category AS sub3
FROM categories c
LEFT JOIN
(categories c1
LEFT JOIN
(categories c2
LEFT JOIN categories c3
ON c3.parent = c2.category)
ON c2.parent = c1.category)
ON c1.parent = c.category
WHERE c.parent IS NULL;
Having that, it's not a big deal to present it in UI accordingly.
$sql = "SELECT id, name, parent_id,
(SELECT COUNT(*) FROM categories WHERE parent_id = cat.id) as child_count
FROM categories cat";
$result = db_select_all($sql);
function build_menu($pid, $level = 1) {
global $result;
$filtered = array_filter($result, function($elem) use($pid) {
return $elem['parent_id'] == $pid;
});
if(empty($filtered)) return;
foreach($filtered as $item) {
extract($item);
echo "<div>" . str_repeat("---", $level) . $name;
build_menu($id, $level + 1);
echo "</div>" . PHP_EOL;
}
}
$menu_elements = array_filter($result, function($item) {
return $item['parent_id'] == '';
});
foreach($menu_elements as $menu) {
$name = $menu['name'];
$id = $menu['id'];
echo "<div>" . $name;
build_menu($id);
echo "</div>" . PHP_EOL;
}

Inner while loop not working

This is a code pinch from a webpage of my project. Here I want to display user selected categories and then want to display its subjects that belong to the categories. There, users could have more than one category, and it is no problem. I can print all those categories in my first while loop. The problem is when I'm trying to print subjects, they only show one row as a result, but there are more subjects in each category. Can anybody tell me what is happening?
This is my code.
Note: Both queries are working properly. I tried those using a mysql client program.
<?php
require_once ('../../includes/config.inc.php');
require_once( MYSQL1 );
$q = "SELECT institute_category.category_id, category_name
FROM institute_category
INNER JOIN category ON institute_category.category_id = category.category_id
WHERE institute_category.institute_id = $instituteId";
$r = mysqli_query( $dbc, $q);
while ( $row = mysqli_fetch_array ( $r, MYSQLI_ASSOC) ) {
$categoryId = $row['category_id'];
$category = $row['category_name'];
echo '<fieldset class="alt">
<legend><span>Category : <em style="color: red;">' . $category .
'</em></span></legend>';
$qy = "SELECT category_subject.category_id, category_subject.subject_id, subjects
FROM category_subject
INNER JOIN category ON category_subject.category_id = category.category_id
INNER JOIN subject ON category_subject.subject_id = subject.subject_id
WHERE category_subject.category_id = $categoryId";
$result = mysqli_query( $dbc, $qy);
$c = $i = 0;
echo '<table class="form_table" ><tr>';
while($row = mysqli_fetch_array( $result, MYSQLI_ASSOC )){
// if remainder is zero after 2 iterations (for 2 columns) and when $c > 0, end row and start a new row:
if( ($c % 2) == 0 && $c != 0){
echo "</tr><tr>";
}
echo '<td width="50%"><input type="checkbox" name="subject[]" value="' .
$row['category_id'] . ":" . $category . ":" . $row['subject_id'] .
":". $row['subjects'] . '" /> ' . $row['subjects'] .
'</td>' . "\n";
$c++;
} // while..
// in case you need to fill a last empty cell:
if ( ( $i % 2 ) != 0 ){
// str_repeat() will be handy when you want more than 2 columns
echo str_repeat( "<td> </td>", ( 2 - ( $i % 2 ) ) );
}
echo "</tr></table>";
}
echo '</fieldset>';
?>
Turning my comment into an answer:
Only looking at your code for a second I can see that you're using the $row variable both for the outer loop and for the inner loop. Try renaming the outer loop's $row variable to $outerRow and the inner loop's $row variable to $innerRow. This may be the first problem. This may apply to other variables, too, like for example the $result variable.
user different variable name for inner loop $result and $row like $inresult and $inrow
Because you have used a same variable $row in both of the loops, $row[] in inner loop could be taking reference of outer one and in that result set the variable you are looking for not present so that not printing anything from inner loop, please change the variable of inner or outer loop.

Loop the subcategories, subcategories, etc

$sth = $db->query("SELECT * FROM categories WHERE parent = 0");
$sth->setFetchMode(PDO::FETCH_ASSOC);
while ($row = $sth->fetch()) {
echo $row['title'] . '<br />';
$sth2 = $db->prepare("SELECT * FROM categories WHERE parent = ?");
$sth2->bindParam(1, $row['id']);
$sth2->execute();
while ($row2 = $sth2->fetch()) {
echo '..' . $row2['title'] . '<br />';
$sth3 = $db->prepare("SELECT * FROM categories WHERE parent = ?");
$sth3->bindParam(1, $row2['id']);
$sth3->execute();
while ($row3 = $sth3->fetch()) {
echo '....' . $row3['title'] . '<br />';
}
}
}
As you can see I have 'hard coded' every while loop for mainly a "main category", 'a subcategory' and finally it's subcategory.
However I feel that I could change this, to automatically, loop the subcategories in the subcategories, if there are such.
The structure is simple of my SQL:
id
parent
title
description
I am clueless how I should solve this little problem... A while loop over a if-statement or something? Help me out.
What you can do if the number of categories is not too big, is get all categories in one go from the database and then build a multi-dimensional array in php.
See this question and the answers for more details.

Categories