i have shop on wordpress/woocommerce. When I added function to have sold out products at the end of list I'm getting warnings:
Warning: Attempt to read property "ID" on null in .../ftp/wp/wp-includes/class-wp-query.php on line 4044
Warning: Attempt to read property "post_title" on null in .../ftp/wp/wp-includes/class-wp-query.php on line 4046
Warning: Attempt to read property "post_name" on null in .../ftp/wp/wp-includes/class-wp-query.php on line 4048
Warning: Attempt to read property "post_type" on null in .../ftp/wp/wp-includes/class-wp-query.php on line 4196
Warning: Undefined array key 0 in .../ftp/wp/wp-includes/capabilities.php on line 76
lines from 4031 to 4064:
public function is_page( $page = '' ) {
if ( ! $this->is_page ) {
return false;
}
if ( empty( $page ) ) {
return true;
}
$page_obj = $this->get_queried_object();
$page = array_map( 'strval', (array) $page );
if ( in_array( (string) $page_obj->ID, $page, true ) ) {
return true;
} elseif ( in_array( $page_obj->post_title, $page, true ) ) {
return true;
} elseif ( in_array( $page_obj->post_name, $page, true ) ) {
return true;
} else {
foreach ( $page as $pagepath ) {
if ( ! strpos( $pagepath, '/' ) ) {
continue;
}
$pagepath_obj = get_page_by_path( $pagepath );
if ( $pagepath_obj && ( $pagepath_obj->ID == $page_obj->ID ) ) {
return true;
}
}
}
return false;
}
code for sorting products:
/**
* Sorting out of stock WooCommerce products - Order product collections by stock status, in-stock products first.
*/
class iWC_Orderby_Stock_Status
{
public function __construct()
{
// Check if WooCommerce is active
if (in_array('woocommerce/woocommerce.php', apply_filters('active_plugins', get_option('active_plugins')))) {
add_filter('posts_clauses', array($this, 'order_by_stock_status'), 2000);
}
}
public function order_by_stock_status($posts_clauses)
{
global $wpdb;
// only change query on WooCommerce loops
if (is_woocommerce() && (is_shop() || is_product_category() || is_product_tag())) {
$posts_clauses['join'] .= " INNER JOIN $wpdb->postmeta istockstatus ON ($wpdb->posts.ID = istockstatus.post_id) ";
$posts_clauses['orderby'] = " istockstatus.meta_value ASC, " . $posts_clauses['orderby'];
$posts_clauses['where'] = " AND istockstatus.meta_key = '_stock_status' AND istockstatus.meta_value <> '' " . $posts_clauses['where'];
}
return $posts_clauses;
}
}
new iWC_Orderby_Stock_Status;
/**
* END - Order product collections by stock status, instock products first.
*/
when i removed those code i don't get warnings.
Any ideas how to remove those warnings?
my shop: customoweplugi.pl
If you do not know what impact the changes you have made may have on the code and you don't understand it at least to the point that you are confident that those changes won't mess anything, then don't make those changes in the first place!
I do not recommend it, but if you just don't want to see this warnings then you can use an "Error Control Operator". Just add "#" before your expression like:
#$page_obj->ID
IT WONT SOLVE THE ERRORS, but will only prevent them from displaying!
Read more about Error Control Operators here:
https://www.php.net/manual/en/language.operators.errorcontrol.php
Related
I would like to add a quanity eg 2 which users are allowed to buy within its timeframe. Also the error notice message is showing on the product page. How would I go about letting the product added to cart and the error message showing on the cart page?
Based on Allow customers to buy only one product from defined WooCommerce product category answer code, here is my code attempt:
// Based partially on wc_customer_bought_product(), will return a boolean value based on orders count (false for O orders and true when there is at least one paid order)
function has_bought( $value = 0 ) {
if ( ! is_user_logged_in() && $value === 0 ) {
return false;
}
global $wpdb;
// Based on user ID (registered users)
if ( is_numeric( $value ) ) {
$meta_key = '_customer_user';
$meta_value = $value == 0 ? (int) get_current_user_id() : (int) $value;
}
// Based on billing email (Guest users)
else {
$meta_key = '_billing_email';
$meta_value = sanitize_email( $value );
}
$paid_order_statuses = array_map( 'esc_sql', wc_get_is_paid_statuses() );
$count = $wpdb->get_var( $wpdb->prepare("
SELECT COUNT(p.ID) FROM {$wpdb->prefix}posts AS p
INNER JOIN {$wpdb->prefix}postmeta AS pm ON p.ID = pm.post_id
WHERE p.post_status IN ( 'wc-" . implode( "','wc-", $paid_order_statuses ) . "' )
AND p.post_type LIKE 'shop_order'
AND pm.meta_key = '%s'
AND pm.meta_value = %s
AND UNIX_TIMESTAMP(p.post_date) >= (UNIX_TIMESTAMP(NOW()) - (86400 * 1))
LIMIT 1
", $meta_key, $meta_value ) );
// Return a boolean value based on orders count
return $count > 0 ? true : false;
}
function filter_woocommerce_add_to_cart_validation( $passed, $product_id, $quantity, $variation_id = null, $variations = null ) {
// Set categories
$categories = array ( 'kitchen' );
// If passed & has category
if ( $passed && has_term( $categories, 'product_cat', $product_id ) ) {
// Initialize
$value = '';
// User logged in
if ( is_user_logged_in() ) {
// Get the current user's ID
$value = get_current_user_id();
} else {
// Get billing_email
$value = WC()->customer->get_billing_email();
// When empty
if ( empty ( $value ) ) {
// Get account email
$value = WC()->customer->get_email();
}
}
// NOT empty
if ( ! empty ( $value ) ) {
if ( has_bought( $value ) ) {
// Display an error message
wc_add_notice( __( 'My custom error message', 'woocommerce' ), 'error' );
// False
$passed = false;
}
}
}
return $passed;
}
add_filter( 'woocommerce_add_to_cart_validation', 'filter_woocommerce_add_to_cart_validation', 10, 5 );
for various reasons a webshop needs to allow guest orders, but then saves the data to an existing user if the email is already registered. However, a product (parent variable product) should not be bought twice.
In that case, the customer should be returned to the checkout page and get a notice that this product is already bought.
So far i have the following code (not complete):
add_action('woocommerce_checkout_process', function () {
if (!is_user_logged_in() && $_POST['billing_email'] && $_POST['createaccount'] == 1) {
$user = get_user_by('email', $_POST['billing_email']);
$order = wc_get_order( $order_id );
$items = $order->get_items();
foreach ( $items as $item ) {
$product_id = $item->get_product_id();
if (!empty($user)){
if (wc_customer_bought_product($user->user_email,$user->ID,$product_id))
return false;
wp_set_current_user($user->ID);
}
if (!empty($user)) {
if (wc_customer_bought_product($user->user_email,$user->ID,$product_id))
return true;
exit;
}
else{
///guest, continue
}
}}});
But that gives only an internal server error.
Anyone?
First, instead of using wc_customer_bought_product(), you can use the following enhanced conditional function from this existing answer (based on the same source code than wc_customer_bought_product()). This function will handle your variable products case.
// Conditional function( stackOverFlow answer: https://stackoverflow.com/a/46217461/3730754 )
function has_bought_items( $user_var = 0, $product_ids = 0 ) {
global $wpdb;
// Based on user ID (registered users)
if ( is_numeric( $user_var) ) {
$meta_key = '_customer_user';
$meta_value = $user_var == 0 ? (int) get_current_user_id() : (int) $user_var;
}
// Based on billing email (Guest users)
else {
$meta_key = '_billing_email';
$meta_value = sanitize_email( $user_var );
}
$paid_statuses = array_map( 'esc_sql', wc_get_is_paid_statuses() );
$product_ids = is_array( $product_ids ) ? implode(',', $product_ids) : $product_ids;
$line_meta_value = $product_ids != ( 0 || '' ) ? 'AND woim.meta_value IN ('.$product_ids.')' : 'AND woim.meta_value != 0';
// Count the number of products
$count = $wpdb->get_var( "
SELECT COUNT(p.ID) FROM {$wpdb->prefix}posts AS p
INNER JOIN {$wpdb->prefix}postmeta AS pm ON p.ID = pm.post_id
INNER JOIN {$wpdb->prefix}woocommerce_order_items AS woi ON p.ID = woi.order_id
INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS woim ON woi.order_item_id = woim.order_item_id
WHERE p.post_status IN ( 'wc-" . implode( "','wc-", $paid_statuses ) . "' )
AND pm.meta_key = '$meta_key'
AND pm.meta_value = '$meta_value'
AND woim.meta_key IN ( '_product_id', '_variation_id' ) $line_meta_value
" );
// Return true if count is higher than 0 (or false)
return $count > 0 ? true : false;
}
You can't get yet the order or order items… Instead you will check cart items.
Then here is your revisited code that handle all kind of customers and variable products as asked:
add_action('woocommerce_checkout_process', 'wc_action_checkout_process' );
function wc_action_checkout_process() {
$user_id = get_current_user_id();
// 1. - For logged in users
if( $user_id > 0 ) {
$user_var = $user_id;
}
// 2. - For others
else {
if ( isset($_POST['billing_email']) && ! empty($_POST['billing_email']) ) {
$email = sanitize_email( $_POST['billing_email'] );
$user = get_user_by( 'email', $email );
// 2.a - When a user exist for the billing email return an error notice
if ( is_a($user, 'WP_User') ) {
wc_add_notice( sprintf( __('The email "%s" is already registered for "%s" user name. Please login.'), $email, $user->user_login ), 'error' );
}
// 2.b - User doesn't exits (real "guest")
else {
$user_var = $email;
}
}
}
// Loop through cart items to check products
foreach ( WC()->cart->get_cart() as $cart_item ) {
if ( has_bought_items( $user_var, $cart_item['product_id'] ) ) {
wc_add_notice( sprintf( __('You have already purchased "%s" product before. You can only purchase it once.'), $cart_item['data']->get_name() ), 'error' );
}
}
}
Code goes in functions.php file of your active child theme (or active theme). Tested and works.
I need to override Schema 'availability' option to Preorder for Woocommerce (3.9.2) products on backorder. Currently WC generates 'InStock' for them. Which filters and how do I apply? My current code (below) ruins Wordpress, seems like filter is wrong:
function tt_WC_change_schema_availability( $availability ) {
if ( is_product() && ! is_admin() ) {
$stock_status = $product->is_in_stock();
$output = 'OutOfStock'; // default, out of stock
if ($stock_status){
$output = 'InStock';
$qty = $product->get_stock_quantity();
if ( ! ($qty > 0) ) {
$output = 'PreOrder';
}
}
return 'http://schema.org/' . $output;
}
}
add_filter( 'woocommerce_structured_data_product_offer', 'tt_WC_change_schema_availability' );
It looks like you are close, but the $product object also has to be included in your function. It's the second parameter that can be passed to this filter. Also, you want to return part of the array that gets sent to the woocommerce_structured_data_product_offer filter. This would be $availability['availability'] since you're passing the variable as $availability
function tt_WC_change_schema_availability( $availability, $product ) {
if ( is_product() && ! is_admin() ) {
$stock_status = $product->get_stock_status();
$output = 'OutOfStock'; // default, out of stock
if ($stock_status){
$output = 'InStock';
$qty = $product->get_stock_quantity();
if ( ! ($qty > 0) ) {
$output = 'PreOrder';
}
}
return $availability['availability'] = 'http://schema.org/' . $output;
}
}
add_filter( 'woocommerce_structured_data_product_offer', 'tt_WC_change_schema_availability' , 10, 2 );
Many thanks to #Howard E! I've found out that $stock_status returns a string, also added extra check if product stock's managed. Here's working function:
function tt_WC_change_schema_availability( $markup_offer, $product ) {
if ( is_product() && ! is_admin() ) {
$managed = $product->managing_stock();
if ($managed === TRUE) {
$stock_status = $product->get_stock_status();
if ($stock_status == 'instock'){
$output = 'InStock';
}
if ($stock_status == 'onbackorder') {
$output = 'PreOrder';
}
if ($stock_status == 'outofstock') {
$output = 'OutOfStock';
}
}
$markup_offer['availability'] = 'http://schema.org/' . $output;
}
return $markup_offer;
}
add_filter( 'woocommerce_structured_data_product_offer', 'tt_WC_change_schema_availability' , 10, 2 );
Hi solution provided here WooCommerce - Check if item's are already in cart working perfect. This is the function code:
function woo_in_cart($arr_product_id) {
global $woocommerce;
$cartarray=array();
foreach($woocommerce->cart->get_cart() as $key => $val ) {
$_product = $val['product_id'];
array_push($cartarray,$_product);
}
if (!empty($cartarray)) {
$result = array_intersect($cartarray,$arr_product_id);
}
if (!empty($result)) {
return true;
} else {
return false;
};
}
Usage
$my_products_ids_array = array(22,23,465);
if (woo_in_cart($my_products_ids_array)) {
echo 'ohh yeah there some of that products in!';
}else {
echo 'no matching products :(';
}
But I need use as if(in_array) but no luck so far. What i doing wrong?
$my_products_ids_array = array("69286", "69287",);
if (in_array("69286", $my_products_ids_array)) {
echo '<p>' . the_field ( 'cart_field', 'option' ) . '</p>';
}
if (in_array("69287", $my_products_ids_array)) {
echo '<p>' . the_field ( 'cart_field-1', 'option' ) . '</p>';
}
Thank you
Your main function code is outdated.
For Advanced custom fields (ACF):
you need to use get_field() (that return the field value) instead of the_field() (that echo the field value).
You may need to add a product ID as 2nd argument in get_field('the_slug', $product_id ).
So try:
function is_in_cart( $ids ) {
// Initialise
$found = false;
// Loop Through cart items
foreach( WC()->cart->get_cart() as $cart_item ) {
// For an array of product IDS
if( is_array($ids) && ( in_array( $cart_item['product_id'], $ids ) || in_array( $cart_item['variation_id'], $ids ) ) ){
$found = true;
break;
}
// For a unique product ID (integer or string value)
elseif( ! is_array($ids) && ( $ids == $cart_item['product_id'] || $ids == $cart_item['variation_id'] ) ){
$found = true;
break;
}
}
return $found;
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
The custom conditional function is_in_cart( $ids ) accept a string (a unique product Id) or an array of product Ids.
Your revisited usage (ACF get_field may need a post ID (product ID)):
if ( is_in_cart( "69286" ) ) {
echo '<p>' . get_field ( 'cart_field' ) . '</p>'; // or get_field ( 'cart_field', "69286" )
}
if ( is_in_cart( "69287" ) ) {
echo '<p>' . get_field ( 'cart_field-1' ) . '</p>'; // or get_field ( 'cart_field', "69287" )
}
I am looking to make some modifications to a function in WooCommerce, on a file called class-wc-product-variation.php in woocommerce/includes/
The function I'm looking to modify is:
public function variation_is_visible() {
$visible = true;
// Published == enabled checkbox
if ( get_post_status( $this->variation_id ) != 'publish' ) {
$visible = false;
}
// Out of stock visibility
elseif ( get_option('woocommerce_hide_out_of_stock_items') == 'yes' && ! $this->is_in_stock() ) {
$visible = false;
}
// Price not set
elseif ( $this->get_price() === "" ) {
$visible = false;
}
return apply_filters( 'woocommerce_variation_is_visible', $visible, $this->variation_id, $this->id );
}
I need to add another elseif line in there like so:
elseif ( get_option('woocommerce_hide_out_of_stock_items') != 'yes' && ! $this->is_in_stock() ) {
$visible = false;
}
How do I do this without making changes to the core files?
You should never modify the core files in plugin. In the given function, there is filter woocommerce_variation_is_visible available. Use that filter to customize as per your need.
Code in core file is:
return apply_filters( 'woocommerce_variation_is_visible', $visible, $this->variation_id, $this->id );