I can't quite seem to wrap my head around the logic needed to create an Alphabetical Index page. I can get a rudimentary version working but what I'm trying to do is create something like this /.
So far I have my code outputting the first letter of a set of taxonomy terms and then every time it finds a new letter it adds the letter to the index.
What I cannot figure out is A) How to equally divide the posts into columns (Bootstrap markup being wrapped in <div class="col-sm-2">
and B) If I can get that working then how to group 3 letters together at a time ie) A-C, D-F, etc.
Here is my code so far:
<?php
$args = array(
'taxonomy' => 'brands',
'hide_empty' => 0,
'orderby' => 'name',
'order' => 'ASC',
);
$category_list = get_categories($args);
$curr_letter = '';
foreach ( $category_list as $category ) {
$this_letter = strtoupper(substr($category->name,0,1));
if ($this_letter != $curr_letter) {
echo !empty( $curr_letter ) ? '</div>' : null;
echo '<div class="row">';
echo '<h3 class="col-sm-2">' . $this_letter . '</h3>';
$curr_letter = $this_letter;
}
echo '<div class="col-sm-2">' . $category->name . '</div>';
}
?>
I thought of trying to store every nth iteration of the loop into a new array for each of the columns and then emptying the arrays when it hits a new letter but I'm not entirely sure how to do that. Perhaps someone can point me in the right direction.
I can't quite seem to wrap my head around the logic needed to create an Alphabetical Index page. I can get a rudimentary version working but what I'm trying to do is create something like this https://www.carryology/brands/.
So far I have my code outputting the first letter of a set of taxonomy terms and then every time it finds a new letter it adds the letter to the index.
What I cannot figure out is A) How to equally divide the posts into columns (Bootstrap markup being wrapped in <div class="col-sm-2">
and B) If I can get that working then how to group 3 letters together at a time ie) A-C, D-F, etc.
Here is my code so far:
<?php
$args = array(
'taxonomy' => 'brands',
'hide_empty' => 0,
'orderby' => 'name',
'order' => 'ASC',
);
$category_list = get_categories($args);
$curr_letter = '';
foreach ( $category_list as $category ) {
$this_letter = strtoupper(substr($category->name,0,1));
if ($this_letter != $curr_letter) {
echo !empty( $curr_letter ) ? '</div>' : null;
echo '<div class="row">';
echo '<h3 class="col-sm-2">' . $this_letter . '</h3>';
$curr_letter = $this_letter;
}
echo '<div class="col-sm-2">' . $category->name . '</div>';
}
?>
I thought of trying to store every nth iteration of the loop into a new array for each of the columns and then emptying the arrays when it hits a new letter but I'm not entirely sure how to do that. Perhaps someone can point me in the right direction.
You can do it like so:
// Group the categories by their FIRST LETTER.
$groups = [];
foreach ( $category_list as &$term ) {
$letter = strtoupper( substr( $term->name, 0, 1 ) );
if ( ! isset( $groups[ $letter ] ) ) {
$groups[ $letter ] = [];
}
$groups[ $letter ][] = $term;
}
// Now display them, three groups in each row.
echo '<div class="row">';
$i = 0;
$letters = array_keys( $groups );
foreach ( $groups as $letter => $terms ) {
$new_row = ( $i % 3 === 0 );
if ( $i && $new_row ) {
echo '</div><div class="row">';
}
// Show either "A - C", "A & B", or just a letter.
if ( ! $i || $new_row ) {
$range = array_slice( $letters, $i, 3 );
if ( $end = $range[ count( $range ) - 1 ] ) {
$sign = ( count( $range ) > 2 ) ? ' - ' : ' & ';
$range = $letter . $sign . $end;
echo '<h2 class="col-12">' . $range . '</h2>';
} else {
echo '<h2 class="col-12">' . $letter . '</h2>';
}
}
echo '<div class="col-sm">';
if ( ! empty( $terms ) ) {
echo '<ul>';
foreach ( $terms as $term ) {
echo '<li>' . $term->name . '</li>';
}
echo '</ul>';
}
echo '</div>';
$i++;
}
echo '</div>';
The output would look like so:
So this is the proper version, based on the layout on Carryology/brands/
(as of writing).
I have commented the code (although not all parts) and it shouldn't be hard for you to make modifications to the code:
// Group the categories by their FIRST LETTER.
$letters = range( 'A', 'Z' );
$groups = [];
foreach ( $category_list as &$term ) {
$letter = strtoupper( substr( $term->name, 0, 1 ) );
if ( ! isset( $groups[ $letter ] ) ) {
$groups[ $letter ] = [];
}
$groups[ $letter ][] = $term;
}
$cols = 3; // number of letters in a range
for ( $i = 0; $i < count( $letters ); $i++ ) {
if ( $i % $cols === 0 ) {
$list = array_slice( $letters, $i, $cols );
// Start the row.
echo '<div class="row">';
// Show the heading. (letter range)
$end = $list[ count( $list ) - 1 ];
$sign = ( count( $list ) > 2 ) ? ' - ' : ' & ';
$range = $end ? $sign . $end : '';
// Show either "A - C", "A & B", or just a letter.
echo '<h3 class="col-sm-2">' . $list[0] . $range . '</h3>';
// Put all the letter terms in a single group.
$groups2 = [];
for ( $j = 0; $j < count( $list ); $j++ ) {
if ( isset( $groups[ $list[ $j ] ] ) ) {
$groups2 = array_merge( $groups2, $groups[ $list[ $j ] ] );
}
}
$cols2 = 5; // number of columns in a row, excluding the heading.
$n = count( $groups2 );
$rows = floor( $n / $cols2 );
$diff = $n - $rows * $cols2;
for ( $k = 0, $l = 0; $k < $cols2; $k++ ) {
$x = $diff ? 1 : 0;
// Start a column.
echo '<ul class="col-xs-4 col-sm-2">';
// Show the group terms.
foreach ( array_slice( $groups2, $l, $rows + $x ) as $term ) {
echo '<li>' . $term->name . '</li>';
$l++;
}
$diff = $diff ? $diff - 1 : 0;
// Column end.
echo '</ul>';
}
// Row end.
echo '</div>';
}
}
The output would look like so: