Create and display array from MySQL database - php

I'm trying to modifiy some code I have that creates and array of objects for a menu from a database I need it to add the SubCategoryID to each item in the array
and for the life of me I'm having a mental blank on how to do it
<?php
include_once ('includes/sqlopen.php');
$sql = mysqli_query($conn, "SELECT CategoryName, SubcategoryName,
SubcategoryID
FROM products
GROUP BY SubcategoryID
ORDER BY CategoryName");
$menu = array();
while ($row = mysqli_fetch_assoc($sql)) {
if (!in_array($row['CategoryName'], $menu['category'])) {
$menu['category'][] = $row['CategoryName'];
}
if (!empty($row['SubcategoryName']))
$menu['SubcategoryName'][$row['CategoryName']][] = $row['SubcategoryName'];
}
echo "Start of Array";
echo "<br>";
foreach ($menu['category'] as $cat) {
echo $cat."<br>";
foreach ($menu['SubcategoryName'][$cat] as $subcat) {
echo "--" . $subcat."<br>";
}
}
echo "<br>";
echo "End of Array";
include_once ('includes/sqlclose.php');
?>
Basically I want to achieve this:
Parent Cat 1 (link to prodlist.php?id=100)
--Child Cat1 (link to prodlist.php?id=103)
--Child Cat2 (link to prodlist.php?id=104)
Parent Cat 2 (link to prodlist.php?id=200)
Parent Cat 3 (link to prodlist.php?id=300)
--Child Cat1 (link to prodlist.php?id=301)
--Child Cat2 (link to prodlist.php?id=302)
and the database is layed out:
CategoryName SubcategoryName ItemName SubCategoryID
ParentCat1, ChildCat1, Item1, 103
ParentCat1, ChildCat1, Item2, 103
ParentCat1, ChildCat2, Item1, 104
ParentCat2, Item1, 200
ParentCat3, ChildCat1, Item1, 301
ParentCat3, ChildCat2, Item2, 302
** UPDATE- Thanks to everyone that is trying to help me, I know I'm probably not that great at explaining what I need help with..
I have been playing around with it for a little bit now and it is kind of doing what I want. I just have a small glitch where if 2 different parent categories have subcategories with the same name it will add both subcat ids to the array
$menu = array();
while ($row = mysqli_fetch_assoc($sql)) {
if (!in_array($row['CategoryName'], $menu['category'])) {
$menu['category'][] = $row['CategoryName'];
}
if (!empty($row['SubcategoryName']))
$menu['SubcategoryName'][$row['CategoryName']][] = $row['SubcategoryName'];
$menu['SubcategoryID'][$row['SubcategoryName']][] = $row['SubcategoryID'];
}
Output
--Accessories
id--766
id--243
id--992
id--871
id--977
Monitor Arms
--Spacedec
id--789
--Visidec
id--791
--Telehook
id--792
--Spacepole
id--804
--Accessory
id--866
--Monitor Accessories
id--990
id--584
--Stands
id--991
id--538

First of all, you can improve your database structure
Like below:
id|category|parent_id
Simple and clean isn't.
Now try this:
//your database records
$rows = [
['id'=>1,'category'=>'Indland','parent_id'=>0],
['id'=>2,'category'=>'Udland','parent_id'=>0],
['id'=>3,'category'=>'Sport','parent_id'=>0],
['id'=>4,'category'=>'Handbold','parent_id'=>3],
['id'=>5,'category'=>'Fodbold','parent_id'=>3],
['id'=>6,'category'=>'Sample','parent_id'=>4]
];
print_r(getList($rows));
// Recursive function
function getList($rows, $id=0){
// Start a new list
$newList = null;
foreach($rows as $key => $row) {
if ($row['parent_id'] == $id) {
// Add an UL tag when LI exists
$newList == null ? $newList = "<ul>" : $newList;
$newList .= "<li>";
$newList .= "{$row['category']}";
// Find childrens
$newList .= getList(array_slice($rows,$key),$row['id']);
$newList .= "</li>";
}
}
// Add an UL end tag
strlen($newList) > 0 ? $newList .= "</ul>" : $newList;
return $newList;
}
This will result in:

The DB structure is the obvious issue here as it's using a fixed structure. While this could be efficient in certain scenarios it's not easily extensible. A way to achieve what you are currently trying to achieve will be this:
Fetch and group all items (in code) by CategoryName (I'm hoping the category name is a slug?)
For each category name, display all subcategories with link to id
Translated to code this will be something like this (Your current SQL query is fine):
$menu = [];
while ($row = mysqli_fetch_assoc($sql)) {
if (!isset($menu[$row['CategoryName']])) {
$menu[] = $row['CategoryName'];
}
if (!empty($row['SubCategoryName']))
$menu[$row['CategoryName']][] = $row['SubcategoryName'];
}
}
echo "Start of Array";
echo "<br>";
foreach ($menu as $cat => $catsubs) {
echo $cat."<br>";
foreach ($catsubs as $sub) {
echo "--" . $sub."<br>";
}
}
echo "<br>";
echo "End of Array";

ok guys I think I have it.. ill post it up so that on the rare chance anyone else can benefit from it.. Thanks again to everyone that helped me
Code
<?php
include_once ('includes/sqlopen.php');
$sql = mysqli_query($conn, "SELECT CategoryName, SubcategoryName, CategoryCode, SubcategoryID
FROM products GROUP BY SubcategoryID ORDER BY CategoryName");
$menu = array();
while ($row = mysqli_fetch_assoc($sql)) {
// Creates First Level Array Items For Parent IDs
if (!in_array($row['CategoryName'], $menu['category'])) {
$menu['category'][] = $row['CategoryName'];
}
if (!empty($row['SubcategoryName']))
// Creates Second Level Array for Child IDs
$menu['SubcategoryName'][$row['CategoryName']][] = $row['SubcategoryName'];
// Creates Third Level Array for Category IDs
$menu['SubcategoryID'][$row['SubcategoryName']][$row['CategoryName']][] = $row['SubcategoryID'];
}
echo "Start of Array";
echo "<br>";
foreach ($menu['category'] as $cat) {
echo $cat."<br>";
foreach ($menu['SubcategoryName'][$cat] as $subcat) {
echo "--" . $subcat."<br>";
foreach($menu['SubcategoryID'][$subcat][$cat] as $id)
echo "id--".$id."<br>";
} echo "<br>";
}
echo "<br>";
echo "End of Array";
include_once ('includes/sqlclose.php');
?>
<pre>
<?php print_r ($menu); ?>
</pre>
Foreach Loop Output
CPU
--AMD Socket FM2
id--778
--Mobile
id--558
--Intel Socket 1150 (4th Gen)
id--825
--Intel Socket 1151 (6th Gen Skylake)
id--945
--AMD Socket AM3
id--693
--Intel Xeon
id--980
--Intel Socket 2011 (3rd and 4th Gen)
id--744
--Intel Socket 1151 (7th Gen Kabylake)
id--1021
--AMD Socket AM4
id--1023
print_r Output
[AMD Socket FM2] => Array
(
[CPU] => Array
(
[0] => 778
)
)
[Mobile] => Array
(
[CPU] => Array
(
[0] => 558
)
)
[Intel Socket 1150 (4th Gen)] => Array
(
[CPU] => Array
(
[0] => 825
)
)
[Intel Socket 1151 (6th Gen Skylake)] => Array
(
[CPU] => Array
(
[0] => 945
)
)
[AMD Socket AM3] => Array
(
[CPU] => Array
(
[0] => 693
)
)
[Intel Xeon] => Array
(
[CPU] => Array
(
[0] => 980
)
)
[Intel Socket 2011 (3rd and 4th Gen)] => Array
(
[CPU] => Array
(
[0] => 744
)
)

Related

How to make a list into a tree structure in PHP

From the following php script:
while($row = mysqli_fetch_array($res, MYSQLI_ASSOC))
{
$category = $row["category"];
$thing = $row["thing"];
echo $category." ".$thing;
echo "<br>";
}
I echo the following lines:
Book book1
Book book2
Chair chair1
Chair chair2
Table table1
but I would like to display as the following result :
Book
book1
book2
Chair
chair1
chair2
Table
table1
I know I have to make a simple loop somewhere here but I don't get the result I want.
Any help would be appreciated. Thanks.
Bonus : I am planning on make all these words clickable to get the them in a variable. If you could insert this tip into your answer it would be nice.
EDIT :
Here is what I tried so far (trying to add also an icon):
while($row = mysqli_fetch_array($res, MYSQLI_ASSOC))
{
foreach($row as $item)
{
echo "<div style='padding-right: 5px' class='glyphicon glyphicon-book'></div>";
echo $item;
echo "<div style='padding-left: 20px'>";
echo "</div>";
echo "<br>";
}
}
$table = array();
while($row = mysqli_fetch_array($res, MYSQLI_ASSOC))
{
if ( !isset( $table[ $row["category"] ] ) ) {
// No category exists, create now
$table[ $row["category"] ] = array();
}
// append to category
$table[ $row["category"] ][] = $row["thing"];
}
// some additional sorting here
/* todo */
print_r($table);
This solution piggybacks off of PHP inherent named keys by "strings" (which behind the scenes are hashed into "buckets". Since your data only shows a data structure that nests one level deep, it is not a recursive solution. But even still, your basic requirement is on category, so will work even if you have a nested data structure.
isset will check if the category exists. If not a new sub array is created which will hold those values.
[] syntax is PHP style to append to an array.
Thanks to #GetSet answer, I could create the right multidimensional array I needed to get the foloowing result:
Array ( [Book] => Array ( [0] => book1 [1] => book2 ) [Chair] => Array ( [0] => chair1 [1] => chair2 ) [Table] => Array ( [0] => table1 ) )
I wanted then to display on a html page like that:
Book
book1
book2
Chair
chair1
chair2
Table
table1
After reading more about PHP arrays, I made out this solution which seem working for me (with the icones):
foreach ($table as $key => $value)
{
echo "<div style='padding-right: 5px; font-size:13px;' class='glyphicon glyphicon-book'></div>";
echo $key;
echo "<div style='height: 2px'></div>";
foreach ($value as $v2)
{
echo "<div style='padding-right: 7px;padding-left: 15px; font-size:11px;' class='fa fa-file-text-o'></div>";
echo $v2;
echo "<div style='height: 5px'></div>";
}
echo "<div style='height: 10px'></div>";
}
Here is the result:
I know have to work on making clickable these words.
Also, I would love to be able to reduce one category by clicking on the book icon but I imagine I have to use JavaScript for that and I don't really know this language.

How to add <hr> tags between unique rows from query resultset?

I'm trying to add an <hr> tag between lines when a new name is encountered.
$conn = new mysqli("localhost", "root", "", "test");
$rs = $conn->query("SELECT * FROM usuarios");
$info = [];
$i = 0;
while($rows = $rs->fetch_array()) {
$info[$i]["pass"] = $rows["pass"];
$info[$i]["name"] = $rows["name_real"];
$i++;
}
// I want to print a line just after the last duplicated value
for($i = 0; $i < count($info) - 1; $i++) {
if($info[$i]["name"] !== $info[$i +1]["name"] && // some duplicate condition) {
$info[$i]["line"] = "<hr>";
};
}
This is the structure of my info array build from the resultset.
Array
(
[0] => Array
(
[pass] => 12
[name] => Martin
)
[1] => Array
(
[pass] => 20
[name] => Martin
)
[2] => Array
(
[pass] => 2
[name] => Martin
)
[3] => Array
(
[pass] => 2
[name] => Alberto
)
)
My desired result would be something like:
<p>Martin<p>
<p>Martin<p>
<p>Martin<p>
<hr>
<p>Alberto<p>
If you don't care what the duplicate names are or how many duplicates exist, and you just want to see whether or not there are any, it looks like it could be simpler code than some of the possible duplicate answers.
Get the names
$names = array_column($array, 'name');
Then check if the full list of names is equal to the unique list.
$has_duplicates = $names != array_unique($names);
Disclaimer: This answer looks odd now. It was provided for Revision 1 of the question. I seem to have misunderstood the question somewhat, and then Revision 2 transformed it to the extent that this answer no longer applies at all. Still, I think it's a useful way to do the thing that it seemed was trying to be done at first.
This solution would be handy:
$result = array();
$names = array_count_values(array_column($source, 'name'));
foreach($names as $key=>$val) {
$result[$key] = ($val == 1 ? false : true);
}
This can be achieved with just one loop. First, use your mysqli query to order the resultset by name_real. (If you are only going to use name_real, you can change the SELECT clause to reflect this. I have shown this in the commented query.) Then write a condition that checks for a new/unique name_real -- if so, echo <hr>.
Code: (Demo)
//$rs = $conn->query("SELECT `name_real` FROM usuarios ORDER BY `name_real`;");
$rs=[
['pass'=>2,'name_real'=>'Alberto'],
['pass'=>12,'name_real'=>'Martin'],
['pass'=>20,'name_real'=>'Martin'],
['pass'=>2,'name_real'=>'Martin']
];
$prev=NULL;
//while($rows = $rs->fetch_array()) {
foreach($rs as $rows){
if($prev && $rows['name_real']!=$prev){ // if not first iteration, and new name_real
echo "<hr>";
}
echo "<p>{$rows['name_real']}</p>";
$prev=$rows['name_real']; // preserve this value for next iteration's check
}
Output:
<p>Alberto</p>
<hr>
<p>Martin</p>
<p>Martin</p>
<p>Martin</p>

loop repeating comparing arrays

I have this code and I want to compare the data that came from db with the one user selects but it seems that the foreach($question_answers as $answer){
and foreach ($user_answer as $key => $value) { it is not good it is repeating for 4 times more, I need a way to compare $user_answer which is what user has selected request from the view, and the $answer['order'] the order comes from database. So I need a loop or something to compare these two.. any help..? thank you.
foreach($questions as $question) {
$question_answers = OrderingAnswer::where('question_id', $question->id)
->where('deleted',0)
->get()
->toArray();
$users_answers = $request->except('_token', 'test_id');
$user_answer = $users_answers[ $question->id];
// $user_answer ---> Array ( [0] => 1 [1] => 4 [2] => 3 [3] => 2 )
foreach ($question_answers as $answer) {
//$answer['order'] ---> 1
foreach ($user_answer as $key => $value) {
if ($answer['order'] == $value ) {
echo "ok ===>" .$value . "<br>" ;
} else {
echo "no -------------------------------- >".$value . "<br>" ;
}
}
}
I have tried this - it is working perfect. (This question was really intresting)
Here basically i am comparing multidimensional array value to a single dimensional array value. it will just give you array of matched results.
<?php
$question_answers= array(
array("id"=>251,'question_id'=>242,'order'=>1),
array("id"=>252,'question_id'=>243,'order'=>2)
);
$user_answers = array(1,4,3,2);
// you can just use this part
$question_answers = array_filter($question_answers, function($arr) use ($user_answers) {
return in_array($arr['order'], $user_answers);
});
echo "<pre>";
print_r($question_answers);
?>

PHP Create Array

I'm trying to generate an array that will look like this:
Array ( [123 Smith St, Begora] => L1234 [55 Crumble Road, Mosmana] => L2456 [99 Jones Ave, Gestana] => L3456 )
which will ultimately be used for a select menu on an html form.
I'm retrieving a list of records and propertyID numbers from a database as follows:
foreach($records as $record) {
$propertyID = $record->getField('propertyID');
$property = $record->getField('propertyAddress');
echo $propertyID.'<br>';
echo $property.'<br>';
}
which displays like this when I retrieve 3 records:
L1234
123 Smith St, Begora
L2456
55 Crumble Road, Mosmana
L3456
99 Jones Ave, Gestana
I just can't work out how to convert this into an Array which I can then use later on in my page for generating a select menu.
Just do
foreach($records as $record) {
$propertyID = $record->getField('propertyID');
$property = $record->getField('propertyAddress');
$addresses[$property] = $propertyID;
}
Something like this:
$array = array();
foreach($records as $record) {
$array[$record->getField('propertyAddress')] = $record->getField('propertyID');
}

Adjacency tree from single table

I've read a lot of people discussing nested lists, but I was wondering how to iterate through an adjacancy list/tree in PHP.
I have a table with: id, title, parent_id
And I've selected all records out into an array called $pages.
Then using this php:
function makeList($pages, $used) {
if (count($pages)) {
echo "<ul>";
foreach ($pages as $page) {
echo "<li>".$page['pag_title'];
$par_id = $page['pag_id'];
$subsql("SELECT * FROM pages WHERE pag_parent = ".$par_id."");
// running the new sql through an abstraction layer
$childpages = $dbch->fetchAll();
makeList($childpages, $used, $lastused);
echo "</li>";
}
echo "</ul>";
}
}
This sort of works but I end up with any sub menu being repeated e.g.
HomeNewsSub-newsArticlesArticleNewsSub-newsArticlesArticleSub-newsArticle
I've tried adding the current id into an array that gets passed through the function, and then using in_array to check if it's there, but I have had no joy doing that.
Any help would be much appreciated.
I need to parse the whole tree so choosing parent as 0 isn't an option
Since it already does the SQL, you dont have to do it outside before the first function call.
function makeList($par_id = 0) {
//your sql code here
$subsql("SELECT * FROM pages WHERE pag_parent = $par_id");
$pages = $dbch->fetchAll();
if (count($pages)) {
echo '<ul>';
foreach ($pages as $page) {
echo '<li>', $page['pag_title'];
makeList($page['pag_id']);
echo '</li>';
}
echo '</ul>';
}
}
For storing it more tree like you might want to look at this site: Storing Hierarchical Data in a Database.
If you create an array of pages grouped by parent id it is quite easy to recursively build the list. This will only require one database query.
<?php
//example data
$items = array(
array('id'=>1, 'title'=>'Home', 'parent_id'=>0),
array('id'=>2, 'title'=>'News', 'parent_id'=>1),
array('id'=>3, 'title'=>'Sub News', 'parent_id'=>2),
array('id'=>4, 'title'=>'Articles', 'parent_id'=>0),
array('id'=>5, 'title'=>'Article', 'parent_id'=>4),
array('id'=>6, 'title'=>'Article2', 'parent_id'=>4)
);
//create new list grouped by parent id
$itemsByParent = array();
foreach ($items as $item) {
if (!isset($itemsByParent[$item['parent_id']])) {
$itemsByParent[$item['parent_id']] = array();
}
$itemsByParent[$item['parent_id']][] = $item;
}
//print list recursively
function printList($items, $parentId = 0) {
echo '<ul>';
foreach ($items[$parentId] as $item) {
echo '<li>';
echo $item['title'];
$curId = $item['id'];
//if there are children
if (!empty($items[$curId])) {
makeList($items, $curId);
}
echo '</li>';
}
echo '</ul>';
}
printList($itemsByParent);
Where is $page coming from? You might have an sql injection vulnerability in your code if you're not escaping it or using a prepared statement.
Also the SELECT statement inside a for loop jumps out as a bad practice. If the table is not that big, then select the contents of the entire table and then iterate through the result set in PHP to build the tree data structure. This could take up to n*(n-1)/2 iterations in the pathological case of your tree being a linked list. Stop when all nodes have been added to the tree, or the number of remaining nodes remains the same from one iteration to the next - this means the remaining nodes are not children of your root node.
Alternatively, if your database supports recursive SQL queries, you can use that, and it will only select the nodes that are children of your parent node. You will still have to build the tree object yourself in PHP. The form of the query would be something like:
WITH temptable(id, title, parent_id) AS (
SELECT id, title, parent_id FROM pages WHERE id = ?
UNION ALL
SELECT a.id, a.title, a.parent_id FROM pages a, temptable t
WHERE t.parent_id = a.id
) SELECT * FROM temptable
Substitute the '?' on the second line with the starting page ID.
The simplest fix would just be, when you are doing the initial select to set $pages (which you don't show), add a WHERE clause like:
WHERE pag_parent = 0
(or IS NULL, depending how you're storing "top level" pages).
That way you won't select all the children initially.
When that table gets large, recursion can get unwieldy. I wrote an blog post about a recursion-less method: http://www.alandelevie.com/2008/07/12/recursion-less-storage-of-hierarchical-data-in-a-relational-database/
Finding top parent, all parents, and all children of a node (enhancements for Tom Haigh's answer):
<?php
//sample data (can be pulled from mysql)
$items = array(
array('id'=>1, 'title'=>'Home', 'parent_id'=>0),
array('id'=>2, 'title'=>'News', 'parent_id'=>1),
array('id'=>3, 'title'=>'Sub News', 'parent_id'=>2),
array('id'=>4, 'title'=>'Articles', 'parent_id'=>0),
array('id'=>5, 'title'=>'Article', 'parent_id'=>4),
array('id'=>6, 'title'=>'Article2', 'parent_id'=>4)
);
//create new list grouped by parent id
$itemsByParent = array();
foreach ($items as $item) {
if (!isset($itemsByParent[$item['parent_id']])) {
$itemsByParent[$item['parent_id']] = array();
}
$itemsByParent[$item['parent_id']][] = $item;
}
//print list recursively
function printList($items, $parentId = 0) {
echo '<ul>';
foreach ($items[$parentId] as $item) {
echo '<li>';
echo $item['title'];
$curId = $item['id'];
//if there are children
if (!empty($items[$curId])) {
printList($items, $curId);
}
echo '</li>';
}
echo '</ul>';
}
printList($itemsByParent);
/***************Extra Functionality 1****************/
function findTopParent($id,$ibp){
foreach($ibp as $parentID=>$children){
foreach($children as $child){
if($child['id']==$id){
if($child['parent_id']!=0){
//echo $child['parent_id'];
return findTopParent($child['parent_id'],$ibp);
}else{ return $child['title'];}
}
}
}
}
$itemID=7;
$TopParent= findTopParent($itemID,$itemsByParent);
/***************Extra Functionality 2****************/
function getAllParents($id,$ibp){ //full path
foreach($ibp as $parentID=>$nodes){
foreach($nodes as $node){
if($node['id']==$id){
if($node['parent_id']!=0){
$a=getAllParents($node['parent_id'],$ibp);
array_push($a,$node['parent_id']);
return $a;
}else{
return array();
}
}
}
}
}
$FullPath= getAllParents(3,$itemsByParent);
print_r($FullPath);
/*
Array
(
[0] => 1
[1] => 2
)
*/
/***************Extra Functionality 3****************/
//this function gets all offspring(subnodes); children, grand children, etc...
function getAllDescendancy($id,$ibp){
if(array_key_exists($id,$ibp)){
$kids=array();
foreach($ibp[$id] as $child){
array_push($kids,$child['id']);
if(array_key_exists($child['id'],$ibp))
$kids=array_merge($kids,getAllDescendancy($child['id'],$ibp));
}
return $kids;
}else{
return array();//supplied $id has no kids
}
}
print_r(getAllDescendancy(1,$itemsByParent));
/*
Array
(
[0] => 2
[1] => 3
)
*/
print_r(getAllDescendancy(4,$itemsByParent));
/*
Array
(
[0] => 5
[1] => 6
)
*/
print_r(getAllDescendancy(0,$itemsByParent));
/*
Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 4
[4] => 5
[5] => 6
)
*/
?>

Categories