Why is this php code working this way - php

I realize this is a wordpress plugin but I'm hoping someone can help me out with the overall php structure of my code.
<?php
add_filter( 'wprss_auto_create_terms', 'filter_categories', 10, 3 );
/**
* Filter categories to only allow "Plugin Reviews".
* Change "Plugin Reviews" into "Plugins" if found.
*/
function filter_categories( $categories, $taxonomy, $source_id ) {
// Check for feed source 3878
if ( $source_id == '3878' ) {
// Loop all categories
foreach( $categories as $i => $category ) {
// If "Plugin Reviews" is found, change it to "Plugins"
if ( $category['name'] == 'Plugin Reviews' )
$categories[$i]['name'] = 'Plugins';
// If not "Plugin Reviews", remove it
else unset( $categories[$i] );
}
}
// Return the categories
return $categories;
}
I'm able to change any feed category names with the name "Plugin Reviews" to "Plugins" because all current posts in the feed currently contain the category "Plugin Reviews". If I try a category that all the posts do not have they are all call Uncategorized regardless if some have the category name or not. What am I missing and any ideas how this could be fixed?

Related

Wordpress custom taxonomy subcategory template

I'm trying to create specific templates based on custom taxonomy subcategory slugs.
Custom taxonomy is registered with CPT UI plugin.
An example explaining the problem:
Registered custom taxonomy: Fruits
Fruits has a category: Apples
Apples has a subcategories with slugs: red-apple, green-apple, yellow-apple
My goal is to use specific template for each subcategory: red-apple, green-apple and yellow-apple
What I have tried so far:
I created a files such as taxonomy-fruits-red-apple.php, taxonomy-apples-red-apple.php etc - none worked.
Used the following code:
function wpd_subcategory_template( $template ) {
$cat = get_queried_object();
if ( isset( $cat ) && $cat->category_parent ) {
$template = locate_template( 'my-template.php' );
}
return $template;
}
add_filter( 'category_template', 'wpd_subcategory_template' );
None of these methods worked.
Any ideas?
Thx in advance!

woocommerce widget "filter products by attribute" displays unavailable products

