PHP while() wrapping items - php

I have this html code:
<div class="row elem2">
<div class="item"></div>
<div class="item"></div>
</div>
<div class="row elem4">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
<div class="row elem3">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
and I am looking for a way to implement it in my php while (wordpress).
The while is as
while ( have_posts() ) : the_post();
echo '<div class="item"></div>';
endwhile;
I've tried a lot of stuff, but none have worked. I need to divide every 2 items and put them in a wrap <div class="row elem2"> after that the next 4 items in <div class="row elem4"> and after that the next 3 items in <div class="row elem3">
I did a lot of searching, but I am not even sure what to search for.

A little crude but here's one solution
$i = 0; // Number of items made so far in the row
$mode = 0; // Current row type enumerated by $elem
$elem = array(2,4,3); // Enumeration of the desired row sizes
while ( have_posts() ) : the_post();
// Make a new row when there's no items yet
if ($i == 0) echo '<div class="row elem'. $elem[$mode] .'">';
echo '<div class="item"></div>';
$i++;
// Once the items in the current row has reached the row's maximum size
if ($i % $elem[$mode] == 0):
echo '</div>';
$i = 0; // Reset items made for the row back to 0
$mode = ($mode + 1) % 3; // Increment mode and wrap if necessary
endif;
endwhile;
if ($i > 0) echo '</div>'; // Finish the last row if it wasn't finished
This is what the modulos was built for!

Related

PHP - Adding 2 div to a foreach loop every 4 times with ACF Repeater

I have an ACF Repeater field i'd like to output as an accordion grid, like so:
<div class="intro row">
<div class="item item-1">name 1</div>
<div class="item item-2">name 2</div>
<div class="item item-3">name 3</div>
<div class="item item-4">name 4</div>
</div>
<div class="expanded row">
<div class="expand" id="item-1">expanded info 1</div>
<div class="expand" id="item-2">expanded info 2</div>
<div class="expand" id="item-3">expanded info 3</div>
<div class="expand" id="item-4">expanded info 4</div>
</div>
<div class="intro row">
<div class="item item-5">name 5</div>
<div class="item item-6">name 6</div>
<div class="item item-7">name 7</div>
<div class="item item-8">name 8</div>
</div>
<div class="expanded row">
<div class="expand" id="item-5">expanded info 5</div>
<div class="expand" id="item-6">expanded info 6</div>
<div class="expand" id="item-7">expanded info 7</div>
<div class="expand" id="item-8">expanded info 8</div>
</div>
I can group the initial row fine, it's just the second "expanded" row i'm having trouble with. How can I repeat and group the second row of 4 correctly in the same loop? My current PHP:
<?php // check if the repeater field has rows of data
if( have_rows('features') ):
// loop through the rows of data
// add a counter
$count = 0;
$group = 0;
while ( have_rows('features') ) : the_row();
$name = get_sub_field('feature_name');
$expandedInfo = get_sub_field('feature_info');
if ($count % 4 == 0) {
$group++;
?>
<div class="intro row">
<?php
}
?>
<div class="item item-<?php echo $count; ?>">
<?php echo $name ?>
</div><!-- item-->
<?php
if ($count % 4 == 3) {
?>
</div><!-- intro-->
<?php
}
$count++;
endwhile;
else :
// no rows found
endif;
?>
The second 'expanded' row can be done so that you store each count (item-1,item-2) in an array or just traverse through all the count when you close the intro row.
<?php
if ($count % 4 == 3) {
?>
</div><!-- intro-->
<div class="expanded row">
<?php
$start = $count-3;
// if $count is 4, $start will be 1, and the $i will go to 4
// if $count is 8, $start will be 5
for($i=$start;$i<=$count;$i++){
echo '<div class="expand" id="item-' . $i . '"></div>';
} ?>
</div>
<?php
}
This is just an example. I would suggest you to store each $count in an array and then use the count($array) to get the number of them. After you have traversed the array, reset it.
The Array Approach
<?php // check if the repeater field has rows of data
if( have_rows('features') ):
// loop through the rows of data
// add a counter
$count = 0;
$group = 0;
// Content Array
$content_array = array();
while ( have_rows('features') ) : the_row();
$name = get_sub_field('feature_name');
$expandedInfo = get_sub_field('feature_info');
// Adding the Expanded Info
$content_array[ 'item-' . $count ] = $expandedInfo;
if ($count % 4 == 0) {
$group++;
?>
<div class="intro row">
<?php
}
?>
<div class="item item-<?php echo $count; ?>">
<?php echo $name ?>
</div><!-- item-->
<?php
if ($count % 4 == 3) {
?>
</div><!-- intro-->
<div class="expanded row">
<?php
foreach( $content_array as $item_id => $expanded_info ) {
echo '<div class="expanded" id="' . $item_id . '">';
echo $expanded_info;
echo '</div>';
} ?>
</div>
<?php
// Resetting the Array
$content_array = array();
}
$count++;
endwhile;
else :
// no rows found
endif;
?>
okey, lets see ,
using variables to store your templates may helps a lot in this context ,
as follow :
$intro = '';
$expanded = '';
while (have_rows('features')) :
the_row();
if ($count % 4 == 0) {
$group++;
$intro .= '<div class="intro row">';
$expanded .= '<div class="expanded row">';
}
$intro .= '<div class="item item-' . $count . '"></div><!-- item-->';
$expanded .= '<div class="expand" id="item-' . $count . '"></div>';
if ($count % 4 == 3) {
$intro = '</div><!-- intro-->';
$expanded = '</div><!-- intro-->';
}
$count++;
endwhile;
I've made a quick example to explain to show you how using variables may fix your issue : https://3v4l.org/cKPP4
Can't test it right now, but I think something like that should work.
<?php // check if the repeater field has rows of data
if( have_rows('features') ):
// loop through the rows of data
// add a counter
$count = 0;
$group = 0;
$array = array();
while ( have_rows('features') ) : the_row();
$name = get_sub_field('feature_name');
$expandedInfo = get_sub_field('feature_info');
if ($count % 4 == 0) {
$group++;
?>
<div class="intro row">
<?php
}
?>
<div class="item item-<?php echo $count; ?>">
<?php echo $name ?>
</div><!-- item-->
<?php
array_push($array, $expandedInfo);
if ($count % 4 == 3) {
?>
</div><!-- intro-->
<div class="expanded row">
<?php
for ($i=0; $i < count($array); $i++) {
echo '<div class="expand" id="item-'.$i + 1.'">'.$array[$i].'</div>';
}
echo '</div>';
}
$count++;
endwhile;
else :
// no rows found
endif;
?>

