get only child id from multi level array in PHP - php

I have multi level categories array.
i only want to get child ID's, if parent don't have child. then return that parent id as well
id parent_id name
1 0 fashion
2 1 women
3 1 men
4 2 jeans
5 2 skirts
6 5 short skirts
7 5 long skirts
8 0 Home & entertainment
I tried this fucntion.
function buildtree($src_arr, $parent_id = 0, $tree = array()){
foreach($src_arr as $idx => $row){
if($row['parent_id'] == $parent_id){
foreach($row as $k => $v)
$tree[$row['id']][$k] = $v;
unset($src_arr[$idx]);
$tree[$row['id']]['children'] = self::buildtree($src_arr, $row['id']);
}
}
ksort($tree);
return $tree;
}
But it also gives me all parents id's.
desired outcome with id ( i am typing name for easy understanding)
array(
[0] = men
[1] = jeans
[2] = short skirts
[3] = long skirts
[4] = home & entertainment
)
Thanks for help.

A simple where clause with not in logic will help you select the children, then the parents that have no children.
select c.id, c.name
from categories c
where c.id not in (select parent_id from categories order by id desc)
Which gives a result set of
+----+----------------------+
| id | name |
+----+----------------------+
| 3 | men |
| 4 | jeans |
| 6 | long skirts |
| 7 | short skirts |
| 8 | home & entertainment |
+----+----------------------+
5 rows in set
See SQL Fiddle

Done in PHP you can use this function, which gives you a flat array with all edge childs based on a given parent_id. If you want to check a whole database table sql-only is the better option.
function getchildren($src_arr, $parent_id = 0)
{
$allchildren = array();
foreach($src_arr as $idx => $row){
if($row['parent_id'] == $parent_id){
$children = static::getchildren($src_arr, $row['id']);
if ($children) {
$allchildren = array_merge($allchildren, $children);
} else {
$allchildren[] = $row;
}
}
}
return $allchildren;
}

Related

converting a related database table object into a nested array - PHP