I am using on of the Widgets included with WooCommerce called Filter products by attribute. I created a widgetized area on the category page in my Storefront-child-theme functions.php (see code below).
But when I filter by attribue size M for example, it lists products where size M is out of stock...
Any idea how to fix this?
EXAMPLE: Filtering by size M displays this product, which is not available:
/* FILTER BY SIZE WIDGET */
// Adding the widget area.
if (function_exists('register_sidebar')) {
register_sidebar(array(
'name' => 'Below category title',
'id' => 'extra-widget-area',
'description' => 'Below category title',
'before_widget' => '<div class="widget below-cat-title-widget">',
'after_widget' => '</div>',
'before_title' => '<h6 class="below-cat-title-widget-title">',
'after_title' => '</h6>'
));
}
// placing the widget
add_action( 'woocommerce_archive_description', 'add_my_widget_area', 31 );
function add_my_widget_area() {
if (function_exists('dynamic_sidebar')) {
dynamic_sidebar('Below category title');
}
}
add_action( 'woocommerce_archive_description', 'filter_button_function', 21 );
function filter_button_function() {
// if ( is_product_category() ) {
// global $wp_query;
// $cat_id = $wp_query->get_queried_object_id();
// $cat_desc = term_description( $cat_id, 'product_cat' );
if (get_locale() == 'fr_FR') {
$filter_html = '<div class="filter_by_size" class="subtitle">'.'FILTRE PAR TAILLE'.' <span class="filter-icon"></span>'.'</div>';
} else {
$filter_html = '<div class="filter_by_size" class="subtitle">'.'FILTER BY SIZE'.' <span class="filter-icon"></span>'.'</div>';
}
echo $filter_html;
// }
}
I'll be direct : by default, you can't. It's not related to a certain theme or plugin, but to Woocommerce itself. This is a problem that exists in woocommerce for a very long time. It's not possible by default in woocommerce to handle variable products stock visibility (stock status) based on it's variations visibility as it's something necessary and needed by Woocommerce1.
A way of doing this is to add this action function :
add_action('woocommerce_before_shop_loop_item', 'out_of_stock_variations_loop');
function out_of_stock_variations_loop()
{
global $product;
$filter = 'size';
if ($product->product_type === 'variable') {
$available = $product->get_available_variations();
if ($available) {
foreach ($available as $instockvar) {
if (isset($instockvar[ 'attributes' ][ 'attribute_pa_' . $filter ])) {
if ($_GET[ 'filter_' . $filter ]) {
if ( !in_array( $instockvar[ 'attributes' ][ 'attribute_pa_' . $filter ], explode(',', $_GET[ 'filter_' . $filter ]) , true ) || ($instockvar[ 'max_qty' ] <= 0) ) {
echo "<style>.post-" . $product->get_id() . " {display: none}</style>";
} else {
echo "<style>.post-" . $product->get_id() . " {display: list-item !important}</style>";
}
}
}
}
}
}
}
?>
Which will only show the list of products in stock. The problem with this is that it will leave blank spaces where the not-in-stock products are deleted by this query, and it can be difficult to circumvent this problem only with css because, on each row, first and last products have first and last classes that determines their layout in CSS. To overcome this, add this jQuery to your child-theme js script:
function openLayeredNavFilterIfSelected() {
if (jQuery('.wc-layered-nav-term').hasClass('woocommerce-widget-layered-nav-list__item--chosen')) {
/*keep layered nav filter open, if at least an attribute is selected*/
jQuery('.woocommerce-widget-layered-nav-list__item--chosen').parent().parent().show();
if (!jQuery('.filter-icon').hasClass('arrow_up')) {
jQuery('.filter-icon').addClass('arrow_up');
}
/*redistribute products rows to avoid missing spaces*/
jQuery('.site-main ul.products.columns-3 li.product').removeClass('first').removeClass('last');
jQuery('.site-main ul.products.columns-3 li.product:visible').each(function(i) {
if (i % 3 == 0) jQuery(this).addClass('first'); //add class first to firsts products of rows
if (i % 3 == 2) jQuery(this).addClass('last'); //add class last to lasts products of rows
});
}
}
openLayeredNavFilterIfSelected();
jQuery(window).resize(openLayeredNavFilterIfSelected);
And you should be good to go.
1 WOOF Products Filter plugin and out of stock variations issue in Woocommerce
Related :
- filter products by attribute and hide out of stock items of variable products
- How to prevent `out of stock` items to show as a filter option in the sidebar (layered nav widget ) in WooCommerce?
- https://wordpress.org/support/topic/hide-out-of-stock-variations-when-filtering/#post-7718128
- https://xtemos.com/forums/topic/filter-attributes-dont-show-out-of-stock-variations/
- https://wordpress.org/support/topic/exclude-out-of-stock-products-from-filter/
Not a really good answer here.
As #Islam Elshobokshy said, it's a bit complex to edit the product loop query to remove out of stock variations.
Still, you can trick using CSS to "hide" out of stock variations from the list. But it may "break" your product list/grid: if there is enough product for 2 pages (10 items/page), but one product is hidden on page 1 by this filter, page 1 will only have 9 products. We are after the product query.
I quickly tested and wrote something inspired from this:
this works for a size attribute (it could be adapted to handle all attributes from Woocommerce, or URL parameters "filter_*", or hardcoded for every attribute used)
Code:
add_action('woocommerce_before_shop_loop_item', 'out_of_stock_variations_loop');
function out_of_stock_variations_loop()
{
global $product;
$filter = 'size'; //to edit
if ($product->product_type === 'variable') {
$available = $product->get_available_variations();
if ($available) {
foreach ($available as $instockvar) {
if (isset($instockvar[ 'attributes' ][ 'attribute_pa_' . $filter ])) {
if (($instockvar[ 'attributes' ][ 'attribute_pa_' . $filter ] === $_GET[ 'filter_' . $filter ]) && ($instockvar[ 'max_qty' ] <= 0)) {
//sadly echo some inline CSS :(
echo "<style>.post-" . $product->get_id() . " {display: none}</style>";
//some tests using is_visible() filters, but we are too late here
//add_filter( 'woocommerce_product_is_visible', function($visible, $id) use ($the_id) {return $id === $the_id ? false : $visible;}, 50, 2);
}
}
}
}
}
}
To achieve the same without inline CSS, you can:
override /woocommerce/template/content-product.php to your-theme/woocommerce/content-product.php
move the code below in a method($product), edit it a bit to return true/false only for required products
Call it in the "Ensure visibility" block like if ( empty( $product ) || ! $product->is_visible() || !$method($product) )
this will prevent outputting the product, but will not resolve the grid/list issue.
In the end, updating the loop query to match your need may be complex, and negatively impact performance. But, to explore more, I would start something like:
add a query_var by detecting GET filter parameter (or directly detecting wp_query parameter matching the filter/attribute you know that will filter)
find and hook around the woocommerce product wp_query
hook a method that will detect your custom query_var
edit the query, but if it comes to raw SQL, it may be painful..

get_the_category_list how to remove uncategorized?

