Create a menu from database (mixed sub menus) - php

I created a database to store a menu informations with 3 level (2 sub menus level).
You can see it on the left here : https://volt-services.fr/sites/stge/index.php#
Here is the table in the database:
And the code to translate it to HTML:
<?php
$req = $DB->query("SELECT * FROM menu ORDER BY ordered");
foreach ($req as $r) {
if($r->level == 1) {
$tab[$r->ordered] = array($r->id,$r->name,$r->class,$r->icon);
} elseif ($r->level == 2) {
$tab[$r->parent][4][$r->ordered] = array($r->id,$r->name);
} elseif ($r->level == 3) {
$tab[$r->cat][4][$r->parent][2][$r->ordered] = array($r->id,$r->name);
}
}
foreach ($tab as $key => $value) {
?>
<li data-menu="<?= $value[0]; ?>" class="<?= $value[2]; ?> menu-item">
<span class="material-icons-outlined"><?= $value[3]; ?></span>
<span class="titre_menu"> <?= $value[1]; ?></span>
<ul id="sub_menu<?= $value[0]; ?>" class="box_sub_exp <?= $value[2]; ?>">
<?php foreach ($value[4] as $ke => $val) { // SUB MENU POSITION 4 ?>
<li class="<?= $value[2]; ?> submenu-item"><?= $val[1]; ?>
<?php if(!empty($val[2])) { // IS A MENU IN POSITION 2 ?>
<ul class="box_sub_sub_exp">
<?php foreach ($val[2] as $k => $v) { // SUB MENU POSITION 2 ?>
<li class="<?= $value[2]; ?> submenu-item"><?= $v[1]; ?></li>
<?php } ?>
</ul>
<?php } ?>
</li>
<?php } ?>
</ul>
</li>
<?php } ?>
</ul>
</nav>
</section>
On the first sub menu everything is OK. But on the second, the first line is missing, on the third 2 first lines are missing, etc...
I think there is a problem with my loop, could you please help me to find it ?
FIXED : the problem was in the request, not "ORDER BY ordered" but by "id"

Even if you set a numeric key with $r->ordered, you have to sort your table before your display loop !
Give a numeric key to a table line doesn't "auto-sort" it on loop.
Try using ksort() on $tab before your display foreach. So it's will use keys you've define and sort your table in order.

Related

Dynamically give list items <li> a unique class with PHP

I am dynamically populating a list based on what is in the database, and I would like each list item to have a unique class name - something simple, for example the first would have the class item-1, and the second would have the class item-2.
This is the php code I am using to create the list:
<?php
$metas=trim(get_post_meta($post->ID,'hike_meta',true),'');
$metas_arr=explode("\n",$metas);
$metas1=array_slice($metas_arr,0,3);
$metas2=array_slice($metas_arr,3,3);
?>
<div class="loca_meta">
<ul>
<?php foreach($metas1 as $meta){
list($k, $v) = explode('|', $meta);
echo "<li><span class='bold'>$k</span>:<br>$v</li>";
}?>
</ul>
</div>
<div class="loca_meta">
<ul>
<?php foreach($metas2 as $meta){
list($k, $v) = explode('|', $meta);
echo "<li><span class='bold'>$k</span>:<br>$v</li>";
}?>
</ul>
</div>
What extra code do I need, to give each list item a unique class? As a side note, I am very much a beginner when it comes to php, so please let me know if you need any more information.
You can add $counter variable to get the incremented value and assign it to class.
<div class="loca_meta">
<ul>
<?php $counter = 0; foreach($metas1 as $meta){
list($k, $v) = explode('|', $meta);
echo "<li><span class='bold <?php echo "item_".$counter ?>'>$k</span>:<br>$v</li>";
$counter++;
}?>
</ul>
</div>
<div class="loca_meta">
<ul>
<?php foreach($metas2 as $meta){
list($k, $v) = explode('|', $meta);
echo "<li><span class='bold <?php echo "item_".$counter ?>'>$k</span>:<br>$v</li>";
$counter++;
}?>
</ul>
</div>
You can use for loop:
<?php for($i = 0; $i < count($metas1); $i++){
list($k, $v) = explode('|', $metas[$i]);
echo "<li><span class="bold item-".$i+1.">$k</span>:<br>$v</li>";
}?>
Result will be:
<li><span class="bold item-1"><!-- content --></span>:<br><!-- content --></li>
<li><span class="bold item-2"><!-- content --></span>:<br><!-- content --></li>
<li><span class="bold item-3"><!-- content --></span>:<br><!-- content --></li>
and so on.

How can I sort the second dropdown list by date to have the newest page title the first?

