add dash to sub menu within array in foreach loop - php

I have created a tree level menu but I wanted to separate all sub menus by simply adding -- to every sub menu like
main menu
-- 1st level
---- 2nd level
------ 3rd level
and so on
while doing it in LI it is so easy I can simply put tag before running my function but in select option I am unable to achive my target can anyone here would be able to help me out with this please
function fetch_menu($data) {
foreach($data as $menu) {
echo "<option value='".$menu->cid."'>".$menu->cname."</option>";
if(!empty($menu->sub)) {
fetch_sub_menu($menu->sub);
}
}
}
function fetch_sub_menu($sub_menu, $dash = '--'){
foreach($sub_menu as $menu){
echo "<option value='".$menu->cid."'>".$dash.$menu->cname."</option>";
if(!empty($menu->sub)) {
fetch_sub_menu($menu->sub, '--');
}
}
}
The problem is that while applying the above shown code that dash are not increasing for every 2nd or 3rd level menu
Here is how my data is organized in array form
array (
[cid] => 1,
[cname] => 'Main Menu',
[pcid] => 0,
[sub] => array(
[cid] => 2,
[cname] => '1st Level',
[pcid] => 1,
[sub] => array(
[cid] => 3,
[cname] => '2nd Level',
[pcid] => 2,
[sub] => array(
)
)
)
)

You need to add the new dashes to your $dash variable before recursivly calling the function again. That should work
function fetch_sub_menu($sub_menu, $dash = '--'){
foreach($sub_menu as $menu){
echo "<option value='".$menu->cid."'>".$dash.$menu->cname."</option>";
if(!empty($menu->sub)) {
fetch_sub_menu($menu->sub, $dash.'--'); // <-- adding two dashes to $dash
}
}
}

Related

Objects Array Get Specific Object and display together PHP