Change column size based on amount of elements

So I'm working on a HTML carousel using Twitter Bootstrap, Wordpress and ACF Fields.
This carousel shows 2 items per row. Each of these items has a class of "col-md-6". So by showing 2 items per row, the total is 2 columns of "col-md-6" (which is perfect since this completes the 12 columns required by Bootstrap):
Here is my code:
<?php if (have_rows('columns_carousel_slide')) {
$count = 0; ?>
<div class="item active"><div class="row">
<?php while(have_rows('columns_carousel_slide')) {
the_row();
if ($count > 0 && (($count % 2) == 0)) {
?>
</div> <!--.item -->
</div> <!--.row -->
<div class="item">
<div class="row">
<?php } ?>
<div class="col-md-6">
<h2><?php the_sub_field('columns_carousel_slide_title'); ?></h2>
</div> <!--.col-md-6 -->
<?php $count++; } ?>
</div> <!--.item -->
</div> <!--.row -->
<?php } ?>
However, I would like to know if there's a way to detect if there's 1 item per row and if so, then show a "col-md-12" instead of a "col-md-6" in order to fill in the remaining space of not having 2 items.
Any ideas are welcome.
Thanks!
--
Edit: As suggested by Jakub, I've updated my code to the following:
<?php if (have_rows('columns_carousel_slide')) {
$count = 0; ?>
<div class="item active"><div class="row">
<?php
$multiplier = 1; //that could actually go before the while
if (count(get_field('columns_carousel_slide'))%2 === 1 &&
$count === count(get_field('columns_carousel_slide'))-1) {
$multiplier = 2;
} ?>
<?php while(have_rows('columns_carousel_slide')) {
the_row();
if ($count > 0 && (($count % 2) == 0)) {
?>
</div> <!--.item -->
</div> <!--.row -->
<div class="item">
<div class="row">
<?php } ?>
<div class="col-md-<?php echo (6*$multiplier);?>">
<h2><?php the_sub_field('columns_carousel_slide_title'); ?></h2>
</div> <!--.col-md-6 -->
<?php $count++; } ?>
</div> <!--.item -->
</div> <!--.row -->
<?php } ?>
However, I think I've must have missed something because I am getting total of "col-md-12" for all the rows.
Assuming that "get_field" returns the array with all rows, then you would need to change the following:
<div class="col-md-6">
<h2><?php the_sub_field('columns_carousel_slide_title'); ?></h2>
</div> <!--.col-md-6 -->
with this:
<?php
$multiplier = 1; //that could actually go before the while
if (count(get_field('columns_carousel_slide'))%2 === 1 &&
$count === count(get_field('columns_carousel_slide'))-1) {
$multiplier = 2;
} ?>
<div class="col-md-<?php echo (6*$multiplier);?>">
<h2><?php the_sub_field('columns_carousel_slide_title'); ?></h2>
</div> <!--.col-md-12 -->
A short explanation:
initially we set the multiplier to 1 (so that 6*1 = 6 for paired
columns).
we do a condition to check if number of items is even
and we are currently processing the last item. If so - we set the
multiplier to 2 (so that 6*2 = 12 for single column)
we set the class to be "col-md-" and the result of our calculation (either 6 or 12)

