targeting nested foreach loop array in php - php

I want to foreach loop through the $tabs['downloads'] subarray element (see bottom of following snippet) within the parent foreach $content['tabs'] loop. My attempt is not working. I get an Invalid argument. Suggestions are appreciated.
Sample of $content array:
$content=
[tabs' => [
0 => [
'label' => 'Afoxé',
'downloads' => [
0 => 'afoxe2.mscz',
1 => 'afoxe2.pdf'],
'content' => "blah blah",
],
]
Nested foreach loop:
<? $i = 1; foreach($content['tabs'] as $key => $tab):
$tabid=$tab['label'] . "-" . $tabsuffix; ?>
<div class="tab-pane fade <?= ($i == 1 ? "show active" : "")?>" id="pills-<?=$tabid?>" role="tabpanel"
aria-labelledby="pills-<?=$tabid?>-tab" tabindex="0">
<?= $Parsedown->text($tab["content"]) ?>
<? foreach($tab['downloads'] as $download): ?>
<a class="btn btn-orange blob text-white" role="button" href="../public/<?= $download ?>"
download="<?= $download ?>"><?= $download ?></a>
<? endforeach; ?>
</div>
<? $i++; endforeach; ?>

The only undefined index I see in your code is the video field.
That is easily fixable with the use of the Null Coalescing Operator. This only works with PHP 7+ What it does is return its first operand if it exists and is not null; otherwise it returns its second operand.
As an alternative you can always use if(isset($tab['video'])) before using the tested key anywhere else.
There is also the $tabsuffix but I will assume that is defined before the foreach.
However, if, for example, not all your entries have the downloads key, you should also handle that:
<? $i = 1;
foreach($content['tabs'] as $key => $tab):
$tabid=$tab['label'] . "-" . $tabsuffix; ?>
<div class="tab-pane fade <?= ($i == 1 ? "show active" : "")?>" id="pills-<?=$tabid?>" role="tabpanel"
aria-labelledby="pills-<?=$tabid?>-tab" tabindex="0">
<? if( stripos(str_replace(' ', '', $tab['video'] ?? ''), '</iframe>' ) !== false ) : // <--- check this line ?>
<div class="ratio ratio-16x9">
<?= $tab["video"] ?>
</div>
<? endif; ?>
<?= $Parsedown->text($tab["content"]) ?>
<? foreach($tab['downloads'] ?? [] as $download): // <--- and this line ?>
<a class="btn btn-orange blob text-white" role="button" href="../public/<?= $download ?>"
download="<?= $download ?>"><?= $download ?></a>
<? endforeach; ?>
</div>
<? $i++; endforeach; ?>
You can see this code working here. I have also added data with a video and one without the downloads key

Related

bootstrap 5 collapse in foreach | how to set only first element with shown

<?php foreach ($offers as $item): ?>
<h2 class="accordion-header" id="heading<?php echo $item->id; ?>"></h2>
<div id="collapse<?php echo $item->id; ?>" class="accordion-collapse collapse show" aria-labelledby="heading<?php echo $item->id; ?>" data-bs-parent="#default-accordion-example">
<div class="accordion-body">
<?php echo $item->id; ?>
</div>
<?php endforeach;?>
This give me result: example 10 collapse tabs with content: 1,5,6,8,10,15,20....
issue: all collapse is opened.
I know the problem is loop:
class="accordion-collapse collapse show"
But how to resolve and make first tab show and rest close in foreach?
show should only be on the first element you want opened. The easiest way to keep track is to use the key, if the array doesn't have custom keys, or an iterator. Use a ternary to check for that first key, and only echo show if it's the first key.
<?php foreach ($offers as $key => $item): ?>
<h2 class="accordion-header" id="heading<?php echo $item->id; ?>"></h2>
<div id="collapse<?php echo $item->id; ?>" class="accordion-collapse collapse <?php echo $key == 0 ? 'show' : '' ?>" aria-labelledby="heading<?php echo $item->id; ?>" data-bs-parent="#default-accordion-example">
<div class="accordion-body">
<?php echo $item->id; ?>
</div>
<?php endforeach;?>