I am facing a problem problem here and need some help.
I have saved in my database a json_aray array with objects.
After decoding and print_r it I get this result.
Array (
[0] => stdClass Object (
[Title] => Image
[Info] => info
[ImageURL] => url.jpg )
[1] => stdClass Object (
[Title] => Image
[Info] => info
[ImageURL] => url.jpg )
[2] => stdClass Object (
[Title] => Accommodation
[Info] => info
[ImageURL] => image.jpg )
[3] => stdClass Object (
[Title] => Accommodation
[Info] => info
[ImageURL] => image.jpg )
[4] => stdClass Object (
[Title] => Accommodation
[Info] => info
[ImageURL] => image.jpg )
[5] => stdClass Object (
[Title] => Image
[Info] => info
[ImageURL] => image.jpg )
[6] => stdClass Object (
[Title] => Location
[Info] => info
[ImageURL] => image.jpg )
)
So here is the CASE:
I am trying to create a div for every different Object->Title e.g
I tried to check that with foreach loop but the result was creating 5 different divs instead of one.
Is there any built-in function in php to get all the $obj->Info if the $obj->Title == 'foo' or somehow check before the foreach loop what data the array has and separate them?
What i did in my code is below
while($row = sqlsrv_fetch_array($get_details)) {
$a = $row['additionalInformation'];
$b = json_decode($a);
$c = $b->AdditonalInfo;
foreach ($c as $d){
echo '<div class="class">';
if(($d->Title !== 'Image')){
echo '<h2>'.$d->Title.'</h2>';
echo '<p>'.$d->Info.'</p>';
}
echo "</div>";
}
}
UPDATE
Change my code with the actual one
This is what I get
But I need to create Accommodation or every same Title just once and all Info that has Title == 'Accommodation' under that div.
IF my question is clear enough please let me know so I can update it.
So to sum up i need to display all [Info] that has same [Title] in one div and create different div for each unique '[Title]'
Thanks In Advance
Requirements: Show 'nested groups' in a sequential stream of records.
Here we have a nested group indicated by a field called 'Title'.
The input records must be sorted so that all the records in each group are together and in the required order.
Now, the first thought is to use a 'foreach' loop. This is not the clearest approach because you cannot detect the end of the group by looking at the current record. Also, the foreach loop reads the next record at the end of the loop. So, you end up trying to work out where you are in the group to know what to do.
I use a technique called 'read ahead'. The idea is to read a record before the loop and the read the next record immediately after you have processed the current one. The 'trick' is that you process all the records in a group inside a loop.
So, the logic is an iteration of:
Process start of group - already has the first record of the group - important.
Process all the detail records belonging to the group - records are read inside this loop.
Process the end of the group - the current record is the first of the next group
repeat for each group.
It results in easier to understand code.
I have split all the separate actions into function so that you easily modify the individual actions.
Notice the code matches the structure of the data. There is always one place in the code to put the required action for that particular data item.
Full working Source Code at eval.in
Run the code
outputAllGroups($src);
exit();
Process All the Groups
function outputAllGroups(&$inputList)
{
reset($inputList);
$currentDetails = current($inputList); // read the first record
while ($currentDetails !== false) { // not end of records
// start the title group
$currentTitle = $currentDetails->Title;
outputGroupHeader($currentDetails);
// process all records in the group
while ( $currentDetails !== false
&& $currentDetails->Title === $currentTitle) {
outputDetails($currentDetails);
$currentDetails = readNextDetails($inputList); // may end group
}
// end the title group
outputGroupFooter($currentTitle);
}
}
Functions for the individual actions
function outputGroupHeader($details)
{
echo '<div class="TitleGroup">'. "<!-- Start Group: {$details->Title} -->". PHP_EOL
. '<div class="Title">'. $details->Title .'</div>' . PHP_EOL;
}
function outputGroupFooter($title)
{
echo '</div>'. "<!-- End Group: {$title} -->". PHP_EOL;
}
function outputDetails($details)
{
echo '<div class="details">'. PHP_EOL,
$details->Info . PHP_EOL,
$details->ImageURL .PHP_EOL,
'</div>' . PHP_EOL;
}
function readNextDetails(&$inputList)
{
$allOk = next($inputList); // advance next
return $allOk !== false ? current($inputList) : $allOk; // advance next
}
Output
<div class="TitleGroup"><!-- Start Group: Image -->
<div class="Title">Image</div>
<div class="details">
info1
url1.jpg
</div>
<div class="details">
info2
url2.jpg
</div>
</div><!-- End Group: Image -->
<div class="TitleGroup"><!-- Start Group: Accommodation -->
<div class="Title">Accommodation</div>
<div class="details">
info3
image3.jpg
</div>
<div class="details">
info4
image4.jpg
</div>
<div class="details">
info5
image5.jpg
</div>
</div><!-- End Group: Accommodation -->
<div class="TitleGroup"><!-- Start Group: Image -->
<div class="Title">Image</div>
<div class="details">
info6
image6.jpg
</div>
</div><!-- End Group: Image -->
<div class="TitleGroup"><!-- Start Group: Location -->
<div class="Title">Location</div>
<div class="details">
info7
image7.jpg
</div>
</div><!-- End Group: Location -->
Data
$src = Array (
'0' => (object) array(
'Title' => 'Image',
'Info' => 'info1',
'ImageURL' => 'url1.jpg', ),
'1' => (object) array(
'Title' => 'Image',
'Info' => 'info2',
'ImageURL' => 'url2.jpg', ),
'2' => (object) array(
'Title' => 'Accommodation',
'Info' => 'info3',
'ImageURL' => 'image3.jpg', ),
'3' => (object) array(
'Title' => 'Accommodation',
'Info' => 'info4',
'ImageURL' => 'image4.jpg', ),
'4' => (object) array(
'Title' => 'Accommodation',
'Info' => 'info5',
'ImageURL' => 'image5.jpg', ),
'5' => (object) array(
'Title' => 'Image',
'Info' => 'info6',
'ImageURL' => 'image6.jpg', ),
'6' => (object) array(
'Title' => 'Location',
'Info' => 'info7',
'ImageURL' => 'image7.jpg', ),
);
Hi please do so to get for each result unique div. For the 2 quetion (Is there any built-in function in php to get all the $obj->Info if the $obj->Title == 'foo' or somehow check before the foreach loop what data the array has and separate them?) I will find some solution late :)
$a = $row['additionalInformation'];
$b = json_decode($a);
$c = $b->AddInfo;
$createDivArray = array();
echo '<div class="style1">';
foreach ($c as $value){
if (in_array($value->Title, $createDivArray)) {
continue;
}
$createDivArray[] = $value->Title;
echo "<h4>$value->Title</h4>";
echo "<p>$value->Info</p>";
}
echo "</div>";

CodeIgniter - foreach loop

TL;DR
I'm trying to loop all the rows and output it with html. When I'm looping it, I get an array with the properties and values of a single row which is also the last row, when I do not loop it, I get 2 arrays (each array is a row) within an array. For some reason it doesn't loop it and display all the rows as it should, how do I fix this?
I'm trying to make a loop of sections and the articles within them, where as each article has a parent section.
In the database, there are 5 sections and 2 articles, article #1 has section #1 as it's parent and article #2 has section #2 as it's parent.
When print it as an array without looping it, I get the following array;
Array
(
[0] => Array
(
[s_id] => 1
[s_name] => News
[s_slug] => news
[s_visibility] => 1
[s_type] => 1
[s_status] => 1
[s_permission] => 0
[s_external] => 0
[s_location] => news
[s_color] => 1
[s_homepage] => 1
[a_id] => 1
[a_section] => 1
[a_title] => Ted Cruz’s ‘Secret’ Skill That No President Has Likely Had Since Thomas Jefferson
[a_description] => Apparently Cruz, whose famed 2013 marathon filibuster speech over defunding Obamacare jumped across a range of topics, has an uncanny capability to remember things he hears verbatim.
[a_content] => Apparently Cruz, whose famed 2013 marathon filibuster speech over defunding Obamacare jumped across a range of topics, has an uncanny capability to remember things he hears verbatim.
[a_views] => 0
[a_visibility] => 1
[a_date] => 17.11.2015
[a_author] => 1632422528
[a_category] => 1
[a_slug] =>
)
[1] => Array
(
[s_id] => 2
[s_name] => VOD
[s_slug] => vod
[s_visibility] => 1
[s_type] => 2
[s_status] => 1
[s_permission] => 0
[s_external] => 0
[s_location] => vod
[s_color] => 2
[s_homepage] => 1
[a_id] => 2
[a_section] => 2
[a_title] => GTA V PC Edition Released
[a_description] => Wondering where the score is? Our GTA Online review will remain scoreless, as a score does not properly reflect its continuously changing nature. Here's how and why we decided to do it this way.
[a_content] => Wondering where the score is? Our GTA Online review will remain scoreless, as a score does not properly reflect its continuously changing nature. Here's how and why we decided to do it this way.
[a_views] => 0
[a_visibility] => 1
[a_date] => 19.11.2015
[a_author] => 1632422528
[a_category] => 1
[a_slug] =>
)
)
But when I run it into a foreach loop, it outputs only a single array which is the later one (1).
I'm using CodeIgniter 3, the loop is inside a library called "Global_functions",
The model is "Functions_model".
Global_functions (only the related function, not the entire class because it contains other unrelated functions):
public function get_homepage_sections()
{
$getHomeData = $this->CI->functions_model->get_homepage_data();
foreach ($getHomeData as $get_sections)
{
switch ($get_sections['s_color'])
{
case 1:
$sectionColor = "blue";
break;
case 2:
$sectionColor = "purple";
break;
case 3:
$sectionColor = "orange";
break;
case 4:
$sectionColor = "green";
break;
default:
$sectionColor = "";
break;
}
$outputData = '
<li>
<div><h2 class="category ' . $sectionColor . '">' . $get_sections['s_name'] . '</h2></div>';
$outputData .= '
</li>';
}
return $get_sections;
}
Functions_model;
public function get_homepage_data()
{
$selected_columns = array(
'sections.s_id',
'sections.s_name',
'sections.s_slug',
'sections.s_visibility',
'sections.s_type',
'sections.s_status',
'sections.s_permission',
'sections.s_external',
'sections.s_location',
'sections.s_color',
'sections.s_homepage',
'articles.a_id',
'articles.a_section',
'articles.a_title',
'articles.a_description',
'articles.a_content',
'articles.a_views',
'articles.a_visibility',
'articles.a_date',
'articles.a_author',
'articles.a_category',
'articles.a_slug'
);
$query = $this->db->select( $selected_columns )
->from( config_item('sections') . ', ' . config_item('articles') )
//->join( config_item('articles'), 'articles.a_section = sections.s_id' )
->where( 'articles.a_section = sections.s_id' )
//->or_where( 'user_email', $user_string )
->get();
if ( $query->num_rows() >= 1 )
{
return $query->result_array();
}
}
Being called from the controller as follows (homepageSection);
public function index()
{
/* if ($this->require_role('admin')) {
echo $this->load->view('examples/page_header', '', TRUE);
echo '<p>You are logged in!</p>';
echo $this->load->view('examples/page_footer', '', TRUE);
}*/
// return $isAutoRememberMe;
//extra_for_auth();
// Call a function of the model
$data['getGlobalMessage'] = $this->global_functions->get_global_message();
$data['userOptions'] = $this->global_functions->extra_for_auth();
$data['homepageSection'] = $this->global_functions->get_homepage_sections();
//print_r ($data);
$this->parser->parse('template/header', $data);
$this->parser->parse('sections/homepage', $data);
$this->load->view('template/footer');
}
section/homepage file contains the call {homepageSection}, as you can see the parser is called and parses the file rather than loading it with view().
I think there is an error in get_home_sections()
instead of:
return $get_sections;
you should:
return $getHomeData
you are returning the last item from the array, that looks like the problem you stated.
Another point i see a little bit confusing is the var you are cummulating html.
$outputData is being reset in each loop, and you are not doing nothing with it. It's a good idea to initialize it at the begining of the funcion as empty string
$outputData = '';
and in the loop you should do an additive assingment with .=

Recursive table which child parent relationship

I have a problem with a recursive function that takes too many resources to display a child-parent relation in a dropdown list. For example:
home
-menu 1
-menu 2
home 1
-menu 3
-menu 4
I've written some code for a recursive call to the database each time, so that's the reason why my code takes so many resources to run.
Below is my code:
--call recursive
$tmp = $this->get_nav_by_parent(0);
$a_sel = array('' => '-Select-');
$a_sel_cat = array('home' => 'home');
$this->get_child_nav_cat($tmp, 0, $a_sel);
--
public function get_nav_by_parent($parent) {
$all_nav = $this->db
->select('id, title, parent')
->where('parent',$parent)
->order_by('position')
->get('navigation_links')
->result_array();
$a_tmp = array();
foreach($all_nav as $item)
{
if($parent != 0){
$item['title'] = '--' . $item['title'];
}
$a_tmp[] = $item;
}
return $a_tmp;
}
-- Recursive function
public function get_child_nav_cat($a_data, $parent, &$a_sel) {
foreach($a_data as $item) {
$a_sel[$item['page_slug_key']] = $item['title'];
$atmp = $this->get_nav_by_parent($item['id']);
$this->get_child_nav_cat($atmp, $item['id'], $a_sel);
}
return $a_sel;
}
Please give me suggestions for the best solution to display the data as child-parent relationship in select box.
Thanks in advance!
Best way to display parent child relationship is mentain parent and child flag in Database instead of
fetching value using loop.
In your case Home, Home 1 is parent flag and menus belong on child flag.
fetch data from db and your loop look like this:-
$arr = array(0 => array('name' => 'home','parent' => 0),
1 => array('name' => 'menu 1 ','parent' => 1),
2 => array('name' => 'menu 2 ','parent' => 1),
3 => array('name' => 'home 1','parent' => 0),
4 => array('name' => 'menu 3 ','parent' => 2),
5 => array('name' => 'menu 4','parent' => 2)
);
$dd_html = '<select>';
foreach($arr as $k => $v){
if($v['parent'] == 0 )
$dd_html .='<option>'.$v['name'].'</option>';
else
$dd_html .='<option>--'.$v['name'].'</option>';
}
$dd_html .= '</select>';
echo $dd_html;
Output :-
home
-menu 1
-menu 2
home 1
-menu 3
-menu 4
Set ParentID=0 for detecting root item
then execute this:
SELECT * FROM table ORDER BY ParentID, ID
Then iterate through result and when ParentID changed, create new level.

implementing multi-level "iterator" in PHP