Tile Thumbnails in a Grid

I want to tile thumbnails into 3 columns using bootstrap's grid classes like this (this entry only has 3 images):
The 4th image would go to the next row <div class="row"></div> within a <div class="col-sm-4"></div>, 5th and 6th images in the same row but separate <div class="col-sm-4"></div>. Then the 7th image goes to the 3rd row etc...
The details of the images including urls are taken from DB using php.
<div class="panel-body">
<?php foreach($screenshots as $key=>$screenshot): ?>
<div class="col-sm-4">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title"><?=$screenshot["ss_name"]?></h3>
</div>
<div class="panel-body">
<img class="img-rounded" style="display: block; text-align:center;" src="<?=UPLOADS_FOLDER.$screenshot['ss_url']?>" alt="<?=$screenshot['ss_name']?>">
<p><?=$screenshot["ss_description"]?></p>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
I've managed to figure out the algorithim:
<?php
$total_entries = count($screenshots);
$total_cols = 3;
$total_rows = ceil($total_entries / $total_cols);
for($col = 0; $col < $total_cols; ++$col):
?>
<div class="row" style="margin: 1% 0.5%;">
<?php for($row = 0; $row < $total_rows; ++$row): ?>
<div class="col-sm-4">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Panel Title</h3>
</div>
<div class="panel-body">
row: <?=$row?> | col: <?=$col?>
</div>
</div>
</div>
<?php endfor; ?>
</div>
<?php endfor; ?>
But I'm stuck trying to figure out how to find of the index of the screenshot to show.
You have row and column mixed up in the output above.
Once that is fixed, if you need an integer index you should be able to calculate it from the row and column values. For a zero based array of images, something like (row*3)+column
That said, in Bootstrap, you should not need to create the individual rows in the way you have. If you put all the col-sm-3 divs one after the other, without breaking out new rows, this will sort itself out anyway.
Doing it this way, you can use col-Xxx to specify different numbers of columns for different screen widths without changing your code.
The trick is to send every new data in a single column define by <div class="column-sm-4"> and in order to have three items in a row, calling <div class="row"> after every three items. Which can be done by using a counter initialized at 0 and then incrementing its value by 1 every time and inserting new row when dividing it by 3 results an integer:
$count = 0;
if (is_int($count/3)){
echo '<div class="row">';
}
We need to insert a div before first element so we check if its the starting:
if ($count==0 OR is_int($count/3)){
echo '<div class="row">';
}
Then in order to close the div we need to check again if the division results an integer:
if(is_int($count/3)){
echo '</div>' ;
}
We also need to close the div after the last element so we check whether the element is last one or not by:
if($count==$total_entries OR is_int($count/3)){
echo '</div>' ;
}
The full code is:
<?php
$count = 0;
$total_entries = count($screenshots);
foreach($screenshots as $key=>$screenshot):
if ($count==0 OR is_int($count/3)){
echo '<div class="row">';
}
?>
<div class="column-sm-4">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title"><?=$screenshot["ss_name"]?></h3>
</div>
<div class="panel-body">
<img class="img-rounded" style="display: block; text-
align:center;" src="<?=UPLOADS_FOLDER.$screenshot['ss_url']?>"
alt="<?=$screenshot['ss_name']?>">
<p><?=$screenshot["ss_description"]?></p>
</div>
</div>
</div>
<?php
$count++;
if($count==$total_entries OR is_int($count/3)){
echo '</div>' ;
}
endforeach;
?>

