I have this code that is getting all categories and subcategories from a woocommerce based website in a hierarchical order, and it works but each and every time when I want to add a new level of depth
category
->sub-cat
->sub-sub-cat
->sub-sub-sub-cat
->etc
I have to add one more foreach loop inside the last loop (I'm not even sure if you can understand me to cause honestly I don't even know how to explain this - sorry, please be patient, it is my second month in PHPs 'fields')
So, I'm trying to get rid of all those foreach loops (to make it more dynamic) and I heard that there is a way to do that by using something called recursion, the kind of stuff that I can use to make this happen. I don't really understand how that works and I've read like the whole day about it so if there is somebody who can help me understand how to get this done I'll be happy?
Here is my code, part of it is taken from StackOverflow.
<ul>
<?php
$taxonomy = 'product_cat';
$orderby = 'name';
$hierarchical = 1;
$args = array(
'taxonomy' => $taxonomy,
'orderby' => $orderby,
'hierarchical' => $hierarchical
);
$cat = get_categories( $args );
foreach ($cat as $c) {
if($c->category_parent == 0) {
$catID = $c->term_id;
echo '<li>'. $c->name .'';
$args = array(
'taxonomy' => $taxonomy,
'parent' => $catID,
'orderby' => $orderby,
'hierarchical' => $hierarchical
);
$cat = get_categories( $args );
if($cat) {
echo '<ul>';
foreach($cat as $c) {
$catID = $c->term_id;
echo '<li>'. $c->name , apply_filters( 'woocommerce_subcategory_count_html', ' (' . $c->count . ')', $category ) .'';
$args = array(
'taxonomy' => $taxonomy,
'parent' => $catID,
'orderby' => $orderby,
'hierarchical' => $hierarchical
);
$cat = get_categories( $args );
if($cat){
echo '<ul>';
foreach ($cat as $c) {
$catID = $c->term_id;
echo '<li>'. $c->name , apply_filters( 'woocommerce_subcategory_count_html', ' (' . $c->count . ')', $category ) .'';
$args = array(
'taxonomy' => $taxonomy,
'parent' => $catID,
'orderby' => $orderby,
'hierarchical' => $hierarchical
);
$cat = get_categories( $args );
if($cat){
echo '<ul>';
foreach ($cat as $c) {
$catID = $c->term_id;
echo '<li>'. $c->name , apply_filters( 'woocommerce_subcategory_count_html', ' (' . $c->count . ')', $category ) .'</li>';
}
echo '</ul>';
}
echo '</li>';
}
echo '</ul>';
}
echo '</li>';
}
echo '</ul>';
}
echo '</li>';
}
}
?>
It works perfectly, though I'm aware that it is not too smart, so please don't be too harsh on me.
Here is an example of recursion:
You create a function that looks at the item of the array if it's an array you call the function again and pass the item to it (the subarray).
If it's not an array you echo it.
$arr = [1,2,3,[4,5,[6]]];
print_items($arr);
Function print_items($arr){
Foreach($arr as $item){
If(is_array($item)){
print_items($item);
}Else{
Echo $item;
}
}
}
https://3v4l.org/vbjaO
A WordPress-specific printing function might look like this:
<?php
function display_child_categories_of($category_id) {
$subcategories = get_categories(array('child_of' => category_id));
foreach ($subcategories as $subcategory) {
echo '<li>';
echo $subcategory->name;
echo '<ul class="children">';
display_child_categories_of($subcategory);
echo '</ul>';
echo '</li>';
}
}
?>
<ul>
<?php
display_child_categories_of(0); // gets all top-level categories
?>
</ul>
This is a barebones version; you'll want to add in the additional parameters you're using to the get_categories() arguments and then tweak the HTML as needed.
However, as #jaswrks mentioned in the comments, you should use the built-in walk_category_tree() WordPress function instead if it works for your situation.
Related
I am getting certain items using the get_term() function and displaying the result as a list.
It works perfectly as intended, but how do I sort the results alphabetically?
Code is as follows:
<?php
$terms = get_terms(array(
'taxonomy' => 'behandlungsfeld',
'parent' => 0,
'post_status' => 'publish',
));
foreach ($terms as $term) {
$the_term_children = get_terms(array(
taxonomy' => 'behandlungsfeld',
'child_of' => $term->term_id,
));
echo '<li data-dropdown-text="' . $term->name . '">';
echo '<ul>';
foreach ($the_term_children as $term_child) {
$term_child_fetched = get_term($term_child, 'behandlungsfeld');
echo '<li>' . $term_child_fetched->name . '</li>';
}
echo '</ul>';
}
?>
You can use the orderby arg like so:
get_terms(['orderby' => 'name']);
I have the below code, it works initially with regards to the fact that it displays the products for all taxonomies. However if a product is set to 2 taxonomies then it will display twice on the page as opposed to showing the first instance of the product.
<?php if ( $terms && !is_wp_error( $terms ) ) {
foreach ( $terms as $term ) {
$args = array(
'post_type' => 'products',
'posts_per_page' => -1,
'orderby' => 'menu_order',
'tax_query' => array(
array(
'taxonomy' => 'product_cat',
'field' => 'slug',
'terms' => $term->slug,
),
),
'order' => 'asc',
);
runQuery($args);
}
} ?>
Here is the runQuery function:
<?php $x = 0;
function runQuery($args) {
global $x;
$query = new WP_Query( $args );
if ($query->have_posts()) : while ($query->have_posts()) : $query->the_post();
$cat_terms = get_the_terms($post->id, 'product_cat');
$datagroups = '';
foreach ($cat_terms as $key => $cat) {
if (count($cat_terms) == ($key + 1)) {
$datagroups .= '"' . $cat->name . '"';
} else {
$datagroups .= '"' . $cat->name . '", ';
}
}
?>
HTML Here that is displayed;
<?php $x ++;
endwhile;
endif;
wp_reset_postdata();
}?>
I have figured this out, I essentially just had to check the current post in the loop to see if it had already been displayed:
<?php $x = 0;
$displayed = [];
function runQuery($args) {
global $displayed;
global $x;
$query = new WP_Query( $args );
if ($query->have_posts()) : while ($query->have_posts()) : $query->the_post();
$cat_terms = get_the_terms($post->id, 'product_cat');
$datagroups = '';
if ( in_array( get_the_ID(), $displayed ) ){
continue;
}
// update array with currently displayed post ID
$displayed[] = get_the_ID();
foreach ($cat_terms as $key => $cat) {
if (count($cat_terms) == ($key + 1)) {
$datagroups .= '"' . $cat->name . '"';
} else {
$datagroups .= '"' . $cat->name . '", ';
}
}
?>
Source that may help others: https://wordpress.stackexchange.com/questions/285091/avoid-duplicate-post-from-same-taxonomy
I am currently using the following code to fetch WooCommerce product categories:
<?php
$orderby = 'name';
$order = 'asc';
$hide_empty = false ;
$cat_args = array(
'orderby' => $orderby,
'order' => $order,
'hide_empty' => $hide_empty,
);
$product_categories = get_terms( 'product_cat', $cat_args );
if( !empty($product_categories) ){
echo '<ul>';
foreach ($product_categories as $key => $category) {
echo '<li>';
echo '<a href="'.get_term_link($category).'" >';
echo $category->name;
echo '</a>';
echo '<li>';
}
echo '</ul>';
}
?>
This currently displays all categories, however I wish this to only show the child categories.
For example, if you are on the Category 1 page, it should show all children within that category only.
I've looked at many examples on here but have been unable to find something that works for what I need.
You can add this to woocommerce/archive-product.php file
$queried_object = get_queried_object();
$parent = $queried_object->term_id;
$categories = get_term_children( $parent, 'product_cat' );
if ( $categories && ! is_wp_error( $category ) ) :
echo '<ul>';
foreach($categories as $category) :
$term = get_term( $category, 'product_cat' );
echo '<li>';
echo '<a href="'.get_term_link($term).'" >';
echo $term->name;
echo '</a>';
echo '</li>';
endforeach;
echo '</ul>';
endif;
This will only work on your archives. And categories with children.
It will also output grand child categories.
Hope this helps.
Here is a way to get only product subcategories linked list ordered by name ASC:
// The product category taxonomy
$taxonomy = 'product_cat';
// Get the parent categories IDs
$parent_cat_ids = get_terms( $taxonomy, array(
'parent' => 0,
'hide_empty' => false,
'fields' => 'ids'
) );
// Get only "child" WP_Term Product categories
$subcategories = get_terms( $taxonomy, array(
'exclude' => $parent_cat_ids,
'orderby' => 'name',
'order' => 'asc',
'hide_empty' => false,
) );
if( ! empty( $subcategories ) ){
echo '<ul>';
foreach ($subcategories as $subcategory) {
echo '<li>
<a href="'. get_term_link($subcategory) .'" >' . $subcategory->name.'</a>
</li>';
}
echo '</ul>';
}
The code is tested and works
Add Below Code in theme's functions.php file.
add_filter('get_child_category', 'get_child_cat', 10, 1);
function get_child_cat($term_id = 0){
global $wpdb;
$result = array('status' => 'fail');
if($term_id != 0){
$result = $wpdb->get_results( "select ".$wpdb->prefix."terms.term_id, ".$wpdb->prefix."terms.name, ".$wpdb->prefix."terms.slug, ".$wpdb->prefix."terms.term_group, ".$wpdb->prefix."term_taxonomy.term_taxonomy_id, ".$wpdb->prefix."term_taxonomy.taxonomy, ".$wpdb->prefix."term_taxonomy.description, ".$wpdb->prefix."term_taxonomy.parent, ".$wpdb->prefix."term_taxonomy.count from ".$wpdb->prefix."terms left join ".$wpdb->prefix."term_taxonomy on ".$wpdb->prefix."term_taxonomy.term_id = ".$wpdb->prefix."terms.term_id where ".$wpdb->prefix."term_taxonomy.parent = $term_id" );
}
return $result;
}
Use below code for get array output for child category.
$cat = apply_filters( 'get_child_category', $term_id ); // where $term_id is your parent category id.
NOTE: This code is only used for single level term.
change code as per your requirement.
I'd like to display my categories 2 at the time - 2 in each row and have as many rows as needed.
I've got so far with this code (which displays all the categories):
<?php
$i = 1;
$args = array(
'orderby' => 'name',
'parent' => 0,
'hide_empty' => 0
);
$categories = get_categories( $args );
echo count( $categories );
echo "<div class='a'>\n";
foreach ( $categories as $category ) {
echo "<div class='b'>\n";
echo "<div class='c'><a href='" . get_category_link( $category->term_id ) . "'>" . $category->name . "</a></div>\n";
echo "</div>\n";
}
echo "</div>\n";
?>
I know it can be done using list, but this is something I want to avoid.
So this is it (took a while) and it works just fine ;-)
$args = array(
'orderby' => 'name',
'parent' => 0,
'hide_empty' => 1
);
$categories = get_categories( $args );
$list_cats = array();
foreach ( $categories as $category ) {
array_push($list_cats, $category->name);
}
$total_cats=count($categories);
echo "<div class='a'>";
for ($a=0; $a<$total_cats;) {
for ($j=0; $j<2; $j++) {
echo " <div class='b'><div class='c'>" . $list_cats[$a] . "</div></div>";
$a++;
}
echo "</div>";
if($a<$total_cats){
echo "<div class='a'>";
}
}
I am working on a Wordpress-Design and i want to creat a Custom Menu.
$items = wp_get_nav_menu_items('Menu', array(
'order' => 'ASC',
'orderby' => 'menu_order',
'post_type' => 'nav_menu_item',
'post_status' => 'publish',
'output' => ARRAY_A,
'output_key' => 'menu_order',
'nopaging' => true,
'update_post_term_cache' => false));
echo '<pre>'; print_r($items); echo '</pre>';
foreach($items as $item){
echo '<div class="menu_entry">'.$item->title.'</div>';
}
The problem is, i need the "current-page"-Class, which is WordPress creating - in the Standard Menu.
Any Ideas how to add this class?
Solution time:
WordPress's function that adds these classes is _wp_menu_item_classes_by_context(). This is called already when you use wp_nav_menu but not wp_get_nav_menu_items. Fortunately, the latter provides a filter so we can do it ourselves:
add_filter( 'wp_get_nav_menu_items', 'prefix_nav_menu_classes', 10, 3 );
function prefix_nav_menu_classes($items, $menu, $args) {
_wp_menu_item_classes_by_context($items);
return $items;
}
You can do a compare on the current page / cat etc ID against the menu items object_id which is the ID of the page / category etc its linked to.
Something like (untested);
global $post;
$thePostID = $post->ID;
foreach($items as $item){
if($thePostID === $item->object_id) {
echo '<div class="menu_entry">'.$item->title.'</div>';
}else{
echo '<div class="menu_entry">'.$item->title.'</div>';
}
}
Using the function get_queried_object_id(). This works fine in all the pages, including the Blog page.
See an example:
if ( $menu_items = wp_get_nav_menu_items( 'menu' ) ) {
foreach ( $menu_items as $menu_item ) {
$current = ( $menu_item->object_id == get_queried_object_id() ) ? 'current' : '';
echo '<li class="' . $current . '">' . $menu_item->title . '</li>';
}
}