I'm trying to create a iterator like this one, for a list of comments:
// the iterator class, pretty much the same as the one from the php docs...
abstract class MyIterator implements Iterator{
public $position = 0,
$list;
public function __construct($list) {
$this->list = $list;
$this->position = 0;
}
public function rewind() {
$this->position = 0;
}
public function current() {
return $this->list[$this->position];
}
public function key() {
return $this->position;
}
public function next() {
++$this->position;
}
public function valid() {
return isset($this->list[$this->position]);
}
}
The comment iterator:
class MyCommentIterator extends MyIterator{
public function current(){
return new Comment($this->list[$this->position]);
}
}
And this is how I use it:
$comments = GetComments(); // gets the comments from the db
if($comments): ?>
<ol>
<?php foreach(new MyCommentIterator($comments) as $comment): ?>
<li>
<p class="author"><?php echo $comment->author(); ?></p>
<div class="content">
<?php echo $comment->content(); ?>
</div>
<!-- check for child comments and display them -->
</li>
<?php endforeach; ?>
</ol>
<?php endif; ?>
So everything is working fine, besides one thing: I can't figure it out how to process nested comments :(
The $comments array returns a flat list of comments, like:
[0] => object(
'id' => 346,
'parent' => 0, // top level comment
'author' => 'John',
'content' => 'bla bla'
),
[1] => object(
'id' => 478,
'parent' => 346, // child comment of the comment with id =346
'author' => 'John',
'content' => 'bla bla'
)
...
I need to somehow be able to check for child comments (on multiple levels) and insert them before the </li>'s of their parent comments...
Any ideas?
Recursion is your friend.
displaycomment(comment):
$html .= "<ol>" . comment->html;
foreach comment->child:
$html .= "<li>" . displaycomment(child) . "</li>";
$html .= "</ol>";
return $html;
All code appearing in this post is pseudo. Any resemblance to real code, working or broken, is purely coincidental.
You might want to look into the RecursiveIterator Interface PHP Manual. If you extend your iterator with the methods by that interface, you're able to iterate over your comments with an instance of RecursiveIteratorIterator PHP Manual sequentially.
However as your output is a flat list, you need to take care of the logic for the levels on your own, e.g. inserting <ol> per depth up, and </ol> per depth down.
Use the flags to control the order how children are traversed.
You are using a flat array, but in reality, the items of that array are a tree or hierarchical data structure.
You are basically displaying a sequential list. Maybe you should construct a tree / hierarchical data structure first, without displaying, and later display data from the tree list.
/* array */ function FlatArrayToTreeArray(/* array */ $MyFlatArray)
{
...
}
/* void */ function IterateTree(/* array */ $MyTreeArray)
{
...
}
/* void */ function Example() {
$MyFlatArray = Array(
0 => object(
'id' => 346,
'parent' => 0, // top level comment
'author' => 'John',
'title' => 'Your restaurant food its too spicy',
'content' => 'bla bla'
),
1 => object(
'id' => 478,
'parent' => 346, // child comment of the comment with id =346
'author' => 'Mike',
'title' => 'Re: Your restaurant food its too spicy',
'content' => 'bla bla'
),
2 => object(
'id' => 479,
'parent' => 478, // child comment of the comment with id =346
'author' => 'John',
'title' => 'Re: Your restaurant food its too spicy',
'content' => 'bla bla'
),
3 => object(
'id' => 479,
'parent' => 346, // child comment of the comment with id =346
'author' => 'Jane',
'title' => 'Re: Your restaurant food its too spicy',
'content' => 'bla bla'
)
);
$MyTreeArray = FlatArrayToTreeArray($myflatarray);
IterateTree($MyTreeArray);
} // function Example()
Cheers.

How to indent text in a select drop down menu using PHP & CSS? [REPHRASED]

I asked a similar question earlier but I'll ask it again in a different way. How can I indent categories and endless sub categories that I have in a select drop down menu using PHP & CSS?
Here is my PHP code.
while (list($id, $parent_id, $category) = mysqli_fetch_array($r, MYSQLI_NUM)) {
// Add to the select menu:
echo '<option value="' . $id . '">' . $category . '</option>\n';
}
Here is the output.
1. Apple
2. Arts & Entertainment
1. Amusement
2. Art
3. Artists
1. A
1. a1
2. a2
2. B
3. C
4. D
3. Automotive
4. Network
5. Server
6. Web Design
1. CSS
2. HTML
The numbers are just there to see the categories and sub categories easier.
In the past I've done this by prepending to the content of each nested option.
$spacer = ' ';
while (list($id, $parent_id, $category) = mysqli_fetch_array($r, MYSQLI_NUM)) {
$padding = str_repeat($spacer, $depth);
// Add to the select menu:
echo '<option value="' . $id . '">' . $padding . $category . '</option>\n';
}
Where $depth represents the 'steps' an option should be indented.
The way I accomplished this was to first put my options into a multidimensional array where children are stored as an array of their parent's...
Like this:
Array(
1 => Array(
'name' => 'Apple'
),
2 => 'Arts & Entertainment',
'children' => Array(
1 => Array(
'name' => 'Amusement'
),
2 => Array(
'name' => 'Art'
),
3 => Array(
'name' => 'Artists'
'children' => Array(
1 => Array(
'name' => 'A',
'children' => Array(
1 => Array(
'name' => 'a1'
)
)
)
)
)
)
)
)
Then I run through the array with a recursive function that steps into each nested array (if present) and appends the $padding to the 'name' value, wraps the option tags around it then returns it. The returned string is appended to the return from all previous calls, and you're left with an indented option menu. Magic.

Categories