i have some dummy related data in my table "you can take a look at it bellow". i want to nest the data according to their relationships into one array.
Table Data:
+-----+------+--------+
| uid | name | parent |
+-----+------+--------+
| 1 | A | 0 |
| 2 | B | 1 |
| 3 | C | 1 |
| 4 | D | 2 |
| 5 | E | 3 |
| 7 | G | 3 |
| 9 | H | 4 |
| 10 | I | 4 |
| 11 | J | 7 |
+-----+------+--------+
the array is going to be like array('A' =>array('B'=>'D','C'=>array(...)).
am currently using codeigniter and here is what i've done
CODE
public function nestDataIntoArray($id)
{
$this->db->where('uid', $id);
$query = $this->db->get('binary_tbl');
$result = [];
if ($query->num_rows() > 0) {
foreach ($query->result() as $k) {
// secondLevel deep
$secondLevel = $this->getSubLevel($k->uid);
$secondLevelRowCount = $secondLevel->num_rows();
if ($secondLevelRowCount > 0 ) {
if ($secondLevelRowCount > 1) {
foreach ($secondLevel->result() as $key) {
// create second level deep array
$result[$k->name][$key->name][] = $this->getSubLevel($key->uid)->row('name');
// thirdLevel deep
$thirdLevel = $this->getSubLevel($key->uid);
$thirdLevelRowCount = $thirdLevel->num_rows();
if ($thirdLevelRowCount > 0) {
if($thirdLevelRowCount > 1){
foreach ($thirdLevel->result() as $tKey) {
// create third level deep array
$result[$k->name][$key->name][$tKey->name] = $this->getSubLevel($tKey->uid)->row('name');
}
}else{
foreach ($thirdLevel->result() as $tKey) {
$result[$k->name][$key->name][$tKey->name] = $this->getSubLevel($tKey->uid)->row('name');
}
}
} else {
$result[$k->name][$key->name] = $this->getSubLevel($key->uid)->result_array();
} //end thirdLevel deep
}
}
} else {
$result[$k->name] = $this->getSubLevel($key->uid)->row('name');
} // end second level deep
}
} else {
$result = NULL;
}
return $result;
}
private function getSubLevel($id)
{
return $this->db->select('*')->from('binary_tbl')->where('supermember', $id)->get();
}
upon invoking the nestDataIntoArray(1) method i got the following output
OUTPUT
array (size=1)
'A' =>
array (size=2)
'B' =>
array (size=2)
0 => string 'D' (length=1)
'D' => string 'H' (length=1)
'C' =>
array (size=3)
0 => string 'E' (length=1)
'E' => null
'G' => string 'J' (length=1)
this output seems to be fine but i really dont want that Zero index and some of the data still has one or two data related to them which i still have to loop through to get and that to me just seems to be alot of unnecessary coding. So the question is: which other better way can i achieve this and how do i get rid of that zero index?

Providing the Custom Name to both Key and Value Pairs of an Array

I have a table named questions in the following form:
Table Name: questions
qn_id | question |
---------------------------------------------------------------
19 | What is your rating on your Team Work?
20 | What is your rating on your Skills?
21 | What is your rating on your Personal Quality?
I have another table named rating as shown below:
Table Name: rating
id | employee_id | question_id | self_score | supervisor_score
-----------------------------------------------------------------
1 | 205 | 19 | 4 | 3
2 | 205 | 20 | 5 | 4
3 | 205 | 21 | 4 | 5
Note: There are two people involved in providing the score. One is supervisee and another is supervisor.
The score provided by the supervisee is kept in the column named self_score. In the table, employee_id is the id of the supervisee. Supervisor also gives the rating to the supervisee for the same question as shown aboove in the table.
I have generated the data from the database as an Array which is shown below:
$params = Array
(
[selfscore_19] => 4
[supervisorscore_19] => 3
[selfscore_20] => 5
[supervisorscore_20] => 4
[selfscore_21] => 4
[supervisorscore_21] => 5
)
Note: In the key named [selfscore_19] , selfscore_ is the string and 19 is the question id.
Questions are same for both - 'self' and 'supervisor'.
So I have appended the strings named 'selfscore' and 'supervisorscore' with the question_id.
In the array shown below, key is: [selfscore_19] which has been appended with the question_id i.e 19 and
the value is score provided by 'self' which is 4.
And other data follows similar pattern.
Attempt:
The code below shows the attempt done by me so far:
Note: I have only shown the main part of the method.
foreach($params as $key => $value){
$exp_key = explode('_', $key);
if($exp_key[0]=='selfscore'){
$arr_result[] = $value;
}
}
foreach($params as $key => $value){
$exp_key = explode('_', $key);
if($exp_key[0]=='supervisorscore'){
$arr_result[] = $value;
}
}
if(isset($arr_result)){
echo '<pre>';
print_r($arr_result);
echo '<pre>';
}
This is the output of the above code:
Current Output:
Array
(
[0] => 4
[1] => 5
[2] => 4
[3] => 3
[4] => 4
[5] => 5
)
Now I want to tranform the Current Output in the form of following Output.
Although I have already researched about this case, but I am finding it a bit tricky.
What modification should I do to achieve the required output?
Suggestions are highly appreciated.
Required Output:
$score['self_score']=array(
array('question_id'=>19,'score'=>4)
array('question_id'=>20,'score'=>5)
array('question_id'=>21,'score'=>4)
);
$score['supervisor_score']=array(
array('question_id'=>19,'score'=>3),
array('question_id'=>20,'score'=>4)
array('question_id'=>21,'score'=>5)
);
Do it with a single loop. Use the first part of $exp_key as the key of the resulting array, and the second part as the value of the question_id key of the sub-array.
foreach ($arr_result as $key => $value) {
$exp_key = explode("_", $key);
$score[$exp_key[0]][] = array("question_id" => $exp_key[1], "score" => $value);
}
I think I'll recommend that you use a UNION to join two simple queries so that your initial resultset is very close to what you want (close enough to simply iterate and/or modify it).
SQL: (Demo)
(SELECT 'group' AS 'self_score', question_id, self_score
FROM rating
WHERE employee_id = 205
ORDER BY id)
UNION
(SELECT 'supervisor_score', question_id, supervisor_score
FROM rating
WHERE employee_id = 205
ORDER BY id)
Resultset:
group | question_id | self_score
------------------|---------------|------------
self_score | 19 | 4
self_score | 20 | 4
self_score | 21 | 5
supervisor_score | 19 | 3
supervisor_score | 20 | 4
supervisor_score | 21 | 5
If you are open to a PDO solution, fetchAll(PDO::FETCH_GROUP) will come in pretty handy.

Efficient Database Queries ( PHP + PDO SQL)

At the moment I have a database structure like so:
| id | name | parent_id
| 1 | Human Resources | 0
| 2 | Marketing | 0
| 3 | Operations | 0
| 4 | Design | 0
| 5 | Marketing Design| 4
| 6 | Graphic Design | 4
| 7 | Print Design | 4
| 8 | Human Personal | 1
| 9 | Food Ops | 3
As you can see these are the departments within the business and also sub departments.
A sub-departments parent_id is the id of the department
do, for example:
id: 4, name: Design, parent_id: 0
id: 7, Print Design, parent_id: 4
Print Design is a sub department of design
I have called everything from the database in one query and now I need them in this structure:
$depts = array(
"Human Resources" => array("Human Personal"),
"Marketing" => array(),
"Operations" => array("Food Ops"),
"Design" => array("Marketing Design", "Graphic Design", "Print Design"),
...
);
so far I have:
foreach ($results as $result) {
if ($result['parent_id'] == 0) {
$parentCategories_arr[array($result['id'] => $result['name'])];
} else {
$returnedResults_arr[$result['parent_id']] = array($result['name']);
}
}
However I completely think that I have missed the point. so my question:
How do I loop through all the contents of that results and add the parent categories into an array with their sub categories as an array?
Maybe there is an easier way, but it works : (hate to say that sentence) - try to make it better maybe
$mFinalArray = fixMyArray($results);
print_r($mFinalArray);
function fixMyArray($results){
// Create categories - parent_id == 0
foreach($results as $index => $result) // $index = 0|1|2|3|4|5|6|7|8|9
if($result['parent_id'] == 0) // $result['parent_id'] = current item parent_id
$mCategories[$result['name']] = $result['id']; // $mCategories['Human Resources'] = 1|2|3|4
// Insert each data to the right parent
foreach($results as $index => $result) // $index = 0|1|2|3|4|5|6|7|8
if($result['parent_id'] != 0)
foreach($mCategories as $subindex => $category) // $subindex = Human Resources | Marketing | Operations | Design
if($result['parent_id'] == $category) // $category = 0|1|2|3|4
$mFinalArray[$subindex][] = $result['name']; // ex. $mFinalArray['Human Resources'][] = Human Personal
return $mFinalArray;
}
*Last line has an extra [ ] $mFinalArray[$subindex][ ]= $result['name'] . That means append to array.
Output :
Array
(
[Design] => Array
(
[0] => Marketing Design
[1] => Graphic Design
[2] => Print Design
)
[Human Resources] => Array
(
[0] => Human Personal
)
[Operations] => Array
(
[0] => Food Ops
)
)

Database array only returns one row

I am sorry for my lazy title. I hope that a moderator could improve it so the database won't get infected.
I got the following code (forum.php);
<?php
$res = $db->query('
SELECT *
FROM forums_categories
ORDER BY category_id
');
while ($row = $db->fetch_array($res)) {
$categories = array(
'ID' => $row['category_id'],
'NAME' => $row['category_name']
);
echo '<pre>';
print_r($categories);
echo '</pre>';
}
And I got the following database structure;
|---------------|-------------------|
| category_id | category_name |
|---------------|-------------------|
| 1 | Example 1 |
| 2 | Example 2 |
| 3 | Example 3 |
| 4 | Example 4 |
| 5 | Example 5 |
| 6 | Example 6 |
|---------------|-------------------|
But my array only returns 1 value:
Array
(
[ID] => 1
[NAME] => Example 1
)
Oh and if somebody likes to know how my $db->fetch_array looks like:
<?php
function fetch_array($result)
{
return mysql_fetch_assoc($result);
}
How can I return all rows in my array? Thank you for reading and thank you for replying!
You're overwriting the previous value of $categories on each iteration
$categories[] = array(
'ID' => $row['category_id'],
'NAME' => $row['category_name']
);
You might also want to initialize an empty array
$categories = array();
before your loop to avoid warnings.

Is there a better way to build my PHP array with search capabilities?

I operate a website which allows hundreds of clients to sell products online. When a customer visits one of my client's websites, they do so in the form
www.site.com?userid=12345
On the landing page, we do a mysql lookup for information about client with userid 12345 along with a list of products he/she can sell. We only offer a suite of ~30 products and we define them so we know what they look like but not all clients can offer at the same price. The mysql table for this information looks like this:
userid | category | itemid | price
=================================
12345 | 1 | 1 | 1.00
12345 | 1 | 2 | 2.00
12345 | 1 | 3 | 3.00
12345 | 2 | 4 | 4.00
12345 | 2 | 5 | 5.00
67890 | 1 | 1 | 2.25
67890 | 2 | 4 | 8.00
When the customer visits www.site.com?userid=12345, a lookup is done with the following:
SELECT category, itemid, price FROM tablename WHERE userid = 12345
*Note - yes I prevent sql injection.
The issue I run into is how to build the subsequent array in PHP. My initial thought is:
<?php
...
while ($row = $stmt->fetch()) {
$products[]['category'] = $row['category'];
$products[]['itemid'] = $row['itemid'];
$products[]['price'] = $row['price'];
}
//the array will end up looking like this:
Array
(
[0] => Array
(
[category] => 1
[itemid] => 1
[price] => 1
)
[1] => Array
(
[category] => 1
[itemid] => 2
[price] => 2
)
[2] => Array
(
[category] => 1
[itemid] => 3
[price] => 3
)
[3] => Array
(
[category] => 2
[itemid] => 4
[price] => 4
)
[4] => Array
(
[category] => 2
[itemid] => 5
[price] => 5
)
)
?>
The reason I need to capture information like this: On the actual site itself, there are two main categories (Food and Drinks). If a client has any products in $products with category 1, show the Food html section on the page. If a client has any products in $products with category 2, show the Drinks html section on the page.
Then, there is a predefined set of itemids available to all categories. In the case of food, there are 4:
pepperoni
sausage
cheese
vegetable
If a user does not have one of these itemids, he/she cannot sell that product and that product does not appear within the Food html section. If they are able to sell that product, I want to be able to reference that section/item's price and display it next to the actual item.
I know my $products array will contain all of the information I need but I am not sure if the way I am structuring it is best or how to really reference it when building out the page as described.
For example (I know this code is not right but saying what I am trying to accomplish):
"If value of 1 exists in the $products category array, show the Food section."
"If value of 1 exists in the $products itemid category array, show Pepperoni pizza as an option on the page and display the price associated with that specific category/itemid combination."
Any guidance is appreciated.
$products[]['category'] = $row['category'];
$products[]['itemid'] = $row['itemid'];
$products[]['price'] = $row['price'];
This will create a new array for each attribute; use
$products[] = $row;
…to copy the row contents to the products array.
I'm not sure I follow everything you ask, but as you're looping through the rows, why not do your category checks there.
//setup some flags
$food = false;
$drink = false;
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { //fetch an assoc array
$products[] = $row;
if(!$food AND 1 == $row['category']){ //haven't seen food yet, check for it
$food = true;
} elseif (!$drink AND 2 == $row['category']) { //same for drink
$drink = true;
}
}
//later
if($food){
//display food
}
Of course, instead of just tracking those flags explicitly, it seems a more generic approach would be better:
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { //fetch an assoc array
$products[] = $row;
$categories[$row['category']] = true;
}
//later
if(in_array(1, $categories)){
//display food
}
But that still leaves a bit to be desired. How about this:
//use some OOP to make this cleaner (class constants)
define('FOOD' , 1);
define('DRINK', 2);
//setup default values
$categories = array(FOOD => false, DRINK = false);
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { //fetch an assoc array
$products[] = $row;
$categories[$row['category']] = true;
}
//later
if($categories[FOOD]){
//display food
}
And, if I understand your question correctly, you may need to just check if a product is available (using the product id) - change your product loop to track that as the key:
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { //fetch an assoc array
$products[$row['itemid']] = $row;
$categories[$row['category']] = true;
}
//later
if(isset($products[$searchId])){
//user has this product
}
If you use Zend you can do $products = $query->fetchAssoc with itemid as first column and get the array
$products[itemid1] => array(itemid1, 'category', 'price'),
$products[itemid2] => array(itemid2, 'category', 'price').
But for category I think better solution is:
$products[$row['category']][$row['itemid']] = $row['price'];
So yo can use it like this:
if (!empty($products[$foodid])) {
echo "Food HTML part";
if (!empty($products[$foodid][$cheeseid])) {
echo "Cheese price = {$products[$foodid][$cheeseid]}";
}
}
I hope I understand your question :)
Sorry for my English

Categories