i have two db tables in my codeigniter project. As simplified summary,
page page_lang
------------------------------------- ----------------------------
id_page | id_menu | id_parent | level id_page | title | etc..
------------------------------------- ----------------------------
1 | 1 | 0 | 0 1 | Name 1 | etc..
2 | 1 | 1 | 1 2 | Name 1.1 | etc..
3 | 1 | 2 | 2 3 | Name 1.1.1 | etc..
4 | 1 | 2 | 1 4 | Name 1.2 | etc.
I am trying to create a dropdown select box which contains all page nested with indents as output like;
<option value="id_page">Name 1</option>
<option value="id_page"> » Name 1.1</option>
<option value="id_page"> - Name 1.1.1</option>
<option value="id_page"> » Name 1.2</option>
In this case, in need join page and page_lang and create a recursive loop, i quess.
But I am stacked on designing the fastest possible code. Thank you for any help.
Your recursive function will look something like this
function recursivePageOptions( $level, $nodes ) {
$set = array();
foreach ($nodes as $node) {
$nest = '';
for($x=1; $x<=$level; $x++)
$nest.= ' ';
$page = '<option value="'.$node['page']['id'].'">';
$page.= $nest . $node['page']['title'] . '</option>';
$set[] = $page;
if (isset($node['children'])) {
$set = array_merge(
$set,
recursivePageOptions( $level+1, $node['children'] );
);
}
}
return $set;
}
So what you need to do before this recursive function is called is get your page information into an array structure that looks like this:
[
'My Homepage' => [
'page' => ['My Homepage', 24, ... ],
'children' => [
'Level 1 Page' => [
'page' => ['Level 1 Page', 39, ... ],
'children' => [
'Level 2 Page' => [
'page' = ['Level 2 Page', 51, ... ]
]
]
],
'Another Level 1 Page' =< [
'page' => ['Another Level 1 Page', 56, ... ]
]
]
]
]
Its up to you to figure this part out in detail, essentially you will be getting rows out of the database and looping through them in such a way as to generate an array structure like the one above.
Related
I want to create a nested json that comes with post.
I have 3 fields. Adults, children and baby. But, there are also dates for children and baby areas. For example; Children 1 Birthday, Children 2 Birthday, Baby 1 Birthday, Baby 2 Birthday
When I try with the code below, I can't print child dates by room. Writes in bulk. How can I POST with php like json below?
+---------+----------+-------+
| Adults | Children | Baby | (select box field)
+--------------------+--------
| 1 | 0 | 0 |
+--------------------+--------
| 2 | 1 | 1 | (also available for children and baby areas)
+--------------------+-------+
| 3 | 2 | 2 |
+---------+----------+-------+
rooms: [
{
adults: 2,
child: [
child_total: 1
child_date: [
"2017-08-10"
],
],
baby: [
baby_total: 1
baby_date: [
"2017-07-01"
],
],
},
{
adults: 1,
child: [
child_total: 2
child_date: [
"2017-08-08",
"2017-08-09"
],
],
baby: [
baby_total: 2
baby_date: [
"2017-06-08",
"2017-05-09"
],
],
}
],
foreach ($this->input->post('adults') as $key => $value) {
foreach ($_POST['child_total'] as $value2) {
$child_date = [];
foreach ($_POST['child_date'] as $key2 => $value3) {
$child_date[] = $value3;
}
}
$rooms[] = array(
'adults' => $_POST['adults'][$key],
'child' => array(
'child_total' => $_POST['child_total'][$key],
'child_date' => $child_date
)
);
}
print json_encode($rooms, JSON_PRETTY_PRINT);
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
)
)
I am developing a website using laravel PHP and trying to do a comment system using the following structure:
- Comment 1 (id = 1)
-- Reply 1 (id = 2) (parent_id = 1)
--- Reply 2.1 (id = 3) (parent_id = 2)
-- Reply 2 (id = 4) (parent_id = 1)
I am wondering how would I do a foreach to cover that? Since i don't know how many child comments a comment will have.
I wouldn’t store comments and replies in a separate table as they’re both comment entities at the end of the day. Simply have a parent_id column in your comments table, and you can fetch both comments and replies in one database query as opposed to two.
I assume you also have a foreign key linking a comment to something like a post. You can then fetch all comments for that post ID:
$comments = Comment::latest()->where('post_id', '=', $post->id)->get();
Then sort them based on their parent_id value:
$comments = $comments->keyBy('parent_id');
You can then iterate over them in your Blade template like this and every iterate, check if there are comments with that comment’s ID as its parent ID:
<!-- Kick-start the loop -->
#foreach($comments[0] as $comment)
#include('partials.comment')
#endforeach
The content of partials/comment.blade.php
<blockquote class="comment">
<p class="comment-body">{{ $comment->body }}</p>
<footer>
<span class="comment-author">{{ $comment->user->name }}</span>,
<time class="comment-date" pubdate="pubdate">{{ $comment->created_at }}</time>
</footer>
</blockquote>
#if(isset($comments[$comment['id']])
#each('partials.comment', $comments[$comment['id'], 'comment')
#endif
Table Like:
+------------+-----------+---------+
| comment_id | parent_id | comment |
+------------+-----------+---------+
| 1 | 0 | test |
| 2 | 1 | test1 |
| 3 | 0 | test2 |
| 4 | 0 | test3 |
| 5 | 1 | test4 |
| 6 | 2 | test4 |
| 7 | 4 | test5 |
| 8 | 5 | test6 |
| 9 | 6 | test7 |
| 10 | 4 | test8 |
| 11 | 3 | test9 |
+------------+-----------+---------+
Get first level parent:
$comments = Comment::where('parent_id', '0')->orderBy('comment_id', 'asc')->get();
$result = array();
foreach($comments as $comment){
$list = array();
$list = array_merge($list, [['comment_id' => $comment->comment_id, 'parent_id' => $comment->parent_id, 'comment' => $comment->comment]]);
$result = array_merge($result, $this->get_child_comment($comment->comment_id,0, $list));
}
function get_child_comment($pid,$level,$list=array()) {
$sub_comments = Comment::where('parent_id','=',$pid)->where('comment_id','!=',$pid)->orderBy('comment_id', 'asc')->get();
foreach($sub_comments as $sub_comment){
$space=" "; sigm='-';
for($j=0; $j<=$level; $j++)
{
$space .=$space;
}
for($j=0; $j<=$level; $j++)
{
$space .= $sigm;
}
$sub_comment->comment = html_entity_decode($space, ENT_QUOTES, "utf-8").' '.$sub_comment->comment;
$list = array_merge($list, array(['comment_id' => $sub_comment->comment_id, 'parent_id' => $sub_comment->parent_id, 'comment' => $sub_comment->comment]));
$list = $this->get_child_comment($sub_comment->comment_id, $level+1, $list);
}
return $list;
}
}
return get array.simple print using foreach:
foreach($result as $val) {
echo $val['comment'].'<br>';
}
Output:
test
- test1
-- test4
--- test7
- test4
-- test6
test2
- test9
test3
- test5
- test8
You can represent comments in following structure:
$comments = [
(object)[
'id' => 1,
'text' => 'Comment 1',
'children' => [
(object)[
'id' => 2,
'text' => 'Reply 1',
'children' => [
(object)[
'id' => 2,
'text' => 'Reply 1.1'
]
]
],
(object)[
'id' => 4,
'text' => 'Reply 2',
]
]
]
];
And print them using recursive function like this:
function printComments($comments, $prefix = '-') {
foreach ($comments as $comment) {
echo $prefix.$comment->text.'<br>';
isset($comment->children) && printComments($comment->children, $prefix.'-');
}
}
Or calling view recursively in case of Laravel:
#include('comments.view.path') inside of
#include('comments.view.path')
For convinient retrieving of comments in the represented structure and generally for working with tree structure I suggest using nested set model and etrepat/baum for Laravel which has toHierarchy() method.
Ok, so here's my table structure:
+--------------------------+ +----------------+ +-------------------------------+
| pages | | menus | | menu_pages |
+--------------------------+ +----+-----------+ +-------------------------------+
| id | title | slug | | id | name | | menu_id | page_id | parent_id |
+-------+---------+--------+ +----+-----------+ +---------+---------+-----------+
| 1 | Home | index | | 1 | default | | 1 | 1 | 0 |
+-------+---------+--------+ +----+-----------+ +---------+---------+-----------+
| 2 | About | about | | 2 | footer | | 1 | 2 | 0 |
+-------+---------+--------+ +----+-----------+ +---------+---------+-----------+
| 3 | Test 1 | test-1 | | 1 | 3 | 2 |
+-------+---------+--------+ +---------+---------+-----------+
| 4 | Test 2 | test-2 | | 1 | 4 | 2 |
+-------+---------+--------+ +---------+---------+-----------+
| 5 | Test 3 | test-3 | | 1 | 5 | 4 |
+-------+---------+--------+ +---------+---------+-----------+
So basically, we have pages, menus, and a menu_pages linking table which specifies the menu, the page, and the parent of each menu item.
Here's my query:
$query = "SELECT pages.id, pages.title, pages.slug, menu_pages.parent_id
FROM menus, pages, menu_pages WHERE menus.name = '$menu'
AND menus.id = menu_pages.menu_id
AND pages.id = menu_pages.page_id";
$results = $db->Query($query);
Here's the question: How do I get the menu items properly nested under their respective parents in an array? I've tried quite a few things already, but none of them worked beyond simply 2 levels, so I won't clutter up the question with it. Obviously I need some kind of recursion in PHP, or to modify my query maybe in a way that I can get the SQL to return them properly?
It should look something like this in the output:
[0] => array(
'id' => 1,
'title' => 'Home',
'slug' => '/',
'parent_id' => '0'
)
[1] => array(
'id' => 2,
'title' => 'About',
'slug' => 'about',
'parent_id' => 0,
'sub_menu' => array(
[0] => array(
'id' => 3,
'title' => 'Test 1',
'slug' => 'test-1',
'parent_id' => 2
)
[1] => array(
'id' => 4,
'title' => 'Test 2',
'slug' => 'test-2',
'parent_id' => '2',
'sub_menu' => array(
[0] => array(
'id' => 5,
'title' => 'Test 3',
'slug' => 'test-3',
'parent_id' => 4
)
)
)
)
)
Thanks for the help!
This isn't quite as simple as it first sounds - if you want to get into how to do it with SQL, you are looking for a recursive sql statement.
Unfortunately mysql doesn't support this directly, and you would need to write a body of code to get it working. There is an example of how to do it here. Not simple.
http://explainextended.com/2009/03/17/hierarchical-queries-in-mysql/
In case you're interested in how to pick this apart, you would implement it in Oracle like this:
SELECT p.id, p.title, p.slug, mp.parent_id, level
FROM menu_pages mp
JOIN pages p ON ( p.id = mp.page_id )
JOIN menus m ON ( m.id = mp.menu_id )
CONNECT BY PRIOR mp.page_id = mp.parent_id
START WITH ( m.name = 'default' AND mp.parent_id = 0 )
You are basically saying:
START WITH a query for the top level of the menu
CONNECT that back to the result set by joining the parent to the child
You end up with a result set like this:
id title slug parent level
------------------------------------
1 Home index 0 1
2 About about 0 1
3 Test 1 test-1 2 2
4 Test 2 test-2 2 2
5 Test 3 test-3 4 3
All this actually gives you in addition to what you already have is:
The "level" of each point in the menu.
If a sub menu appeared multiple times in your structure it would be repeated correctly.
Sections of the menu that are not connected properly will not be returned.
So, for small, simple and consistent menus it's probably over-kill anyway.
Plus, even in this case you would need to process this in PHP to get the structure you're looking for.
So, using that as inspiration you can see how you could implement it in mysql by just doing the post processing.
You start off with your original query:
SELECT pages.id
, pages.title
, pages.slug
, menu_pages.parent_id
FROM menus
, pages
, menu_pages
WHERE menus.name = 'default'
AND menus.id = menu_pages.menu_id
AND pages.id = menu_pages.page_id
You can then loop over this result and build the array structure yourself manually.
In order to avoid the problem of recursion, we're instead going to take advantage of the fact that we can have two variables pointing at the same data structure - we're going to use references so that changing the value of the variable in one reference will change the value of the variable in the other.
I.E. The difficulty you get is finding the right point in the hierarchy to add each child. With references you don't have to.
Create an empty menu array
Loop over the results from your SQL statement
Create a copy of each menu item and put it into a simply indexed store (by the id of the item)
If you have the root menu item, add it to your menu array (as a reference)
If you don't have the root menu item, find the parent in your simple store and add your new item to it.
At the end you should have the nice nested structure you're looking for.
Like this:
<?php
// As if it came back from mysql...
// Assumed that it's ordered so that every possible parent appears before all its childern
$aResults = array( array( 'id' => 1, 'title' => 'Home', 'slug' => 'index', 'parent_id' => 0 )
, array( 'id' => 2, 'title' => 'About', 'slug' => 'about', 'parent_id' => 0 )
, array( 'id' => 3, 'title' => 'Test 1', 'slug' => 'test-1', 'parent_id' => 2 )
, array( 'id' => 4, 'title' => 'Test 2', 'slug' => 'test-2', 'parent_id' => 2 )
, array( 'id' => 5, 'title' => 'Test 3', 'slug' => 'test-3', 'parent_id' => 4 ) );
// the menu you're creating
$aMenu = array();
// the simple store of the menu items you're going to use to find the parents
$aBaseMenuIndex = array();
foreach( $aResults as $aMenuItem ) {
$aMenuItem['sub_menu'] = array();
// add your menu item to the simple store
$aBaseMenuIndex[ $aMenuItem['id'] ] = $aMenuItem;
if ( $aMenuItem['parent_id'] == 0 ) {
// if it's a base menu item, add it to the menu
$aMenu[] =& $aBaseMenuIndex[ $aMenuItem['id'] ];
} else {
// if it's not a base item, add it to the sub menu, using the simply indexed store to find it
// adding it here will also add it to $aMenu, as $aMenu contains a reference to this
$aBaseMenuIndex[ $aMenuItem['parent_id'] ]['sub_menu'][] =& $aBaseMenuIndex[ $aMenuItem['id'] ];
}
}
var_dump( $aMenu );
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.