Display none to element in foreach if == 0

I have a foreach loop, and in that I have a more information button. I want it so that when x == 0 then the display to that more information button is 'none'
This is the variable that is different in each foreach item.
$issuesFixedCount
This is the class that needs to be display none if the variable == 0.
<a href="<?='version/?version=' . $item->name; ?>" class="moreInfo">Meer
My code:
<div class="pb-2 mb-3">
<h1 class="h2">Recente Jira releases</h1>
<div class="release-items" id="release-items">
<?php
foreach ($items as $item):
$proj = new ProjectService();
$versionService = new VersionService();
$version = $proj->getVersion('', $item->name);
$res = $versionService->getRelatedIssues($version);
$uns = $versionService->getUnresolvedIssues($version);
$issuesFixedCount = $res->issuesFixedCount;
$issuesUnresolvedCount = $uns->issuesUnresolvedCount;
$roundedIssue = $issuesFixedCount - $issuesUnresolvedCount;
if ($res->issueFixedCount == 0){
echo "
<script>
const moreInfoVar = document.querySelector('.moreInfo');
const.style.display = 'none';
</script>";
}
?>
<div class="item" id="item">
<h4>Versie: <?= $item->name; ?></h4>
<div class="details">
<p>Issues in versie: <?= $res->issuesFixedCount; ?> </p>
<p>Afgeronde issues: <?= $roundedIssue;?> </p>
<p>Nog te verwerken issues: <?= $uns->issuesUnresolvedCount;?> </p>
Meer info<br>
</div>
<small><?= $item->releaseDate; ?></small>
<a class="toggle">Toon meer</a>
</div>
<?php
endforeach;?>
<?php
$startAtValueNext = $_GET['startAt'];
$startAtNext = $startAtValueNext += 5;
$startAtValuePrevious = $_GET['startAt'];
$startAtPrevious = $startAtValuePrevious -= 5;
if ($startAtPrevious <= 0){
$startAtPrevious = 0;
}
?>
</div>
<div class="Next-Previous">
<- Vorige
Volgende ->
</div>
</div>
There's no need to inject JavaScript in this way. Instead, simply modify what is sent to the browser in the first place:
<p>Nog te verwerken issues: <?= $uns->issuesUnresolvedCount;?> </p>
<a href="<?='version/?version=' . $item->name; ?>" class="moreInfo" <?= ($res->issueFixedCount == 0 ? 'style="display:none;"' : ''); ?>>Meer info</a><br>
</div>
P.S. document.querySelector('.moreInfo') will only ever select the first element it finds which has that class. It doesn't target the specific element you're outputting on the current row, so that's one reason why that approach doesn't work.

wordpress hero invalid argument supplied for foreach()