<?php
foreach ($navItems as $ni) {
?>
<li class="<?php echo $ni->classes ?><?php echo($ni->hasSubmenu)?" dropdown":""; ?>">
<?php
if ($ni->hasSubmenu) {
?>
<?php echo (isset($translate) && $translate == true) ? t($ni->date) : $ni->name; ?> <span class="caret"></span>
<?php
} else{
?>
<?php echo (isset($translate) && $translate == true) ? t($ni->name) : $ni->name; ?>
<?php
}
?>
<?php
if ($ni->hasSubmenu) {
echo '<ul class="dropdown-menu">'; //opens a dropdown sub-menu
} else {
echo '</li>'; //closes a nav item
echo str_repeat('</ul></li>', $ni->subDepth); //closes dropdown sub-menu(s) and their top-level nav item(s)
}
?>
</li>
<?php
}
I want that "Szelíd beszéd" on the top, that is the newest added to the page.
As Concrete5 returns a single array of objects rather than a nested array, you can't tell what is a child and what isn't until you loop through it.
As such, I would suggest you rebuild the nav items array, and when you get to those items, append a page list of them and then continue.
This may not work, as you would lose references on those pages for the likes of 'hasSubmenu' or 'subDepth' but give it a go.
<?php
$navItemsNew = array();
$addedOrderedList = false;
$parentID = 4; //replace 4 with the ID of the parent page
foreach ($navItems as $ni) {
if($ni->cObj->getCollectionParentId() == $parentID){
if($addedOrderedList){
continue;
}else{
$pl = new PageList();
$pl->filterByParentID($parentID); //Get all posts beneath that parent
$pl->sortByPublicDate(); // Sort by public date, newest first
$pl->setItemsPerPage(0); //return all items in 1 page
$posts = $pl->getPage(1);
$addedOrderedList = true;
array_merge($navItemsNew, $posts);
continue;
}
}
$navItemsNew[] = $ni;
}
foreach ($navItemsNew as $ni) {
?>
<li class="<?php echo $ni->classes ?><?php echo($ni->hasSubmenu)?" dropdown":""; ?>">
<?php
if ($ni->hasSubmenu) {
?>
<?php echo (isset($translate) && $translate == true) ? t($ni->date) : $ni->name; ?> <span class="caret"></span>
<?php
} else{
?>
<?php echo (isset($translate) && $translate == true) ? t($ni->name) : $ni->name; ?>
<?php
}
?>
<?php
if ($ni->hasSubmenu) {
echo '<ul class="dropdown-menu">'; //opens a dropdown sub-menu
} else {
echo '</li>'; //closes a nav item
echo str_repeat('</ul></li>', $ni->subDepth); //closes dropdown sub-menu(s) and their top-level nav item(s)
}
?>
</li>
<?php
}
The pages in submenu comes from xml, from another website, tomorow will be added 1 new page ..and will show us in in autonav last child...but we need first!

Hide if get_the_category() returns no results

I'm listing related pages based on category. This is what I'm using to list all related categories. What I'm trying to do is hide the entire block if it doesn't return any categories. I'm not sure how to do that with the foreach.
<h3>Related Category</h3>
<ul>
<?php foreach((get_the_category()) as $catCS) {
if($catCS->parent == 4){ ?>
<li><?php echo $catCS->cat_name; ?></li>
<?php }
} ?>
</ul>
Not sure if you know, but WordPress have a command to get parent categories get_category_parents, so if you use that you could use it like this:
<?php
$result = get_category_parents($cat, true, '</li><li>');
$result = substr($result, 0, -4);
if(!is_wp_error($result))
{
?>
<h3>Related Category</h3>
<ul>
<li><?php echo $result; ?>
</ul>
<?php
}
?>
NOTE: substr is a small hack to remove the last empty <li> opening due to how I am using </li><li> as a separator.
This is untested, but you could filter the current array into a resulting array and test if that is empty or not.
<?php
# save the result
$categories = array();
# fill $categories if any match
foreach ((get_the_category()) as $cat)
{
if($cat->parent == 4)
{
$categories[] = $cat;
}
}
# print nothing if $categories is empty
if (!empty($categories))
{
?>
<h3>Related Category</h3>
<ul>
<?php
foreach($categories as $catCS)
{
?>
<li><?php echo $catCS->cat_name; ?></li>
<?php
}
?>
</ul>
<?php
}
?>
There might be a better way but this should work.

PHP loop split by 4