Any idea how to make this loop

I need to create a loop like this via PHP ( a WordPress WP_Query ) ,
it's like one large item then two small items and then two small items again , and then , one large item and so on...
Here's the plain HTML :
<div class="swiper-slide big">
<div class="item"></div>
</div>
<div class="swiper-slide normal">
<div class="item"></div>
<div class="item"></div>
</div>
<div class="swiper-slide normal">
<div class="item"></div>
<div class="item"></div>
</div>
<div class="swiper-slide big">
<div class="item"></div>
</div>
<div class="swiper-slide normal">
<div class="item"></div>
<div class="item"></div>
</div>
<div class="swiper-slide normal">
<div class="item"></div>
<div class="item"></div>
</div>
<div class="swiper-slide big">
<div class="item"></div>
</div>
The <div.item>s are post elements.
I couldn't find any pattern to make such loop , one option is to use a counter and compare each number , but it's not a stable way to do this.
could anyone help please.
f.n : the .swiper-slide DIVs are just wrappers , every time the loop runs , we only have a <div.item> and we need to group these .items with the pattern i explained above.
Instead of using a "counter", you could use a modulo equation.
In your example, there are 3 rules we can apply:
One big, then four small
The first and third small start a new parent div
The second and fourth small end the parent div
Assuming that your posts are in an array, with numbered keys, you can use this:
foreach ($itemArray as $key => $item) {
$modulo = ($key+1)%5;
switch($modulo) {
case 1:
echo <<<END
<div class="swiper-slide big">
<div class="item"></div>
</div>
END;
break;
case 2:
case 4:
echo <<<END
<div class="swiper-slide normal">
<div class="item"></div>
END;
break;
case 3:
case 0:
echo <<<END
<div class="item"></div>
</div>
END;
break;
}
}
//Closing possible open div
$arrCountModulo=count($itemArray)%5;
if($arrCountModulo==2||$arrCountModulo==4) {
echo "</div>";
}
Here is an updated example where you pass the items as an array :
<?php
function myloop($items) {
$i = 0;
$isBig = array( true, false, false);
while(sizeof($items) > 0) {
if($isBig[$i]) {
echo "Big ! ", array_shift($items);
echo " \n";
} else {
echo "Normal... ", array_shift($items), " ", array_shift($items);
echo " \n";
}
$i = ( $i + 1) % sizeof($isBig);
}
}
myloop(array("item1","item2","item3","item4","item5","item6","item7","item8","item9","item10"));
?>
Cli output :
$ php loop.php
Big ! item1
Normal... item2 item3
Normal... item4 item5
Big ! item6
Normal... item7 item8
Normal... item9 item10
Use a counter variable(for this example, $i) to track how many div sections are printed. Your code should be like this:
Edited:
Based on your requirement, I've updated my answer.
$itemArray = array("a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q");
$arrLength = count($itemArray);
for($i = 0; $i < $arrLength; ++$i){
if($i % 3 == 0){
if($i != 0){
?>
</div>
<?php
}
?>
<div class="swiper-slide big">
<div class="item"><?php echo $itemArray[$i]; ?></div>
</div>
<div class="swiper-slide normal">
<?php
}else{
?>
<div class="item"><?php echo $itemArray[$i]; ?></div>
<?php
}
}
Try this:
$bignormal = 0;
for ($i=0;$i<$whatever;$i++) {
if ($bignormal>0) {
echo "normal";
}
else {
echo "big";
}
$bignormal++;
if ($bignormal>2) {
$bignormal = 0;
}
}
Not the greatest piece of code in the world, but it works.
Explaination:
$bignormal is (by default) set to 0. $bignormal is thus not greater than 0, so we echo big and increment $bignormal.
Next iteration, $bignormal is 1, thus being greater than 0 so we echo normal and increment $bignormal.
Next iteration, $bignormal is 2, and thus is greater than 0 so we echo normal. However, when we increment $bignormal it is now greater than 2 (with a value of 3), so the final if statement sets $bignormal to 0.
Next iteration $bignormal is set to 0, so thus it's not greater than 0, so thus we echo big and increment $bignormal yet again.
Etc.
In conclusion...
Nothing that complicated, and could probably be compacted down heaps with some better logic :D