Is there a way to do this to get a category from your wordpress Permalink then get each parent category from there? I am trying to make bread crumbs...
wp_list_categories() preformats it and gives you the uncategorized section too which I would like to remove.
$category_id = wp_list_categories();
echo $category_id ;
My categories I am testing with are:
Categories
Test Prep
Test Taking Skills
Study Skills
Uncategorized
I got this working but how do you remove Uncategorized?
$categories_list = get_the_category_list( __( ', ', 'mytheme' ) );
$category_id = get_cat_ID($categories_list);
$categories = get_categories(array('parent_of' => $category_id));
foreach($categories as $category) {
echo '' . $category->name.'<br> ';
}
Here are the ways you can do it:
get_the_category_list()
You'll need to use a filter the_category_list. Add this to your functions.php. If you look at the internals of get_the_category_list() it uses this filter to build the categories before output:
add_filter( 'the_category_list', static function( $categories ){
// Loop through all the categories that are found
foreach ( $categories as $index => $category ) {
// if the category object slug equals "uncategorized"
if ( $category->slug === 'uncategorized' ) :
// remove it from the list of categories
unset($categories[$index]);
endif;
}
// return the categories
return $categories;
});
wp_list_categories()
This one is a bit easier because you can pass the exclude argument and use the slug:
wp_list_categories([
'exclude' => 'uncategorized',
]);
Do you need the "uncategorized" category?
If not, you could set another category term to be the standard category:
Wordpress Backend to "Settings" -> "Writing" -> "Default Post Category" and choose a category term.
After that, you are able to delete it from the categories and so will not appear in get_the_category_list because it does not exist anymore.

Auto assign Woocommerce Product Categories from Product Tags on import

I am trying to set automatically Woocommerce Product Categories from Product Tags when importing products using WP All Imports plugin.
For example: I am importing specific breadcrumb slugs as Product tags: Sports/Shoes/Running Shoes > Now I wanne connect that slug to an predefined Category hierarchy:
Tag: Sports/Shoes/Running Shoes => Product cat: Shoes/Running
Maybe there is an plugin where to drop Tags in predifined Product Categorys to show the selected products in category?
I found the following code that is close to what I need to achieve my goal:
function auto_add_category ($product_id = 0) {
if (!$product_id) return;
// because we use save_post action, let's check post type here
$post_type = get_post_type($post_id);
if ( "product" != $post_type ) return;
$tag_categories = array (
'ring' => 'Jewellery'
'necklace' => 'Jewellery',
'dress' => 'Clothing',
);
// get_terms returns ALL terms, so we have to add object_ids param to get terms to a specific product
$product_tags = get_terms( array( 'taxonomy' => 'product_tag', 'object_ids' => $product_id ) );
foreach ($product_tags as $term) {
if ($tag_categories[$term->slug] ) {
$cat = get_term_by( 'name', $tag_categories[$term->slug], 'product_cat' );
$cat_id = $cat->term_id;
if ($cat_id) {
$result = wp_set_post_terms( $product_id, $cat_id, 'product_cat', true );
}
}
}
}
add_action('save_post','auto_add_category');
First of all you need to understand the logic by yourself.
For me its not clear how can you filter which one of the tag collection should be organized like described.
Once you know the logic you can use the 'Function editor' field in the import settings.
At the bottom of the screen where you have the mapping of all import.
You can put your function there and use it in the tag field. For example:
you have the function :
<?php
function import_rearrange_tags( $tag, $count = 1 ) {
// Some code here
return $tag;
}
?>
So you can use it your mapping like a filter like this :
[import_rearrange_tags({tags[1]}, 3)]
I use the second parameter to show the whole idea.

How do I targert child Woocommerce Categories with pre_get_posts

I have created an online store that sales E-books and E-magazines, for books I have a "ebooks" product category with child categories and for the magazines I have a "emagazines" category with child categories like "women", "men" and "business". For the emagazines I have created a pre_get_posts function that is meant to modify the queries on the entire magazines section of my store. Here is the code I have
function magazine_filter($query) {
if ( !is_admin() && $query->is_main_query() && $query->query_vars['product_cat'] == 'emagazines' :
// My code goes here
endif ;
}
add_action('pre_get_posts','magazine_filter');
The problem is that after I target the "emagazines" category, my function only affects the eMagazines category and not the children categories like "Women", "Men", "Business" etc. The obvious solution would be to manually target all my subcategories as well but this is loooong. I want this to be dynamic so that this function always targets any children categories of the "eMagazines" category as they are created.
Please help
This is untested, but something like this should work. This first grabs the term you're looking for by its slug (for easier identification), and then grabs all terms that are a descendant of the target term. I can't remember if the original parent term is returned as a result in the query, so if it isn't, uncomment the line after the term query:
function magazine_filter($query) {
$emag = get_term_by('slug', 'emagazines', 'product_cat');
$tree = get_terms('product_cat', array(
'fields'=>'ids', //Should be 'ids' not 'id'
'hide_empty'=>false,
'child_of'=>$emag->term_id
));
//$tree[] = $emag->term_id; //Uncomment this if the emagazines term ID isn't included.
if ( !is_admin() && $query->is_main_query() && in_array($query->query_vars['product_cat'], $tree) ) :
// My code goes here
endif ;
}
add_action('pre_get_posts','magazine_filter');
EDIT: Here's the documentation on get_terms. I originally had the 'fields' argument set as 'id' instead of 'ids' which was causing problems.
I don't know what $query->query_vars['product_cat'] returns, but if it's the term slug, consider changing to:
$tree = get_terms('product_cat', array(
'fields'=>'id=>slug',
'hide_empty'=>false,
'child_of'=>$emag->term_id
));
//$tree[$emag->term_id] = $emag->slug; //Uncomment this if the emagazines term slug isn't included.

Categories