How would I achieve this PHP task?
I have an unordered html list and and array.
My code adds list tags to each item in the array to create one big unordered list
<ul>
<?php foreach ($rows as $id => $row): ?>
<li><?php print $row ?></li>
<?php endforeach; ?>
</ul>
current output below
<ul>
<li>01</li>
<li>02</li>
<li>.....</li>
<li>.....</li>
<li>15</li>
</ul>
What I want is to split the list items so that they are in groups of 4 as sub unordered lists. If the number is not divisible by 4 then the remainder should be a smaller list at the end, for example.
<ul>
<ul>
<li>01</li>
<li>02</li>
<li>.....</li>
<li>.....</li>
</ul>
<ul>
<li>05</li>
<li>06</li>
<li>.....</li>
<li>.....</li>
</ul>
<ul>
<li>09</li>
<li>10</li>
</ul>
</ul>
Thanks in advance.
<ul>
<?php foreach ($rows as $id => $row): ?>
<?php if ($id > 0 && $id % 4 === 0): ?>
</ul><ul>
<?php endif ;?>
<li><?php echo $row; ?></li>
<?php endforeach; ?>
</ul>
(Note that if the key of your $rows array is not simply an index number, you'll need to maintain your own counter variable.)
<?php
$id=0;
foreach ($rows as $id => $row)
{
$id+=1;
if ($id % 4 == 0)
{
echo "<ul>";
}
echo "<li>$i</li>";
if ($id % 4 == 0)
{
echo "</ul>";
}
}
if ($id % 4 != 0)
{
echo "</ul>";
}
?>
foreach ($rows as $id => $row) {
if ($id % 4 == 0) {
// do something
}
}
Try something like this :
<?php
$rows = array("2","3","5","6","8","9","0","3","5");
$i =0;
foreach ($rows as $id => $row):
if($i % 4 == 0)
{ echo "</ul><ul>";}
$i++;
?>
<li><?php print $row ?></li>
<?php endforeach; ?>
</ul>
In the end this is the solution I went for. This solution includes a statement to close the tags if there are a number of items not divisible by four, for example if there are five items in total. Thanks to everyone, I couldn't have done it without your input.
$count;
foreach ($rows as $id => $row)
{
if ($count % 4 == 0)
{
echo "<ul>";
}
echo '<li>' . $row . '</li>';
if ($count % 4 == 3 || $count == count($rows)-1)
{
echo "</ul>";
}
$count++;
}

Making a nested list with php foreach

I'm trying to make a nested list with php by using a foreach loop but I'm kind of stuck now. My code uses a foreach loop and checks if the item is a heading, if it is it will start a nested list below it. The problem now is that if it's not a heading I want to put the corresponding list items into a single ul element below its heading. Now as you can see it puts every single list items that isn't a heading into a seperate ul element of its own because of the foreach loop. How can I fix this?
<ul>
<?php foreach($listitems as $listitem) : ?>
<?php if( $listitem['heading'] == 1) : ?>
<li><?php echo $listitem['listitem']; ?><!--begin nested list-->
<?php endif; ?>
<?php if( $listitem['heading'] == 0) : ?>
<ul><li><?php echo $listitem['listitem']; ?></li></ul>
<?php endif; ?>
<?php endforeach; ?>
</li><!--end nested list-->
</ul>
This is the desired html output:
<ul>
<li>Javascript Basics<!--begin nested list-->
<ul>
<li>Getting Started</li>
<li>Data and Variables</li>
<li>Functions</li>
<li>Scope</li>
<li>Working With Objects</li>
<li>Creating Objects</li>
<li>Arrays</li>
<li>Conditions And Decisions</li>
<li>Loops</li>
</ul>
</li><!--end nested list-->
</ul>
You need to set the inner lists start and end tags when the heading changes.
Something like this
<ul>
<?php
$NonHeadCount = 0;
$HeadingOpen = false;
foreach($listitems as $listitem)
{
if( $listitem['heading'] == 1)
{
$HeadingOpen = true;
if ($NonHeadCount != 0)
{
echo "</ul>";
}
echo "<li>".$listitem['listitem'];
$NonHeadCount = 0;
}
if( $listitem['heading'] == 0)
{
if ($NonHeadCount == 0)
{
echo "<ul>";
}
echo "<li>".$listitem['listitem']."</li>";
}
}
if ($NonHeadCount != 0)
{
echo "</ul>";
}
if ($HeadingOpen)
{
echo "</li>";
}
?>
</ul>
You have to change the $listitems format.
Some like this:
<?php
$listitems = array(
'item1' => array(
'item11',
'item12',
'item13'
),
'item2' => array(
'item21',
'item22',
),
'item3',
'item4'
);
?>
Then, do this:
<?php
echo "<ul>";
foreach($listitems as $item => $listitem):
echo "<li>$item</li>";
if(count($listitem) > 0):
echo "<ul>";
foreach($lisitem as $item):
echo "<li>$item</li>";
endforeach;
echo "</ul>";
endif;
endforeach;
echo "<ul>";
?>

Categories