How can I display a two column array in PHP/HTML?

I have an array of icons. Right now, we only display them in buckets of 4. So if you have 7 icons, the 4th icon on the first slide will repeat as the 8th on the second slide. That's because the 3rd index of the array is stored in that same spot. To fix this, I want to loop through the array instead of explicitly iterating icon by icon.
<?php if (!empty($icons)) { // if we have icons attributes
// NOTE: we've changed it to show as many sets as we can
// (sets of 4)
$number_of_sets = ceil(count($icons) / 4);
$k=0; // for inner iteration
for ($j=0;$j < $number_of_sets; $j++) {
$slide_total ++;
?>
<div class="cf slide icon-slide">
<?php
// up to 4 icons
if (is_array($icons) && !empty($icons[$k])) {
$icon1 = $icons[$k];
$k++;
}
?>
<div class="col left">
<div class="cf icon">
<div class="col thumb">
<img src="<?=$icon1['thumb']?>" alt="<?=htmlentities($icon1['post_title'])?>" />
</div>
<div class="colR details">
<h4><?=$icon1['post_title']?></h4>
<p><?=$icon1['post_content']?></p>
</div>
</div>
<?php
// up to 4 icons
if (is_array($icons) && !empty($icons[$k])) {
$icon2 = $icons[$k];
$k++;
}
?>
<div class="cf icon">
<div class="col thumb">
<img src="<?=$icon2['thumb']?>" alt="<?=htmlentities($icon2['post_title'])?>" />
</div>
<div class="colR details">
<h4><?=$icon2['post_title']?></h4>
<p><?=$icon2['post_content']?></p>
</div>
</div>
</div>
<?php
// up to 4 icons
if (is_array($icons) && !empty($icons[$k])) {
$icon3 = $icons[$k];
$k++;
}
?>
<div class="colR right">
<div class="cf icon">
<div class="col thumb">
<img src="<?=$icon3['thumb']?>" alt="<?=htmlentities($icon3['post_title'])?>" />
</div>
<div class="colR details">
<h4><?=$icon3['post_title']?></h4>
<p><?=$icon3['post_content']?></p>
</div>
</div>
<?php
// up to 4 icons
if (is_array($icons) && !empty($icons[$k])) {
$icon4 = $icons[$k];
$k++;
}
?>
<div class="cf icon">
<div class="col thumb">
<img src="<?=$icon4['thumb']?>" alt="<?=htmlentities($icon4['post_title'])?>" />
</div>
<div class="colR details">
<h4><?=$icon4['post_title']?></h4>
<p><?=$icon4['post_content']?></p>
</div>
</div>
</div>
</div> <!-- // end icon slide -->
<?php
} // end for $j (number of sets of 4 icons
?>
My proposed solution:
<?php if (!empty($icons)) {
$num_cols = 2;
$i = 0;
$slide_items = 4;
?>
<div class="cf slide icon-slide">
<?php foreach ( $icons as $icon ) {
echo $i++%$num_cols==0 ? '</div><div class="col" style="width: 250px;">' : '';
?>
<div class="cf icon">
<div class="col thumb">
<img src="<?=$icon['thumb']?>" alt="<?=htmlentities($icon['post_title'])?>" />
</div>
<div class="colR details">
<h4><?=$icon['post_title']?></h4>
<p><?=$icon['post_content']?></p>
</div>
</div>
<?php } } // end if we have icons attributes ?>
I'm having trouble figuring out how to make another slide after I hit 4 icons. Adding the following line before the end of the foreach loop hasn't worked.
echo $i++%$slide_items==0 ? '</div><div class="cf slide icon-slide">' : '';
Here's some logic for the loop:
$i = count( $icons );
if ( $i % 4 != 0 )
$i = $i + ( $i % 4 );
for( $n = 0; $n < $i; $n++ )
{
if ( $n % 4 == 0 )
{
// code to open a row here
}
// code to open a column here
// echo your icon here, use isset() or !empty().
// code to close a column here
if ( $n > 0 && $n % 3 == 0 )
{
// code to close a row here
}
}
The IFs at the top is for keeping the column number consistent. Otherwise there'll be some weirdness at the end of the loop.

Categories