I've noticed that the div image-16-8 from my hero.php file is not being displayed.
When I inspect element I get Invalid argument supplied for foreach() in
Here is the code:
<ul data-simple-slider>
<?php foreach ($images as $image): ?>
<li>
<div class="image-16-8" style="background-image: url(<?= $image->url; ?>); "></div>
</li>
<?php endforeach; ?>
</ul>
I've read a similiar question in which adding an if condition before foreach seems to fix the problem.
I'm not sure how to do this as I'm a beginner in php.
Any help is greatly appreciated!
Update Here is the full hero div -
<?php
function tbhHeroShortcode($atts)
{
$values = shortcode_atts(array(
'images' => '',
'first-line' => '',
'second-line' => '',
'video' => '',
'link' => '',
), $atts);
ob_start();
?>
<div class="hero">
<?
$images = decode_shortcode_data($values['images']);
if ($images): ?>
<ul data-simple-slider>
<?php foreach ($images as $image): ?>
<li>
<div class="image-16-8" style="background-image: url(<?= $image->url; ?>); "></div>
</li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
<div class="hero-content">
<div class="hero-content-first-line">
<h1 class="header"><?= decode_shortcode_data($values['first-line']) ?></h1>
</div>
<h1 class="italic-header"><?= decode_shortcode_data($values['second-line']) ?></h1>
<div class="hero-content-cta">
<a class="hollow-button" href="<?= decode_shortcode_data($values['link']) ?>">Learn More</a>
</div>
</div>
<?php if (count($images) > 1): ?>
<div class="hero-controls">
<i class="fa fa-chevron-left hero-controls__left" aria-hidden="true"></i>
<i class="fa fa-chevron-right hero-controls__right" aria-hidden="true"></i>
</div>
<?php endif; ?>
</div>
<?php
$component = ob_get_contents();
ob_end_clean();
return $component;
}
add_shortcode('tbhHero', 'tbhHeroShortcode');
check if the $images is_iterable
if(is_iterable($images)) {
// foreach gets indented here
}
Also see the manual page for is_iterable http://php.net/manual/en/function.is-iterable.php
A nice polyfill for PHP5 users (let's hope they are not on <5.4)
<?php
if(!function_exists('is_iterable')) {
function is_iterable($var) {
return ( is_array( $var ) || ( $var instanceof Traversable ) );
}
}

Foreachloop accordion where the title closes every 2 and the body closes every 2 but are contained in a single row

So I am trying to build a special accordion or collapse in this sense as it's Bootstrap, and I am having a hard time getting the code to cooperate.
I am using a foreach loop to display a repeater. Within it, I have the header, and the body. I am doing rows of 2 each. However, this is how I need it laid out. I need 1 row to hold the header and body, then inside that, 2 more rows. One row is for the header, and one row is for the body. This way the content is full span under the header.
Here is a visual of what I am talking about
I am not sure with my code what I am doing wrong. I've tried running a foreach loop twice, I've tried a modulus inside the loop to close the header row and reopen it, and the same for the body. However that just doesn't sit well at all. Every odd item opens and closes the head and that's it. The body is contained within the header.
I don't have anything live to showcase, but here is my code:
<div class="panel-group" id="accordion_<?php echo $accordion_widget_title; ?>" role="tablist" aria-multiselectable="true">
<div class="panel panel-default">
<?php $x = 1; ?>
<?php foreach( $instance['row_repeater'] as $i => $repeater ) :
// Concatenate the Accordion Title, then convert it to a lower case string
$accordion_title = preg_replace('/\s+/', '-', $repeater[ 'row_accordion_title' ] );
$accordion_title = strtolower( $accordion_title );
$accordion_title = $accordion_title . '_' . $x;
// Start Accordion Title Loop ?>
<div class="panel-heading" role="tab" id="heading-<?php echo $accordion_title; ?>">
<a role="button" data-toggle="collapse" data-parent="#accordion-<?php echo $accordion_widget_title; ?>" href="#item-<?php echo $accordion_title; ?>" aria-expanded="false" aria-controls="item-<?php echo $accordion_title; ?>">
<h4 class="panel-title">
<?php _e( $repeater[ 'row_accordion_title' ], 'boss' ); ?>
</h4>
</a>
</div>
<?php if ( ( $x % 2 == 0 ) ) : ?>
</div><div class="panel panel-default">
<?php endif; ?>
<?php $x++; ?>
<?php endforeach; ?>
<div class="panel panel-default">
<?php foreach( $instance['row_repeater'] as $i => $repeater ) :
// Concatenate the Accordion Title, then convert it to a lower case string
$accordion_title = preg_replace('/\s+/', '-', $repeater[ 'row_accordion_title' ] );
$accordion_title = strtolower( $accordion_title );
$accordion_title = $accordion_title . '_' . $x;
// Start Accordion Title Content ?>
<div id="item-<?php echo $accordion_title; ?>" class="panel-collapse collapse" role="tabpanel" aria-labelledby="heading-<?php echo $accordion_title; ?>">
<div class="panel-body">
<?php _e( $repeater[ 'row_accordion_content' ], 'boss' ); ?>
</div>
</div>
<?php if ( ( $x % 2 == 0 ) ) : ?>
</div><div class="panel panel-default">
<?php endif; ?>
<?php $x++; ?>
<?php endforeach; ?>
</div>
Here is another variation of what I've tried
<div class="panel-group<?php if ( !empty( $additional_class ) ) : echo $additional_class; endif; ?>" id="accordion_<?php echo $accordion_widget_title; ?>" role="tablist" aria-multiselectable="true">
<?php $x = 1; ?>
<?php foreach( $instance['row_repeater'] as $i => $repeater ) : ?>
<?php
// Concatenate the Accordion Title, then convert it to a lower case string
$accordion_title = preg_replace('/\s+/', '-', $repeater[ 'row_accordion_title' ] );
$accordion_title = strtolower( $accordion_title );
$accordion_title = $accordion_title . '_' . $x;
?>
<?php // Start Accordion Title Loop ?>
<?php if ( ( $x % 2 == 1 ) || ( $x == 1 ) ) : ?>
<div class="panel panel-default">
<?php endif; ?>
<div class="panel-heading" role="tab" id="heading-<?php echo $accordion_title; ?>">
<a role="button" data-toggle="collapse" data-parent="#accordion-<?php echo $accordion_widget_title; ?>" href="#item-<?php echo $accordion_title; ?>" aria-expanded="false" aria-controls="item-<?php echo $accordion_title; ?>">
<h4 class="panel-title">
<?php _e( $repeater[ 'row_accordion_title' ], 'boss' ); ?>
</h4>
</a>
</div>
<?php $x++; ?>
<?php if ( ( $x % 2 == 1 ) || ( $x == 1 ) ) : ?>
</div>
<?php endif; ?>
<?php endforeach; ?>
</div>
<div class="panel-group" data-parent="#accordion-<?php echo $accordion_widget_title; ?>" id="accordion_<?php echo $accordion_widget_title; ?>" role="tablist" aria-multiselectable="true">
<?php $x = 1; ?>
<?php foreach( $instance['row_repeater'] as $i => $repeater ) : ?>
<?php
// Concatenate the Accordion Title, then convert it to a lower case string
$accordion_title = preg_replace('/\s+/', '-', $repeater[ 'row_accordion_title' ] );
$accordion_title = strtolower( $accordion_title );
$accordion_title = $accordion_title . '_' . $x;
?>
<?php // Start Accordion Title Content ?>
<?php if ( ( $x % 2 == 1 ) || ( $x == 1 ) ) : ?>
<div class="panel-body-row">
<?php endif; ?>
<div id="item-<?php echo $accordion_title; ?>" class="panel-collapse collapse" role="tabpanel" aria-labelledby="heading-<?php echo $accordion_title; ?>">
<div class="panel-body">
<?php _e( $repeater[ 'row_accordion_content' ], 'boss' ); ?>
</div>
</div>
<?php $x++; ?>
<?php if ( ( $x % 2 == 1 ) || ( $x == 1 ) ) : ?>
</div>
<?php endif; ?>
<?php endforeach; ?>
</div>
I'm just not 100% sure what it is I'm not grasping.
I actually think that the PHP function array_chunk will make your life much easier. http://php.net/manual/en/function.array-chunk.php
I won't try to recreate all your HTML here, but here's the basic idea you could use:
//Split our repeaters into an array of arrays, each with 2 elements
$rows = array_chunk($instance['row_repeater'], 2);
foreach($rows as $row){
echo "<div class='row'>";
foreach($row as $items){
echo "<div class='col-sm-6'>";
echo $items('row_accordion_title');
// echo the rest of your repeater stuff...
echo "</div>";
}
echo "</div>";
}
I think your approach to generating the HTML with PHP is making it difficult to troubleshoot. You should have the foreach loop contain the entire html output you are trying to achieve for each iteration to make it easier to keep track of. Additionally I would make sure that you are creating an incremental set of IDs for the collapsible elements to avoid confusing Boostrap's JS components. Something like:
<div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true">
<?php
for($i = 0; $i < count($instance['row_repeater']); $i++){
$accordion_title = preg_replace('/\s+/', '-', $instance['row_repeater'][$i][ 'row_accordion_title' ] );
$accordion_title = strtolower( $accordion_title );
$accordion_title = $accordion_title . '_' . $x;
$out = "<div class=\"panel panel-default\">
<div class=\"panel-heading\" role=\"tab\" id=\"heading".$i+1."\">
<h4 class=\"panel-title\">
<a role=\"button\" data-toggle=\"collapse\" data-parent=\"#accordion\" href=\"#collapse".$i+1."\" aria-expanded=\"true\" aria-controls=\"collapse".$i+1."\">
".$accordion_title."
</a>
</h4>
</div>
<div id=\"collapse".$i+1."\" class=\"panel-collapse collapse in\" role=\"tabpanel\" aria-labelledby=\"heading".$i+1."\">
<div class=\"panel-body\">
".$instance['row_repeater'][$i][ 'row_accordion_content' ]."
</div>
</div>
</div>";
echo $out;
}
?>
</div>
Doing this will help to simplify your looping and make it easier to track down issues in your code. Additionally using the iteration of $i in the for look you can make sure you create unique IDs to prevent the bootstrap triggers from stepping on each other. Hope this helps

How to arrange subtitles under a title in Yii2

I am trying to arrange some subtitles under a main tile but it is repeating the title.
<div id="downloads" class="tab-pane" role="tabpanel">
<?php
$product_id = $model->id;
$downloads_model = Printingmachinedownloads::find()->where(['productid'=>$product_id])->all();
foreach ($downloads_model as $doc) {
$doc_type = $doc['type'];
$doc_label = $doc['documentlabel'];
$doc_title = $doc['documentname'];
?>
<div class="amazingcarousel-container-1 tab_style">
<h3><?php echo $doc_type;?></h3>
<a target = '_blank' href="<?php echo Yii::$app->homeUrl?>images/printingmachine/downloads/<?php echo $doc_title; ?>">
<?php echo $doc_label ?>
</a>
</div>
<?php
}
// $doc_title = $downloads_model[0]->documentlabel;
?>
</div>
</div>
The output is:
What I need is
Brochures
abc
def
ghi
Specificationsheet
xyz
lmn
opq
Can Anyone tell me what I have to do?
Thanks in Advance
If you don't have issue with new MySQL version group by concept, you can use something like:
...
$downloads_model = Printingmachinedownloads::find()->where(['productid'=>$product_id])->groupBy(['type'])->all();
foreach($downloads_model as $doc):
<div class="amazingcarousel-container-1 tab_style">
<h3><?= type['type'] ?></h3>
$subtitles = Printingmachinedownloads::find()->where(['productid'=>$product_id, 'type' => $doc['type'] )->all();
foreach($subtitles as $subtitle):
<a target = '_blank' href="<?php echo Yii::$app->homeUrl?>images/printingmachine/downloads/<?php echo $subtitle['documentname']; ?>">
<?php echo $subtitle['documentlabel'] ?>
</a>
enforeach;
</div>
endforeach;
...
If have issue with group by you can use little non standard way, it's not best practice and effective way:
$downloads_model = Printingmachinedownloads::find()->where(['productid'=>$product_id])->orderBy(['type'=>SORT_DESC])->all();
$previous_title = ''; $iteration = 1;
foreach($downloads_model as $doc):
$subtitles = Printingmachinedownloads::find()->where(['productid'=>$product_id, 'type' => $doc['type'] )->all();
$count = count($subtitles);
$previous_title = $previous_title ?: $doc['type'];
if( $previous_title == $doc['type'] && $count == $iteration ):
<div class="amazingcarousel-container-1 tab_style">
<h3><?= $previous_title ?></h3>
foreach($subtitles as $subtitle):
<a target = '_blank' href="<?php echo Yii::$app->homeUrl?>images/printingmachine/downloads/<?php echo $subtitle['documentname']; ?>">
<?php echo $subtitle['documentlabel'] ?>
</a>
enforeach;
</div>
$previous_title = ''; $iteration = 1;
else:
$iteration++;
endif;
endforeach;
I didn't test it, but something similar should work.
But my suggestion and effective way will be you use relational tables 1 title container another related table containing sub titles.

Categories