I need to render a nested set tree as a li-structure with unlimited depth. While I understand how to do it in plain php (like here: PHP: How to generate a <ul><li> tree in an xml2assoc array result?), I hate echoing html tags and would like to have it done in a template. Is it possible with PHP as a templating language? Where should I define a recursive function?
For me, I depends on how much 'code' is needed in each iteration.
For simple trees, I would just declare a function at the top of the view-file. (Since I think that function has only real value in that separate file).
For trees with a bit more rendering, I would create a separate partial file. That file could be called in the view file and in the partial file itself.
You could also create a helper file, which you use on that specific page, put than the partial would make more sense and is easier to implement (and you can use all other helper functions and symfony functions)
hm, here is my sollution:
<?php
/**
* #var $records
* #var $field
*/
?>
<?php if( isset($records) && is_object($records) && count($records) > 0 ): ?>
<div id="document-nested-set">
<ul class="nested_set_list">
<?php $prevLevel = 0; $is_first = true; ?>
<?php foreach($records as $record): ?>
<?php if($prevLevel > 0 && $record['level'] == $prevLevel) echo '</li>';
if($record['level'] > $prevLevel) echo '<ul>';
elseif ($record['level'] < $prevLevel) echo str_repeat('</ul></li>', $prevLevel - $record['level']); ?>
<?php $rel = $record['lft']=='1'?'root':($record['is_approved'] && $record['is_checked']?'document':'document_grey') ?>
<li id ="phtml_<?php echo $record->id ?>" rel="<?php echo $rel ?>" <?php echo $is_first?'class="open"':'' ?>>
<ins> </ins><?php echo $record->$field;?>
<?php $prevLevel = $record['level']; $is_first = false; ?>
<?php endforeach; ?>
</ul>
</div>
<?php endif;?>
an easy example:
<?php
$input = array('c' => array('c1' => 't1', 'c2' => array('c21' => array('c211' => 't2'), 'c22' => 't3')));
$iterate = function($array) use (&$iterate) {
$out = '<ul>';
foreach($array as $key => $child)
$out .= '<li>'.$key.': '.( is_array($child) ? $iterate($child) : $child ).'</li>';
return $out.'</ul>';
}
?>
<html><body><?php echo $iterate($input); ?></body></html>
Related
I have following Two-dimensional array:
$data = array
(
array("1.2"),
array("2.5"),
array("4.7"),
array("5.7"),
array("3.5"),
array("7.2"),
array("4.7"),
array("3.5")
);
Now I am displaying my records through loop:
<ul>
<?php
for($i=0; $i<count($data); $i++):
?>
<li><?php echo $data[$i][0]; ?></li>
<?php
endfor;
?>
</ul>
and this is the result:
Now I want to check some condition inside loop and add class="red" to li.
Example 1:
If 4.7 found inside the loop, add class="red" to next all li tags.
Example 2:
If 3.5 found inside the loop, add class="red" to next all li tags.
Example 3:
If 5.7 found inside the loop, add class="red" to next all li tags.
Any idea how to add class to li tags when some condition match.
Thanks.
You can just switch on the class as soon as a matching item is found.
$switch_value = '4.7'; // set the value where you want to switch colors
$class = ''; // initialize the class to empty string
foreach ($data as $value) {
echo "<li$class>$value[0]</li>";
// set the class to red the first time the value is found
if ($value[0] == $switch_value) $class = ' class="red"';
}
It's important to set the class after echoing the list item to get the output you want.
You can do it this way:
<ul>
<?php
$class = ''; $num = 4.7;
for($i=0; $i<count($data); $i++):
?>
<li class='<?php echo $class; ?>'><?php echo $data[$i][0]; ?></li>
<?php
if( $data[$i][0] == $num ) $class = 'red';
endfor;
?>
</ul>
Just change the $num to desired value programmatically.
Edit: move the if block to end of for block to leave the number's occurrence.
Just check a condition with a value, and assign it
<ul>
<?php
$isRed = false;
$value_to_search = 3.5;
for($i=0; $i<count($data); $i++):
?>
<li class="<?php echo ($isRed == true)?'redClass':'';"><?php echo $data[$i][0]; ?></li>
<?php
if($data[$i][0] == $value_to_search )
$isRed = true;
endfor;
?>
</ul>
I guess you can use something like:
<ul>
<?php
$red = false;
for($i=0; $i<count($data); $i++){
if($data[$i][0] == "3.5" or $data[$i][0] == "4.7" or $data[$i][0] == "5.7" or $red){
echo "<li class=\"red\">{$data[$i][0]}</li>";
$red = true;
}else{
echo "<li>{$data[$i][0]}</li>";
}
}
?>
</ul>
Output:
<ul><li>1.2</li><li>2.5</li><li class="red">4.7</li><li class="red">5.7</li><li class="red">3.5</li><li class="red">7.2</li><li class="red">4.7</li><li class="red">3.5</li></ul>
Ideone Demo
http://ideone.com/s2gdmG
What I have:
I have an array of links and their meta data.
Some links need to be grouped together and some are regular links.
What I'm trying to do:
I'm trying to iterate through my array and group li's with their relative ul, based on a common id.
The problem:
One problem is only making unique ul's based on said id and not make a ul for each item it iterates through.
The second more difficult problem is grouping each item within the relative ul.
Here is an example array I have to work with:
<?php
$array = array();
$array['page']['colours']['link'] = '/colours';
$array['page']['colours']['link-title'] = 'Colours';
$array['page']['colours']['subpage']['green']['aside']['link'] = '/colours/green';
$array['page']['colours']['subpage']['green']['aside']['link-title'] = 'Green';
$array['page']['colours']['subpage']['blue']['aside']['collapse']['id'] = 'cool-colours';
$array['page']['colours']['subpage']['blue']['aside']['link'] = '/colours/blue';
$array['page']['colours']['subpage']['blue']['aside']['link-title'] = 'Blue';
$array['page']['colours']['subpage']['purple']['aside']['collapse']['id'] = 'cool-colours';
$array['page']['colours']['subpage']['purple']['aside']['link'] = '/colours/purple';
$array['page']['colours']['subpage']['purple']['aside']['link-title'] = 'Purple';
$array['page']['colours']['subpage']['orange']['aside']['collapse']['id'] = 'bright-colours';
$array['page']['colours']['subpage']['orange']['aside']['link'] = '/colours/orange';
$array['page']['colours']['subpage']['orange']['aside']['link-title'] = 'Orange';
$array['page']['colours']['subpage']['yellow']['aside']['collapse']['id'] = 'bright-colours';
$array['page']['colours']['subpage']['yellow']['aside']['link'] = '/colours/yellow';
$array['page']['colours']['subpage']['yellow']['aside']['link-title'] = 'Yellow';
$array['page']['colours']['subpage']['red']['aside']['collapse']['id'] = 'bright-colours';
$array['page']['colours']['subpage']['red']['aside']['link'] = '/colours/red';
$array['page']['colours']['subpage']['red']['aside']['link-title'] = 'Red';
$array['page']['colours']['subpage']['pink']['aside']['collapse']['id'] = 'bright-colours';
$array['page']['colours']['subpage']['pink']['aside']['link'] = '/colours/pink';
$array['page']['colours']['subpage']['pink']['aside']['link-title'] = 'Pink';
?>
Here is what I have currently for iterating through this array:
<aside id="sidebar">
<?php
if ( !empty( $array ) ) { ?>
<ul class="menu">
<?php foreach ( $array['page']['colours']['subpage'] as $subpage ) {
if( !empty( $subpage['aside'] ) ) {
$historyArray = array();
?>
<li>
<?php if( !empty( $subpage['aside']['collapse'] ) ) { ?>
<?php if( !in_array( $subpage['aside']['collapse']['id'], $historyArray ) ) { ?>
<a data-toggle="collapse" data-parent="#sidebar" href="#<?php echo $subpage['aside']['collapse']['id'] ?>" class="<?php echo $currentPageLink === $subpage['aside']['link'] ? 'active' : '' ?>"><?php echo $subpage['aside']['link-title'] ?></a>
<ul id="<?php echo $subpage['aside']['collapse']['id'] ?>">
<li>
<?php echo $subpage['aside']['link-title'] ?>
</li>
</ul>
<?php
$historyArray[] = $subpage['aside']['collapse']['id'];
}
?>
<?php } else { ?>
<?php echo $subpage['aside']['link-title'] ?>
<?php } ?>
</li>
<?php } } ?>
</ul>
<?php } ?>
</aside>
Wow, now THAT'S a confusing array! But everything is doable, right? It's just a matter of what you're willing to try. :)
I think you need to create a new array that fits the listing purpose, then iterate through that for the list (The huge complex array will remain intact for whatever else it's used for). Consider this:
<?php
if ( !empty( $array ) ) {
//Create the new array $uls[id][color]['link'/'link-title']
foreach ( $array['page']['colours']['subpage'] as $color => $subpage ) {
$uls[$subpage['aside']['collapse']['id']][$color]['link'] = $subpage['aside']['link'];
$uls[$subpage['aside']['collapse']['id']][$color]['link-title'] = $subpage['aside']['link-title'];
}
}
if ( !empty( $uls ) ) {
echo '<aside id="sidebar">';
echo '<ul class="menu">';
//iterate through the new $uls array
foreach($uls as $id => $color) {
//for each id, we create a ul, then iterate through that id's array
echo '<li><ul id="' . $id . '">';
foreach($color as $link_array) {
//echo the list item for each link in the id
$active = ($currentPageLink === $link_array['link']) ? 'active' : '';
echo '<li>' . $link_array['link-title'] . '</li>';
}
echo '</ul></li>';
}
echo '</ul>';
echo '</aside>';
}
?>
One thing I didn't add was the data-collapse link. I wasn't sure exactly what you were going for there. That shouldn't be too hard to stick in there though.
Model
function Show_all_products()
{
return $this->db->get('printer')->result();
}
View
The table contents is being echoed in a loop
$i = -1;
echo '<ul class="products">'; foreach($products as $product) :
if($i % 11 == 10) echo '</ul><ul class="products">';
?>
<li><?php echo $product->name; ?> </li>
<?php
$i += 1;
endforeach;
echo "</ul>";
The product names are saved as Something_Something_Blah I cannot modify the product names as they are configured to show clean URL's and Breadcrumbs.
The issue is that the links in this view show as Something_Something_Blah
I tried to do a str_replace as $product = str_replace('_',' ', $product); However this isn't working.
How do i strip the '_' and insert \s ?
I see you have <?php echo $product->name; ?> which would mean $product is an object.
So you should call str_replace('_', ' ', $product->name);
<?php
$i = -1;
echo '<ul class="products">'; foreach($products as $product) :
$product->name = str_replace('_',' ', $product->name);
if($i % 11 == 10) echo '</ul><ul class="products">';
?>
<li><?php echo $product->name; ?> </li>
<?php
$i += 1;
endforeach;
echo "</ul>";
?>
str_replace must be called from within the foreach or an error will be returned. That was my mistake.
As per PHP documentation, str_replace() accepts a string or array as it's third argument. You are passing it an object. You should pass $product->name to it.
Furthermore, please strive to embed PHP in your HTML and not HTML in your PHP. You're going to find yourself echoing everything. You don't need to manually create the $i iterator either, because the foreach loop will create one for you, provided you use the foreach($x as $key => $val) syntax. You can lose the <?php echo $var;?> for <?= $var;?> too, AND concatenate with the . symbol:
<ul class="products">
<? foreach($products as $pK => $pV):?>
<? if($pK % 11 == 10):?></ul><ul class="products"><? endif;?>
<li>
<?= $pV->name;?>
</li>
<? endforeach;?>
</ul>
Consider this:
$menuItems = array (
"page1" => "1.php",
"page2" => "2.php",
"page3" => "3.php",
"page4" => "4.php",
"page5" => "5.php",
);
and now I create the menu like this:
<ul class="menu">
<?php
foreach($menuItems as $name => $url) {
echo "<li><a href='$url' class='$class'>$name</a></li>";
}
?>
</ul>
Works great. But now I need to add a class .current on the current page.
To get the current page I do this:
<?php
function curPageName() {
return substr($_SERVER["SCRIPT_NAME"],strrpos($_SERVER["SCRIPT_NAME"],"/")+1);
}
$curpage = curPageName();
?>
And it also works great.
SO I guess the question is how do I assign $curpage to the ... current page? :)
Thank you.
Alex
<ul class="menu">
<?php
foreach($menuItems as $name => $url) {
if ($url === $curpage){
$class.=' .current';
}
echo "<li><a href='$url' class='$class'>$name</a></li>";
}
?>
</ul>
Easy, inside your loop, check if $url == $curpage, and append appropriately.
Example:
<?php
$menuItems = array(
"page1" => "1.php",
"page2" => "2.php",
"page3" => "3.php",
"page4" => "4.php",
"page5" => "5.php",
);
$current_item = "2.php"; //Assume we got this from the function
?>
<ul class="menu">
<?php
foreach ($menuItems as $name => $url) {
echo "<li><a href='$url'";
if ($url == $current_item) {
echo " class='current'";
}
echo ">$name</a></li>\n";
}
?>
</ul>
<ul class="menu">
<?php
foreach($menuItems as $name => $url) {
$class = 'default';
if (curPageName() == $name) {
$class.='.current';
}
echo "<li><a href='$url' class='$class'>$name</a></li>";
}
?>
</ul>
So I made it work with your help! Thanks guys.
Special thanks for Adel - although you had a mistake there I fixed it like this:
foreach($menuItems as $name => $url) {
$class = 'default';
if (curPageName() == $url) {
$class.=' \. current';
}
echo "<li><a href='$url' class='$class'>$name</a></li>";
}
Awesome! Thanks again!
<?php
$menuItems = array(
'page1' => '1.php',
'page2' => '2.php',
'page3' => '3.php',
'page4' => '4.php',
'page5' => '5.php',
);
$current_page = curPageName();
?>
<ul class="menu">
<?php
foreach ($menuItems as $name => $url) {
?>
<li><a href="<?php=$url?>"
<?php
if ($url == $current_page) {
?>
class="current"
<?php
}
?>
><?php=$name?></a></li>
<?php
}
?>
</ul>
Why use the curPageName () at each iteration?
You can use a ternary syntax to solve your problem. It consists in this syntax:
(if condition ? true : false)
So we can use this way on your answer comparing the curPageName with the url inside the foreach loop.
$url == curPageName() ? 'current' : 'notCurrent'
Now you just need to use this in your favor.
I like the syntax of printf. So the code is more clean. Try this:
<?php
/**
* curPageName() is defined in question
* my_links() is a shortcut to array defined in question
*/
$template = "<li%s>%s</li>";
$links = my_links();
$current = curPageName();
print '<ul class="menu">';
foreach ($likns as $name => $url) {
$class = $current == $url ? ' class="current"' : '';
printf($template, $class, $url, $name);
}
print '</ul>';
Each %s item in $template will be changed by sequential variable. So, if you need to change the template the code is clean and easyest to give a maintance.
Or, if you want another easiest template way, use the php template syntax. Note the use of : after the foreach statement and the use of endforeach. You can see that I am using the short code of echo <?=$var?> that is more compreensible than <?php echo $var ?>.
<?php
/**
* curPageName() is defined in question
* my_links() is a shortcut to array defined in question
*/
$links = my_links();
$current = curPageName();
?>
<ul class="menu">
<?php foreach ($links as $name => $url): ?>
<li<?=($current == $url ? ' class="current"' : '')?>>
<?=$name?>
</li>
<?php endforeach ?>
</ul>
I currently have some VERY long winded code for the menu I use on my site.
My website
It's almost 2000 lines long lol. I think I may be able to use a switch but I've tried and cannot implement it to work properly.
On each menu when you click a button it stays highlighted, telling the user that they are on that page. The only way I could get this to work was like so...
if($subject == 'art') {
echo '<div id="spacer2"><br></div>';
echo '<div class="idName2" id="menu2">';
echo 'All';
echo '<div id="spacer2"><br></div>';
echo '</div>';
echo '<div class="idName3" id="menu3">';
echo 'Art';
echo '</div>';
echo '<div class="idName2" id="menu2">';
echo 'Biology';
echo 'English';
echo 'Chemistry';
echo 'Mathematics';
echo 'History';
echo 'Religion';
echo 'Geography';
echo 'Music';
echo 'Philosophy';
echo 'Psychology';
echo 'Economics';
echo 'Sociology';
echo 'Technology';
echo 'Electronics';
echo 'Food';
echo 'Law';
echo 'Politics';
echo '</div>';
}
elseif($subject == 'biology') {
and then there's however many 'elseifs' as there are menu items, which ends up totalling to 2000 lines of code which is obviously very inefficient and it also makes it unbelievably time consuming to change anything... can someone point me in the right direction in what I need to do please!
Navigation is always a pain. Here is a simple solution that might help.
Put everything in an array, like this:
$menu = array(
'All' => '/browse ...',
'Art' => '/browse ... art',
'Biology' => '/browse ... biology',
// etc.
)
Then, you can build all the links based on the current subject:
$subject = 'Art';
foreach ($menu as $title => $url) {
if ($title == $subject) {
echo "<b>$title</b><br>\n";
} else {
echo "$title<br>\n";
}
}
This is a rather simplistic solution, but it can be extended to create a more complicated menus structure.
Another solution you could look at is Zend_Navigation.
http://framework.zend.com/manual/en/zend.navigation.introduction.html
What about something like:
$subjects = array('art', 'biology', 'english', 'chemistry' /*, etc */);
foreach ($subjects as $current_subject)
{
if ($current_subject == $subject)
{
//write it the id="menu3" way
}
else
{
//write it another way
}
}
You can transform 'biology' to 'Biology' using the ucfirst function.
Also note that there can't be two or more elements with the same ID (menu2 in your case).
You should use an array to represent your menus. Even better, you could place it in a resource file (xml would be preferable), but explaining that exceeds the scope of this answer (nonetheless, you should look into it). Using an external resource for the menu has the added benefit that you can change your menu structure without modifying any code.
Sticking to the simple approach, this is how you should represent your menu without an external resource:
$menus = array('Art', 'Biology', 'English', ...);
You can generate the "A"-"Z" submenus with a simple range command: range('A', 'Z')
So your code echoing the menus would be something like:
<?php
$current_letter = $_REQUEST['letter'];
$current_menu = $_REQUEST['menu'];
$letters = range('A', 'Z');
foreach($letters as $letter) :
$class = $current_letter == $letter ? 'class="active"' : '';
?>
<a <?php echo $class; ?> href="browse.php?letter=<?php echo $letter; ?>&menu=<?php echo $current_menu; ?>&listtype=<?php echo $listtype"><?php echo $letter; ?></a>
<?php endforeach; ?>
This displays the top menubar (A-Z links). For the side menu, here is how to display your categories:
<?php
foreach($menus as $menu) :
$class = $current_menu == $menu ? 'class="active"' : '';
?>
<a <?php echo $class; ?> href="browse.php?letter=<?php echo $current_letter; ?>&menu=<?php echo $menu; ?>&listtype=<?php echo $listtype"><?php echo $menu; ?></a>
<?php endforeach; ?>
Haven'tested it, but apart from possible typos this approach should work for you.
Btw, please don't throw things at me for not validating input and using $_REQUEST directly. It should be done, but that is a whole other topic.